我的目的是使用分层过滤进行多模块日志记录
记录作者Vinay Sajip提出的方式,至少据我猜测;-)
你可以跳到“ How I want it to work ”
不幸的是,我很快就了解到,使用日志工具比使用该语言的大多数其他经验要复杂得多,而且我已经犯了许多常见的(设计)错误,例如:尝试为多个模块甚至是(using Python logger class to generate multiple logs for different log levels)等方案实现集中式单一Logger类日志记录 . 但显然有更好的设计空间,花费时间寻找和学习它可能会更糟糕 . 所以,现在我希望我走在正确的轨道上 . 否则Vinaj将不得不澄清其余的;-)
我安排我的日志记录如下:
-
每个python模块都有own logger
-
每个 Logger 的名称与定义它的模块相同,例如
logger = logging.getLogger(__name__)
-
像这样,每个模块中的代码可以使用自己的(本地定义的) Logger 将日志消息(logging.LogRecord)发送给处理程序(logging.Handler)
-
使用logging.config实现日志记录配置的完全灵活性(注意:在下面的代码中我只是从basicConfig开始)
这种方法是一种推荐的方法,我同意其可能的优势 . 例如,我可以使用完全限定的模块名称(代码中已存在的命名层次结构)打开/关闭外部库的DEBUG .
现在为了获得更高级别的控制,我想使用logging.Filter类,以便只能过滤(允许) Logger 层次结构中的选定子树 .
这一切都很好,但这里描述的过滤
过滤器实例用于执行LogRecords的任意过滤 .
Logger 和处理程序可以选择使用筛选器实例进行筛选
根据需要记录 . 基本过滤器类仅允许事件
低于 Logger 层次结构中的某个点 . 例如,过滤器
用“A.B”初始化将允许 Logger “A.B”记录的事件,
“A.B.C”,“A.B.C.D”,“A.B.D”等,但不是“A.BB”,“B.A.B”等 . 如果
使用空字符串初始化,传递所有事件 .
仍然不适合我 .
我的猜测是我对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构建(根 Logger ,它是在 import logging
之后创建的 __main__
获取了一个附加到它的流处理程序,因此我们有一个控制台),启用(相应的Logger.disabled = False)和两个模块 Logger 栏和foo传播到根 Logger (所以我们总共有三个 Logger ) .
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
我希望它如何运作
我想集中过滤它,即在我的根 Logger 中,无需导入所有外部模块的所有 Logger .
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的任何消息 . 嘿,但是找到它的更好的方法是什么比总结所有的SO,伙计们? ;-)
我会尝试找出bar_logger在发出任何内容之前等待来自根 Logger 的决定的方法 . 我只是希望这确实是首先应该如何运作的 .
1 回答
Solution
将过滤器添加到处理程序而不是 Logger :
Explanation
在您发布的流程图中,请注意有两颗钻石:
附加到 logger 的过滤器是否拒绝记录?
附加到 hander 的过滤器是否拒绝记录?
因此,在拒绝LogRecord时会出现两次波动 . 如果您将过滤器附加到根 Logger ,但通过foo或bar Logger 启动LogRecord,则LogRecord不会被过滤,因为LogRecord会自由地通过foo或bar Logger ,并且根 Logger 过滤器永远不会进入玩 . (再次查看流程图 . )
相反,
basicConfig
定义的StreamHandler能够过滤任何LogRecord传递给它 .所以:将过滤器添加到处理程序而不是 Logger :
产量
如果要允许从名称以
foo
或bar
开头的 Logger 进行记录,而不是从任何其他 Logger 进行记录,则可以创建如下白名单过滤器:同样,您可以使用以下方法将 Logger 名称列入黑名单: