refactor(logging): replace structlog with stdlib logging compat layer
- 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
This commit is contained in:
70
backend/tests/logging_capture.py
Normal file
70
backend/tests/logging_capture.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user