Python日志记录:即使处理程序级别为INFO

时间:2020-02-12 20:22:30

标签: python logging

在现有项目上修改代码时,我发现即使这些日志的处理程序级别设置为INFO级别,调试消息也正在输出到stderr,因此我不希望有任何调试消息。造成这种现象的原因似乎是导入正在使用logging.debug(),在此之后调用更改了将消息输出到控制台的方式。

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

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

上面的代码具有以下输出,出于某种原因,其中仅输出一条调试消息,并将其发送到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

对我来说,为什么自从调用logging.debug()以来,为什么将StreamHandler添加到根记录器中,而在根目录上没有处理程序,它将添加一个。 https://docs.python.org/3.5/howto/logging.html#logging-flow处的图似乎也很相关,因为它显示了来自具有父级的记录器的消息会转到父级,而根记录器应是test_logger的父级。

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

test_logger.propagate = False

https://docs.python.org/3/library/logging.html#logging.Logger.propagate上的文档对此进行了解释:

消息直接传递到祖先记录器的处理程序-既不考虑所讨论祖先记录器的级别或过滤器。

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

有人能解释这里发生了什么吗?如果我可能缺少一些强制性的步骤?例如,在此代码中,我应该初始化根记录器,以免添加默认处理程序,即使我创建了命名记录器并计划使用该记录器也是如此。

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

我逐步浏览了Python日志记录库(v3.7.3),以在代码中演示此行为,因为起初对我来说这很奇怪。

此堆栈跟踪摘要的callHandlers()方法显示了这种情况。

callHandlers, init.py:1585

handle, init.py:1529

_log, init.py:1519

debug, init.py:1371

1 个答案:

答案 0 :(得分:0)

您似乎误读了文档。传播消息时,它们不会传播到父记录器。它们直接传播到父级的处理程序。实际情况并非如此,但是可以这样思考:传播将父级的处理程序添加到子级记录器。由于您的test_logger的级别为DEBUG,而对logging.debug()的直接调用会导致将NOTSET级别的处理程序添加到作为test_logger父级的根中情况似乎是test_logger拥有此处理程序,该处理程序将记录所有消息,因此发送到test_logger的所有调试消息都将由此处理程序记录。