logging 模块升级版 loguru

发布于:2025-09-13 ⋅ 阅读:(14) ⋅ 点赞:(0)

logging 模块 组成及使用

一、Loguru 相对于 Logging 的改进(为什么选择 Loguru?)

标准库的 logging 模块非常强大和灵活,是许多大型项目和框架的基石。然而,它的配置和使用方式也相当繁琐和复杂。Loguru 的核心设计哲学就是为开发者提供一个开箱即用、配置更简单、更符合直觉的日志系统。可以说是logging的高级封装。

  1. 开箱即用,无需繁琐配置

    • Logging: 你需要手动创建 Logger、设置日志级别、配置 HandlerFormatterFilter。一个基本的配置可能就需要十多行代码。
    • Loguru: 直接从 loguru 导入 logger 实例即可开始记录日志,所有基础配置(如输出到 stderr、格式、级别)都已预设好。
  2. 更简洁友好的输出格式

    • Logging: 默认格式较为简单,需要手动配置才能获得丰富信息(如时间、级别、模块、行号)。
    • Loguru: 默认提供了色彩丰富、信息详尽的日志输出,包括时间、级别、模块、行号和日志信息,视觉上更易于阅读。
  3. 更容易地输出到文件

    • Logging: 需要创建 FileHandler 并将其添加到 logger 中。

      import logging
      import sys
      # 日志配置
      logging.basicConfig(format='{asctime}:{levelname} {name}.py {message}', 
                          datefmt='%Y-%m-%d %H:%M:%S',
                          style='{',
                          level=logging.DEBUG,
                          stream=sys.stdout)
      # 日志输出
      logger = logging.getLogger('test')
      logger.info('日志提示信息')
      
    • Loguru: 只需要一个 add() 方法调用,即可轻松添加文件日志,并支持强大的日志文件管理功能(如循环、压缩、 retention 清理等)。

      from loguru import logger
      logger.remove() # 移除默认配置
      logger.add(sink=sys.stdout)
      logger.info('日志提示信息')
      
  4. 更安全的多进程日志记录

    • Logging: 在多进程环境下,如果使用 FileHandler 可能会遇到日志内容交错或丢失的问题,需要额外的处理(如 QueueHandler)。
    • Loguru: 内部处理了多进程并发写入的问题,默认就是安全的。
  5. 使用 catch 装饰器自动记录异常

    • Logging: 记录函数异常通常需要在函数内部使用 try...except 并手动记录 error

      import logging
      logger = logging.getLogger('追踪错误')
      try:
          res = 2/0
      except Exception:
          logger.error('failed',exc_info=True)  # 设置参数exc_info
      
    • Loguru: 使用一个简单的装饰器 @logger.catch 即可自动捕获并记录函数中发生的任何异常,包括完整的堆栈跟踪。

      from loguru import logger
      
      @logger.catch()
      def cal():
          return 20/0
      cal()
      
  6. 更方便的运行时日志修改

    • 通过 add(), remove() 方法可以非常动态地添加或删除日志 sink(输出目标),而无需重新配置整个日志系统。

二、loguru 模块 项目简单使用

1. 日志输出相关配置
# utils.py 
from loguru import logger

# 日志存储路径
log_file_path = 'xxxx'

# 日志配置函数
def setup_logger(log_file_path=None):
    logger.remove()  # 移除默认配置(避免重复输出)
    
    if log_file_path:
       # 配置文件日志处理器
       logger.add(
            sink=log_file_path,        # 日志文件路径
            rotation="100 MB",         # 按大小分割
            retention="30 days",       # 保留30天
            level='INFO',              # 日志记录级别
            format='<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>',                 
            filter=None,
            colorize=None,
            serialize=False,
            backtrace=True,
            diagnose=True,
            enqueue=False,
    )
    # 同时输出到控制台
    logger.add(
            sink=sys.stdout,           # 标准控制台输出
            level='INFO',              # 日志记录级别
            format='<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>',                 
            filter=None,
            colorize=True

    )
    return logger
2. 项目各子模块间日志隔离

在默认情况下,你从不同文件导入的 logger 对象是同一个全局单例对象。

  1. 全局唯一性:整个 Python 解释器进程中,只有一个 loguru.logger 实例。
  2. 配置共享:你在任何一个文件中对这个 logger 进行的配置(比如添加 sink、设置级别、修改格式),都会立即在所有其他导入了 logger 的文件中生效。
  3. 线程安全:Loguru 的全局 logger 是线程安全的,你可以在多线程环境中安全地使用它,而无需担心日志信息错乱。
# 不同模块不同日志配置(假设3个子模块 a、b、c)
# utils.py

from loguru import logger
import os

# 日志基础路径
base_path = 'xxx'
# 不同应用日志存储路径
apps = [('app_a','a'),('app_b','b'),('app_c','c')]

def setup_logger():  
    logger.remove()  # 移除默认配置(避免重复输出)
    
    for app,app_log_path in apps:
        
        # 为每个app创建不同存储路径
        log_file_path = os.path.join(base_path,app_log_path)
        os.makedirs(log_file_path,exist_ok=True)
        
        # 日志存储文件名称
        log_file_name = os.path.join(log_file_path,'{time:YYYY-MM-DD}.log')
        
        # 为日志配置存储格式
        logger.add(
            sink=log_file_name,
            level='INFO',
            rotation='00:00', # 指示何时关闭当前日志文件和何时开启新文件的条件
            retention="30 days", # 设置日志保留时间,自动清理
            format='{time:YYYY-MM-DD HH:mm:ss} - {extra[label]} - {level} - {message}', # label 为logger.bind函数为日志绑定的额外键值对标签
            enqueue=True,  # 异步写入,提高性能
            filter=lambda record,label=app: print(record["extra"]) or record['extra'].get('label') == label # 分别添加3个日志过滤器,为3个应用进行日志过滤分流 (print 打印调试使用)   
        )
        
# 日志初始化        
setup_logger()
# main.py 

from utils import logger

# 日志记录器绑定上应用标签
logger.bind(label='app_a').error('a应用日志信息')
logger.bind(label='app_b').info('b应用日志信息')
logger.bind(label='app_c').info('c应用日志信息')

# 生成日志文件
-- XXX
   - a
    - 2025-09-11.log # 2025-09-11 01:39:14 - app_a - ERROR - a应用日志信息
   - b
    - 2025-09-11.log # 2025-09-11 01:39:14 - app_b - INFO - b应用日志信息
   - c
    - 2025-09-11.log