Python Logging 模块详解

Python 的 logging 模块是一个功能强大且灵活的日志记录系统,用于跟踪程序的运行状态、调试和错误分析。本文将从基础到高级,详细介绍如何使用 logging 模块的各项功能。

  1. [为什么使用 Logging 模块](#为什么使用 Logging 模块)
  2. 基础使用
    • 日志级别
    • 基本配置 basicConfig
    • 简单日志记录示例
  3. Logger、Handler 和 Formatter
    • Logger
    • Handler
    • Formatter
  4. 配置日志记录器
    • 使用不同的 Handler
    • 添加多个 Handler
  5. 日志格式化
    • 格式化字符串
    • 自定义 Formatter
  6. 高级功能
    • RotatingFileHandler 和 TimedRotatingFileHandler
    • 过滤器
    • 日志记录到多个目的地
  7. 最佳实践
  8. 示例代码
  9. 总结

为什么使用 Logging 模块

在开发过程中,打印日志是调试和监控程序运行的重要手段。虽然可以使用 print 函数进行简单的输出,但 logging 模块提供了更为灵活和强大的日志记录功能,包括不同的日志级别、日志输出到不同的目的地、日志格式化、日志轮转等。

基础使用

日志级别

logging 模块定义了以下标准日志级别,从低到高依次为:

  • DEBUG: 最详细的信息,通常只在诊断问题时使用。
  • INFO: 确认程序按预期运行的信息。
  • WARNING: 指示某些意外情况或即将发生的问题,但程序仍在继续运行。
  • ERROR: 更严重的问题,程序无法执行某些功能。
  • CRITICAL: 非常严重的问题,程序可能无法继续运行。

基本配置 basicConfig

basicConfig 是配置日志记录的简便方法,适用于简单的日志需求。它可以设置日志级别、日志格式、日志输出位置等。

import logging

logging.basicConfig(level=logging.INFO)
logging.debug("这是一个调试信息")
logging.info("这是一个普通信息")
logging.warning("这是一个警告信息")
logging.error("这是一个错误信息")
logging.critical("这是一个严重错误信息")

在上述代码中,只有 INFO 及以上级别的日志会被输出,因为 level 设置为 INFO

简单日志记录示例

import logging

logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

logging.debug("调试信息")
logging.info("普通信息")
logging.warning("警告信息")
logging.error("错误信息")
logging.critical("严重错误信息")

输出示例:

2024-04-27 12:00:00,000 - DEBUG - 调试信息
2024-04-27 12:00:00,001 - INFO - 普通信息
2024-04-27 12:00:00,002 - WARNING - 警告信息
2024-04-27 12:00:00,003 - ERROR - 错误信息
2024-04-27 12:00:00,004 - CRITICAL - 严重错误信息

Logger、Handler 和 Formatter

logging 模块的核心概念包括 Logger、Handler 和 Formatter。理解它们之间的关系对于有效配置日志记录器至关重要。

Logger

Logger 是应用程序中记录日志的对象。每个 Logger 都有一个名称(通常是模块名),并且可以有不同的日志级别。

创建 Logger 的方式:

import logging

logger = logging.getLogger(__name__)

Handler

Handler 负责将日志记录输出到不同的目的地,如控制台、文件、网络等。常见的 Handler 包括:

  • StreamHandler: 输出到控制台或其他流。
  • FileHandler: 输出到文件。
  • RotatingFileHandler: 输出到文件,并支持日志轮转。
  • TimedRotatingFileHandler: 按时间轮转日志文件。

Formatter

Formatter 定义日志的输出格式,包括时间、日志级别、消息等。

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

配置日志记录器

通过组合 Logger、Handler 和 Formatter,可以灵活地配置日志记录。

使用不同的 Handler

示例:同时输出到控制台和文件

import logging

# 创建 Logger
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)

# 创建控制台 Handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)

# 创建文件 Handler
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)

# 创建 Formatter 并设置给 Handler
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)

# 将 Handler 添加到 Logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)

# 记录日志
logger.debug("这是一条调试信息")
logger.info("这是一条普通信息")
logger.warning("这是一条警告信息")
logger.error("这是一条错误信息")
logger.critical("这是一条严重错误信息")

