Source code for qrobot.logger.logger
"""Logging helpers for the quantum-robot library.
Applications configure handlers, destinations, and verbosity. quantum-robot only
returns named loggers and installs a ``NullHandler`` on its package logger so
library use never changes the caller's global logging configuration.
"""
import logging
import sys
from dataclasses import dataclass
from pathlib import Path
_PACKAGE_LOGGER_NAME = "qrobot"
logging.getLogger(_PACKAGE_LOGGER_NAME).addHandler(logging.NullHandler())
[docs]
@dataclass(frozen=True)
class LoggingConfig:
"""Optional application-owned logging configuration for quantum-robot.
Parameters
----------
level : int
Standard-library logging level. Defaults to ``logging.INFO``.
file_path : Path | None
Optional destination for a rotating debug log. No file is created when
omitted.
console : bool
Whether to emit records to standard output. Defaults to ``True``.
"""
level: int = logging.INFO
file_path: Path | None = None
console: bool = True
[docs]
def get_logger(logger_name: str) -> logging.Logger:
"""Return a qrobot-namespaced logger without configuring any handlers."""
return logging.getLogger(f"{_PACKAGE_LOGGER_NAME}.{logger_name}")
[docs]
def configure_logging(config: LoggingConfig) -> logging.Logger:
"""Configure quantum-robot logging explicitly for an application.
Only handlers previously installed by this function are replaced. This
keeps repeated setup calls idempotent without changing unrelated logging
configuration owned by the application.
"""
logger = logging.getLogger(_PACKAGE_LOGGER_NAME)
logger.setLevel(config.level)
logger.propagate = False
for handler in logger.handlers[:]:
if getattr(handler, "_qrobot_managed", False):
logger.removeHandler(handler)
handler.close()
formatter = logging.Formatter("%(asctime)s — %(name)s — %(levelname)s — %(message)s")
if config.console:
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(formatter)
console_handler.setLevel(config.level)
console_handler._qrobot_managed = True # type: ignore[attr-defined]
logger.addHandler(console_handler)
if config.file_path is not None:
config.file_path.parent.mkdir(parents=True, exist_ok=True)
file_handler = logging.FileHandler(config.file_path)
file_handler.setFormatter(formatter)
file_handler.setLevel(config.level)
file_handler._qrobot_managed = True # type: ignore[attr-defined]
logger.addHandler(file_handler)
return logger