logging.getLogger(__name__)是关键,因它基于模块名自动构建层级logger树,支持各模块独立设level、handler及propagate=False,避免root干扰和日志重复或丢失。

为什么 logging.getLogger(__name__) 是关键
每个模块用 logging.getLogger(__name__) 获取独立 logger,而不是直接调用 logging.basicConfig() 或全局 logging.getLogger()。Python 的 logger 是按层级组织的(比如 a.b.c 是 a.b 的子 logger),__name__ 自动生成模块路径名,天然形成树状结构,为分级控制打下基础。
常见错误是全项目只用一个 root logger,结果改了某处级别,所有输出跟着变——这根本不是“不同模块不同级别”,只是全局开关。
如何给模块 A 设 INFO、模块 B 设 DEBUG
核心是分别获取、分别配置:不共享 handler,不共用 level,也不依赖 root 的传播(默认会向上传播到 root,造成干扰)。
- 在模块 A 开头写:
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)logger.addHandler(your_handler)logger.propagate = False # 关键!阻止向上送到 root - 在模块 B 开头写:
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)logger.addHandler(another_handler)logger.propagate = False - handler 可以复用(比如都用
StreamHandler),但 level 和 propagate 必须各自设
propagate=False 不设会怎样
如果不关掉 propagate,即使模块 B 的 logger 设了 DEBUG,它的日志仍会一路传到 root logger;而 root 默认是 WARNING 级别,结果 DEBUG 日志被 root 挡掉——你明明写了 logger.debug("test"),却什么也不输出,非常迷惑。
立即学习“Python免费学习笔记(深入)”;
更糟的是,如果 root 已配了 handler(比如 basicConfig 已执行),那模块日志会重复打印两次:一次走自己的 handler,一次走 root 的 handler。
所以只要不是有意做日志聚合,每个模块 logger 都该显式加一句 logger.propagate = False。
有没有更省事的集中配置方式
有,用 dictConfig 一次性定义多个 logger,适合中大型项目。但要注意:字典里必须显式写出每个 logger 的 "propagate": False,否则默认仍是 True。
例如配置模块 foo.bar 为 DEBUG、baz 为 WARNING:
{
"version": 1,
"loggers": {
"foo.bar": {
"level": "DEBUG",
"handlers": ["console"],
"propagate": False
},
"baz": {
"level": "WARNING",
"handlers": ["file"],
"propagate": False
}
}
}这种写法干净,但调试时不如逐模块设来得直观;一旦 dict 配错(比如漏写 propagate),问题会藏得比较深。