在上述代码中:

  • DEBUG 级别的日志会被记录到 app.log 文件中。
  • INFO 及以上级别的日志会输出到控制台。

添加多个 Handler

可以为同一个 Logger 添加多个 Handler,以实现日志输出到多个目的地。例如,输出到控制台和多个文件。

import logging

logger = logging.getLogger('multi_handler_logger')
logger.setLevel(logging.DEBUG)

# 控制台 Handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING)

# 文件 Handler1
file_handler1 = logging.FileHandler('debug.log')
file_handler1.setLevel(logging.DEBUG)

# 文件 Handler2
file_handler2 = logging.FileHandler('error.log')
file_handler2.setLevel(logging.ERROR)

# 设置 Formatter
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
file_handler1.setFormatter(formatter)
file_handler2.setFormatter(formatter)

# 添加 Handler 到 Logger
logger.addHandler(console_handler)
logger.addHandler(file_handler1)
logger.addHandler(file_handler2)

# 记录日志
logger.debug("调试信息")
logger.info("普通信息")
logger.warning("警告信息")
logger.error("错误信息")
logger.critical("严重错误信息")

在此示例中:

  • 所有 DEBUG 及以上级别的日志记录到 debug.log
  • 所有 ERROR 及以上级别的日志记录到 error.log
  • 所有 WARNING 及以上级别的日志输出到控制台。

日志格式化

通过 Formatter,可以自定义日志的输出格式,包含时间、模块名、日志级别、消息等信息。

格式化字符串

Formatter 使用格式化字符串定义日志的结构,常用的格式化字段包括:

  • %(asctime)s: 时间
  • %(name)s: Logger 名称
  • %(levelname)s: 日志级别
  • %(message)s: 日志消息
  • %(filename)s: 文件名
  • %(lineno)d: 行号
  • %(funcName)s: 函数名

示例:

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

自定义 Formatter

可以通过继承 logging.Formatter 自定义日志格式化器,实现更复杂的格式需求。

import logging

class CustomFormatter(logging.Formatter):
    def format(self, record):
        record.msg = f"自定义前缀: {record.msg}"
        return super().format(record)

# 使用自定义 Formatter
formatter = CustomFormatter('%(asctime)s - %(levelname)s - %(message)s')

handler = logging.StreamHandler()
handler.setFormatter(formatter)

logger = logging.getLogger('custom_formatter')
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)

logger.info("这是一个测试信息")

输出:

2024-04-27 12:00:00,000 - INFO - 自定义前缀: 这是一个测试信息

高级功能

RotatingFileHandler 和 TimedRotatingFileHandler

RotatingFileHandler

RotatingFileHandler 允许在日志文件达到一定大小后进行轮转,生成新的日志文件。

import logging
from logging.handlers import RotatingFileHandler

logger = logging.getLogger('rotating_logger')
logger.setLevel(logging.INFO)

# 设置 RotatingFileHandler,最大 1MB,保留 3 个备份
handler = RotatingFileHandler('rotating.log', maxBytes=1*1024*1024, backupCount=3)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

logger.addHandler(handler)

for i in range(10000):
    logger.info(f"日志信息 {i}")
TimedRotatingFileHandler

TimedRotatingFileHandler 根据时间进行日志轮转,例如每天、每小时等。

import logging
from logging.handlers import TimedRotatingFileHandler

logger = logging.getLogger('timed_rotating_logger')
logger.setLevel(logging.INFO)

# 设置 TimedRotatingFileHandler,每天轮转,保留 7 天的日志
handler = TimedRotatingFileHandler('timed_rotating.log', when='midnight', interval=1, backupCount=7)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

logger.addHandler(handler)

logger.info("这是一个定时轮转的日志信息")

过滤器

过滤器用于更细粒度地控制哪些日志记录被输出。可以基于 Logger 名称、日志级别等条件进行过滤。

import logging

class LevelFilter(logging.Filter):
    def __init__(self, level):
        super().__init__()
        self.level = level

    def filter(self, record):
        return record.levelno == self.level

logger = logging.getLogger('filter_logger')
logger.setLevel(logging.DEBUG)

# Handler1 只记录 ERROR 级别
error_handler = logging.StreamHandler()
error_handler.setLevel(logging.ERROR)
error_filter = LevelFilter(logging.ERROR)
error_handler.addFilter(error_filter)
logger.addHandler(error_handler)

