![](/img/trans.png)
[英]Logging - changing level from INFO to DEBUG disables all logging messages
[英]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() 方法显示了这是如何发生的。
看来您误读了文档。 传播消息时,它们不会传播到父记录器。 它们直接传播到父级的处理程序。 实际上并非如此,但这样想会有所帮助:传播将父记录器的处理程序添加到子记录器。 由于您的test_logger
具有DEBUG
级别并且直接调用logging.debug()
会导致将NOTSET
级别的处理程序添加到 root 这是test_logger
的父级,情况就好像test_logger
具有此处理程序将记录任何消息,所以发送到test_logger
的所有调试消息都将由该处理程序记录。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.