3. Python 日志模块入门:logging 核心概念

在结构化日志实践中,Python 内置的 logging 模块是基础设施。它提供了四个核心组件:Logger、Handler、Formatter 和 Filter。理解它们之间的关系与职责,是构建可观测、可维护日志系统的第一步。

3.1 四组件职责与协作关系

下图展示了日志记录从产生到输出的完整链路:Logger 接收日志调用,经过 Filter 筛选后,将 LogRecord 传递给 Handler,Handler 使用 Formatter 格式化后输出到目标(控制台、文件等)。

logging 四组件协作流程图 logging 四组件协作流程 Logger 记录器(入口) Filter 过滤器(可选) Handler 处理器(输出) Formatter 格式化器 输出目标 文件/控制台 说明: Logger 是入口,Filter 决定是否放行,Handler 负责输出, Formatter 定义日志的最终文本格式。

3.2 组件职责详解

组件 职责 典型场景
Logger 日志记录入口,提供 debug()、info()、warning()、error()、critical() 等方法。支持层级命名(如 app.service.module)。 每个模块或类创建一个 Logger 实例,通过名称区分日志来源。
Handler 决定日志的输出目的地。常见实现包括 StreamHandler(控制台)、FileHandler(文件)、RotatingFileHandler(轮转文件)。 开发环境输出到控制台,生产环境同时输出到文件和日志收集系统。
Formatter 定义日志记录的最终文本格式。通过 %(name)s、%(levelname)s、%(message)s 等占位符组合。 结构化日志中常使用 JSON 格式的 Formatter,便于后续解析。
Filter 提供更细粒度的日志过滤能力。可以基于日志级别、Logger 名称、自定义属性等条件决定是否放行某条日志。 只允许特定模块的 DEBUG 日志通过,或过滤掉包含敏感信息的日志。

3.3 快速入门示例

以下代码展示了 logging 模块最基础的配置与使用方式:

import logging

# 1. 创建 Logger
logger = logging.getLogger("my_app")
logger.setLevel(logging.DEBUG)

# 2. 创建 Handler(控制台输出)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

# 3. 创建 Formatter 并绑定到 Handler
formatter = logging.Formatter(
    fmt="%(asctime)s | %(name)s | %(levelname)-8s | %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S"
)
console_handler.setFormatter(formatter)

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

# 5. 使用 Logger 记录日志
logger.debug("这是一条 DEBUG 日志")
logger.info("这是一条 INFO 日志")
logger.warning("这是一条 WARNING 日志")
logger.error("这是一条 ERROR 日志")
实践建议:在生产代码中,不要直接使用 logging.basicConfig() 进行全局配置。推荐为每个模块创建独立的 Logger 实例,并通过配置文件或代码显式管理 Handler 和 Formatter,以便在测试和 Mock 场景中灵活替换。

3.4 日志级别与传播机制

Logger 默认继承父 Logger 的 Handler,形成层级传播。理解级别和传播规则,能避免日志重复或丢失。

级别 数值 使用场景
DEBUG 10 调试信息,仅在开发环境开启
INFO 20 正常操作的关键节点信息
WARNING 30 潜在问题,不影响当前功能
ERROR 40 功能异常,需要关注
CRITICAL 50 严重错误,可能导致系统崩溃
常见陷阱:Logger 的 setLevel() 和 Handler 的 setLevel() 是独立生效的。Logger 级别决定是否生成 LogRecord,Handler 级别决定是否输出该记录。两者取“较高”者(数值较大)为实际生效级别。例如 Logger 设为 DEBUG,Handler 设为 WARNING,则只有 WARNING 及以上级别的日志才会输出。

3.5 层级 Logger 与传播示例

通过层级命名,可以实现精细的日志控制。下面的 SVG 展示了父子 Logger 的传播关系:

层级 Logger 传播示意图 层级 Logger 传播示意图 Root Logger Handler: 控制台 Logger "app" Handler: 文件 Logger "app.service" 无自定义 Handler 传播规则: Logger "app.service" 没有自己的 Handler,日志会传播给父 Logger "app" 和 Root Logger, 最终同时输出到文件和控制台。可通过 logger.propagate = False 阻止传播。

3.6 最佳实践要点

  • 按模块命名 Logger:使用 __name__ 作为 Logger 名称,自动继承包层级结构。
  • 避免重复日志:检查 logger.handlers 是否为空,再添加 Handler,防止重复绑定。
  • 结构化先行:从项目初期就使用 extra 参数传递结构化字段,为后续 JSON 格式化做准备。
  • 测试友好:在单元测试中,使用 unittest.mock.patch 或 logging.Handler 的子类捕获日志,验证日志内容。
核心记忆点:Logger 是入口,Handler 是出口,Formatter 是翻译,Filter 是守门员。四者组合,构成了 Python 日志体系的完整骨架。