简体   繁体   English

如何避免从 Python 中的自定义记录器调用根处理程序?

[英]How to avoid root handler being called from the custom logger in Python?

I have a basic config for the logging module with debug level - now I want to create another logger with error level only.我有一个具有调试级别的日志记录模块的基本配置 - 现在我只想创建另一个具有错误级别的记录器。 How can I do that?我怎样才能做到这一点?

The problem is that the root handler is called in addition to the error-handler - this is something I want to avoid.问题是除了错误处理程序之外还调用了根处理程序 - 这是我想要避免的事情。

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

The above code prints boo twice while I expect once only, and I have no idea what to do with this annoying issue...上面的代码打印了两次 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)>]

Some basics about the logging module关于日志记录模块的一些基础知识

  • logger: a person who receives the log string, sorts it by a predefined level, then uses his own handler if any to process the log and, by default passes the log to his superior. logger:接收日志字符串的人,按预定义的级别对其进行排序,然后使用他自己的处理程序(如果有)来处理日志,并且默认情况下将日志传递给他的上级。
  • root logger: the superior of superiors, does all the things that a normal logger does but doesn't pass the received log to anyone else. root logger:上级中的上级,做普通 logger 所做的所有事情,但不会将收到的日志传递给其他任何人。
  • handler : a private contractor of a logger, who actually does anything with the log, eg. handler :记录器的私人承包商,他实际上对日志做任何事情,例如。 formats the log, writes it to a file or stdout , or sends it through tcp/udp.格式化日志,将其写入文件或stdout ,或通过 tcp/udp 发送。
  • formatter : a theme, a design that the handler applies to the log. formatter :主题, handler应用于日志的设计。
  • basicConfig : a shortcut way to config the root logger. basicConfig :配置root记录器的快捷方式。 This is useful when you want him to do all the job and all his lower rank loggers would just pass the log to him.当您希望他完成所有工作并且他的所有较低级别的伐木工只会将日志传递给他时,这很有用。
    With no argument, basicConfig sets root logger's level to WARNING and add a StreamHandler that output the log to stderr .在没有参数的情况下, basicConfigroot记录器的级别设置为WARNING并将 output 日志添加到stderrStreamHandler

What you did你做了什么

  1. You created a format and used a shortcut basicConfig to config the root logger.您创建了一种format并使用快捷方式basicConfig来配置root记录器。 You want the root logger to do all the actual logging things您希望根记录器执行所有实际的记录操作
  2. You created a new low-rank logger Temp您创建了一个新的低等级记录器Temp
  3. You want it to accept logs with only ERROR level and above.您希望它只接受ERROR级别及以上的日志。
  4. You created another StreamHandler .您创建了另一个StreamHandler Which output to stdout by default.其中 output 默认为stdout
  5. You want it to handle only ERROR level and above您希望它只处理ERROR级别及以上
  6. Oh, you assigned it to Temp logger, which made 5. redundant since the level is set in 3.哦,你把它分配给了Temp记录器,这使得5.多余,因为级别设置为3.
    Oh wait, thought you just want the root logger to do the job since 1. !哦等等,以为你只是想让root记录器从1.开始就完成这项工作。!
  7. You logged an ERROR with your logger.您使用记录器记录了一个ERROR

What happened发生了什么

Your Temp logger accepted a string boo at ERROR level.您的Temp记录器接受了ERROR级别的字符串boo Then told its handler to process the string.然后告诉它的处理程序处理字符串。 Since this handler didn't have any formatter assigned to it, it outputted the string as-is to stdout : boo由于该处理程序没有分配任何formatter ,因此它按原样将字符串输出到stdoutboo
After that, Temp logger passed the string boo to his superior, the root logger.之后, Temp记录器将字符串boo传递给他的上级,即root记录器。

The root logger accepted the log since the log level is ERROR > WARNING .由于日志级别为ERROR > WARNING ,因此root记录器接受了日志。 The root logger then told its handler to process the string boo .然后root记录器告诉其处理程序处理字符串boo This handler apply the format string to boo .此处理程序将格式字符串应用于boo Add timestamp, add location, add the name of logger that passed the log, etc.添加时间戳,添加位置,添加通过日志的记录器的名称等。
Finally it outputted the result to stderr : 2021-04-26 18:54:24,329:<module>:1:ERROR:Temp:boo最后将结果输出到stderr2021-04-26 18:54:24,329:<module>:1:ERROR:Temp:boo

Make it right改正它

Since your code does exactly what you tell it to do, you have to tell it as much detail as possible.由于您的代码完全按照您的要求执行,因此您必须尽可能详细地告诉它。

  • Only use basicConfig when you are lazy.只有在你懒惰的时候才使用basicConfig By removing basicConfig line, your problem solved.通过删除basicConfig行,您的问题解决了。
  • Use logger = logging.getLogger('__name__') so that the logger has the name of the module.使用logger = logging.getLogger('__name__')以便记录器具有模块的名称。 Looking at the log and know exactly which import path that it came from.查看日志并确切知道它来自哪个import path
  • Decide if a logger should keep the log for it own or pass it up the chain with propagate property.决定一个记录器是应该为它自己保留日志还是通过propagate属性将它传递到链上。 In your case, logger.propagate = False also solves the problem.在您的情况下, logger.propagate = False也可以解决问题。
  • Use a dictConfig file so you don't get messed with the config code.使用dictConfig文件,这样您就不会弄乱配置代码。
  • In practice, you should not add handlers to your logger, and just let the logger pass the log all the way to the root and let the root do the actual logging.在实践中,您不应该将处理程序添加到您的记录器,而只是让记录器将日志一直传递给root并让根执行实际的日志记录。 Why?为什么?
    • Someone else uses your code as a module, can control the logging.其他人将您的代码用作模块,可以控制日志记录。 For example, not output to stdout but to tcp/udp, output with a different format, etc.例如,不是 output 到 stdout 而是到 tcp/udp,output 具有不同的格式等。
    • You can turn off the logging from a specific logger entirely, by propagating=False .您可以通过propagating=False完全关闭特定记录器的日志记录。
    • You know exactly all the handlers and formatters in the code if you only added them to the root logger.如果您只将它们添加到root记录器,您就可以准确地知道代码中的所有处理程序和格式化程序。 You have a centralized control over the logging.您可以集中控制日志记录。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM