【问题标题】:About NOTSET in python logging关于 Python 日志中的 NOTSET
【发布时间】:2014-02-24 23:56:35
【问题描述】:

正如logger.setLevel 文档所说:

创建记录器时,级别设置为 NOTSET(当记录器是根记录器时会处理所有消息,或者当记录器是非根记录器时会委托给父级)。请注意,根记录器的创建级别为 WARNING。

所以我想如果我创建一个具有 NOTSET 级别的根记录器,将显示调试和信息日志。

代码使用basicConfig设置root logger的级别为NOTSET是对的:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import logging

logging.basicConfig(level=logging.NOTSET)
log = logging.getLogger()

log.debug('debug')
log.info('info')
log.warning('warning')
log.error('error')
log.critical('critical')

输出是:

DEBUG:root:debug
INFO:root:info
WARNING:root:warning
ERROR:root:error
CRITICAL:root:critical

但如果我创建一个根记录器,并为其添加具有 NOTSET 级别的处理程序,例如:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import logging

log = logging.getLogger()
hd = logging.StreamHandler()
hd.setLevel(logging.NOTSET)
log.addHandler(hd)

log.debug('debug')
log.info('info')
log.warning('warning')
log.error('error')
log.critical('critical')

输出是:

warning
error
critical

但我认为它也会输出调试和信息消息。

【问题讨论】:

  • 此测试中缺少的是: log.log(logging.NOTSET, 'notset') 尽管文档暗示了什么,它似乎没有将任何内容记录到任何地方。 log.handlers 在这里是 None 。但是,像下面的解决方案一样显式添加处理程序也不能专门解决日志级别为零的问题。
  • @DevPlayer 你是对的,现在看看这个问题,我认为用'logging.DEBUG'设置root logger就足够了,而且更好; logging.NOTSET 不是一个有效的级别,它用于委托。
  • 这很奇怪,文档中的哪里记录了这种行为?这让我一整天都感到困惑。

标签: python logging


【解决方案1】:

好的,我误解了文档中的Logger's levelHandler's Level

setLevel() 方法,就像在记录器对象中一样,指定将被分派到适当目的地的最低严重性。为什么有两个 setLevel() 方法?记录器中设置的级别决定了它将传递给其处理程序的消息的严重性。每个处理程序中设置的级别决定了该处理程序将发送哪些消息。

如果我把代码改成这样,就可以了:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import logging

log = logging.getLogger()
log.setLevel(logging.NOTSET) # Set Logger's level to NOTSET, default is WARNING
#print "Logger's Level: ", log.level
hd = logging.StreamHandler()
hd.setLevel(logging.NOTSET)
#print "Handler's Level: ", hd.level
log.addHandler(hd)

log.debug('debug')
log.info('info')
log.warning('warning')
log.error('error')
log.critical('critical')

【讨论】:

  • 您需要为此添加更多详细信息才能使其本身有意义。
【解决方案2】:

让我们通过例子解释一下,在创建记录器的具体实例时,UNSET 不做任何事情(出于某种神秘的原因):

def logging_unset_level():
    """My sample logger explaining UNSET level

    Resources: 
    - https://stackoverflow.com/questions/21494468/about-notset-in-python-logging
    - https://www.youtube.com/watch?v=jxmzY9soFXg&t=468s
    - https://github.com/CoreyMSchafer/code_snippets/tree/master/Logging-Advanced
    """
    import logging

    logger = logging.getLogger(__name__) # loggers are created in hierarchy using dot notation, thus __name__ ensures no name collisions.
    print(f'DEFAULT VALUE: logger.level = {logger.level}')

    file_handler = logging.FileHandler(filename='my_log.log')
    log_format = "{asctime}:{levelname}:{lineno}:{name}:{message}" # see for logrecord attributes https://docs.python.org/3/library/logging.html#logrecord-attributes
    formatter = logging.Formatter(fmt=log_format, style='{') 
    file_handler.setFormatter(fmt=formatter) 

    stdout_stream_handler = logging.StreamHandler(stream=sys.stdout)
    stdout_stream_handler.setLevel(logging.INFO) 
    log_format = "{name}:{levelname}:-> {message}" # see for logrecord attributes https://docs.python.org/3/library/logging.html#logrecord-attributes
    formatter = logging.Formatter(fmt=log_format, style='{')
    stdout_stream_handler.setFormatter(fmt=formatter)

    logger.addHandler(hdlr=file_handler)
    logger.addHandler(hdlr=stdout_stream_handler)

    logger.log(logging.NOTSET, 'notset')
    logger.debug('debug')
    logger.info('info')
    logger.warning('warning')
    logger.error('error')
    logger.critical('critical')

屏幕的输出是:

DEFAULT VALUE: logger.level = 0
__main__:WARNING:-> warning
__main__:ERROR:-> error
__main__:CRITICAL:-> critical

stdout 的输出是:

2020-04-15 17:00:38,384:WARNING:200:__main__:warning
2020-04-15 17:00:38,384:ERROR:201:__main__:error
2020-04-15 17:00:38,384:CRITICAL:202:__main__:critical

正如问题正文所预期的那样,当记录器被实例化时,其设置为UNSET。如果您出于某种原因使用基本配置,它确实可以工作,因为文档暗示它应该从 UNSET 到更高级别的所有内容都被记录但是当具体实例化记录器时,此设置只会使错误消息因某种原因起作用(查找给我一个参考!)。

好的,现在如果您再次运行它,但增加顶级记录器的级别,一切都会按您的预期运行(除了未设置):

logger.setLevel(logging.DEBUG)

stdout 的输出(请注意,出于教学原因,在我的示例中 stdout 处理程序具有级别 INFO,与文件处理程序不同,试图显示来自顶级记录器的 .level 值的继承):

DEFAULT VALUE: logger.level = 10
__main__:INFO:-> info
__main__:WARNING:-> warning
__main__:ERROR:-> error
__main__:CRITICAL:-> critical

以及记录器文件my_log.log的输出(这个确实输出了调试语句!:D):

2020-04-15 17:05:58,782:DEBUG:198:__main__:debug
2020-04-15 17:05:58,784:INFO:199:__main__:info
2020-04-15 17:05:58,784:WARNING:200:__main__:warning
2020-04-15 17:05:58,784:ERROR:201:__main__:error
2020-04-15 17:05:58,784:CRITICAL:202:__main__:critica

符合预期。它继承了顶级记录器级别,因此它打印了从调试级别向上的所有内容。

【讨论】:

    【解决方案3】:

    我也认为这很令人困惑。我将 NOTSET 解释为不产生日志。但是……

    级别和处理程序条目被解释为根记录器,除了如果将非根记录器的级别指定为 NOTSET,系统会咨询更高层次的记录器以确定记录器的有效级别。 source

    级别被解释为记录器,而 NOTSET 被认为是“记录所有内容”。 source

    这是有道理的,因为 NOTSET (0) 低于 DEBUG (10)

    【讨论】:

      猜你喜欢
      • 2014-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-02
      • 1970-01-01
      相关资源
      最近更新 更多