CoolFace
Apppublic

lamhieu/docsifer

sourceHugging Facemitupdated 4mo agoView on Hugging Face
13likes
logging_config.py121 linesDownload Raw Back to docsifer
1"""Logging setup with optional structured JSON output and request-id binding.2 3Call :func:`configure_logging` exactly once at application start (inside the4FastAPI lifespan).5"""6 7from __future__ import annotations8 9import json10import logging11import logging.config12import sys13from contextvars import ContextVar14from typing import Any15 16#: Context variable carrying the current request id for the active task.17request_id_var: ContextVar[str | None] = ContextVar("request_id", default=None)18 19 20class RequestIdFilter(logging.Filter):21    """Inject the current request id (if any) into every log record."""22 23    def filter(self, record: logging.LogRecord) -> bool:24        record.request_id = request_id_var.get() or "-"25        return True26 27 28class JsonFormatter(logging.Formatter):29    """Minimal JSON formatter producing a single line per record."""30 31    _RESERVED = {32        "args",33        "asctime",34        "created",35        "exc_info",36        "exc_text",37        "filename",38        "funcName",39        "levelname",40        "levelno",41        "lineno",42        "module",43        "msecs",44        "message",45        "msg",46        "name",47        "pathname",48        "process",49        "processName",50        "relativeCreated",51        "stack_info",52        "thread",53        "threadName",54    }55 56    def format(self, record: logging.LogRecord) -> str:57        payload: dict[str, Any] = {58            "ts": self.formatTime(record, "%Y-%m-%dT%H:%M:%S%z"),59            "level": record.levelname,60            "logger": record.name,61            "msg": record.getMessage(),62            "request_id": getattr(record, "request_id", "-"),63        }64        if record.exc_info:65            payload["exc"] = self.formatException(record.exc_info)66        # Allow callers to attach extra fields via ``logger.info("...", extra={...})``.67        for key, value in record.__dict__.items():68            if key in self._RESERVED or key.startswith("_") or key == "request_id":69                continue70            try:71                json.dumps(value)72                payload[key] = value73            except (TypeError, ValueError):74                payload[key] = repr(value)75        return json.dumps(payload, ensure_ascii=False, default=str)76 77 78def configure_logging(level: str = "INFO", json_output: bool = True) -> None:79    """Configure the root logger and library loggers in one place.80 81    Args:82        level: Log level name (``DEBUG``/``INFO``/...).83        json_output: When ``True`` emit one JSON object per line; otherwise use84            a human-readable format (useful in local development).85    """86    formatter: dict[str, Any]87    if json_output:88        formatter = {"()": "docsifer.logging_config.JsonFormatter"}89    else:90        formatter = {91            "format": "%(asctime)s %(levelname)-8s %(name)s [%(request_id)s] %(message)s",92            "datefmt": "%H:%M:%S",93        }94 95    config: dict[str, Any] = {96        "version": 1,97        "disable_existing_loggers": False,98        "filters": {99            "request_id": {"()": "docsifer.logging_config.RequestIdFilter"},100        },101        "formatters": {"default": formatter},102        "handlers": {103            "default": {104                "class": "logging.StreamHandler",105                "stream": sys.stdout,106                "formatter": "default",107                "filters": ["request_id"],108            }109        },110        "loggers": {111            "": {"handlers": ["default"], "level": level, "propagate": False},112            "uvicorn": {"handlers": ["default"], "level": level, "propagate": False},113            "uvicorn.error": {"handlers": ["default"], "level": level, "propagate": False},114            "uvicorn.access": {"handlers": ["default"], "level": level, "propagate": False},115            "gunicorn.error": {"handlers": ["default"], "level": level, "propagate": False},116            "gunicorn.access": {"handlers": ["default"], "level": level, "propagate": False},117        },118    }119 120    logging.config.dictConfig(config)121