Python日志系统实战指南:深入理解Logger模块与功能

Python日志系统详解:Logger完全指南

目录

  • 1. Logger简介
  • 2. Logger的基本组成和流程
  • 3. Logger对象详解
  • 4. Handler对象详解
  • 5. Formatter详解
  • 6. 完整实例
  • 7. Logger日志级别详解
  • 8. 高级Logger配置方法
  • 9. Logger使用最佳实践
  • 10. 常见问题与解决方案
  • 11. 总结
  • 1. Logger简介

    Logger是Python内置模块logging中的一个重要组件,用来记录程序运行过程中的各种事件,比如错误、警告、信息、调试信息等等。

    简单说:

  • 你在程序里加Logger,就是给程序安装"记录仪"
  • 记录运行时发生了什么,方便开发调试,也方便线上问题追踪
  • 为什么要用Logger而不是直接print?

  • print只能简单地输出到屏幕,Logger可以同时输出到屏幕、文件、甚至发送到远程服务器
  • Logger可以设置日志等级,比如只记录错误,不记录普通信息
  • Logger格式统一,输出清晰(比如带时间、模块名、行号)
  • Logger可以灵活切换配置,而不需要改动代码

  • 2. Logger的基本组成和流程

    Logger系统主要由四个部分组成:

    组件 作用
    Logger 日志记录器,产生日志
    Handler 处理器,决定日志往哪里输出(屏幕?文件?)
    Formatter 格式器,规定日志长什么样(时间?级别?内容?)
    Filter(可选) 过滤器,筛选哪些日志能过

    简单理解为:

    Logger产生日志 → Handler接收日志 → Formatter美化日志 → 输出到你想要的地方。


    3. Logger对象详解

    Logger对象就是"日志生产者",专门负责产生日志消息。

    每个Logger都有:

  • 名字(比如模块名)
  • 日志等级(决定记录什么级别以上的日志)
  • 一个或多个Handler(处理日志输出)
  • 配置(如是否向父Logger传递日志)
  • 创建Logger的方法:

    import logging
    
    # 创建Logger对象
    logger = logging.getLogger('my_logger')  
    # 可以随便起名字,推荐用当前模块名(如 __name__)
    

    设置Logger的日志级别:

    logger.setLevel(logging.DEBUG)  # 设为DEBUG,记录所有等级的日志
    

    Logger的常用方法:

    logger.debug('这是调试信息')
    logger.info('这是普通信息')
    logger.warning('这是警告信息')
    logger.error('这是错误信息')
    logger.critical('这是严重错误信息')
    logger.exception('这是捕获异常信息,包含堆栈')  # 通常在except块中使用
    

    4. Handler对象详解

    Handler对象是"日志搬运工",负责把日志消息送到指定地方。

    常用的Handler类型:

    Handler类型 作用
    StreamHandler 输出到控制台(标准输出)
    FileHandler 输出到文件
    RotatingFileHandler 输出到文件,支持按大小切割
    TimedRotatingFileHandler 输出到文件,支持按时间切割
    SMTPHandler 输出到邮件
    SysLogHandler 输出到系统日志
    HTTPHandler 输出到HTTP服务器

    每个Handler也可以设置自己的日志级别(比如只接收ERROR级别以上的日志)。

    创建Handler的例子:

    # 输出到控制台
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.DEBUG)
    
    # 输出到文件
    file_handler = logging.FileHandler('app.log')
    file_handler.setLevel(logging.ERROR)
    
    # 输出到按大小切割的文件(最多10MB,备份5个文件)
    rotating_handler = logging.handlers.RotatingFileHandler(
        'app.log', maxBytes=10*1024*1024, backupCount=5
    )
    
    # 按时间切割(每天午夜切割,保留7天)
    timed_handler = logging.handlers.TimedRotatingFileHandler(
        'app.log', when='midnight', interval=1, backupCount=7
    )
    

    5. Formatter详解

    Formatter就是"日志化妆师",负责给日志加上漂亮的格式。

    格式化占位符:

    占位符 含义
    %(asctime)s 日期时间,如:2023-01-01 12:00:00,123
    %(name)s 记录器名称
    %(levelname)s 日志级别名称
    %(levelno)s 日志级别数值
    %(pathname)s 文件的完整路径
    %(filename)s 文件名
    %(module)s 模块名
    %(funcName)s 函数名
    %(lineno)d 行号
    %(message)s 日志消息内容
    %(process)d 进程ID
    %(processName)s 进程名称
    %(thread)d 线程ID
    %(threadName)s 线程名称

    创建Formatter例子:

    # 一般格式
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    
    # 详细格式(带文件名、行号、函数名)
    detailed_formatter = logging.Formatter(
        '%(asctime)s [%(levelname)s] %(filename)s:%(lineno)d %(funcName)s() - %(message)s'
    )
    
    # 精确时间格式(带毫秒)
    precise_formatter = logging.Formatter(
        '%(asctime)s.%(msecs)03d - %(levelname)s - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )
    

    设置Formatter到Handler:

    console_handler.setFormatter(formatter)
    file_handler.setFormatter(detailed_formatter)
    

    6. 完整实例(快速搭建一个Logger)

    下面这段代码,一次性串联了完整流程:

    import logging
    
    # 1. 创建Logger对象
    logger = logging.getLogger('my_logger')
    logger.setLevel(logging.DEBUG)  # 设置日志级别
    
    # 2. 创建Handler
    console_handler = logging.StreamHandler()
    file_handler = logging.FileHandler('app.log')
    
    # 3. 设置Handler的日志级别
    console_handler.setLevel(logging.DEBUG)
    file_handler.setLevel(logging.ERROR)
    
    # 4. 创建Formatter并绑定到Handler
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    console_handler.setFormatter(formatter)
    file_handler.setFormatter(formatter)
    
    # 5. 将Handler添加到Logger
    logger.addHandler(console_handler)
    logger.addHandler(file_handler)
    
    # 6. 写日志
    logger.debug('调试信息')
    logger.info('普通信息')
    logger.warning('警告信息')
    logger.error('错误信息')
    logger.critical('严重错误信息')
    

    效果:

  • DEBUG/INFO/WARNING在控制台输出
  • ERROR/CRITICAL同时输出到控制台并记录到app.log文件中

  • 7. Logger日志级别详细解释

    等级 数值 说明 使用场景
    DEBUG 10 调试细节 开发阶段,详细追踪问题
    INFO 20 普通流程信息 常规操作,程序运行状态
    WARNING 30 警告(程序还能跑,但可能出问题) 需要注意但不是错误的情况
    ERROR 40 错误(程序某些功能无法继续) 运行出错,但不会导致崩溃
    CRITICAL 50 严重错误(整个程序可能崩掉) 紧急情况,程序无法继续运行

    Logger只会处理大于等于设定等级的日志信息。

    重要提示:默认Logger等级是WARNING,所以如果不设置,DEBUG和INFO是不会显示的!


    8. 更高级的Logger配置方法

    除了在代码里一行行设置,还可以用配置文件或者字典集中管理Logger,非常适合大项目。

    8.1 使用配置文件(INI格式)

    创建一个logging.conf:

    [loggers]
    keys=root,myLogger
    
    [handlers]
    keys=consoleHandler,fileHandler
    
    [formatters]
    keys=simpleFormatter
    
    [logger_root]
    level=WARNING
    handlers=consoleHandler
    
    [logger_myLogger]
    level=DEBUG
    handlers=consoleHandler,fileHandler
    qualname=myLogger
    propagate=0
    
    [handler_consoleHandler]
    class=StreamHandler
    level=DEBUG
    formatter=simpleFormatter
    args=(sys.stdout,)
    
    [handler_fileHandler]
    class=FileHandler
    level=ERROR
    formatter=simpleFormatter
    args=('app.log', 'a')
    
    [formatter_simpleFormatter]
    format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
    

    然后在代码中这样用:

    import logging.config
    
    logging.config.fileConfig('logging.conf')
    logger = logging.getLogger('myLogger')
    
    logger.debug('调试信息')
    

    8.2 使用字典配置(推荐!)

    直接在代码里用字典管理所有设置:

    import logging.config
    
    logging_config = {
        'version': 1,
        'formatters': {
            'simple': {'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'}
        },
        'handlers': {
            'console': {
                'class': 'logging.StreamHandler',
                'formatter': 'simple',
                'level': 'DEBUG',
            },
            'file': {
                'class': 'logging.FileHandler',
                'formatter': 'simple',
                'level': 'ERROR',
                'filename': 'app.log'
            },
        },
        'loggers': {
            'myLogger': {
                'handlers': ['console', 'file'],
                'level': 'DEBUG',
                'propagate': False,
            }
        }
    }
    
    logging.config.dictConfig(logging_config)
    logger = logging.getLogger('myLogger')
    logger.info('通过字典配置输出日志')
    # getLogger参数可以是任意字符串,不一定要在配置字典里定义。如果配置了就用配置,否则用默认规则。
    

    8.3 使用YAML配置(更易读)

    如果项目支持PyYAML,可以用YAML格式配置:

    version: 1
    formatters:
      simple:
        format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
    handlers:
      console:
        class: logging.StreamHandler
        level: DEBUG
        formatter: simple
      file:
        class: logging.FileHandler
        level: ERROR
        formatter: simple
        filename: app.log
    loggers:
      myLogger:
        level: DEBUG
        handlers: [console, file]
        propagate: false
    

    使用方法:

    import yaml
    import logging.config
    
    with open('logging.yaml', 'r') as f:
        config = yaml.safe_load(f)
        logging.config.dictConfig(config)
    
    logger = logging.getLogger('myLogger')
    

    9. Logger使用小技巧(最佳实践)

    9.1 分模块使用Logger

    每个模块用自己的Logger,别全靠root logger:

    # 在 user_service.py 中
    logger = logging.getLogger(__name__)  # 'user_service'
    
    # 在 order_service.py 中
    logger = logging.getLogger(__name__)  # 'order_service'
    

    9.2 合理设置日志级别

  • 开发环境:DEBUG
  • 测试环境:INFO
  • 生产环境:WARNING或ERROR
  • 可以通过环境变量动态调整:

    import os
    import logging
    
    log_level = os.environ.get('LOG_LEVEL', 'WARNING').upper()
    numeric_level = getattr(logging, log_level, logging.WARNING)
    logger.setLevel(numeric_level)
    

    9.3 使用日志轮转

    防止单个日志文件太大或太多:

    import logging
    from logging.handlers import RotatingFileHandler
    
    # 限制每个文件最大10MB,最多5个备份
    handler = RotatingFileHandler('app.log', maxBytes=10*1024*1024, backupCount=5)
    

    9.4 记录异常信息

    使用logger.exception自动记录堆栈信息:

    try:
        1 / 0
    except Exception as e:
        logger.exception('除零错误,完整堆栈:')
        # 等价于:logger.error('除零错误,完整堆栈:', exc_info=True)
    

    9.5 添加上下文信息

    使用LoggerAdapter或Filter添加自定义字段:

    class RequestIdFilter(logging.Filter):
        def __init__(self, request_id):
            super().__init__()
            self.request_id = request_id
            
        def filter(self, record):
            record.request_id = self.request_id
            return True
    
    # 使用方法
    request_filter = RequestIdFilter(request_id='12345')
    handler.addFilter(request_filter)
    formatter = logging.Formatter('%(asctime)s [%(request_id)s] %(levelname)s: %(message)s')
    

    9.6 性能优化

    日志可能影响性能,特别是DEBUG级别,可以使用懒加载方式:

    # 不好的方式(即使不输出也会计算字符串)
    logger.debug("User data: " + str(large_user_data))
    
    # 好的方式(只有真正需要输出时才计算)
    logger.debug("User data: %s", large_user_data)
    
    # 更好的方式(进一步避免不必要的计算)
    if logger.isEnabledFor(logging.DEBUG):
        logger.debug("User data: %s", large_user_data)
    

    10. 常见问题与解决方案

    10.1 为什么我的日志没有显示?

    可能原因

  • Logger或Handler日志级别设置过高
  • 没有添加适当的Handler
  • 日志被父Logger拦截(propagate设置)
  • 解决方法

    # 检查Logger级别
    logger.setLevel(logging.DEBUG)
    
    # 添加控制台Handler(最基本的显示方式)
    if not logger.handlers:
        handler = logging.StreamHandler()
        logger.addHandler(handler)
    
    # 禁止向父Logger传播
    logger.propagate = False
    

    10.2 如何在多线程/多进程环境中安全使用Logger?

    Python的logging模块默认是线程安全的,但多进程需要注意:

    # 多进程安全的文件日志配置
    handler = logging.FileHandler('app.log', delay=True)
    handler.setFormatter(logging.Formatter('%(asctime)s - %(process)d - %(message)s'))
    
    # 更好的方式:使用QueueHandler和QueueListener
    import queue
    from logging.handlers import QueueHandler, QueueListener
    
    log_queue = queue.Queue()
    queue_handler = QueueHandler(log_queue)
    logger.addHandler(queue_handler)
    
    # 在主进程中启动监听器
    file_handler = logging.FileHandler('app.log')
    listener = QueueListener(log_queue, file_handler)
    listener.start()
    
    # 程序结束时停止
    listener.stop()
    

    10.3 如何在Django/Flask等框架中使用Logger?

    大多数框架已有内置Logger配置,可以直接使用或扩展:

    Django:

    import logging
    logger = logging.getLogger(__name__)
    
    # 在settings.py中配置
    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'handlers': {
            'file': {
                'level': 'DEBUG',
                'class': 'logging.FileHandler',
                'filename': 'debug.log',
            },
        },
        'loggers': {
            'my_app': {
                'handlers': ['file'],
                'level': 'DEBUG',
                'propagate': True,
            },
        },
    }
    

    Flask:

    from flask.logging import default_handler
    app.logger.removeHandler(default_handler)
    handler = logging.FileHandler('flask_app.log')
    app.logger.addHandler(handler)
    

    11. 总结

  • Logger是日志生产者:记录各种级别的信息
  • Handler是日志搬运工:决定日志去向(屏幕、文件、邮件等)
  • Formatter是日志化妆师:美化日志格式(时间、级别、内容等)
  • 四步走:创建Logger → 添加Handler → 设置Formatter → 输出日志
  • 配置方式多样:支持代码配置、文件配置、字典配置
  • 最佳实践:模块化、级别分明、文件轮转、性能优化
  • Python的日志系统设计精妙,掌握了它,你就能让程序清晰地"说话",无论是开发调试还是生产环境监控,都能事半功倍。


    参考资源

  • Python官方logging文档
  • Python Logging – 最佳实践
  • Real Python – Python Logging教程
  • 作者:于归pro

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python日志系统实战指南:深入理解Logger模块与功能

    发表回复