记录作者Vinay Sajip提出的方式,至少据我猜测一样; - )
您可以跳到“我希望它如何工作”
不幸的是,我很快就了解到,使用日志工具比使用该语言的大多数其他经验要复杂得多,并且我已经犯了许多常见的(设计)错误,例如:尝试实现集中的单个Logger类记录多个模块甚至是(using Python logger class to generate multiple logs for different log levels)等方案。但显然有更好的设计空间,花费时间寻找和学习它可能会更糟糕。所以,现在我希望我走在正确的轨道上。否则Vinaj将不得不澄清其余部分; - )
我安排我的日志记录:
logger = logging.getLogger(__name__)
这种方法是推荐的方法,我同意其可能的优点。例如,我可以使用完全限定的模块名称(代码中已存在的命名层次结构)打开/关闭外部库的DEBUG。
现在为了获得更高级别的控制,我想使用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传播背后的细节缺乏了解是问题的根源。在跳转到代码之前,我想在这里显示一个流程图(来自cookbook tutorial,起初我以某种方式未能立即发现):
我从两个模块示例开始,每个都使用它自己的命名logger:
bar.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 记录器的任何消息。嘿,但是找到它的更好的方法是什么比总结所有的SO,伙计们? ; - )
我会尝试找出bar_logger在发出任何内容之前等待来自根记录器的决定的方法。我只是希望这确实是首先应该如何运作的。
答案 0 :(得分:35)
<强>解决方案强>
将过滤器添加到处理程序而不是记录器:
handler.addFilter(logging.Filter('foo'))
<强>解释强>
在您发布的流程图中,请注意有两颗钻石:
因此,在拒绝LogRecord时会出现两次波动。如果您将过滤器附加到根记录器,但是通过foo或bar记录器启动LogRecord,则LogRecord不会被过滤,因为LogRecord会自由地通过foo或bar记录器,并且根记录器过滤器永远不会进入玩。 (再次查看流程图。)
相反,basicConfig
定义的StreamHandler能够过滤任何LogRecord传递给它。
所以:将过滤器添加到处理程序而不是记录器:
# foo.py
import logging
import bar
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')
for handler in logging.root.handlers:
handler.addFilter(logging.Filter('foo'))
foo()
bar.bar()
产量
06-24 09:17 foo INFO hello from foo
如果您想允许从名称以foo
或bar
开头的记录器进行记录,而不是从任何其他记录器进行记录,则可以创建如下白名单过滤器:
import logging
foo_logger = logging.getLogger('foo')
bar_logger = logging.getLogger('bar')
baz_logger = logging.getLogger('baz')
class Whitelist(logging.Filter):
def __init__(self, *whitelist):
self.whitelist = [logging.Filter(name) for name in whitelist]
def filter(self, record):
return any(f.filter(record) for f in self.whitelist)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s %(name)-20s %(levelname)-8s %(message)s',
datefmt='%m-%d %H:%M')
for handler in logging.root.handlers:
handler.addFilter(Whitelist('foo', 'bar'))
foo_logger.info('hello from foo')
# 06-24 09:41 foo INFO hello from foo
bar_logger.info('hello from bar')
# 06-24 09:41 bar INFO hello from bar
baz_logger.info('hello from baz')
# No output since Whitelist filters if record.name not begin with 'foo' or 'bar'
同样,您可以使用以下方法将记录器名称列入黑名单:
class Blacklist(Whitelist):
def filter(self, record):
return not Whitelist.filter(self, record)