76

假设我有以下代码:

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

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

似乎处理程序的日志级别正在下降,例如,它被默默地忽略了。这让我想知道,为什么要setLevel在处理程序上?

4

4 回答 4

73

它允许更精细的控制。默认情况下,根记录器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')

现在,您要添加不记录调试信息的新处理程序的图像。您可以通过简单地设置处理程序日志记录级别来做到这一点:

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

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

您可能会将日志记录级别视为对给定记录器及其处理程序Logger“有趣”的消息的全局限制。记录器随后考虑的消息被发送到处理程序,处理程序执行自己的过滤和记录过程。

于 2013-07-16T05:43:45.097 回答
26

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

当调用 log 时,基本上发生的是:

if self.level <= loglevel:
    for handler in self.handlers:
        handler(loglevel, message)

而这些处理程序中的每一个都将调用:

if self.level <= loglevel:
    # do something spiffy with the log!

如果您想对此进行实际演示,可以查看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']
        }
    }
}

因此,在上面的配置中,只有到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)

这导致:

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

再次注意在, ,和日志处理程序设置为 时是如何info记录的,但是当处理程序突然设置为时,只有消息进入文件。infowarningerrorfatalDEBUGFATALFATAL

于 2013-07-16T05:50:01.137 回答
23

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

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

在此处输入图像描述

于 2013-07-16T07:08:38.287 回答
1

规则

当且仅当

handler.level <= message.level
&&
logger.level <= message.level

然后打印消息。

提醒:较低的值更冗长

Level    | Numeric value
---------|--------------
CRITICAL | 50
ERROR    | 40
WARNING  | 30
INFO     | 20
DEBUG    | 10
NOTSET   | 0

参考: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()

这使

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

于 2020-06-24T00:29:52.497 回答