关于Python日志系统的几点建议,python日志几点建议,未经许可,禁止转载!英文


本文由 编橙之家 - douxingxiang 翻译,艾凌风 校稿。未经许可,禁止转载!
英文出处:atlee.ca。欢迎加入翻译组。

Python日志系统非常丰富。添加结构化或非结构化日志输出到python代码,写到文件,输出到控制台,发送到系统日志,或者自定义输出格式都很容易。

我们正在重新检查mozharness中日志的工作机制,希望提取代码能更容易,并减少掺合模式的使用。

下面是一些在python日志中真正帮到我们的建议和技巧:

可以有多个logger

好吧,对一个给定名称确实只会有一个logger。特殊的“根”logger没有名称。对相同名称多次调用getLogger(name)会返回相同的logger对象。这个特性很重要,你不用在代码中显式将logger对象传来传去。你可以通过名称来获取它们。日志模块维护了一个全局日志对象注册表。

你可以使用多个logger对象,每个只限定于其特定模块,甚至类或实例。

每个logger都有一个名称,通常是logger所处模块的名称。你能在Python模块中看到的通用模式大概这样:

Python
# in module foo.py
import logging
log = logging.getLogger(__name__)

这段代码没错是因为foo.py中,__name__等于”foo”。所以在此模块中,log对象只会用于这个模块。

logger可分级的

名称空间中的logger名称使用“.”来分层。这意味着如果你有foo.barfoo.baz两个logger,你可以操作foo,这样它的两个子logger都会起作用。尤其是,你可以设置foo的日志等级来显示或忽略两个子模块的调试消息。

Python
# 我们为所有的foo模块启用全部调试日志输出
import logging
logging.getLogger('foo').setLevel(logging.DEBUG)

日志消息类似事件,会在层次结构中流动

假设我们有个模块foo.bar:

Python
import logging
log = logging.getLogger(__name__)  # __name__ is "foo.bar" here

def make_widget():
    log.debug("made a widget!")

当我们调用make_widget()时,代码生成了一个调试日志消息。层次结构中的每个logger都有机会将这个消息输出、忽略、传递给父级。

日志默认并没有配置其等级(或设置为NOTSET))。这意味着logger只会把消息传递给父级,然后不断重复这个步骤,一直到根logger。

所以如果foo.barlogger没有指定等级,消息将继续传递到foologger。如果foologger没有指定等级,消息将会传递给根logger。

这就是为什么你通常需要在根logger上配置日志输出的原因;它通常会输出全部消息!!!这太常见了,所以有个专门的方法来配置根logger:logging.basicConfig()

这也允许我们根据消息的来源使用混合等级的日志输出:

Python
import logging

# 为所有的foo模块启动调试日志输出
logging.getLogger("foo").setLevel(logging.DEBUG)

# 配置根logger只打印INFO消息,并输出到控制台
# (默认值)
logging.basicConfig(level=logging.INFO)

# 将会输出调试消息
logging.getLogger("foo.bar").debug("ohai!")

如果注释掉setLevel(logging.DEBUG),你将不会看到任何消息。

exc_info是最棒的

所有的内建日志调用都支持exc_info关键字,如果它不是false,当前异常信息将会附加到日志消息中,比如:

Python
import logging
logging.basicConfig(level=logging.INFO)

log = logging.getLogger(__name__)

try:
    assert False
except AssertionError:
    log.info("surprise! got an exception!", exc_info=True)

log.exception()是个特例,等价于log.error(..., exc_info=True)

Python 3.2引入了一个新的关键字stack_info,将会输出当前栈到当前代码。当你想知道如何运行到代码的某处时,非常方便,即使没有异常发生也可以。

“找不到处理函数…”

你很可能遇到过这个消息,尤其是使用第三方模块时。这个错误的意思是,你没有配置任何日志处理函数,但是某处尝试打印日志消息。这个消息沿着层次结构向上传递,直到在结构链的顶处失败(也许我需要一个更好的比喻)。

Python
import logging
log = logging.getLogger()
log.error("no log for you!")

输出:

Python
No handlers could be found for logger "root"

这里可以做两件事:

1.在模块中使用basicConfig()或类似的方法配置日志

2.库作者应该在模块顶层添加一个NullHandler 来防止这种情况发生。看下这里的指南和这篇博客来了解更多信息。

更多?

我极力推荐你读下日志文档和指南,它们提供了更多有用信息(写的也很好!)。你也可以通过定制自己的日志处理器来做更多事情,不同的输出格式、一次输出到多个位置等。玩得高兴!

评论关闭