Уровень Python Logger, унаследованный от корня, установленного в предупреждение по умолчанию

В моей программе я определяю регистратор в начале, который выглядит примерно так:

def start_logger():
        fh = logging.handlers.RotatingFileHandler('logger.log', 
                                                  maxBytes=1000000, 
                                                  backupCount=100)
        fh.setLevel(logging.DEBUG)

        ch = logging.StreamHandler(sys.stdout)
        ch.setLevel(logging.DEBUG)

        fh_fmt = '%(asctime)s %(levelname)8s %(message)s [%(filename)s:%(lineno)d]'
        #fh_fmt = '%(asctime)s - %(funcName)s - %(levelname)s - %(message)s'

        ch_fmt = '%(asctime)s %(levelname)8s %(message)s [%(filename)s:%(lineno)d]'
        #ch_fmt = '%(funcName)s - %(levelname)s - %(message)s'

        fh.setFormatter(logging.Formatter(fh_fmt))
        ch.setFormatter(logging.Formatter(ch_fmt))

        root = logging.getLogger()
        root.addHandler(fh)
        root.addHandler(ch)

Затем у меня есть несколько файлов, которые вызываются из моей основной программы. Чтобы они работали правильно, мне нужно сделать следующее:

import logging
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
log.debug("This debug message is ounly output when I set the level again to debug for this file. Otherwise the the log level for this file is WARNING.")

Почему уровень по умолчанию для всех импортируемых модулей установлен как предупреждение. Почему мне нужно снова установить уровень DEBUG для каждого из них, когда они импортируют мой корневой логгер с log = logging.getLogger (name)? Это лучший способ создать модуль регистрации через пакет с различными модулями, или есть лучшее решение?

1 ответ

Давайте начнем с рассмотрения того, что делает Handler.setLevel:

Устанавливает порог для этого обработчика на lvl. Сообщения, которые менее серьезны, чем lvl, будут игнорироваться. Когда создается обработчик, уровень устанавливается равным NOTSET (что приводит к обработке всех сообщений).

Любые сообщения, менее серьезные, чем lvl, игнорируются. По сути, установка его на DEBUG не имеет смысла (если вы не определяете свои собственные уровни журналов), потому что нет менее серьезного сообщения, чем отладка. Таким образом, это означает, что обработчик не будет игнорировать любые сообщения.

setLevel это правильная идея, но вы называете это не на тот объект. Посмотрите на Logger.setLevel:

Устанавливает порог для этого логгера на lvl. Сообщения, которые менее серьезны, чем lvl, будут игнорироваться. Когда создается регистратор, уровень устанавливается равным NOTSET (что приводит к обработке всех сообщений, когда регистратор является корневым регистратором, или делегированию родительскому элементу, когда регистратор не является корневым регистратором). Обратите внимание, что корневой логгер создается с уровнем WARNING.

Термин "делегирование родительскому объекту" означает, что, если у регистратора есть уровень NOTSET, его цепочка регистраторов предков пересекается, пока не будет найден либо предок с уровнем, отличным от NOTSET, либо пока не будет достигнут корень.

Если предок найден с уровнем, отличным от NOTSET, то уровень этого предка рассматривается как эффективный уровень средства ведения журнала, с которого начался поиск предка, и используется для определения способа обработки события ведения журнала.

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

TL; DR: решение простое: установите уровень в логгере, а не в обработчике. Следующий код должен делать то, что вам нужно:

def start_logger():
    fh = logging.handlers.RotatingFileHandler('logger.log', 
                                              maxBytes=1000000, 
                                              backupCount=100)

    ch = logging.StreamHandler(sys.stdout)

    fh_fmt = '%(asctime)s %(levelname)8s %(message)s [%(filename)s:%(lineno)d]'
    #fh_fmt = '%(asctime)s - %(funcName)s - %(levelname)s - %(message)s'

    ch_fmt = '%(asctime)s %(levelname)8s %(message)s [%(filename)s:%(lineno)d]'
    #ch_fmt = '%(funcName)s - %(levelname)s - %(message)s'

    fh.setFormatter(logging.Formatter(fh_fmt))
    ch.setFormatter(logging.Formatter(ch_fmt))

    logging.basicConfig(level=logging.DEBUG)
    root = logging.getLogger()
    root.addHandler(fh)
    root.addHandler(ch)

Как только вы это сделаете, вам не нужно setLevel звоните, когда делаете детей.

Да, и чтобы ответить на ваши другие вопросы: именно так вы и должны использовать библиотеку журналов. (На самом деле я просто записываю все в корневой логгер, потому что мне не нужна детальность, которую вы разработали, но когда у вас есть для этого аргументы, вы делаете это так, как должны.)

РЕДАКТИРОВАТЬ: Очевидно, setLevel, похоже, не работает на корневой логгер. Вместо этого, прежде чем получить доступ к корневому логгеру, вы должны установить basicConfig. Установка уровня в logging.basicConfig будет делать то, что вам нужно (по крайней мере, это сработало в моих тестах). Обратите внимание, что это соответствует примеру, приведенному в " Ведение журнала из нескольких модулей", поэтому должен решить вашу проблему.

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