You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义Logger为何不使用根日志记录器的格式化器?

自定义Logger无法继承根日志处理器的原因及解决方法

问题:根据logging文档,未配置处理器的日志记录器应该回退到根日志记录器,但自定义Logger实例调用日志方法时没有使用根的格式,而通过logging.getLogger()创建的日志器却正常。示例代码及输出如下:

import logging

class MyLogger(logging.getLoggerClass()):
    pass

mylog = MyLogger("mytest")
log = logging.getLogger("test")
root = logging.getLogger()

h = logging.StreamHandler()
f = logging.Formatter(fmt="[%(levelname)s] %(message)s")
h.setFormatter(f)
root.addHandler(h)

log.error("Normal log")
mylog.error("Test log")

输出:

[ERROR] Normal log
Test log

核心原因

日志模块的传播机制依赖日志器的层级关系:

  • 通过logging.getLogger()创建的日志器,会被日志管理器自动构建层级,将父日志器自动设置为根日志器(对应层级的父日志器)。当自身无处理器时,会将日志传播给父日志器处理,因此能使用根的格式。
  • 直接实例化自定义Logger类时,实例的parent属性默认是None,即使propagate=True(默认值),也找不到父日志器来传播日志,只能使用模块内置的“应急”处理器(无格式,仅输出原始消息)。

解决方法

有两种可行的方式让自定义Logger继承根日志的处理器和格式:

方法1:注册自定义Logger类后用logging.getLogger()创建实例

这是最规范的方式,让日志管理器统一处理层级:

import logging

class MyLogger(logging.getLoggerClass()):
    # 自定义逻辑
    pass

# 注册自定义Logger类,后续getLogger会返回该类的实例
logging.setLoggerClass(MyLogger)
# 创建自定义日志器实例,此时会自动设置父日志器
mylog = logging.getLogger("mytest")
# 若之后需要恢复默认Logger类,可执行:logging.setLoggerClass(logging.Logger)

# 后续配置根日志器的代码不变
root = logging.getLogger()
h = logging.StreamHandler()
f = logging.Formatter(fmt="[%(levelname)s] %(message)s")
h.setFormatter(f)
root.addHandler(h)

mylog.error("Test log")  # 现在会使用根的格式输出

方法2:手动为自定义Logger实例设置父日志器

如果不想全局替换Logger类,可以手动指定父日志器:

import logging

class MyLogger(logging.getLoggerClass()):
    pass

mylog = MyLogger("mytest")
# 手动将父日志器设置为根日志器
mylog.parent = logging.getLogger()

# 后续配置代码不变
root = logging.getLogger()
h = logging.StreamHandler()
f = logging.Formatter(fmt="[%(levelname)s] %(message)s")
h.setFormatter(f)
root.addHandler(h)

mylog.error("Test log")  # 现在会使用根的格式输出

内容的提问来源于stack exchange,提问作者musbur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 12:17:08