
本文详解如何在 Python logging 模块中配置多个 Logger,各自绑定不同格式的 StreamHandler 并避免消息重复输出,核心在于正确控制 propagate 属性与根 logger 的关系。
本文详解如何在 python logging 模块中配置多个 logger,各自绑定不同格式的 streamhandler 并避免消息重复输出,核心在于正确控制 `propagate` 属性与根 logger 的关系。
在 Python 日志系统中,每个自定义 Logger 默认启用 propagate=True,这意味着它会将日志消息向上传递给其父 Logger(最终到达根 logger)。若同时调用 logging.basicConfig(),则根 logger 会被自动配置一个默认的 StreamHandler —— 这正是造成“同一条日志打印两次”的根本原因:一次由自定义 Handler 输出(带特定格式),另一次由根 logger 的 Handler 输出(带基础格式)。
要实现两个 Logger 各自独立、互不干扰、格式分明,关键步骤如下:
-
禁用默认传播链:为每个自定义 Logger 设置
logger.propagate = False,切断其向根 logger 的消息传递; -
避免全局污染:无需调用
basicConfig()(除非你明确需要根 logger 的基础行为),所有 Handler 和 Formatter 均显式绑定到目标 Logger; -
合理设置日志级别:确保 Logger 和 Handler 的级别均允许所需等级的消息通过(如
setLevel(logging.DEBUG)); -
注意命名与隔离性:Logger 名称(如
'logger1'、'mylogger.1')仅用于标识和层级继承,实际行为由propagate和 Handler 决定,与名称本身无关。
以下是一个完整、可直接运行的示例:
import logging
# 创建两个独立 Logger
logger1 = logging.getLogger('logger1')
logger2 = logging.getLogger('mylogger.1')
# 为每个 Logger 分配专属 StreamHandler
handler1 = logging.StreamHandler()
handler2 = logging.StreamHandler()
# 定义不同格式
formatter1 = logging.Formatter('### %(levelname)-8s - %(message)s')
formatter2 = logging.Formatter(
'### %(name)s - %(asctime)s - %(levelname)-8s - %(message)s',
datefmt='%d-%m-%Y %H:%M'
)
handler1.setFormatter(formatter1)
handler2.setFormatter(formatter2)
# 绑定 Handler 到对应 Logger
logger1.addHandler(handler1)
logger2.addHandler(handler2)
# 设置日志级别(推荐显式指定)
logger1.setLevel(logging.DEBUG)
logger2.setLevel(logging.DEBUG)
# ? 关键:关闭传播,防止消息上浮至根 logger
logger1.propagate = False
logger2.propagate = False
# 测试输出
print("→ 日志输出对比:")
print("\n【logger1 输出】")
logger1.info("服务已启动")
logger1.warning("内存使用率偏高")
print("\n【logger2 输出】")
logger2.info("初始化完成")
logger2.error("连接数据库超时")
运行后输出将严格分离:
→ 日志输出对比: 【logger1 输出】 ### INFO - 服务已启动 ### WARNING - 内存使用率偏高 【logger2 输出】 ### mylogger.1 - 24-02-2025 14:30 - INFO - 初始化完成 ### mylogger.1 - 24-02-2025 14:30 - ERROR - 连接数据库超时
✅ 注意事项总结:
- 不要混用
basicConfig()与手动 Handler 配置,除非你主动管理根 logger; -
propagate=False是解决重复日志的最直接、最可控方式; - 若需多级 Logger(如
'app.db'和'app.api'),仍应为每个叶子节点 Logger 单独设置propagate=False,否则子 Logger 会沿层级向上冒泡; - 所有 Handler 必须显式
addHandler(),Logger 不会自动继承父级 Handler; - 格式字符串中支持的字段(如
%(name)s,%(funcName)s,%(lineno)d)可按需扩展,提升调试信息丰富度。
掌握这一模式后,你即可灵活构建模块化日志体系——例如 Web 请求日志用详细时间+路径格式,后台任务日志用简洁状态码格式,彼此完全解耦、职责清晰。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











