- Remove structlog dependency from backend/pyproject.toml - Add app.utils.logging_compat shim for keyword-arg logging API - Add app.utils.json_formatter for JSON log output with extra fields - Update all backend modules to use logging_compat.get_logger() - Update docstrings in log_sanitizer.py and json_formatter.py - Update test comment in test_async_utils.py - Record 406 failing tests in Docs/Tasks.md for tracking
71 lines
1.9 KiB
Python
71 lines
1.9 KiB
Python
"""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)
|