Spaces:
Sleeping
Sleeping
| """Structured logging with correlation IDs.""" | |
| from __future__ import annotations | |
| import logging | |
| import sys | |
| import uuid | |
| from contextvars import ContextVar | |
| import structlog | |
| correlation_id_var: ContextVar[str] = ContextVar("correlation_id", default="") | |
| def get_correlation_id() -> str: | |
| cid = correlation_id_var.get() | |
| if not cid: | |
| cid = uuid.uuid4().hex[:16] | |
| correlation_id_var.set(cid) | |
| return cid | |
| def add_correlation_id( | |
| logger: structlog.types.WrappedLogger, | |
| method_name: str, | |
| event_dict: dict, | |
| ) -> dict: | |
| event_dict["correlation_id"] = get_correlation_id() | |
| return event_dict | |
| def setup_logging(log_level: str = "INFO", log_format: str = "json") -> None: | |
| shared_processors: list = [ | |
| structlog.contextvars.merge_contextvars, | |
| add_correlation_id, | |
| structlog.stdlib.add_log_level, | |
| structlog.stdlib.add_logger_name, | |
| structlog.processors.TimeStamper(fmt="iso"), | |
| structlog.processors.StackInfoRenderer(), | |
| structlog.processors.UnicodeDecoder(), | |
| ] | |
| if log_format == "json": | |
| renderer = structlog.processors.JSONRenderer() | |
| else: | |
| renderer = structlog.dev.ConsoleRenderer() | |
| structlog.configure( | |
| processors=[ | |
| *shared_processors, | |
| structlog.stdlib.ProcessorFormatter.wrap_for_formatter, | |
| ], | |
| logger_factory=structlog.stdlib.LoggerFactory(), | |
| wrapper_class=structlog.stdlib.BoundLogger, | |
| cache_logger_on_first_use=True, | |
| ) | |
| formatter = structlog.stdlib.ProcessorFormatter( | |
| processors=[ | |
| structlog.stdlib.ProcessorFormatter.remove_processors_meta, | |
| renderer, | |
| ], | |
| foreign_pre_chain=shared_processors, | |
| ) | |
| handler = logging.StreamHandler(sys.stdout) | |
| handler.setFormatter(formatter) | |
| root = logging.getLogger() | |
| root.handlers.clear() | |
| root.addHandler(handler) | |
| root.setLevel(getattr(logging, log_level.upper(), logging.INFO)) | |
| for name in ("uvicorn", "uvicorn.access", "uvicorn.error"): | |
| lg = logging.getLogger(name) | |
| lg.handlers.clear() | |
| lg.propagate = True | |
| def get_logger(name: str | None = None) -> structlog.stdlib.BoundLogger: | |
| return structlog.get_logger(name) | |