Во-первых, давайте посмотрим, что происходит, прежде чем мы сможем перейти к решению.
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 означает, что она будет создавать регистратор с заданным именем только при первом вызове и будет возвращать этот регистратор, если вы вызовете его с тем же именем, независимо от того, сколько раз выПозвоню после.
Так, например:
Вы можете видеть, что это особенно полезно, если вы, например:
- Настройте регистратор на
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
после создания уникального регистратора, и вы увидите, что все работает как положено.