简体   繁体   English

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

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

While modifying code on an existing project, I found that debug messages were being output to stderr even though the handler level for these logs is set to INFO level so I do not expect any debug messages.在修改现有项目的代码时,我发现调试消息是 output 到 stderr,即使这些日志的处理程序级别设置为 INFO 级别,所以我不希望有任何调试消息。 The cause of the behavior seems to be that an import is using logging.debug() where after that call it changed the way messages were being output to the console.该行为的原因似乎是导入正在使用logging.debug() ,在此调用之后,它改变了消息 output 到控制台的方式。

I reduced the issue to the code example below to get help on understanding why these debug level messages are being logged:我将问题简化为下面的代码示例,以帮助理解为什么要记录这些调试级别的消息:

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()))

The code above has the following output, where for some reason only one of the debug messages is output and is going to stderr:上面的代码具有以下 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

It makes sense to me why the StreamHandler is being added to the root logger since logging.debug() is called and with no handler on root it will add one.对我来说,为什么 StreamHandler 被添加到根记录器是有意义的,因为调用了 logging.debug() 并且根上没有处理程序它将添加一个。 The diagram at https://docs.python.org/3.5/howto/logging.html#logging-flow also seems relevant in that it shows that messages from loggers with a parent go to the parent and the root logger should be the parent of test_logger.位于https://docs.python.org/3.5/howto/logging.html#logging-flow的图表似乎也很相关,因为它显示了从具有父级 go 的记录器到父级的消息,并且根记录器应该是父级测试记录器。

Adding the following line of code will prevent the debug message from being generated in stderr so it seems the issue has to do with the message being propagated to its ancestors.添加以下代码行将阻止在 stderr 中生成调试消息,因此问题似乎与将消息传播到其祖先有关。 However, I am not sure that is an appropriate solution or just a workaround.但是,我不确定这是一个合适的解决方案还是只是一种解决方法。

test_logger.propagate = False

The docs on this at https://docs.python.org/3/library/logging.html#logging.Logger.propagate explain that:https://docs.python.org/3/library/logging.html#logging.Logger.propagate上的文档解释说:

Messages are passed directly to the ancestor loggers' handlers - neither the level nor filters of the ancestor loggers in question are considered.消息直接传递给祖先记录器的处理程序——既不考虑相关祖先记录器的级别也不考虑过滤器。

To me that means the root logger should get this message but the debug message that is printed shows the logger as test_logger DEBUG:test_logger:Debug message after logging.debug so it doesn't appear to be coming from the root logger.对我来说,这意味着根记录器应该收到此消息,但打印的调试消息将记录器显示为 test_logger DEBUG:test_logger:Debug message after logging.debug因此它似乎不是来自根记录器。 The root logger also has the default level of logging.WARN (30) and my understanding is that if a handler of a logger processes a debug message, it is only printed if the log level on the logger is equal or lower to the message level?根记录器也有默认级别 logging.WARN (30),我的理解是,如果记录器的处理程序处理调试消息,则仅当记录器上的日志级别等于或低于消息级别时才会打印它?

Is anyone able to explain what is going on here and if possibly I am missing some mandatory step?有没有人能够解释这里发生了什么,如果可能的话我错过了一些强制性步骤? For example, in this code should I be initializing the root logger to avoid default handlers being added even if I create a named logger and plan to use that?例如,在这段代码中,我是否应该初始化根记录器以避免添加默认处理程序,即使我创建了一个命名记录器并计划使用它?

UPDATE: As @blues pointed out, this is working as expected and I was not reading the documentation correctly.更新:正如@blues 所指出的,这是按预期工作的,我没有正确阅读文档。 The messages are propagated directly to parent logger handlers not the actual loggers.消息直接传播到父记录器处理程序,而不是实际的记录器。

I stepped through the Python logging library (v3.7.3) to demonstrate this behavior in the code, as it seemed odd to me at first.我通过 Python 日志库 (v3.7.3) 来演示代码中的这种行为,起初我觉得这很奇怪。

The callHandlers() method of this stack trace snippet shows how this happens.此堆栈跟踪片段的 callHandlers() 方法显示了这是如何发生的。

callHandlers, init .py:1585 callHandlers,初始化.py:1585

handle, init .py:1529 句柄,初始化.py:1529

_log, init .py:1519 _log,初始化.py:1519

debug, init .py:1371 调试,初始化.py:1371

It seems you are misreading the documentation.看来您误读了文档。 When messages are propagated they are not propagated to the parent logger.传播消息时,它们不会传播到父记录器。 They are propagated directly to the handlers of the parent.它们直接传播到父级的处理程序。 It is not actually the case but it helps to think about it like this: propagation adds the handlers of the parent to the child logger.实际上并非如此,但这样想会有所帮助:传播将父记录器的处理程序添加到子记录器。 Since your test_logger has a level of DEBUG and the direct call to logging.debug() causes a handler with NOTSET level being added to root which is a parent of test_logger the situation is as if test_logger had this handler which will log any messages, so all debug messages sent to the test_logger will be logged by this handler.由于您的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