簡體   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