python日志处理程序中setLevel的重点是什么?

Kev*_*rke 52 python logging

假设我有以下代码:

import logging
import logging.handlers

a = logging.getLogger('myapp')
h = logging.handlers.RotatingFileHandler('foo.log')
h.setLevel(logging.DEBUG)
a.addHandler(h)

# The effective log level is still logging.WARN
print a.getEffectiveLevel() 
a.debug('foo message')
a.warn('warning message')
Run Code Online (Sandbox Code Playgroud)

我希望logging.DEBUG处理程序上的设置会导致调试级别的消息写入日志文件.但是,这将打印30作为有效级别(等于logging.WARNING,默认值),并仅将warn消息记录到日志文件,而不是调试消息.

似乎处理程序的日志级别被丢弃在地板上,例如它被默默地忽略.这让我想知道,为什么要setLevel处理器呢?

Bak*_*riu 50

它允许更精细的控制.默认情况下,根记录器具有WARNING级别设置,这意味着它不会打印较低级别的消息(无论处理程序的级别如何设置!).但是,如果将根记录器的级别设置为DEBUG,则确实将消息发送到日志文件:

import logging
import logging.handlers

a = logging.getLogger('myapp')
a.setLevel(logging.DEBUG)   # set root's level
h = logging.handlers.RotatingFileHandler('foo.log')
h.setLevel(logging.DEBUG)
a.addHandler(h)
print a.getEffectiveLevel() 
a.debug('foo message')
a.warn('warning message')
Run Code Online (Sandbox Code Playgroud)

现在,您想添加一个不记录调试信息的新处理程序的图像.您只需设置处理程序日志记录级别即可完成此操作:

import logging
import logging.handlers

a = logging.getLogger('myapp')
a.setLevel(logging.DEBUG)   # set root's level

h = logging.handlers.RotatingFileHandler('foo.log')
h.setLevel(logging.DEBUG)
a.addHandler(h)

h2 = logging.handlers.RotatingFileHandler('foo2.log')
h2.setLevel(logging.WARNING)
a.addHandler(h2)

print a.getEffectiveLevel() 
a.debug('foo message')
a.warn('warning message')
Run Code Online (Sandbox Code Playgroud)

现在,日志文件foo.log将包含两个消息,而该文件foo2.log将仅包含警告消息.您可能对只有错误级别消息的日志文件感兴趣,然后只需添加一个Handler并设置其级别logging.ERROR,使用相同的一切Logger.

你可能会认为的Logger日志记录级别作为一个全球性的限制上的消息对于一个给定的记录器"有趣" 和它的处理程序.之后记录器考虑的消息将发送给处理程序,处理程序执行自己的过滤和日志记录过程.

  • 因此,最佳做法是设置较低级别的根记录器并通过处理程序级别控制日志记录.我对么? (5认同)
  • 这不是“最佳实践”,只是做任何其他事情都没有用。如果处理程序的级别为DEBUG,但是记录器仅发送ERROR,则处理程序当然只会接收(并传递)ERROR。 (5认同)
  • 然而,人们想知道为什么根记录器的默认日志级别不是"DEBUG"或"0",实际上,因为如果你不知道这个"技巧",那么你就像OP一样划伤你的脑袋,并且想知道为什么您的日志消息无法打印. (5认同)
  • @Christoph我相信因为`DEBUG`用于*调试*应用程序,因此只应在开发人员调试它时出现.将默认级别设置为"警告"可避免在真实用户使用应用程序时,某些丢失的配置会导致打印噪音.换句话说:显示低级别消息应该需要用户干预.(显然这只是一种意见,人们也可以反驳). (2认同)
  • 好的,但在这种情况下,据我了解,我至少希望“INFO”成为默认值,然后......无论如何,意见,正如你所说。 (2认同)

cwa*_*ole 21

在Python日志记录中,有两个不同的概念:记录器记录的级别和处理程序实际激活的级别.

当进行日志调用时,基本上发生的是:

if self.level <= loglevel:
    for handler in self.handlers:
        handler(loglevel, message)
Run Code Online (Sandbox Code Playgroud)

然后,每个处理程序将调用:

if self.level <= loglevel:
    # do something spiffy with the log!
Run Code Online (Sandbox Code Playgroud)

如果您想要对此进行真实演示,可以查看Django的配置设置.我会在这里提供相关代码.

