import logging import sys from loguru import logger class InterceptHandler(logging.Handler): def emit(self, record): # 获取对应的 Loguru 级别 try: level = logger.level(record.levelname).name except ValueError: level = record.levelno # 从 logging 记录中找到调用位置 frame, depth = logging.currentframe(), 2 while frame.f_code.co_filename == logging.__file__: frame = frame.f_back depth += 1 logger.opt(depth=depth, exception=record.exc_info).log( level, record.getMessage() ) def setup_logger(): """ 配置 Loguru 日志记录器 - 移除默认的 handler - 添加一个用于 INFO 级别日志的文件 sink - 添加一个用于 ERROR 级别日志的文件 sink - 保持控制台输出(级别为 DEBUG) """ # 检查是否已经初始化,防止重复初始化 if hasattr(setup_logger, '_initialized') and setup_logger._initialized: return # 1. 移除默认的控制台 handler logger.remove() # 2. 添加一个新的控制台 handler,设置级别为 DEBUG logger.add( sys.stderr, level="INFO", format="{time:YYYY-MM-DD HH:mm:ss.SSS} | " "{level: <8} | " "{name}:{function}:{line} - {message}", colorize=True, # 开启颜色 ) # 3. 添加 INFO 日志文件 sink # - level="INFO": 只记录 INFO 及以上级别的日志 # - filter: 确保 ERROR 级别的日志不会写入这个文件 # - rotation: 每天午夜创建一个新文件 # - retention: 只保留最近 14 天的日志 # - enqueue=True: 异步写入,提升性能 # - encoding="utf-8": 确保中文不会乱码 logger.add( "logs/app.info.log", level="INFO", filter=lambda record: record["level"].name in ["INFO", "WARNING"], rotation="00:00", # 每天午夜轮转 retention="14 days", # 保留 14 天 enqueue=True, # 异步写入 backtrace=True, # 记录堆栈信息 diagnose=True, # 诊断信息 encoding="utf-8", ) # 4. 添加 ERROR 日志文件 sink # - level="ERROR": 只记录 ERROR 及以上级别的日志 (ERROR, CRITICAL) # - rotation: 文件大小超过 10 MB 时轮转 # - retention: 只保留最近 30 天的日志 logger.add( "logs/app.error.log", level="ERROR", rotation="10 MB", # 按文件大小轮转 retention="30 days", # 保留 30 天 enqueue=True, # 异步写入 backtrace=True, diagnose=True, encoding="utf-8", ) # 拦截 logging,以便将所有日志都转发到 Loguru logging.basicConfig(handlers=[InterceptHandler()], level=0, force=True) # 设置已初始化标志 setup_logger._initialized = True logger.info("日志系统配置完成。")