简体   繁体   English

为什么 logger.info() 只有在调用 logging.info() 后才会出现?

[英]Why does logger.info() only appear after calling logging.info()?

I am using Python 3.6.4.我正在使用 Python 3.6.4。 I first encountered an issue where logger.setLevel(logging.INFO) was ignored, and came across this answer , which confused me and gave rise to this question.我第一次遇到logger.setLevel(logging.INFO)被忽略的问题,并遇到了这个答案,这让我感到困惑并产生了这个问题。

Given the code below,鉴于下面的代码,

1. Why does logging.info('2') get printed in Snippet 2, but not in 1? 1. 为什么logging.info('2')打印在 Snippet 2 中,而不是 1? (Isn't logging.info() is a module level function? Why does naming a logger affect this call?) logging.info()不是模块级函数吗?为什么命名记录器会影响这个调用?)

2. Why does logger.info('3') get printed, but not logger.info('1') ? 2. 为什么logger.info('3')被打印出来,而不是logger.info('1')

Snippet 1片段 1

>>> import logging
>>> logger = logging.getLogger('foo')  # named logger
>>> logger.setLevel(logging.INFO)
>>> logger.info('1')
>>> logging.info('2')  # prints nothing
>>> logger.info('3')
INFO:foo:3

Snippet 2片段 2

>>> import logging
>>> logger = logging.getLogger()  # no name
>>> logger.setLevel(logging.INFO)
>>> logger.info('1')
>>> logging.info('2')  # printed
INFO:root:2
>>> logger.info('3')
INFO:root:3

As you point out, the difference between the snippets is in how you get the logger object:正如您所指出的,片段之间的区别在于您如何获取logger对象:

logger = logging.getLogger('foo')
logger = logging.getLogger()

The key thing is that, in the second case, you are getting the "root" logger.关键是,在第二种情况下,您将获得“根”记录器。 In the first case, on the other hand, you are getting a "sublogger" of the root called foo .另一方面,在第一种情况下,您将获得名为foo的根的“子记录器”。

Now, let's go step by step.现在,让我们一步一步来。

logger.setLevel(logging.INFO)

Here you set the level of the logger.在这里设置记录器的级别。 In the first case, you are setting the level for the logger foo .在第一种情况下,您正在设置记录器foo的级别。 On creation, new loggers do not have a level, so they process every message;创建时,新记录器没有级别,因此它们处理每条消息; here you are saying that only messages with severity INFO or higher should be processed.在这里,您是说只应处理严重性为INFO或更高的消息。 But, in the second case, logger is the root logger.但是,在第二种情况下, logger是根记录器。 The confusing bit here is that, unlike new loggers, the root logger default level is WARN , so nothing below that level is processed unless you change it.这里令人困惑的一点是,与新的记录器不同,根记录器的默认级别是WARN ,因此除非您更改它,否则不会处理低于该级别的任何内容。 So, after this line:所以,在这一行之后:

  • In the first snippet, the root logger is set to WARN level, and the foo logger is set to INFO level.在第一个片段中,根记录器设置为WARN级别,而foo记录器设置为INFO级别。
  • In the second snippet, the root logger is set to INFO level.在第二个代码段中,根记录器设置为INFO级别。
logger.info('1')

First logged line.第一个记录的行。 Here you have the same behavior in both cases.在这里,您在两种情况下都有相同的行为。 The message is INFO , and logger is set to that severity, so the message is processed.该消息是INFO ,并且logger设置为该严重性,因此该消息被处理。 However, you do not have any handler set in logger , so nothing is actually done with the message.但是,您没有在logger设置任何处理程序,因此实际上没有对消息进行任何处理。

logging.info('2')

Now this is more interesting.现在这更有趣。 What is important here is what logging.info actually does, which is not the same as if you call the info method of the root logger object:这里重要的是logging.info实际做了什么,这与调用根记录器对象的info方法不同:

Log a message with severity 'INFO' on the root logger.在根记录器上记录严重性为“INFO”的消息。 If the logger has no handlers, call basicConfig() to add a console handler with a pre-defined format.如果记录器没有处理程序,请调用 basicConfig() 以添加具有预定义格式的控制台处理程序。

So, this function will make a console handler by itself if no handlers are registered for the root logger.因此,如果没有为根记录器注册处理程序,此函数将自行创建一个控制台处理程序。 So any message received by the root logger or subloggers will now be printed to console.因此,根记录器或子记录器收到的任何消息现在都将打印到控制台。 In the first case, however, turns out that the root logger has still its default severity filter, WARN , so the console handler is registered, but the message is actually ignored.然而,在第一种情况下,根记录器仍然具有其默认的严重性过滤器WARN ,因此控制台处理程序已注册,但消息实际上被忽略了。 In the second case, though, you set the severity level of the root logger to INFO , so the message is processed and printed by the console handler.但是,在第二种情况下,您将根记录器的严重性级别设置为INFO ,因此消息由控制台处理程序处理和打印。

logger.info('3')

Hopefully this should make sense now.希望这现在应该是有意义的。 Now you have a console handler attached to the root logger.现在您有一个附加到根记录器的控制台处理程序。 In the first case, logger is the foo logger, and its severity is set to INFO , so the message is processed and, since it is a sublogger of the root logger, it is printed by the console handler registered for that one.在第一种情况下, loggerfoo记录器,其严重性设置为INFO ,因此消息被处理,并且由于它是根记录器的子记录器,因此它由为该记录器注册的控制台处理程序打印。 In the second case, you are just logging to the root logger, which has INFO severity and the registered console handler.在第二种情况下,您只是登录到具有INFO严重性和已注册控制台处理程序的根记录器。

Note that in the last line of the first case, the message processed by the foo logger is handled by the handler registered in the root logger even though the severity level of the root logger is WARN .请注意,在第一种情况的最后一行,由foo记录器处理的消息由在根记录器中注册的处理程序处理,即使根记录器的严重性级别是WARN Log handlers are not responsible for filtering by severity, that is done by the logger itself, so once a logger has decided that a message should be processed, it is handled by all its handlers and the handlers from parent loggers.日志处理程序不负责按严重性过滤,这是由记录器本身完成的,所以一旦记录器决定应该处理一条消息,它就会由它的所有处理程序和来自父记录器的处理程序处理。 This is a very useful feature, since it allows you to have a higher system-level log level and a lower one for specific modules that you are more interested in (eg for debugging or simply reporting).这是一个非常有用的功能,因为它允许您拥有更高的系统级日志级别,并为您更感兴趣的特定模块(例如,用于调试或简单报告)设置更低的日志级别。

Refer to the accepted answer on that post:请参阅该帖子上已接受的答案

If you don't configure logging with any handlers (as in your post - you only configure a level for your logger, but no handlers anywhere), you'll get an internal handler "of last resort" which is set to output just the message (with no other formatting) at the WARNING level.如果您没有使用任何处理程序配置日志记录(如在您的帖子中 - 您只为您的记录器配置一个级别,但在任何地方都没有处理程序),您将获得一个内部处理程序“最后的手段”,该处理程序设置为仅输出WARNING级别的消息(没有其他格式)。

Your level is currently less than WARNING , so it is not output.您的级别当前低于WARNING ,因此不会输出。 This changes with a call to basicConfig() which you may/should do explicitly, otherwise it's not being handled until it's called from logging.info (or one of the other convenience functions).这会随着对basicConfig()的调用而basicConfig() ,您可以/应该显式调用它,否则它不会被处理,直到从logging.info (或其他便利函数之一)调用它。

The documentation observes this:文档观察到这一点:

Note The above module-level convenience functions, which delegate to the root logger, call basicConfig() to ensure that at least one handler is available.注意上面的模块级便捷函数委托给根记录器,调用basicConfig()以确保至少有一个处理程序可用。 Because of this, they should not be used in threads, in versions of Python earlier than 2.7.1 and 3.2, unless at least one handler has been added to the root logger before the threads are started.因此,它们不应在线程中使用,在早于 2.7.1 和 3.2 的 Python 版本中,除非在启动线程之前至少已将一个处理程序添加到根记录器。 In earlier versions of Python, due to a thread safety shortcoming in basicConfig() , this can (under rare circumstances) lead to handlers being added multiple times to the root logger, which can in turn lead to multiple messages for the same event.在早期版本的 Python 中,由于basicConfig()的线程安全缺陷,这可能(在极少数情况下)导致处理程序被多次添加到根记录器,这反过来又会导致同一事件的多条消息。

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

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