"""Test utilities for capturing stdlib log records.""" from __future__ import annotations import logging from collections.abc import Generator from contextlib import contextmanager from typing import Any class _CaptureHandler(logging.Handler): """Handler that stores every emitted record as a dict.""" def __init__(self) -> None: super().__init__() self.records: list[dict[str, Any]] = [] def emit(self, record: logging.LogRecord) -> None: entry: dict[str, Any] = { "event": record.getMessage(), "level": record.levelname.lower(), "logger": record.name, } # Merge extra fields attached to the record. std_attrs = { "name", "msg", "args", "levelname", "levelno", "pathname", "filename", "module", "exc_info", "exc_text", "stack_info", "lineno", "funcName", "created", "msecs", "relativeCreated", "thread", "threadName", "processName", "process", "message", "asctime", "taskName", } for key, value in record.__dict__.items(): if key not in std_attrs: entry[key] = value self.records.append(entry) @contextmanager def capture_logs() -> Generator[list[dict[str, Any]], None, None]: """Capture all log records emitted inside the context. Yields a list of dicts, each representing a log entry with keys ``event``, ``level``, ``logger`` and any extra fields. """ handler = _CaptureHandler() handler.setLevel(logging.DEBUG) root = logging.getLogger() root.addHandler(handler) try: yield handler.records finally: root.removeHandler(handler)