Как разделить обработчики логов в Python - PullRequest
0 голосов
/ 20 сентября 2019

У меня есть ситуация, когда я хочу создать два отдельных объекта logger в Python, каждый со своим независимым обработчиком.Под «отдельным» я подразумеваю, что хочу иметь возможность передавать инструкцию журнала каждому объекту независимо, не загрязняя другой журнал.

main.py

import logging
from my_other_logger import init_other_logger

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO, handlers=[logging.StreamHandler(sys.stdout)])

other_logger = init_other_logger(__name__)

logger.info('Hello World') # Don't want to see this in the other logger
other_logger.info('Goodbye World') # Don't want to see this in the first logger


my_other_logger.py

import logging
import os, sys

def init_other_logger(namespace):
    logger = logging.getLogger(namespace)
    logger.setLevel(logging.DEBUG)
    fh = logging.FileHandler(LOG_FILE_PATH)
    logger.addHandler(fh)
    formatter = logging.Formatter('%(levelname)s:%(name)s:%(message)s')
    fh.setFormatter(formatter)
    #logger.propagate = False
    return logger

Единственная конфигурация, которую я смог определить как полезную, это свойство logger.propagate.Выполнение приведенного выше кода «как есть» направляет все операторы журнала и в поток журнала, и в файл журнала.Когда у меня logger.propagate = False, ничего не передается в поток журнала, и оба объекта журнала снова передают свои выходные данные в файл журнала.

Как создать один объект журнала, который отправляет журналы только одному обработчику, а другойобъект журнала, который отправляет журналы другому обработчику?

Ответы [ 2 ]

0 голосов
/ 20 сентября 2019

Во-первых, давайте посмотрим, что происходит, прежде чем мы сможем перейти к решению.

logger = logging.getLogger(__name__): когда вы делаете это, вы получаете или создаете регистратор с именем 'main'.Так как это первый вызов, он создаст этот регистратор.

other_logger = init_other_logger(__name__): когда вы делаете это, вы снова получаете или создаете регистратор с именем 'main'.Поскольку это вызов second , он извлечет созданный выше регистратор.Таким образом, вы не создаете экземпляр нового регистратора, но получаете ссылку на тот же регистратор, созданный выше.Вы можете проверить это, выполнив печать после вызова init_other_logger формы: print(logger is other_logger).

Что произойдет дальше, если вы добавите FileHandler и Formatter в 'main' logger(внутри функции init_other_logger), и вы вызываете 2 журнала вызовов с помощью метода info().Но вы делаете это с тем же регистратором .

Так вот:

logger.info('Hello World')
other_logger.info('Goodbye World')

по сути то же самое, что и это:

logger.info('Hello World')
logger.info('Goodbye World')

Теперь уже не так удивительно, что оба регистратора выводят и файл, иstream.


Решение

Итак, очевидная вещь, которую нужно сделать, это позвонить вашему init_other_logger с другим именем.

Я бы порекомендовал против решение, которое предлагает другой ответ, потому что это НЕ , как все должно быть сделано, когда вам нужен независимый регистратор. В документации четко сказано, что вы должны НИКОГДА не создавать экземпляры логгера напрямую, но всегда через функцию getLogger модуля logging.

Как мыобнаруженный выше, когда вы делаете вызов logging.getLogger(logger_name), он либо получает , либо создает регистратор с logger_name.Так что это прекрасно работает, когда вам нужен уникальный регистратор.Однако помните, что эта функция idemptotent означает, что она будет создавать регистратор с заданным именем только при первом вызове и будет возвращать этот регистратор, если вы вызовете его с тем же именем, независимо от того, сколько раз выПозвоню после.

Так, например:

  • первый вызов вида logging.getLogger('the_rock') - создает ваш уникальный регистратор

  • второй вызов формы logging.getLogger('the_rock') - извлекает указанный выше регистратор

Вы можете видеть, что это особенно полезно, если вы, например:

  • Настройте регистратор на Formatters и Filters где-нибудь в вашем проекте, например, в project_root/main_package/__init__.py.
  • Хотите использовать этот регистратор где-нибудь во вторичном пакете, который находится в project_root/secondary_package/__init__.py.

В secondary_package/__init__.py вы можете сделать простой вызов вида: logger = logging.getLogger('main_package'), и вы будете использовать этот регистратор со всеми его прибамбасами.


Внимание!

Даже если вы на данном этапе будете использовать вашу функцию init_other_logger для созданияуникальный регистратор, он все равно будет выводиться как в файл, так и на консоль.Замените эту строку other_logger = init_other_logger(__name__) на other_logger = init_other_logger('the_rock'), чтобы создать уникальный регистратор и снова запустить код.Вы все равно увидите вывод , записанный как в консоль, так и в файл .

Почему?

, поскольку он будет использовать как FileHandler, так и StreamHandler.

Почему?

Потому как работает лесозаготовительная техника.Ваш регистратор отправит свое сообщение через свои обработчики, затем он будет распространяться вплоть до корневого регистратора, где он будет использовать StreamHandler, который вы подключили с помощью вызова basicConfig.Итак, свойство propagate, которое вы обнаружили , на самом деле - это то, что вам нужно в вашем случае, потому что вы создаете пользовательский регистратор, который вы хотите отправлять сообщения только через свои обработчики, подключенные вручную, и не генерировать никакихв дальнейшем.Раскомментируйте logger.propagate = False после создания уникального регистратора, и вы увидите, что все работает как положено.

0 голосов
/ 20 сентября 2019

Оба ваших обработчика установлены на одном регистраторе.Вот почему они не разделены.

logger is other_logger, потому что logging.getLogger(__name__) is logging.getLogger(__name__)

Либо создайте регистратор непосредственно для второго журнала logging.Logger(name) (я знаю, что документация говорит, что никогда этого не делатьно если вам нужен полностью независимый регистратор, это как это сделать), или используйте другое имя для второго журнала при вызове logging.getLogger().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...