带过滤器的日志记录

66 投票
4 回答
96491 浏览
提问于 2025-04-15 11:40

我正在使用日志记录功能(import logging)来记录消息。

在一个模块里,我用调试级别来记录消息,像这样:my_logger.debug('msg')

这些调试消息有些来自于function_a(),有些来自于function_b();我希望能够根据消息是来自哪个函数来开启或关闭日志记录。

我猜我需要使用日志记录的过滤机制。

有没有人能告诉我,下面的代码需要怎么修改才能实现我想要的功能?

import logging
logger = logging.getLogger( "module_name" )

def function_a( ... ):
    logger.debug( "a message" )

def function_b( ... ):
    logger.debug( "another message" )

if __name__ == "__main__":
    logging.basicConfig( stream=sys.stderr, level=logging.DEBUG )

    #don't want function_a()'s noise -> ....
    #somehow filter-out function_a's logging
    function_a()

    #don't want function_b()'s noise -> ....
    #somehow filter-out function_b's logging
    function_b()

如果我把这个简单的例子扩展到更多模块和每个模块中的更多函数,我会担心会有很多记录器。

我能不能每个模块只保留一个记录器?注意,这些日志消息是“结构化”的,也就是说,如果记录日志的函数在进行某些解析工作,它们的消息前面都会有一个前缀,比如logger.debug("parsing: xxx") - 我能不能用一行代码就关闭所有“解析”消息(不管是哪个模块或函数发出的消息)?

4 个回答

10

我找到了一种更简单的方法,可以在你的主脚本中使用函数:

# rm 2to3 messages
def filter_grammar_messages(record):
    if record.funcName == 'load_grammar':
        return False
    return True

def filter_import_messages(record):
    if record.funcName == 'init' and record.msg.startswith('Importing '):
        return False
    return True

logging.getLogger().addFilter(filter_grammar_messages)  # root
logging.getLogger('PIL.Image').addFilter(filter_import_messages)
27

不要使用全局变量。这是个随时可能出问题的做法。

你可以给你的日志记录器起任何有意义的名字,名字之间用“.”分隔。

你可以把它们当作一个层级来管理。如果你有两个日志记录器,分别叫 a.b.ca.b.d,你可以检查 a.b 的日志级别,并同时修改这两个日志记录器。

你可以创建任意数量的日志记录器,它们的开销很小。

最常见的做法是每个模块一个日志记录器。具体可以参考 命名Python日志记录器

照这样做。

import logging

logger= logging.getLogger( "module_name" )
logger_a = logger.getLogger( "module_name.function_a" )
logger_b = logger.getLogger( "module_name.function_b" )

def function_a( ... ):
    logger_a.debug( "a message" )

def function_b( ... ):
    logger_b.debug( "another message" )

if __name__ == "__main__":
    logging.basicConfig( stream=sys.stderr, level=logging.DEBUG )
    logger_a.setLevel( logging.DEBUG )
    logger_b.setLevel( logging.WARN )

    ... etc ...
99

你只需要创建一个 logging.Filter 的子类。具体可以参考这个链接:http://docs.python.org/library/logging.html#filter-objects。这个子类里会有一个方法,叫 filter(record),它的作用是检查日志记录,如果返回 True 就表示要记录这个日志,如果返回 False 就表示不记录。然后你可以通过调用 addFilter(filter) 方法,把这个过滤器添加到一个 Logger 或者 Handler 上。

举个例子:

class NoParsingFilter(logging.Filter):
    def filter(self, record):
        return not record.getMessage().startswith('parsing')

logger.addFilter(NoParsingFilter())

大概就是这样。

撰写回答