""" utils/logger.py 统一日志工具 —— 所有模块通过 get_logger(name) 获取 logger 日志级别、输出目录、文件名均来自 config.yaml logging 节 """ import logging import os import sys from logging.handlers import RotatingFileHandler from pathlib import Path # 避免循环导入:logger 初始化时不能 import settings # 改为延迟读取,首次调用时从环境变量 / 默认值获取 _LOG_LEVEL_ENV = os.getenv("LOG_LEVEL", "DEBUG").upper() _LOG_DIR_ENV = os.getenv("LOG_DIR", "./logs") _LOG_FILE_ENV = os.getenv("LOG_FILE", "agent.log") _ENABLE_FILE = os.getenv("LOG_FILE_ENABLE", "true").lower() == "true" _FORMATTER = logging.Formatter( fmt="%(asctime)s [%(levelname)-8s] %(name)-24s │ %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) # 全局 handler 缓存(避免重复添加) _handlers_initialized: bool = False _root_logger = logging.getLogger("agent") def _init_handlers() -> None: global _handlers_initialized if _handlers_initialized: return # 尝试从 settings 读取配置(settings 已加载后才有效) try: from config.settings import settings level = getattr(logging, settings.logging.level, logging.DEBUG) log_dir = settings.logging.log_dir log_file = settings.logging.log_file enable_file= settings.logging.enable_file except Exception: level = getattr(logging, _LOG_LEVEL_ENV, logging.DEBUG) log_dir = _LOG_DIR_ENV log_file = _LOG_FILE_ENV enable_file= _ENABLE_FILE _root_logger.setLevel(level) # ── 控制台 Handler ──────────────────────────────────────── if not any(isinstance(h, logging.StreamHandler) for h in _root_logger.handlers): console_handler = logging.StreamHandler(sys.stdout) console_handler.setFormatter(_FORMATTER) console_handler.setLevel(level) _root_logger.addHandler(console_handler) # ── 文件 Handler(RotatingFile)────────────────────────── if enable_file: log_path = Path(log_dir) log_path.mkdir(parents=True, exist_ok=True) file_handler = RotatingFileHandler( filename=log_path / log_file, maxBytes=10 * 1024 * 1024, # 10 MB backupCount=5, encoding="utf-8", ) file_handler.setFormatter(_FORMATTER) file_handler.setLevel(level) _root_logger.addHandler(file_handler) _root_logger.propagate = False _handlers_initialized = True def get_logger(name: str) -> logging.Logger: """ 获取命名 logger 用法: logger = get_logger("MCP.SkillClient") logger = get_logger("TOOL.StaticAnalyzer") logger = get_logger("Agent") """ _init_handlers() return logging.getLogger(f"agent.{name}")