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]
def get_logger(name: str) -> logging.Logger:
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'
def configure_logging( level: str | int = 'INFO', *, force: bool = False, use_rich: Optional[bool] = None) -> logging.Logger:
 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
def set_level(level: str | int) -> None:
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().