为什么附加到根记录器的过滤器不传播到后代记录器? [英] Why doesn't filter attached to the root logger propagate to descendant loggers?

查看:57
本文介绍了为什么附加到根记录器的过滤器不传播到后代记录器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

摘录自 Python日志记录文档的15.7.4段:

From paragraph 15.7.4 of the python logging documentation:

请注意,每当发生事件时,都会查询附加到处理程序的过滤器 由处理程序发出,而附加到记录器的过滤器是 每当将事件记录到处理程序时(使用debug()进行协商, info()等.这表示由 后代记录器将不会被记录器的过滤器设置过滤, 除非该过滤器也已应用于这些后代记录器.

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.

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

I don't understand this design decision. Would it not make more sense for the root logger's filter to be applied to the descendant loggers as well?

推荐答案

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

I agree: this is a counter-intuitive design decision, IMHO.

最简单的解决方案是将过滤器附加到每个可能的处理程序.例如,假设您有一个控制台处理程序,一个邮件处理程序和一个数据库处理程序,则应将"root"过滤器附加到每个过滤器上. :-/

The easiest solution is to attach your filter to every possible handler. For example, say you have a console handler, a mail handler and a database handler, you should attach your "root" filter to each and every one of them. :-/

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,但是由于未记录在案,因此它们将来可能会中断.另外,不能保证它们是线程安全的.

If there are a lot of handlers, you may want to attach your root filter to every handler programmatically rather than manually. I recommend you do this directly on your configuration dictionary (or file, depending on how you load your logging configuration), rather than doing this after the configuration has been loaded, because there seems to be no documented way to get the list of all handlers. I found logger.handlers and logging._handlers, but since they are not documented, they may break in the future. Plus, there's no guarantee that they are thread-safe.

先前的解决方案(在将根过滤器加载到配置中之前,将其根过滤器直接附加到配置中的每个处理程序)假定您在加载日志记录配置之前对其具有控制权,并且不会动态添加任何处理程序(使用Logger# addHandler()).如果不是这样,那么您可能想猴子修补日志记录模块(祝您好运!).

The previous solution (attaching your root filter to every handler directly in the configuration, before it gets loaded) assumes that you have control over the logging configuration before it's loaded, and also that no handler will be added dynamically (using Logger#addHandler()). If this is not true, then you may want to monkey-patch the logging module (good luck with that!).

修改

我只是为了好玩而对猴子补丁Logger#addHandler开枪了.它实际上可以很好地工作并简化了配置,但是我不确定我是否建议这样做(我讨厌猴子补丁,当出现问题时很难调试).使用风险自负...

I took a shot at monkey patching Logger#addHandler, just for fun. It actually works fine and simplifies the configuration, but I'm not sure I would recommend doing this (I hate monkey-patching, it makes it very hard to debug when something goes wrong). Use at your own risks...

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!  :-)

这篇关于为什么附加到根记录器的过滤器不传播到后代记录器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