# backend/app/observability/logging.py — 구조화 JSON 로깅 (request_id·user_id 컨텍스트) import json import logging from contextvars import ContextVar request_id_ctx: ContextVar[str] = ContextVar("request_id", default="-") user_id_ctx: ContextVar[str] = ContextVar("user_id", default="-") class JsonFormatter(logging.Formatter): def format(self, record: logging.LogRecord) -> str: payload = { "level": record.levelname, "logger": record.name, "msg": record.getMessage(), "request_id": request_id_ctx.get(), "user_id": user_id_ctx.get(), } for k in ("method", "path", "status", "dur_ms", "event"): v = getattr(record, k, None) if v is not None: payload[k] = v if record.exc_info: payload["exc"] = self.formatException(record.exc_info) return json.dumps(payload, ensure_ascii=False) _configured = False def configure_logging(level: str = "INFO") -> None: global _configured if _configured: return handler = logging.StreamHandler() handler.setFormatter(JsonFormatter()) root = logging.getLogger() root.handlers = [handler] root.setLevel(level.upper()) _configured = True def get_logger(name: str = "ari") -> logging.Logger: return logging.getLogger(name)