rpx_benchmark.logging_utils
Structured logging for the RPX benchmark toolkit.
Library modules do not configure logging themselves. They only call
get_logger() at module top::
from rpx_benchmark.logging_utils import get_logger
log = get_logger(__name__)
def do_the_thing():
log.info("starting the thing")
log.debug("parameter x=%s", x)
The CLI entrypoint (and any application code driving the toolkit)
calls configure_logging() exactly once to attach a handler. If
rich is installed, a pretty rich.logging.RichHandler is
used so library log lines render with colour, level icons, and path
markers alongside the benchmark progress UI. Otherwise we fall back to
the standard library handler.
The logger hierarchy mirrors the package structure
(rpx_benchmark, rpx_benchmark.hub, rpx_benchmark.runner...)
so power users can turn individual modules up or down with one call::
import logging
logging.getLogger("rpx_benchmark.hub").setLevel(logging.DEBUG)
Log level conventions
DEBUG— internal state useful for bug reports. Off by default.INFO— user-facing progress ("downloading split", "loaded model").WARNING— degradations the user should know about but that do not abort the run ("CUDA unavailable; falling back to CPU", "profiler unavailable; FLOPs not reported").ERROR— recoverable failures that abort one operation.CRITICAL— reserved for unrecoverable state.
1"""Structured logging for the RPX benchmark toolkit. 2 3Library modules do **not** configure logging themselves. They only call 4:func:`get_logger` at module top:: 5 6 from rpx_benchmark.logging_utils import get_logger 7 8 log = get_logger(__name__) 9 10 def do_the_thing(): 11 log.info("starting the thing") 12 log.debug("parameter x=%s", x) 13 14The CLI entrypoint (and any application code driving the toolkit) 15calls :func:`configure_logging` exactly once to attach a handler. If 16``rich`` is installed, a pretty :class:`rich.logging.RichHandler` is 17used so library log lines render with colour, level icons, and path 18markers alongside the benchmark progress UI. Otherwise we fall back to 19the standard library handler. 20 21The logger hierarchy mirrors the package structure 22(``rpx_benchmark``, ``rpx_benchmark.hub``, ``rpx_benchmark.runner``...) 23so power users can turn individual modules up or down with one call:: 24 25 import logging 26 logging.getLogger("rpx_benchmark.hub").setLevel(logging.DEBUG) 27 28Log level conventions 29--------------------- 30 31- ``DEBUG`` — internal state useful for bug reports. Off by default. 32- ``INFO`` — user-facing progress ("downloading split", "loaded model"). 33- ``WARNING`` — degradations the user should know about but that do 34 not abort the run ("CUDA unavailable; falling back to CPU", 35 "profiler unavailable; FLOPs not reported"). 36- ``ERROR`` — recoverable failures that abort one operation. 37- ``CRITICAL`` — reserved for unrecoverable state. 38""" 39 40from __future__ import annotations 41 42import logging 43import os 44import sys 45from typing import Optional 46 47_ROOT_LOGGER_NAME = "rpx_benchmark" 48_CONFIGURED = False 49 50 51# --------------------------------------------------------------------------- # 52# Public API 53# --------------------------------------------------------------------------- # 54 55 56def get_logger(name: str) -> logging.Logger: 57 """Return a module-scoped logger nested under the ``rpx_benchmark`` root. 58 59 Parameters 60 ---------- 61 name : str 62 Usually ``__name__``. If it starts with ``rpx_benchmark`` it is 63 returned as-is; otherwise it is attached as a child of the root 64 logger so external integrations can still funnel output. 65 66 Returns 67 ------- 68 logging.Logger 69 A logger ready to use. Call ``log.info(...)`` etc. 70 71 Examples 72 -------- 73 >>> from rpx_benchmark.logging_utils import get_logger 74 >>> log = get_logger("rpx_benchmark.hub") 75 >>> log.name 76 'rpx_benchmark.hub' 77 """ 78 if not name: 79 name = _ROOT_LOGGER_NAME 80 if name == _ROOT_LOGGER_NAME or name.startswith(_ROOT_LOGGER_NAME + "."): 81 return logging.getLogger(name) 82 return logging.getLogger(f"{_ROOT_LOGGER_NAME}.{name}") 83 84 85def configure_logging( 86 level: str | int = "INFO", 87 *, 88 force: bool = False, 89 use_rich: Optional[bool] = None, 90) -> logging.Logger: 91 """Install a handler on the root ``rpx_benchmark`` logger. 92 93 Safe to call multiple times: subsequent calls are no-ops unless 94 ``force=True``. The CLI calls this once at the start of ``main``; 95 library users can call it from their own entrypoint. 96 97 Parameters 98 ---------- 99 level : str or int 100 Logging level name (``"DEBUG"``, ``"INFO"``, ``"WARNING"``, 101 ``"ERROR"``) or numeric level. Can be overridden at runtime 102 via the ``RPX_LOG_LEVEL`` environment variable. 103 force : bool 104 If True, re-install the handler even if one is already 105 present. Use with care — multiple handlers cause duplicate 106 output. 107 use_rich : bool, optional 108 Force rich or plain handler selection. When omitted, auto- 109 detects: rich if the ``rich`` package is importable and stderr 110 is a TTY, otherwise plain. 111 112 Returns 113 ------- 114 logging.Logger 115 The configured root logger, for chaining. 116 117 Examples 118 -------- 119 >>> from rpx_benchmark.logging_utils import configure_logging 120 >>> log = configure_logging("DEBUG") # doctest: +SKIP 121 >>> log.info("benchmark starting") # doctest: +SKIP 122 """ 123 global _CONFIGURED 124 125 env_level = os.environ.get("RPX_LOG_LEVEL") 126 if env_level: 127 level = env_level 128 129 if isinstance(level, str): 130 level_value = logging.getLevelName(level.upper()) 131 if not isinstance(level_value, int): 132 # Unknown name -> default to INFO with a warning emitted 133 # the first time. 134 level_value = logging.INFO 135 else: 136 level_value = int(level) 137 138 root = logging.getLogger(_ROOT_LOGGER_NAME) 139 root.setLevel(level_value) 140 root.propagate = False 141 142 if _CONFIGURED and not force: 143 return root 144 145 # Wipe any prior handlers so re-configuration is idempotent. 146 for h in list(root.handlers): 147 root.removeHandler(h) 148 149 handler = _build_handler(level_value, use_rich=use_rich) 150 root.addHandler(handler) 151 _CONFIGURED = True 152 return root 153 154 155def set_level(level: str | int) -> None: 156 """Shortcut for ``logging.getLogger('rpx_benchmark').setLevel(level)``. 157 158 Useful for quick toggles without re-running :func:`configure_logging`. 159 """ 160 if isinstance(level, str): 161 level = logging.getLevelName(level.upper()) 162 logging.getLogger(_ROOT_LOGGER_NAME).setLevel(level) 163 164 165# --------------------------------------------------------------------------- # 166# Internal handler factory 167# --------------------------------------------------------------------------- # 168 169 170def _rich_available() -> bool: 171 try: 172 import rich # noqa: F401 173 174 return True 175 except ImportError: 176 return False 177 178 179def _build_handler( 180 level: int, 181 *, 182 use_rich: Optional[bool], 183) -> logging.Handler: 184 """Build a log handler, preferring rich if available and stderr is a tty.""" 185 if use_rich is None: 186 use_rich = _rich_available() and sys.stderr.isatty() 187 188 if use_rich: 189 try: 190 from rich.logging import RichHandler 191 192 handler: logging.Handler = RichHandler( 193 level=level, 194 show_time=False, 195 show_path=False, 196 rich_tracebacks=True, 197 markup=False, 198 ) 199 handler.setFormatter(logging.Formatter("%(message)s")) 200 return handler 201 except ImportError: 202 pass # fall through to plain 203 204 handler = logging.StreamHandler(sys.stderr) 205 handler.setLevel(level) 206 handler.setFormatter( 207 logging.Formatter( 208 fmt="[%(levelname)s] %(name)s: %(message)s", 209 ) 210 ) 211 return handler 212 213 214__all__ = [ 215 "get_logger", 216 "configure_logging", 217 "set_level", 218]
57def get_logger(name: str) -> logging.Logger: 58 """Return a module-scoped logger nested under the ``rpx_benchmark`` root. 59 60 Parameters 61 ---------- 62 name : str 63 Usually ``__name__``. If it starts with ``rpx_benchmark`` it is 64 returned as-is; otherwise it is attached as a child of the root 65 logger so external integrations can still funnel output. 66 67 Returns 68 ------- 69 logging.Logger 70 A logger ready to use. Call ``log.info(...)`` etc. 71 72 Examples 73 -------- 74 >>> from rpx_benchmark.logging_utils import get_logger 75 >>> log = get_logger("rpx_benchmark.hub") 76 >>> log.name 77 'rpx_benchmark.hub' 78 """ 79 if not name: 80 name = _ROOT_LOGGER_NAME 81 if name == _ROOT_LOGGER_NAME or name.startswith(_ROOT_LOGGER_NAME + "."): 82 return logging.getLogger(name) 83 return logging.getLogger(f"{_ROOT_LOGGER_NAME}.{name}")
Return a module-scoped logger nested under the rpx_benchmark root.
Parameters
name : str
Usually __name__. If it starts with rpx_benchmark it is
returned as-is; otherwise it is attached as a child of the root
logger so external integrations can still funnel output.
Returns
logging.Logger
A logger ready to use. Call log.info(...) etc.
Examples
>>> from rpx_benchmark.logging_utils import get_logger
>>> log = get_logger("rpx_benchmark.hub")
>>> log.name
'rpx_benchmark.hub'
86def configure_logging( 87 level: str | int = "INFO", 88 *, 89 force: bool = False, 90 use_rich: Optional[bool] = None, 91) -> logging.Logger: 92 """Install a handler on the root ``rpx_benchmark`` logger. 93 94 Safe to call multiple times: subsequent calls are no-ops unless 95 ``force=True``. The CLI calls this once at the start of ``main``; 96 library users can call it from their own entrypoint. 97 98 Parameters 99 ---------- 100 level : str or int 101 Logging level name (``"DEBUG"``, ``"INFO"``, ``"WARNING"``, 102 ``"ERROR"``) or numeric level. Can be overridden at runtime 103 via the ``RPX_LOG_LEVEL`` environment variable. 104 force : bool 105 If True, re-install the handler even if one is already 106 present. Use with care — multiple handlers cause duplicate 107 output. 108 use_rich : bool, optional 109 Force rich or plain handler selection. When omitted, auto- 110 detects: rich if the ``rich`` package is importable and stderr 111 is a TTY, otherwise plain. 112 113 Returns 114 ------- 115 logging.Logger 116 The configured root logger, for chaining. 117 118 Examples 119 -------- 120 >>> from rpx_benchmark.logging_utils import configure_logging 121 >>> log = configure_logging("DEBUG") # doctest: +SKIP 122 >>> log.info("benchmark starting") # doctest: +SKIP 123 """ 124 global _CONFIGURED 125 126 env_level = os.environ.get("RPX_LOG_LEVEL") 127 if env_level: 128 level = env_level 129 130 if isinstance(level, str): 131 level_value = logging.getLevelName(level.upper()) 132 if not isinstance(level_value, int): 133 # Unknown name -> default to INFO with a warning emitted 134 # the first time. 135 level_value = logging.INFO 136 else: 137 level_value = int(level) 138 139 root = logging.getLogger(_ROOT_LOGGER_NAME) 140 root.setLevel(level_value) 141 root.propagate = False 142 143 if _CONFIGURED and not force: 144 return root 145 146 # Wipe any prior handlers so re-configuration is idempotent. 147 for h in list(root.handlers): 148 root.removeHandler(h) 149 150 handler = _build_handler(level_value, use_rich=use_rich) 151 root.addHandler(handler) 152 _CONFIGURED = True 153 return root
Install a handler on the root rpx_benchmark logger.
Safe to call multiple times: subsequent calls are no-ops unless
force=True. The CLI calls this once at the start of main;
library users can call it from their own entrypoint.
Parameters
level : str or int
Logging level name ("DEBUG", "INFO", "WARNING",
"ERROR") or numeric level. Can be overridden at runtime
via the RPX_LOG_LEVEL environment variable.
force : bool
If True, re-install the handler even if one is already
present. Use with care — multiple handlers cause duplicate
output.
use_rich : bool, optional
Force rich or plain handler selection. When omitted, auto-
detects: rich if the rich package is importable and stderr
is a TTY, otherwise plain.
Returns
logging.Logger The configured root logger, for chaining.
Examples
>>> from rpx_benchmark.logging_utils import configure_logging
>>> log = configure_logging("DEBUG") # doctest: +SKIP
>>> log.info("benchmark starting") # doctest: +SKIP
156def set_level(level: str | int) -> None: 157 """Shortcut for ``logging.getLogger('rpx_benchmark').setLevel(level)``. 158 159 Useful for quick toggles without re-running :func:`configure_logging`. 160 """ 161 if isinstance(level, str): 162 level = logging.getLevelName(level.upper()) 163 logging.getLogger(_ROOT_LOGGER_NAME).setLevel(level)
Shortcut for logging.getLogger('rpx_benchmark').setLevel(level).
Useful for quick toggles without re-running configure_logging().