Python日志系统非常丰富。添加结构化或非结构化日志输出到python代码,写到文件,输出到控制台,发送到系统日志,或者自定义输出格式都很容易。
我们正在重新检查mozharness中日志的工作机制,希望提取代码能更容易,并减少掺合模式的使用。
下面是一些在python日志中真正帮到我们的建议和技巧:
好吧,对一个给定名称确实只会有一个logger。特殊的“根”logger没有名称。对相同名称多次调用getLogger(name)
会返回相同的logger对象。这个特性很重要,你不用在代码中显式将logger对象传来传去。你可以通过名称来获取它们。日志模块维护了一个全局日志对象注册表。
你可以使用多个logger对象,每个只限定于其特定模块,甚至类或实例。
每个logger都有一个名称,通常是logger所处模块的名称。你能在Python模块中看到的通用模式大概这样:
这段代码没错是因为foo.py
中,__name__
等于”foo”。所以在此模块中,log
对象只会用于这个模块。
名称空间中的logger名称使用“.”来分层。这意味着如果你有foo.bar
和foo.baz
两个logger,你可以操作foo
,这样它的两个子logger都会起作用。尤其是,你可以设置foo
的日志等级来显示或忽略两个子模块的调试消息。
假设我们有个模块foo.bar:
当我们调用make_widget()
时,代码生成了一个调试日志消息。层次结构中的每个logger都有机会将这个消息输出、忽略、传递给父级。
日志默认并没有配置其等级(或设置为NOTSET
))。这意味着logger只会把消息传递给父级,然后不断重复这个步骤,一直到根logger。
所以如果foo.bar
logger没有指定等级,消息将继续传递到foo
logger。如果foo
logger没有指定等级,消息将会传递给根logger。
这就是为什么你通常需要在根logger上配置日志输出的原因;它通常会输出全部消息!!!这太常见了,所以有个专门的方法来配置根logger:logging.basicConfig()
。
这也允许我们根据消息的来源使用混合等级的日志输出:
如果注释掉setLevel(logging.DEBUG)
,你将不会看到任何消息。
所有的内建日志调用都支持exc_info
关键字,如果它不是false,当前异常信息将会附加到日志消息中,比如:
log.exception()
是个特例,等价于log.error(..., exc_info=True)
。
Python 3.2引入了一个新的关键字stack_info
,将会输出当前栈到当前代码。当你想知道如何运行到代码的某处时,非常方便,即使没有异常发生也可以。
你很可能遇到过这个消息,尤其是使用第三方模块时。这个错误的意思是,你没有配置任何日志处理函数,但是某处尝试打印日志消息。这个消息沿着层次结构向上传递,直到在结构链的顶处失败(也许我需要一个更好的比喻)。
输出:
这里可以做两件事:
1.在模块中使用basicConfig()
或类似的方法配置日志
2.库作者应该在模块顶层添加一个NullHandler 来防止这种情况发生。看下这里的指南和这篇博客来了解更多信息。