【问题标题】:How to avoid root handler being called from the custom logger in Python?如何避免从 Python 中的自定义记录器调用根处理程序?
【发布时间】:2021-07-20 00:29:39
【问题描述】:

我有一个调试级别的日志记录模块的基本配置——现在我想创建另一个只有错误级别的记录器。我该怎么做?

问题是除了错误处理程序之外还调用了根处理程序 - 这是我想要避免的。

import logging
fmt = '%(asctime)s:%(funcName)s:%(lineno)d:%(levelname)s:%(name)s:%(message)s'
logging.basicConfig(level=logging.DEBUG, format=fmt)

logger = logging.getLogger('Temp')
logger.setLevel(logging.ERROR)
handler = logging.StreamHandler()
handler.setLevel(logging.ERROR)
logger.addHandler(handler)

logger.error('boo')

上面的代码打印了两次 boo,而我只期望一次,我不知道如何处理这个烦人的问题......

In [4]: logger.error('boo')
boo
2021-04-26 18:54:24,329:<module>:1:ERROR:Temp:boo

In [5]: logger.handlers
Out[5]: [<StreamHandler stderr (ERROR)>]

【问题讨论】:

    标签: python logging


    【解决方案1】:

    关于日志模块的一些基础知识

    • logger:接收日志字符串的人,按预定义的级别对其进行排序,然后使用他自己的处理程序(如果有)处理日志,并且默认情况下将日志传递给他的上级。
    • rootlogger:上级中的上级,做普通logger所做的所有事情,但不会将收到的日志传递给其他人。
    • handler:记录器的私人承包商,他实际上对日志做任何事情,例如。格式化日志,将其写入文件或stdout,或通过 tcp/udp 发送。
    • formatter:主题,handler 应用于日志的设计。
    • basicConfig:配置root 记录器的快捷方式。当您希望他完成所有工作并且他的所有较低级别的伐木工只会将日志传递给他时,这很有用。
      在没有参数的情况下,basicConfigroot 记录器的级别设置为WARNING,并添加一个StreamHandler,将日志输出到stderr

    你做了什么

    1. 您创建了format 并使用快捷方式basicConfig 来配置root 记录器。您希望根记录器执行所有实际的记录工作
    2. 您创建了一个新的低级记录器Temp
    3. 您希望它只接受 ERROR 及以上级别的日志。
    4. 您创建了另一个StreamHandler。默认输出到stdout
    5. 您希望它只处理ERROR 及以上级别
    6. 哦,你将它分配给Temp logger,这使得5. 变得多余,因为级别是在3. 中设置的
      哦等等,你以为你只是想让 root 记录器完成这项工作,因为 1.
    7. 您使用记录器记录了ERROR

    发生了什么

    您的Temp 记录器在ERROR 级别接受了字符串boo。然后告诉它的处理程序处理字符串。由于该处理程序没有分配任何formatter,因此它按原样将字符串输出到stdoutboo
    之后,Temp logger 将字符串 boo 传递给他的上级 root logger。

    root 记录器接受了日志,因为日志级别是 ERROR > WARNING。 然后root 记录器告诉它的处理程序处理字符串boo。 此处理程序将格式字符串应用于boo。添加时间戳,添加位置,添加通过日志的记录器名称等
    最后将结果输出到stderr:2021-04-26 18:54:24,329:&lt;module&gt;:1:ERROR:Temp:boo

    改正

    由于您的代码完全按照您的要求执行,因此您必须尽可能详细地告诉它。

    • 只有在你懒惰的时候才使用basicConfig。通过删除 basicConfig 行,您的问题解决了。
    • 使用logger = logging.getLogger('__name__') 以便记录器具有模块的名称。查看日志并确切知道它来自哪个import path
    • 决定记录器是否应为自己保留日志或使用propagate 属性将其传递到链上。在您的情况下,logger.propagate = False 也可以解决问题。
    • 使用dictConfig 文件,以免弄乱配置代码。
    • 实际上,您不应该将处理程序添加到您的记录器中,而只是让记录器将日志一直传递到root 并让根执行实际的记录。为什么?
      • 其他人将您的代码用作模块,可以控制日志记录。例如,不输出到 stdout 而是输出到 tcp/udp,以不同的格式输出等。
      • 您可以通过propagating=False 完全关闭特定记录器的记录。
      • 如果您只将代码中的所有处理程序和格式化程序添加到root 记录器,您就可以准确地了解它们。您可以集中控制日志记录。

    【讨论】:

    • 感谢您的详细解答。
    猜你喜欢
    • 2011-03-08
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 2015-10-17
    • 1970-01-01
    • 2020-05-27
    • 2018-05-05
    • 1970-01-01
    相关资源
    最近更新 更多