LOGGING = {
    #snip
    'handlers': {
        'null': {
            'level': 'DEBUG',
            'class': 'logging.NullHandler',
        },
        'console':{
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'mail_admins': {
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler',
            'filters': ['special']
        }
    },
    'loggers': {
        #snip
        'myproject.custom': {
            # notice how there are two handlers here!
            'handlers': ['console', 'mail_admins'],
            'level': 'INFO',
            'filters': ['special']
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,在上面的配置中,只有日志到达getLogger('myproject.custom').info及以上才会进行日志记录处理.当发生这种情况时,控制台将输出所有结果(它将输出所有结果,因为它被设置为DEBUG水平),而mail_admins记录器将发生所有ERRORs,FATALs和CRITICALs.

我想一些不是Django的代码也可能有所帮助:

import logging.handlers as hand
import logging as logging

# to make things easier, we'll name all of the logs by the levels
fatal = logging.getLogger('fatal')
warning = logging.getLogger('warning')
info = logging.getLogger('info')

fatal.setLevel(logging.FATAL)
warning.setLevel(logging.WARNING)
info.setLevel(logging.INFO)    

fileHandler = hand.RotatingFileHandler('rotating.log')

# notice all three are re-using the same handler.
fatal.addHandler(fileHandler)
warning.addHandler(fileHandler)
info.addHandler(fileHandler)

# the handler should log everything except logging.NOTSET
fileHandler.setLevel(logging.DEBUG)

for logger in [fatal,warning,info]:
    for level in ['debug','info','warning','error','fatal']:
        method = getattr(logger,level)
        method("Debug " + logger.name + " = " + level)

# now, the handler will only do anything for *fatal* messages...
fileHandler.setLevel(logging.FATAL)

for logger in [fatal,warning,info]:
    for level in ['debug','info','warning','error','fatal']:
        method = getattr(logger,level)
        method("Fatal " + logger.name + " = " + level)
Run Code Online (Sandbox Code Playgroud)

这导致:

Debug fatal = fatal
Debug warning = warning
Debug warning = error
Debug warning = fatal
Debug info = info
Debug info = warning
Debug info = error
Debug info = fatal
Fatal fatal = fatal
Fatal warning = fatal
Fatal info = fatal
Run Code Online (Sandbox Code Playgroud)

再次,注意如何info在记录的东西info,warning,error,和fatal当日志处理程序被设置为DEBUG,但是当处理器被设置为FATAL突然只有FATAL电文提出它的文件.


Vin*_*jip 17

处理程序代表记录事件的不同受众.处理程序的级别用于控制特定受众看到的输出的详细程度,并且除了在记录器上设置的任何级别之外还起作用.记录器的级别用于控制从应用程序或库的不同部分进行日志记录的总体详细程度.

有关如何处理日志记录事件的更多信息,请参阅此图:

在此输入图像描述

  • 它默认允许所有事件通过。 (2认同)

gri*_*tis 6

规则

当且仅当

handler.level <= message.level
&&
logger.level <= message.level
Run Code Online (Sandbox Code Playgroud)

然后打印消息。

提醒:值越低,内容越详细

Level    | Numeric value
---------|--------------
CRITICAL | 50
ERROR    | 40
WARNING  | 30
INFO     | 20
DEBUG    | 10
NOTSET   | 0
Run Code Online (Sandbox Code Playgroud)

参考: https: //docs.python.org/3/library/logging.html#logging-levels

换句话说

如果记录器设置为WARNING,则处理程序是否有更详细的设置也没关系。当它到达处理程序时它已经被过滤了。

一个完整的例子

import logging


handler_info = logging.StreamHandler()
handler_info.setLevel("INFO")
handler_info.setFormatter(logging.Formatter(
    f"%(levelname)s message for %(name)s handled by handler_info: %(message)s"))

handler_debug = logging.StreamHandler()
handler_debug.setLevel("DEBUG")
handler_debug.setFormatter(logging.Formatter(
    f"%(levelname)s message for %(name)s handled by handler_debug: %(message)s"))

logger_info = logging.getLogger('logger_info')
logger_info.setLevel("INFO")
logger_info.addHandler(handler_info)
logger_info.addHandler(handler_debug)

logger_debug = logging.getLogger('logger_debug')
logger_debug.setLevel("DEBUG")
logger_debug.addHandler(handler_info)
logger_debug.addHandler(handler_debug)

print()
print("output for `logger_info.info('hello')`")
logger_info.info("hello")
print()
print("output for `logger_info.debug('bonjour')`")
logger_info.debug("bonjour")
print()
print("output for `logger_debug.info('hola')`")
logger_debug.info("hola")
print()
print("output for `logger_debug.debug('ciao')`")
logger_debug.debug("ciao")
print()
Run Code Online (Sandbox Code Playgroud)

这使

output for `logger_info.info('hello')`
INFO message for logger_info handled by handler_info: hello
INFO message for logger_info handled by handler_debug: hello

output for `logger_info.debug('bonjour')`
# nothing, because message.level < logger.level

output for `logger_debug.info('hola')`
INFO message for logger_debug handled by handler_info: hola
INFO message for logger_debug handled by handler_debug: hola

output for `logger_debug.debug('ciao')`
DEBUG message for logger_debug handled by handler_debug: ciao
# nothing from handler_info, because message.level < handler.level

Run Code Online (Sandbox Code Playgroud)