import structlog import logging import sys import json from datetime import datetime from typing import Any, Dict, Optional from pathlib import Path import colorama from colorama import Fore, Back, Style # 尝试导入pythonjsonlogger,如果没有安装则使用备用方案 try: from pythonjsonlogger import jsonlogger HAS_JSON_LOGGER = True except ImportError: HAS_JSON_LOGGER = False jsonlogger = None # 初始化colorama colorama.init() class ColoredConsoleRenderer: """带颜色的控制台日志渲染器""" def __call__(self, logger, method_name: str, event_dict: Dict[str, Any]) -> str: """渲染日志事件""" timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") level = event_dict.get("level", "INFO").upper() message = event_dict.get("event", "") # 根据日志级别选择颜色 level_colors = { "DEBUG": Fore.CYAN, "INFO": Fore.GREEN, "WARNING": Fore.YELLOW, "ERROR": Fore.RED, "CRITICAL": Fore.RED + Back.WHITE + Style.BRIGHT, } color = level_colors.get(level, "") reset = Style.RESET_ALL # 基础信息 log_line = f"{color}[{timestamp}] {level}{reset} {message}" # 添加额外的上下文信息 if "request_id" in event_dict: log_line += f" {Fore.BLUE}[req:{event_dict['request_id']}]{reset}" if "function" in event_dict: log_line += f" {Fore.MAGIC}{event_dict['function']}(){reset}" # 添加其他字段 for key, value in event_dict.items(): if key not in ["level", "event", "timestamp", "request_id", "function"]: log_line += f" {Fore.CYAN}{key}={value}{reset}" return log_line class JSONRenderer: """JSON格式的日志渲染器""" def __call__(self, logger, method_name: str, event_dict: Dict[str, Any]) -> str: """渲染日志事件为JSON格式""" log_data = { "timestamp": datetime.now().isoformat(), "level": event_dict.get("level", "INFO"), "message": event_dict.get("event", ""), } # 添加其他字段 for key, value in event_dict.items(): if key not in ["level", "event"]: log_data[key] = value return json.dumps(log_data, ensure_ascii=False, default=str) class LoggerManager: """日志管理器""" def __init__(self): self._processors = [] self._configured = False def configure(self, log_level: str = "INFO", log_format: str = "console", log_file: Optional[str] = None, log_to_console: bool = True): """配置日志系统""" # 配置structlog处理器 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, ] # 添加自定义处理器 processors.extend(self._processors) # 选择渲染器 if log_format == "json": renderer = JSONRenderer() else: renderer = ColoredConsoleRenderer() processors.append(renderer) # 配置structlog structlog.configure( processors=processors, wrapper_class=structlog.stdlib.BoundLogger, logger_factory=structlog.stdlib.LoggerFactory(), cache_logger_on_first_use=True, ) # 配置标准库logging # 防止重复配置 root_logger = logging.getLogger() if not root_logger.handlers: level = getattr(logging, log_level.upper()) # 如果指定了日志文件,配置文件日志 if log_file: self._setup_file_handler(log_file, log_level) # 如果允许控制台输出,配置控制台日志 if log_to_console: console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(level) console_handler.setFormatter(logging.Formatter("%(message)s")) root_logger.addHandler(console_handler) else: # 如果不输出到控制台,至少设置日志级别 root_logger.setLevel(level) self._configured = True def _setup_file_handler(self, log_file: str, log_level: str = "INFO"): """设置文件日志处理器""" log_path = Path(log_file) log_path.parent.mkdir(parents=True, exist_ok=True) file_handler = logging.FileHandler(log_file, encoding='utf-8') level = getattr(logging, log_level.upper()) file_handler.setLevel(level) if HAS_JSON_LOGGER: file_handler.setFormatter(jsonlogger.JsonFormatter( '%(asctime)s %(name)s %(levelname)s %(message)s' )) else: # 使用标准库的JSON格式化器 file_handler.setFormatter(logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s' )) # 获取根logger并添加文件处理器 root_logger = logging.getLogger() root_logger.addHandler(file_handler) root_logger.setLevel(level) def add_processor(self, processor): """添加自定义处理器""" self._processors.append(processor) def get_logger(self, name: Optional[str] = None) -> structlog.stdlib.BoundLogger: """获取logger实例""" if not self._configured: self.configure() return structlog.get_logger(name) # 全局日志管理器实例 logger_manager = LoggerManager() # 获取logger的便捷函数 def get_logger(name: Optional[str] = None) -> structlog.stdlib.BoundLogger: """获取logger实例""" return logger_manager.get_logger(name) # 简单的日志打印函数 def log(message: str, level: str = "info", **kwargs): """简单的日志打印函数 Args: message: 日志消息 level: 日志级别 (debug, info, warning, error, critical) **kwargs: 额外的上下文信息 """ logger = get_logger() log_method = getattr(logger, level.lower(), logger.info) log_method(message, **kwargs) # 带上下文的日志函数 def log_debug(message: str, **kwargs): """打印debug级别日志""" log(message, "debug", **kwargs) def log_info(message: str, **kwargs): """打印info级别日志""" log(message, "info", **kwargs) def log_warning(message: str, **kwargs): """打印warning级别日志""" log(message, "warning", **kwargs) def log_error(message: str, **kwargs): """打印error级别日志""" log(message, "error", **kwargs) def log_critical(message: str, **kwargs): """打印critical级别日志""" log(message, "critical", **kwargs)