Какой смысл setLevel в обработчике журналирования python?

Допустим, у меня есть следующий код:

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 на обработчике вообще?

3 ответа

Решение

Это позволяет более тонкий контроль. По умолчанию корневой логгер имеет 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 будет содержать только предупреждающее сообщение. Вас может заинтересовать наличие в журнале только сообщений об ошибках, а затем просто добавьте Handler и установите его уровень logging.ERRORвсе использует одно и то же Logger,

Вы можете думать о Logger уровень ведения журнала как глобальное ограничение на то, какие сообщения "интересны" для данного регистратора и его обработчиков. Сообщения, которые впоследствии рассматриваются регистратором, отправляются обработчикам, которые выполняют свой собственный процесс фильтрации и ведения журнала.

В журнале Python есть две разные концепции: уровень, на котором регистрируется логгер, и уровень, который фактически активирует обработчик.

Когда делается звонок в журнал, в основном происходит следующее:

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, FATALс и 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 вошли что-то на info, warning, error, а также fatal когда обработчик журнала был установлен в DEBUG, но когда обработчик был установлен в FATAL только внезапно FATAL сообщения сделали это в файл.

Обработчики представляют разные аудитории для регистрации событий. Уровни в обработчиках используются для контроля многословности вывода, видимого конкретной аудиторией, и действуют в дополнение к любым уровням, установленным в регистраторах. Уровни в регистраторах используются для контроля общей детализации ведения журналов из разных частей приложения или библиотеки.

Смотрите эту диаграмму для получения дополнительной информации о том, как обрабатываются события регистрации:

правило

если и только если

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

другими словами

если регистратор установлен на 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

Другие вопросы по тегам