python - 为什么附加到根记录器的过滤器不传播到后代记录器?

标签 python logging

摘自 python logging documentation 的第 15.7.4 段:

Note that filters attached to handlers are consulted whenever an event is emitted by the handler, whereas filters attached to loggers are consulted whenever an event is logged to the handler (using debug(), info(), etc.) This means that events which have been generated by descendant loggers will not be filtered by a logger’s filter setting, unless the filter has also been applied to those descendant loggers.

我不明白这个设计决定。将根记录器的过滤器也应用于后代记录器不是更有意义吗?

最佳答案

我同意:恕我直言,这是一个违反直觉的设计决定。

最简单的解决方案是将过滤器附加到每个可能的处理程序。例如,假设您有一个控制台处理程序、一个邮件处理程序和一个数据库处理程序,您应该将“根”过滤器附加到它们中的每一个。 :-/

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#添加处理程序 ())。如果这不是真的,那么您可能想要对日志记录模块进行猴子修补(祝您好运!)。

编辑

我试了一下猴子补丁 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!  :-)

关于python - 为什么附加到根记录器的过滤器不传播到后代记录器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6850798/

相关文章:

python - 如何在 Python 中使用 referer 和 href 查找 url?

python - pypi 包装和 secret : why does a github--intended . travis.yml 持有密码?

asp.net-mvc-3 - 使用 Essential.Diagnostics 存储日志文件的相对路径

Python、py.test 和 stderr——从 Cement 日志扩展中捕获日志处理程序输出

hadoop - 如何更改MR作业的日志级别

logging - 我应该记录网站的 404 错误吗?

python - BeautifulSoup - 捕获特定类或文本的所有链接

python - 如何根据特定行中的值对 numpy 数组进行排序?

java - 如何在 Java 中记录异常?

python - 如何在 Pexpect spawn 中设置环境变量而不会出现异常