diff --git a/docs/config_reference.rst b/docs/config_reference.rst index cda52f4f4d..5889308e1b 100644 --- a/docs/config_reference.rst +++ b/docs/config_reference.rst @@ -17,17 +17,19 @@ The syntax we use in the following to describe the different configuration objec .. |schemas/config.json| replace:: ``reframe/schemas/config.json`` .. _schemas/config.json: https://github.com/reframe-hpc/reframe/blob/master/reframe/schemas/config.json .. |access| replace:: :attr:`access` -.. _access: #.systems[].partitions[].access +.. _access: #systems-.partitions-.access .. |basedir| replace:: :attr:`basedir` -.. _basedir: #.logging[].handlers[].basedir +.. _basedir: #logging-.handlers-.basedir .. |datefmt| replace:: :attr:`datefmt` -.. _datefmt: #.logging[].handlers[].datefmt +.. _datefmt: #logging-.handlers-.datefmt .. |environments| replace:: :attr:`environments` -.. _environments: #.environments +.. _environments: #environments +.. |log_level| replace:: :attr:`level` +.. _log_level: #logging-.level .. |handler_name| replace:: :attr:`name` -.. _handler_name: #.logging[].handlers[].name +.. _handler_name: #logging-.handlers-.name .. |resources| replace:: :attr:`resources` -.. _resources: #.systems[].partitions[].resources +.. _resources: #systems-.partitions-.resources Top-level Configuration @@ -1480,6 +1482,21 @@ General Configuration .. versionadded:: 3.10.0 +.. js:attribute:: .general[].perf_info_level + + :required: No + :default: ``"info"`` + + The log level at which the immediate performance info will be printed. + + As soon as a performance test is finished, ReFrame will log its performance on the standard output immediately. + This option controls at which verbosity level this info will appear. + + For a list of available log levels, refer to logging configuration's |log_level|_. + + .. versionadded:: 4.0.0 + + .. js:attribute:: .general[].remote_detect :required: No diff --git a/docs/manpage.rst b/docs/manpage.rst index 537e6bf0bf..f4dc9bc74b 100644 --- a/docs/manpage.rst +++ b/docs/manpage.rst @@ -1366,6 +1366,19 @@ Here is an alphabetical list of the environment variables recognized by ReFrame: ================================== ================== +.. envvar:: RFM_PERF_INFO_LEVEL + + Logging level at which the immediate performance information is logged. + + .. table:: + :align: left + + ================================== ================== + Associated command line option n/a + Associated configuration parameter :js:attr:`perf_info_level` general configuration parameter + ================================== ================== + + .. envvar:: RFM_PERFLOG_DIR Directory prefix for logging performance data. diff --git a/reframe/core/logging.py b/reframe/core/logging.py index cfcb70f075..ed05763ccf 100644 --- a/reframe/core/logging.py +++ b/reframe/core/logging.py @@ -65,6 +65,10 @@ } +def level_from_str(name): + return _log_level_values[name] + + def _check_level(level): if isinstance(level, numbers.Integral): ret = level diff --git a/reframe/frontend/argparse.py b/reframe/frontend/argparse.py index 68d4894e65..55f77c51b4 100644 --- a/reframe/frontend/argparse.py +++ b/reframe/frontend/argparse.py @@ -7,6 +7,8 @@ import argparse import os +import reframe.utility.typecheck as typ + # # Notes on the ArgumentParser design # @@ -38,16 +40,6 @@ # arguments, without having to define a corresponding command line option. -def _convert_to_bool(s): - if s.lower() in ('true', 'yes', 'y', '1'): - return True - - if s.lower() in ('false', 'no', 'n', '0'): - return False - - raise ValueError - - class _Namespace: def __init__(self, namespace, option_map): self.__namespace = namespace @@ -94,15 +86,15 @@ def __getattr__(self, name): ret = ret.split(delim) elif action in ('store_true', 'store_false'): try: - ret = _convert_to_bool(ret) - except ValueError: + ret = typ.Bool(ret) + except TypeError: raise ValueError( f'environment variable {envvar!r} not a boolean' ) from None elif action == 'store' and arg_type != str: try: ret = arg_type(ret) - except ValueError as err: + except TypeError as err: raise ValueError( f'cannot convert environment variable {envvar!r} ' f'to {arg_type.__name__!r}' diff --git a/reframe/frontend/cli.py b/reframe/frontend/cli.py index fbb384e02d..e611b96123 100644 --- a/reframe/frontend/cli.py +++ b/reframe/frontend/cli.py @@ -597,6 +597,15 @@ def main(): action='store_true', help='Dump progress information for the async execution' ) + argparser.add_argument( + dest='perf_info_level', + envvar='RFM_PERF_INFO_LEVEL', + configvar='general/perf_info_level', + action='store', + type=typ.Str[r'critical|error|warning|info|verbose|' + r'debug|debug2|undefined'], + help='Log level at which immediate performance info will be printed' + ) argparser.add_argument( dest='pipeline_timeout', envvar='RFM_PIPELINE_TIMEOUT', diff --git a/reframe/frontend/executors/policies.py b/reframe/frontend/executors/policies.py index e0a5a7cbe8..89eec244bd 100644 --- a/reframe/frontend/executors/policies.py +++ b/reframe/frontend/executors/policies.py @@ -14,7 +14,7 @@ SkipTestError, TaskDependencyError, TaskExit) -from reframe.core.logging import getlogger +from reframe.core.logging import getlogger, level_from_str from reframe.core.pipeline import (CompileOnlyRegressionTest, RunOnlyRegressionTest) from reframe.frontend.executors import (ExecutionPolicy, RegressionTask, @@ -43,12 +43,14 @@ def _print_perf(task): '''Get performance info of the current task.''' perfvars = task.testcase.check.perfvalues + level = level_from_str( + rt.runtime().get_option('general/0/perf_info_level') + ) for key, info in perfvars.items(): name = key.split(':')[-1] - getlogger().info( - f'P: {name}: {info[0]} {info[4]} ' - f'(r:{info[1]}, l:{info[2]}, u:{info[3]})' - ) + getlogger().log(level, + f'P: {name}: {info[0]} {info[4]} ' + f'(r:{info[1]}, l:{info[2]}, u:{info[3]})') class _PollController: diff --git a/reframe/schemas/config.json b/reframe/schemas/config.json index 697004ebbb..d9108b36bb 100644 --- a/reframe/schemas/config.json +++ b/reframe/schemas/config.json @@ -483,6 +483,7 @@ }, "non_default_craype": {"type": "boolean"}, "dump_pipeline_progress": {"type": "boolean"}, + "perf_info_level": {"$ref": "#/defs/loglevel"}, "pipeline_timeout": {"type": ["number", "null"]}, "purge_environment": {"type": "boolean"}, "remote_detect": {"type": "boolean"}, @@ -532,6 +533,7 @@ "general/module_map_file": "", "general/module_mappings": [], "general/non_default_craype": false, + "general/perf_info_level": "info", "general/purge_environment": false, "general/remote_detect": false, "general/remote_workdir": ".", diff --git a/reframe/utility/typecheck.py b/reframe/utility/typecheck.py index 945fbf5305..60bf3e0716 100644 --- a/reframe/utility/typecheck.py +++ b/reframe/utility/typecheck.py @@ -339,6 +339,9 @@ def __getitem__(cls, patt): return ret def __rfm_cast_str__(cls, s): + if not isinstance(s, cls): + raise TypeError(f'cannot convert string {s!r} to {cls.__name__!r}') + return s