""" Structured JSON logging configuration. Each log line is a single JSON object: {"ts":"2026-02-21T21:34:36","level":"INFO","logger":"app.burnin","msg":"...","job_id":1} Extra context fields (job_id, drive_id, devname, stage) are included when passed via the logging `extra=` kwarg. """ import json import logging import traceback from app.config import settings # Standard LogRecord attributes to exclude from the "extra" dump _STDLIB_ATTRS = frozenset(logging.LogRecord("", 0, "", 0, "", (), None).__dict__) class _JsonFormatter(logging.Formatter): def format(self, record: logging.LogRecord) -> str: data: dict = { "ts": self.formatTime(record, "%Y-%m-%dT%H:%M:%S"), "level": record.levelname, "logger": record.name, "msg": record.getMessage(), } # Include any non-standard fields passed via extra={} for key, val in record.__dict__.items(): if key not in _STDLIB_ATTRS and not key.startswith("_"): data[key] = val if record.exc_info: data["exc"] = "".join(traceback.format_exception(*record.exc_info)).strip() return json.dumps(data) def configure() -> None: handler = logging.StreamHandler() handler.setFormatter(_JsonFormatter()) level = getattr(logging, settings.log_level.upper(), logging.INFO) root = logging.getLogger() root.setLevel(level) root.handlers = [handler] # Quiet chatty third-party loggers logging.getLogger("httpx").setLevel(logging.WARNING) logging.getLogger("httpcore").setLevel(logging.WARNING) logging.getLogger("uvicorn.access").setLevel(logging.WARNING)