""" utils/logger.py 统一日志模块:从 settings 读取日志级别与文件路径配置 """ import logging import sys from pathlib import Path # ── ANSI 颜色常量 ────────────────────────────────────────────── class Color: RESET = "\033[0m" BOLD = "\033[1m" CYAN = "\033[96m" GREEN = "\033[92m" YELLOW = "\033[93m" RED = "\033[91m" MAGENTA = "\033[95m" BLUE = "\033[94m" GREY = "\033[90m" class ColorFormatter(logging.Formatter): LEVEL_COLORS = { logging.DEBUG: Color.GREY, logging.INFO: Color.CYAN, logging.WARNING: Color.YELLOW, logging.ERROR: Color.RED, logging.CRITICAL: Color.MAGENTA, } COMPONENT_COLORS = { "CLIENT": Color.BLUE, "LLM": Color.GREEN, "MCP": Color.YELLOW, "TOOL": Color.MAGENTA, "MEMORY": Color.CYAN, "SYSTEM": Color.GREY, "CONFIG": Color.GREEN, } def format(self, record: logging.LogRecord) -> str: from datetime import datetime level_color = self.LEVEL_COLORS.get(record.levelno, Color.RESET) time_str = datetime.now().strftime("%H:%M:%S.%f")[:-3] component = record.name.split(".")[-1].upper() comp_color = self.COMPONENT_COLORS.get(component, Color.RESET) return ( f"{Color.GREY}[{time_str}]{Color.RESET} " f"{comp_color}{Color.BOLD}[{component:6s}]{Color.RESET} " f"{level_color}{record.getMessage()}{Color.RESET}" ) def get_logger(component: str, level: int | None = None) -> logging.Logger: """ 获取指定组件的 Logger 实例,日志级别与文件路径从 settings 读取 Args: component: 组件名称,如 "CLIENT"、"LLM"、"MCP" level: 覆盖日志级别(None 时从 settings 读取) """ # 延迟导入避免循环依赖(settings 初始化时也会用到 logger) from config.settings import settings as cfg if level is None: level = getattr(logging, cfg.logging.level, logging.DEBUG) logger = logging.getLogger(f"agent.{component}") logger.setLevel(level) if logger.handlers: return logger # 终端 Handler console_handler = logging.StreamHandler(sys.stdout) console_handler.setFormatter(ColorFormatter()) logger.addHandler(console_handler) # 文件 Handler(由配置控制开关) if cfg.logging.enable_file: log_dir = Path(cfg.logging.log_dir) log_dir.mkdir(parents=True, exist_ok=True) file_handler = logging.FileHandler( log_dir / cfg.logging.log_file, encoding="utf-8" ) file_handler.setFormatter( logging.Formatter("[%(asctime)s] [%(name)s] %(levelname)s: %(message)s") ) logger.addHandler(file_handler) logger.propagate = False return logger