# Handler2 记录所有级别
all_handler = logging.StreamHandler()
all_handler.setLevel(logging.DEBUG)
logger.addHandler(all_handler)

logger.debug("调试信息")
logger.info("普通信息")
logger.warning("警告信息")
logger.error("错误信息")
logger.critical("严重错误信息")

在上述代码中:

  • error_handler 只输出 ERROR 级别的日志。
  • all_handler 输出所有级别的日志。

日志记录到多个目的地

通过添加多个 Handler,可以将日志同时记录到多个目的地,例如控制台、文件、远程服务器等。

import logging
from logging.handlers import SMTPHandler

logger = logging.getLogger('multi_destination_logger')
logger.setLevel(logging.DEBUG)

# 控制台 Handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

# 文件 Handler
file_handler = logging.FileHandler('multi_destination.log')
file_handler.setLevel(logging.INFO)

# 邮件 Handler(仅在 ERROR 及以上级别发送邮件)
mail_handler = SMTPHandler(
    mailhost=('smtp.example.com', 587),
    fromaddr='error@example.com',
    toaddrs=['admin@example.com'],
    subject='应用程序错误',
    credentials=('username', 'password'),
    secure=()
)
mail_handler.setLevel(logging.ERROR)

# 设置 Formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
mail_handler.setFormatter(formatter)

# 添加 Handler 到 Logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)
logger.addHandler(mail_handler)

# 记录日志
logger.info("这是普通信息")
logger.error("这是一个错误信息")

在此示例中:

  • 所有 DEBUG 及以上级别的日志输出到控制台。
  • 所有 INFO 及以上级别的日志记录到文件。
  • 所有 ERROR 及以上级别的日志通过邮件发送。

最佳实践

  1. 模块化 Logger: 每个模块使用自己的 Logger,通常使用 __name__ 作为 Logger 名称。

    import logging
    
    logger = logging.getLogger(__name__)
    
  2. 避免重复日志: 确保每个 Handler 只被添加一次,防止日志重复输出。

  3. 配置文件管理: 对于复杂的日志配置,使用配置文件(如 logging.config 模块支持的配置文件)进行管理,便于维护。

  4. 合理设置日志级别: 开发阶段使用较低的日志级别(如 DEBUG),生产环境中适当提高日志级别以减少日志量。

  5. 使用 Rotating Handler: 防止日志文件过大,使用轮转 Handler(如 RotatingFileHandlerTimedRotatingFileHandler)。

  6. 敏感信息处理: 避免在日志中记录敏感信息,如密码、密钥等。

示例代码

以下是一个综合示例,展示了如何配置 Logger、多个 Handler、Formatter 和过滤器。

import logging
from logging.handlers import RotatingFileHandler

# 创建 Logger
logger = logging.getLogger('comprehensive_logger')
logger.setLevel(logging.DEBUG)

# 创建 Formatter
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

# 创建控制台 Handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)

# 创建 RotatingFileHandler
file_handler = RotatingFileHandler('comprehensive.log', maxBytes=5*1024*1024, backupCount=2)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)

# 添加 Handler 到 Logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)

# 添加过滤器,只记录 WARNING 及以上级别
class WarningFilter(logging.Filter):
    def filter(self, record):
        return record.levelno >= logging.WARNING

warning_filter = WarningFilter()
file_handler.addFilter(warning_filter)

# 记录日志
logger.debug("这是调试信息,不会在文件中显示")
logger.info("这是普通信息,不会在文件中显示")
logger.warning("这是警告信息,会在文件中显示")
logger.error("这是错误信息,会在文件中显示")
logger.critical("这是严重错误信息,会在文件中显示")

在此示例中:

  • 控制台输出 INFO 及以上级别的日志。
  • 文件输出 WARNING 及以上级别的日志,并使用 RotatingFileHandler 进行日志轮转。

总结

Python 的 logging 模块提供了强大且灵活的日志记录功能,适用于各种规模和复杂度的项目。通过合理配置 Logger、Handler 和 Formatter,可以实现多样化的日志记录需求,帮助开发者更好地调试和维护程序。掌握 logging 模块的使用,有助于提升代码质量和可维护性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值