我的目的是使用分层过滤进行多模块日志记录
日志作者 Vinay Sajip提出的方式,至少就我而言 ;-)
您可以跳至“我希望它如何工作”
不幸的是,我很快就了解到,使用日志记录工具比我使用该语言的大多数其他经验要复杂得多,而且我已经犯了很多常见(设计)错误,例如试图为多个模块实现集中的单个 Logger 类日志记录甚至像(使用 Python 记录器类为不同的日志级别生成多个日志)这样的方案。但显然还有更好的设计空间,花时间去寻找和学习它可能会更糟。所以,现在我希望我走在正确的轨道上。否则Vinaj将不得不澄清其余的;-)
我这样安排我的日志记录:
- 每个 python 模块都有自己的记录器
- 每个记录器的名称与定义它的模块的名称相同,例如
logger = logging.getLogger(__name__)
- 像这样,每个模块内的代码可以使用自己的(本地定义的)记录器将记录消息(logging.LogRecord)发送到处理程序(logging.Handler)
- 使用logging.config来实现日志配置的完全灵活性(注意:在下面的代码中,我只是从basicConfig开始)
这种方法是一种推荐的方法,我同意它可能的优势。例如,我可以使用完全限定的模块名称(代码中已经存在的命名层次结构)打开/关闭外部库的调试。
现在要拥有更高级别的控制,我想使用 logging.Filter 类,以便能够仅过滤(允许)记录器层次结构中的选定子树。
这一切都很好,但是这里描述的过滤
Filter instances are used to perform arbitrary filtering of LogRecords. Loggers and Handlers can optionally use Filter instances to filter records as desired. The base filter class only allows events which are below a certain point in the logger hierarchy. For example, a filter initialized with "A.B" will allow events logged by loggers "A.B", "A.B.C", "A.B.C.D", "A.B.D" etc. but not "A.BB", "B.A.B" etc. If initialized with the empty string, all events are passed.
仍然不适合我。
我的猜测是,我对 LogRecords 传播背后的细节缺乏了解是问题的根源。在跳转到代码之前,我想在这里展示一个流程图(来自食谱教程,起初我不知何故未能立即发现):
示例代码
我从两个模块示例开始,每个模块都使用自己的命名记录器:
酒吧.py:
import logging
logger = logging.getLogger(__name__)
def bar():
logger.info('hello from ' + __name__)
foo.py:
import logging
from bar import bar, logger as bar_logger
logger = logging.getLogger('foo')
def foo():
logger.info('hello from foo')
if __name__ == '__main__':
# Trivial logging setup.
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s %(name)-20s %(levelname)-8s %(message)s',
datefmt='%m-%d %H:%M'
)
# Do some work.
foo()
bar()
日志首先使用 logging.basicConfig 构建(根记录器,它是在获取附加到它的流处理程序之后创建import logging
的__main__
,因此我们有一个控制台),启用(相应的 Logger.disabled=False)和模块记录器bar和foo传播到根记录器(所以我们总共有三个记录器)。
print logger
print bar_logger
print logging.root
# Prints
#<logging.Logger object at 0x7f0cfd520790>
#<logging.Logger object at 0x7f0cfd55d710>
#<logging.RootLogger object at 0x7f0cfd520550>
实际用例是当 bar 是我想要静音(过滤掉)的外部库时。
它是如何工作的,但“我”不喜欢它
# Don't like it
bar_logger.addFilter(logging.Filter('foo'))
# Do some work.
foo()
bar()
仅打印
06-24 14:08 foo INFO hello from foo
我希望它如何工作
我想集中过滤它,即在我的根记录器中,不需要导入所有外部模块的所有记录器。
logging.root.addFilter(logging.Filter('foo'))
印刷
06-24 14:17 foo INFO hello from foo
06-24 14:17 bar INFO hello from bar
一定有一些我想念的明显/愚蠢的错误:我不想要来自酒吧记录器的任何消息。嘿,但是有什么比总结所有内容更好的方法呢,伙计们?;-)
在发出任何内容之前,我将尝试找出 bar_logger 等待根记录器做出决定的方式。我只是希望这确实是它应该首先工作的方式。