"""日志配置模块 - 提供统一的日志记录功能"""
from config import (
LOG_LEVEL,
LOG_FORMAT,
LOG_DATE_FORMAT,
LOG_FILENAME_FORMAT,
LOG_MAX_BYTES,
LOG_BACKUP_COUNT,
LOGS_DIR,
LOGS_DIR_NAME,
APP_LOGGER_NAME,
DEFAULT_LOGGER_NAME,
MCP_APP_NAME,
)
import logging
import sys
from pathlib import Path
from logging.handlers import RotatingFileHandler
from datetime import datetime
# 导入配置
sys.path.insert(0, str(Path(__file__).parent.parent))
def get_logs_dir() -> Path:
"""获取日志目录路径(优先使用工作目录的日志路径)"""
try:
# 延迟导入,避免循环依赖
from .workspace_manager import workspace_manager
logs_dir = workspace_manager.get_logs_dir()
logs_dir.mkdir(parents=True, exist_ok=True)
return logs_dir
except Exception:
# 如果无法获取工作目录,使用默认路径
LOGS_DIR.mkdir(parents=True, exist_ok=True)
return LOGS_DIR
def setup_logger(
name: str = None,
log_file: str = None,
level: int = None,
max_bytes: int = None,
backup_count: int = None
) -> logging.Logger:
"""
配置并返回日志记录器
Args:
name: 日志记录器名称 (默认从配置读取)
log_file: 日志文件路径 (可选)
level: 日志级别 (默认从配置读取)
max_bytes: 单个日志文件最大字节数 (默认从配置读取)
backup_count: 保留的日志文件数量 (默认从配置读取)
Returns:
配置好的日志记录器
"""
# 使用默认值
name = name or DEFAULT_LOGGER_NAME
level = level or LOG_LEVEL
max_bytes = max_bytes or LOG_MAX_BYTES
backup_count = backup_count or LOG_BACKUP_COUNT
logger = logging.getLogger(name)
# 如果已经配置过,直接返回
if logger.handlers:
return logger
logger.setLevel(level)
# 日志格式
formatter = logging.Formatter(
fmt=LOG_FORMAT,
datefmt=LOG_DATE_FORMAT
)
# 控制台处理器
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(level)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
# 文件处理器 (如果指定了日志文件)
if log_file:
log_path = Path(log_file)
log_path.parent.mkdir(parents=True, exist_ok=True)
file_handler = RotatingFileHandler(
filename=log_file,
maxBytes=max_bytes,
backupCount=backup_count,
encoding='utf-8'
)
file_handler.setLevel(level)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
return logger
def setup_app_logger(level: int = None, console_output: bool = True) -> logging.Logger:
"""
配置应用程序日志记录器 - 每次启动创建新的日志文件
Args:
level: 日志级别 (默认从配置读取)
console_output: 是否输出到控制台 (MCP stdio 模式下应设为 False)
Returns:
配置好的日志记录器
"""
# 使用默认值
level = level or LOG_LEVEL
# 获取日志目录(优先使用工作目录的 .batch_task/logs)
logs_dir = get_logs_dir()
# 根据当前时间生成日志文件名 (使用配置的格式)
timestamp = datetime.now().strftime(LOG_FILENAME_FORMAT)
log_file = logs_dir / f"{MCP_APP_NAME}_{timestamp}.log"
# 创建日志记录器
logger = logging.getLogger(APP_LOGGER_NAME)
# 清除现有的处理器(避免重复)
logger.handlers.clear()
logger.setLevel(level)
# 日志格式
formatter = logging.Formatter(
fmt=LOG_FORMAT,
datefmt=LOG_DATE_FORMAT
)
# 控制台处理器(仅在非 MCP stdio 模式下添加)
if console_output:
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(level)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
# 文件处理器
file_handler = logging.FileHandler(log_file, encoding='utf-8')
file_handler.setLevel(level)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
logger.info(f"日志文件: {log_file}")
return logger
# 创建默认日志记录器(用于其他模块)
default_logger = setup_logger()
def get_logger(name: str = None) -> logging.Logger:
"""
获取日志记录器 - 始终返回应用 logger 以确保日志输出
Args:
name: 日志记录器名称 (忽略,保持兼容性)
Returns:
日志记录器
"""
# 始终返回应用 logger,确保所有日志都输出到同一个文件
# 如果应用 logger 还没创建,返回默认 logger
app_logger = logging.getLogger(APP_LOGGER_NAME)
if app_logger.handlers:
return app_logger
return default_logger