"""统一日志模块""" import os import json import logging from datetime import datetime from typing import Optional, Any from functools import lru_cache from .trace import get_trace_id, get_tenant_id, get_user_id class PlatformLogger: """平台日志器 支持本地输出和远程上报两种模式 """ def __init__( self, app_code: str, platform_url: Optional[str] = None, api_key: Optional[str] = None, local_only: bool = True ): self.app_code = app_code self.platform_url = platform_url or os.getenv("PLATFORM_URL", "") self.api_key = api_key or os.getenv("PLATFORM_API_KEY", "") self.local_only = local_only or not self.platform_url # 设置本地日志 self._logger = logging.getLogger(app_code) self._logger.setLevel(logging.DEBUG) if not self._logger.handlers: handler = logging.StreamHandler() handler.setFormatter(logging.Formatter( '%(asctime)s | %(levelname)s | %(name)s | %(message)s' )) self._logger.addHandler(handler) def _format_message(self, message: str, **kwargs) -> str: """格式化日志消息,包含trace_id""" trace_id = get_trace_id() prefix = f"[{trace_id[:8]}] " if trace_id else "" if kwargs: extra = " | " + " ".join(f"{k}={v}" for k, v in kwargs.items()) else: extra = "" return f"{prefix}{message}{extra}" def _log( self, level: str, message: str, log_type: str = "app", category: Optional[str] = None, context: Optional[dict] = None, **kwargs ): """内部日志方法""" formatted = self._format_message(message, **kwargs) # 本地日志 log_method = getattr(self._logger, level.lower(), self._logger.info) log_method(formatted) # TODO: 远程上报(异步) if not self.local_only: self._send_to_platform(level, message, log_type, category, context, kwargs) def _send_to_platform( self, level: str, message: str, log_type: str, category: Optional[str], context: Optional[dict], extra: dict ): """发送日志到平台(异步,后续实现)""" # TODO: 使用httpx异步发送 pass def debug(self, message: str, **kwargs): """调试日志""" self._log("debug", message, **kwargs) def info(self, message: str, **kwargs): """信息日志""" self._log("info", message, **kwargs) def warn(self, message: str, **kwargs): """警告日志""" self._log("warn", message, **kwargs) def warning(self, message: str, **kwargs): """警告日志(别名)""" self.warn(message, **kwargs) def error(self, message: str, error: Optional[Exception] = None, **kwargs): """错误日志""" if error: kwargs["error_type"] = type(error).__name__ kwargs["error_msg"] = str(error) self._log("error", message, log_type="error", **kwargs) def audit(self, action: str, target_type: str, target_id: str, **kwargs): """审计日志""" self._log( "info", f"AUDIT: {action} {target_type}:{target_id}", log_type="audit", action=action, target_type=target_type, target_id=target_id, **kwargs ) @lru_cache() def get_logger(app_code: str) -> PlatformLogger: """获取日志器(单例)""" return PlatformLogger(app_code)