| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- # -*- coding: utf-8 -*-
- import logging
- import sys
- import atexit
- from typing_extensions import override
- from loguru import logger
- from app.config.path_conf import LOG_DIR
- from app.config.setting import settings
- # 全局变量记录日志处理器ID
- _logger_handlers = []
- class InterceptHandler(logging.Handler):
- """
- 日志拦截处理器:将所有 Python 标准日志重定向到 Loguru
-
- 工作原理:
- 1. 继承自 logging.Handler
- 2. 重写 emit 方法处理日志记录
- 3. 将标准库日志转换为 Loguru 格式
- """
- @override
- def emit(self, record: logging.LogRecord) -> None:
- # 尝试获取日志级别名称
- try:
- level = logger.level(record.levelname).name
- except ValueError:
- level = record.levelno
- # 获取调用帧信息,增加None检查
- frame, depth = logging.currentframe(), 2
- while frame and frame.f_code.co_filename == logging.__file__:
- frame = frame.f_back
- depth += 1
- # 使用 Loguru 记录日志
- logger.opt(depth=depth, exception=record.exc_info).log(
- level,
- record.getMessage()
- )
-
- def cleanup_logging():
- """
- 清理日志资源
- 在程序退出时调用,确保所有日志处理器被正确关闭
- """
- global _logger_handlers
-
- for handler_id in _logger_handlers:
- try:
- logger.remove(handler_id)
- except Exception:
- pass
-
- _logger_handlers.clear()
- def setup_logging():
- """
- 配置日志系统
-
- 功能:
- 1. 控制台彩色输出
- 2. 文件日志轮转
- 3. 错误日志单独存储
- 4. 智能异步策略:开发环境同步(避免reload资源泄漏),生产环境异步(高性能)
- """
- global _logger_handlers
-
- # 添加上下文信息
- _ = logger.configure(extra={"app_name": "FastapiAdmin"})
- # 步骤1:移除默认处理器
- logger.remove()
- # 步骤2:定义日志格式
- log_format = (
- # 时间信息
- "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
- # 日志级别,居中对齐
- "<level>{level: <8}</level> | "
- # 文件、函数和行号
- "<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - "
- # 日志消息
- "<level>{message}</level>"
- )
- # 智能选择异步策略:开发环境禁用异步(避免reload时资源泄漏),生产环境启用异步(提升性能)
- use_async = not settings.DEBUG
-
- # 步骤3:配置控制台输出
- handler_id = logger.add(
- sys.stdout,
- format=log_format,
- level="DEBUG" if settings.DEBUG else "INFO",
- enqueue=use_async, # 开发同步,生产异步
- backtrace=True, # 显示完整的异常回溯
- diagnose=True, # 显示变量值等诊断信息
- colorize=True # 启用彩色输出
- )
- _logger_handlers.append(handler_id)
- # 步骤4:创建日志目录
- log_dir = LOG_DIR
- # 确保日志目录存在,如果不存在则创建
- log_dir.mkdir(parents=True, exist_ok=True)
- # 步骤5:配置常规日志文件
- handler_id = logger.add(
- str(log_dir / "info.log"),
- format=log_format,
- level="INFO",
- rotation="00:00", # 每天午夜轮转
- retention=30, # 日志保留天数,超过此天数的日志文件将被自动清理
- compression="gz",
- encoding="utf-8",
- enqueue=use_async # 开发同步,生产异步
- )
- _logger_handlers.append(handler_id)
- # 步骤6:配置错误日志文件
- handler_id = logger.add(
- str(log_dir / "error.log"),
- format=log_format,
- level="ERROR",
- rotation="00:00", # 每天午夜轮转
- retention=30, # 日志保留天数,超过此天数的日志文件将被自动清理
- compression="gz",
- encoding="utf-8",
- enqueue=use_async, # 开发同步,生产异步
- backtrace=True,
- diagnose=True
- )
- _logger_handlers.append(handler_id)
- # 步骤7:配置标准库日志
- logging.basicConfig(handlers=[InterceptHandler()], level="DEBUG" if settings.DEBUG else "INFO", force=True)
- logger_name_list = [name for name in logging.root.manager.loggerDict]
-
- # 步骤8:配置第三方库日志
- for logger_name in logger_name_list:
- _logger = logging.getLogger(logger_name)
- _logger.handlers = [InterceptHandler()]
- _logger.propagate = False
-
- # 注册退出清理函数
- atexit.register(cleanup_logging)
- log = logger
|