Согласен: это нелогичное дизайнерское решение, ИМХО.
Самое простое решение - прикрепить ваш фильтр к каждому возможному обработчику. Например, скажем, у вас есть консольный обработчик, почтовый обработчик и обработчик базы данных, вы должны прикрепить свой «корневой» фильтр к каждому из них. : - /
import logging
import logging.config
class MyRootFilter(logging.Filter):
def filter(self, record):
# filter out log messages that include "secret"
if "secret" in record.msg:
return False
else:
return True
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'my_root_filter': {
'()': MyRootFilter,
},
},
'handlers': {
'stderr': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'filters': ['my_root_filter'],
},
'mail_admins': {
'level': 'ERROR',
'class': 'some.kind.of.EmailHandler',
'filters': ['my_root_filter'],
},
'database': {
'level': 'ERROR',
'class': 'some.kind.of.DatabaseHandler',
'filters': ['my_root_filter'],
},
},
'loggers': {
'some.sub.project': {
'handlers': ['stderr'],
'level': 'ERROR',
},
},
}
logging.config.dictConfig(LOGGING)
logging.getLogger("some.sub.project").error("hello") # logs 'hello'
logging.getLogger("some.sub.project").error("hello secret") # filtered out! :-)
Если имеется много обработчиков, вы можете прикрепить корневой фильтр к каждому обработчику программно, а не вручную. Я рекомендую вам делать это непосредственно в вашем словаре конфигурации (или в файле, в зависимости от того, как вы загружаете свою конфигурацию ведения журнала), вместо того, чтобы делать это после конфигурации, которая была загружена, потому что, кажется, нет документированного способа получить список всех обработчиков. Я нашел logger.handlers и logging._handlers, но, поскольку они не задокументированы, они могут сломаться в будущем. Кроме того, нет гарантии, что они поточно-ориентированы.
Предыдущее решение (прикрепление корневого фильтра к каждому обработчику непосредственно в конфигурации до его загрузки) предполагает, что у вас есть контроль над конфигурацией ведения журнала до ее загрузки, а также что динамический добавление обработчика не выполняется (с помощью Logger # AddHandler ()). Если это не так, то вы, возможно, захотите обезопасить модуль логирования (удачи в этом!).
редактировать
Я сделал снимок, исправляя обезьяны Logger # addHandler, просто для удовольствия. На самом деле он работает нормально и упрощает настройку, но я не уверен, что рекомендовал бы это делать (я ненавижу исправления обезьян, очень сложно отлаживать, когда что-то идет не так). Используйте на свой страх и риск ...
import logging
import logging.config
class MyRootFilter(logging.Filter):
[...] # same as above
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'stderr': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
# it's shorter: there's no explicit reference to the root filter
},
[...] # other handlers go here
},
'loggers': {
'some.sub.project': {
'handlers': ['stderr'],
'level': 'ERROR',
},
},
}
def monkey_patched_addHandler(self, handler):
result = self.old_addHandler(handler)
self.addFilter(MyRootFilter())
return result
logging.Logger.old_addHandler = logging.Logger.addHandler
logging.Logger.addHandler = monkey_patched_addHandler
logging.config.dictConfig(LOGGING)
logging.getLogger("some.sub.project").error("hello") # logs 'hello'
logging.getLogger("some.sub.project").error("hello secret") # filtered out! :-)