Spaces:
Sleeping
Sleeping
| """Structured JSON logger for Godspeed backend. | |
| Usage: | |
| from src.utils.logger import get_logger | |
| logger = get_logger(__name__) | |
| logger.info("graph_ingest_done", ingested=42, duration_ms=1230) | |
| Every log line is a JSON object with at minimum: | |
| time, level, logger, request_id (from ContextVar), message, **extra_fields | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import logging | |
| import time | |
| from contextvars import ContextVar | |
| from typing import Any | |
| # Set per-request by RequestLoggingMiddleware | |
| request_id_var: ContextVar[str] = ContextVar("request_id", default="-") | |
| class _JsonFormatter(logging.Formatter): | |
| def format(self, record: logging.LogRecord) -> str: | |
| log: dict[str, Any] = { | |
| "time": self.formatTime(record, "%Y-%m-%dT%H:%M:%S"), | |
| "level": record.levelname, | |
| "logger": record.name, | |
| "request_id": request_id_var.get(), | |
| "message": record.getMessage(), | |
| } | |
| # Extra structured fields attached via logger.info("msg", extra={...}) | |
| for key, val in record.__dict__.items(): | |
| if key not in { | |
| "name", "msg", "args", "levelname", "levelno", "pathname", | |
| "filename", "module", "exc_info", "exc_text", "stack_info", | |
| "lineno", "funcName", "created", "msecs", "relativeCreated", | |
| "thread", "threadName", "processName", "process", "message", | |
| "taskName", | |
| }: | |
| log[key] = val | |
| if record.exc_info: | |
| log["exc"] = self.formatException(record.exc_info) | |
| return json.dumps(log, default=str) | |
| def _configure_root() -> None: | |
| root = logging.getLogger() | |
| if root.handlers: | |
| return | |
| handler = logging.StreamHandler() | |
| handler.setFormatter(_JsonFormatter()) | |
| root.addHandler(handler) | |
| root.setLevel(logging.INFO) | |
| _configure_root() | |
| def get_logger(name: str) -> logging.Logger: | |
| return logging.getLogger(name) | |
| class Timer: | |
| """Context manager that measures elapsed ms.""" | |
| def __init__(self) -> None: | |
| self._start = 0.0 | |
| def __enter__(self) -> "Timer": | |
| self._start = time.perf_counter() | |
| return self | |
| def __exit__(self, *_: Any) -> None: | |
| pass | |
| def ms(self) -> int: | |
| return int((time.perf_counter() - self._start) * 1000) | |