繁体   English   中英

Python 日志记录:即使处理程序级别为 INFO,调试消息也会记录到 stderr

[英]Python logging: debug messages logged to stderr even though handler level is INFO

在修改现有项目的代码时,我发现调试消息是 output 到 stderr,即使这些日志的处理程序级别设置为 INFO 级别,所以我不希望有任何调试消息。 该行为的原因似乎是导入正在使用logging.debug() ,在此调用之后,它改变了消息 output 到控制台的方式。

我将问题简化为下面的代码示例,以帮助理解为什么要记录这些调试级别的消息:

import logging
import sys

# get root logger singleton
root_logger = logging.getLogger()

# create my own logger object
test_logger = logging.getLogger("test_logger")
test_logger.setLevel(logging.DEBUG)

# add handler to my logger
sh = logging.StreamHandler(sys.stdout)
sh.setLevel(logging.INFO)
test_logger.addHandler(sh)

test_logger.info("Info message from test_logger")

test_logger.debug("Debug message before logging.debug")
print("test_logger handlers before: " + str(test_logger.handlers))
print("test_logger level before: " + str(test_logger.getEffectiveLevel()))
print("Root before: " + str(root_logger.handlers) + " level: "+ str(root_logger.getEffectiveLevel()))

# This call causes the debug messages after it to go to stderr
# Also, it results in the root logger object having a handler added to it 
logging.debug("Debug message from logging")

# This debug message is unexpectedly logged to stderr
test_logger.debug("Debug message after logging.debug")
print("test_logger handlers after: " + str(test_logger.handlers))
print("test_logger logging level after: " + str(test_logger.getEffectiveLevel()))
print("Root after: " + str(root_logger.handlers) + " level: " + str(root_logger.getEffectiveLevel()))

上面的代码具有以下 output,其中出于某种原因只有一条调试消息是 output 并且将发送到 stderr:

DEBUG:test_logger:Debug message after logging.debug
Info message from test_logger
test_logger handlers before: [<StreamHandler <stdout> (INFO)>]
test_logger level before: 10
Root before: [] level: 30
test_logger handlers after: [<StreamHandler <stdout> (INFO)>]
test_logger logging level after: 10
Root after: [<StreamHandler <stderr> (NOTSET)>] level: 30

对我来说,为什么 StreamHandler 被添加到根记录器是有意义的,因为调用了 logging.debug() 并且根上没有处理程序它将添加一个。 位于https://docs.python.org/3.5/howto/logging.html#logging-flow的图表似乎也很相关,因为它显示了从具有父级 go 的记录器到父级的消息,并且根记录器应该是父级测试记录器。

添加以下代码行将阻止在 stderr 中生成调试消息,因此问题似乎与将消息传播到其祖先有关。 但是,我不确定这是一个合适的解决方案还是只是一种解决方法。

test_logger.propagate = False

https://docs.python.org/3/library/logging.html#logging.Logger.propagate上的文档解释说:

消息直接传递给祖先记录器的处理程序——既不考虑相关祖先记录器的级别也不考虑过滤器。

对我来说,这意味着根记录器应该收到此消息,但打印的调试消息将记录器显示为 test_logger DEBUG:test_logger:Debug message after logging.debug因此它似乎不是来自根记录器。 根记录器也有默认级别 logging.WARN (30),我的理解是,如果记录器的处理程序处理调试消息,则仅当记录器上的日志级别等于或低于消息级别时才会打印它?

有没有人能够解释这里发生了什么,如果可能的话我错过了一些强制性步骤? 例如,在这段代码中,我是否应该初始化根记录器以避免添加默认处理程序,即使我创建了一个命名记录器并计划使用它?

更新:正如@blues 所指出的,这是按预期工作的,我没有正确阅读文档。 消息直接传播到父记录器处理程序,而不是实际的记录器。

我通过 Python 日志库 (v3.7.3) 来演示代码中的这种行为,起初我觉得这很奇怪。

此堆栈跟踪片段的 callHandlers() 方法显示了这是如何发生的。

callHandlers,初始化.py:1585

句柄,初始化.py:1529

_log,初始化.py:1519

调试,初始化.py:1371

看来您误读了文档。 传播消息时,它们不会传播到父记录器。 它们直接传播到父级的处理程序。 实际上并非如此,但这样想会有所帮助:传播将父记录器的处理程序添加到子记录器。 由于您的test_logger具有DEBUG级别并且直接调用logging.debug()会导致将NOTSET级别的处理程序添加到 root 这是test_logger的父级,情况就好像test_logger具有此处理程序将记录任何消息,所以发送到test_logger的所有调试消息都将由该处理程序记录。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM