Почему фильтр, прикрепленный к корневому регистратору, не распространяется на регистраторы-потомки? - PullRequest
7 голосов
/ 28 июля 2011

Из параграфа 15.7.4 документации по регистрации Python :

Обратите внимание, что фильтры, прикрепленные к обработчикам, проверяются всякий раз, когда обработчик генерирует событие, тогда как фильтры прикрепляютсяк регистраторам обращаются всякий раз, когда событие регистрируется в обработчике (с использованием debug (), info () и т. д.). Это означает, что события, которые были созданы дочерними регистраторами, не будут фильтроваться настройкой фильтра регистратора, если только фильтр не имеетбыли применены к этим потомкам регистраторов.

Я не понимаю это дизайнерское решение.Разве не имеет смысла применять фильтр корневого регистратора к журналам-потомкам?

Ответы [ 2 ]

3 голосов
/ 10 февраля 2013

Согласен: это нелогичное дизайнерское решение, ИМХО.

Самое простое решение - прикрепить ваш фильтр к каждому возможному обработчику. Например, скажем, у вас есть консольный обработчик, почтовый обработчик и обработчик базы данных, вы должны прикрепить свой «корневой» фильтр к каждому из них. : - /

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!  :-)
0 голосов
/ 03 августа 2016

Думайте об этом так. Лесорубы похожи на водосточные трубы из вашего дома. Фильтр в логгере предотвращает сброс мусора в канализацию, он не фильтрует всю канализацию. То, где вы находитесь в потоке (вверх по течению, вниз по течению), не меняет это поведение.

Обработчики - это трубы. Трубы накапливают поток вверх по течению. Каналом по умолчанию является «Пропустить, перейти к родителю». Если вы хотите повлиять на поток вверх по течению, вам нужно установить фильтр без канала (обработчик). Если вы посмотрите на блок-схему ведения журнала , можно добавить NullHandler (без форматирования или вывода), который фильтрует, а затем распространяет сообщение.

Это то поведение, которое вы желаете.

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