""" 结构化日志配置 使用 structlog 实现结构化、可观测的日志,便于生产环境排查与聚合。 """ import logging import sys import json from typing import Any # 尝试使用 structlog,若未安装则回退到标准 logging try: import structlog USE_STRUCTLOG = True except ImportError: USE_STRUCTLOG = False def configure_logging(debug: bool = False) -> None: """ 配置应用日志 Args: debug: 是否为调试模式 """ log_level = logging.DEBUG if debug else logging.INFO if USE_STRUCTLOG: structlog.configure( processors=[ structlog.stdlib.filter_by_level, structlog.stdlib.add_logger_name, structlog.stdlib.add_log_level, structlog.stdlib.PositionalArgumentsFormatter(), structlog.processors.TimeStamper(fmt="iso"), structlog.processors.StackInfoRenderer(), structlog.processors.format_exc_info, structlog.processors.UnicodeDecoder(), structlog.dev.ConsoleRenderer() if debug else structlog.processors.JSONRenderer(), ], wrapper_class=structlog.stdlib.BoundLogger, context_class=dict, logger_factory=structlog.stdlib.LoggerFactory(), cache_logger_on_first_use=True, ) else: logging.basicConfig( level=log_level, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", stream=sys.stdout, ) class _StdLoggerCompat: """兼容 structlog 风格 kwargs 的标准 logging 适配器。""" _PASSTHROUGH_KEYS = {"exc_info", "stack_info", "stacklevel", "extra"} def __init__(self, logger: logging.Logger): self._logger = logger def _split_kwargs(self, kwargs: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]: passthrough: dict[str, Any] = {} fields: dict[str, Any] = {} for key, value in kwargs.items(): if key in self._PASSTHROUGH_KEYS: passthrough[key] = value else: fields[key] = value return passthrough, fields @staticmethod def _format_fields(fields: dict[str, Any]) -> str: try: return json.dumps(fields, ensure_ascii=False, default=str) except Exception: return str(fields) def _log(self, method: str, msg: Any, *args: Any, **kwargs: Any) -> None: passthrough, fields = self._split_kwargs(kwargs) text = str(msg) if fields: text = f"{text} | {self._format_fields(fields)}" getattr(self._logger, method)(text, *args, **passthrough) def debug(self, msg: Any, *args: Any, **kwargs: Any) -> None: self._log("debug", msg, *args, **kwargs) def info(self, msg: Any, *args: Any, **kwargs: Any) -> None: self._log("info", msg, *args, **kwargs) def warning(self, msg: Any, *args: Any, **kwargs: Any) -> None: self._log("warning", msg, *args, **kwargs) def error(self, msg: Any, *args: Any, **kwargs: Any) -> None: self._log("error", msg, *args, **kwargs) def exception(self, msg: Any, *args: Any, **kwargs: Any) -> None: kwargs.setdefault("exc_info", True) self._log("error", msg, *args, **kwargs) def critical(self, msg: Any, *args: Any, **kwargs: Any) -> None: self._log("critical", msg, *args, **kwargs) def __getattr__(self, item: str) -> Any: return getattr(self._logger, item) def get_logger(name: str): """获取 logger 实例""" if USE_STRUCTLOG: return structlog.get_logger(name) return _StdLoggerCompat(logging.getLogger(name))