| """全局日志模块 - 单例模式的日志管理器""" |
|
|
| import sys |
| import logging |
| from pathlib import Path |
| from logging.handlers import RotatingFileHandler |
|
|
| from app.core.config import setting |
|
|
|
|
| |
| FILTER_PATTERNS = [ |
| "chunk: b'", |
| "Got event:", |
| "Closing", |
| ] |
|
|
|
|
| class MCPLogFilter(logging.Filter): |
| """MCP日志过滤器 - 过滤大量数据的DEBUG日志""" |
|
|
| def filter(self, record: logging.LogRecord) -> bool: |
| """过滤日志""" |
| |
| if record.name == "sse_starlette.sse" and record.levelno == logging.DEBUG: |
| msg = record.getMessage() |
| return not any(p in msg for p in FILTER_PATTERNS) |
|
|
| |
| if "mcp.server.streamable_http" in record.name and record.levelno == logging.DEBUG: |
| return False |
|
|
| return True |
|
|
|
|
| class LoggerManager: |
| """日志管理器(单例)""" |
|
|
| _instance = None |
| _initialized = False |
|
|
| def __new__(cls): |
| if cls._instance is None: |
| cls._instance = super().__new__(cls) |
| return cls._instance |
|
|
| def __init__(self): |
| """初始化日志系统""" |
| if LoggerManager._initialized: |
| return |
|
|
| |
| log_dir = Path(__file__).parents[2] / "logs" |
| log_dir.mkdir(exist_ok=True) |
| log_level = setting.global_config.get("log_level", "INFO").upper() |
| log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" |
| log_file = log_dir / "app.log" |
|
|
| |
| self.logger = logging.getLogger() |
| self.logger.setLevel(log_level) |
|
|
| |
| if self.logger.handlers: |
| return |
|
|
| |
| formatter = logging.Formatter(log_format) |
| mcp_filter = MCPLogFilter() |
|
|
| |
| console = logging.StreamHandler(sys.stdout) |
| console.setLevel(log_level) |
| console.setFormatter(formatter) |
| console.addFilter(mcp_filter) |
|
|
| |
| file_handler = RotatingFileHandler( |
| log_file, maxBytes=10*1024*1024, backupCount=5, encoding="utf-8" |
| ) |
| file_handler.setLevel(log_level) |
| file_handler.setFormatter(formatter) |
| file_handler.addFilter(mcp_filter) |
|
|
| |
| self.logger.addHandler(console) |
| self.logger.addHandler(file_handler) |
|
|
| |
| self._configure_third_party() |
|
|
| LoggerManager._initialized = True |
| |
| def _configure_third_party(self): |
| """配置第三方库日志级别""" |
| config = { |
| "asyncio": logging.WARNING, |
| "uvicorn": logging.INFO, |
| "fastapi": logging.INFO, |
| "aiomysql": logging.WARNING, |
| "mcp": logging.CRITICAL, |
| "fastmcp": logging.CRITICAL, |
| } |
| |
| for name, level in config.items(): |
| logging.getLogger(name).setLevel(level) |
|
|
| def debug(self, msg: str) -> None: |
| """调试日志""" |
| self.logger.debug(msg) |
|
|
| def info(self, msg: str) -> None: |
| """信息日志""" |
| self.logger.info(msg) |
|
|
| def warning(self, msg: str) -> None: |
| """警告日志""" |
| self.logger.warning(msg) |
|
|
| def error(self, msg: str) -> None: |
| """错误日志""" |
| self.logger.error(msg) |
|
|
| def critical(self, msg: str) -> None: |
| """严重错误日志""" |
| self.logger.critical(msg) |
|
|
|
|
| |
| logger = LoggerManager() |
|
|