Python日志基础

标准库日志模块

Python在标准库 中包含了一个日志模块,提供了可伸缩的框架来发送日志消息。这个模块被广泛使用并且成为大多数开发者用于日志记录。

这个日志模块提供了一种应用程序配置的不同处理方式,以及路由日志消息到不同处理器的方法。这样通过一个高度可配置来处理不同的使用场景。

为了发出一个日志消息,调用者首先需要请求一个命名的logger。这个命名被应用程序用于配置不同的规则用于不同的loggers。这个logger就可以用于发送简单的格式化的消息给不同的日志级别(DEBUG, INFO, ERROR 等等),然后logger即可以被应用程序用于处理不同优先级的消息。

以下是例子:

import logging
log = loggging.getLogger("my-logger")
log.info("Hello, world")

在内部,这个消息被加入到一个LogRecord对象,然后路由到这个logger注册的处理器对象。这个处理器就使用格式器将这个LogRecord转换成字符串并发送这个字符串。

幸运的是,程序员并不需要详细了解这个机制。Python文档包含了一个如何使用logging模块的文档。以下是该文档的一个使用指南。

从模块发出到日志

模块,也就是其他程序寓言所说的库,是发送日志消息到最好方式。模块不需要配置如何记录日志,因为这是应用程序的责任,只要导入和使用模块。模块只需要让应用程序能够路由它的日志消息就可以了。

基于上述理由,每个模块通常会使用一个logger,以便应用程序能够路由不同模块的日志,并使得模块中的日志代码简化。

import loggin

log = logging.getLogger(__name__)

def do_something():
    log.debug("Doing something!")

就这么简单,在Python中,__name__包含了当前模块到完整名字,所以可以在任何模块使用。

配置logging

主应用将配置loggging子系统,这样日志消息就会去往它们该去到地方。Python logging模块提供了大量的调整方法,配置也非常简单。

总的来说,logging的配置包括添加一个Formatter和一个Handler到根logger。因为这个root logger是共用的,这个logging模块提供了一个称为basicConfig的功能来处理主要的使用场景。

应用程序将尽可能配置logging,通常是应用程序首先要做的事情,这样应用程序启动时就不会丢失日志。

最后,应用程序将在主应用代码中包装一个try/except代码块,以便通过logging接口而不是标准错误stderr来发送异常。

案例1:发送日志到Systemd的标准输出

这个最简单的也可能是最好的配置项是使用systemd,应用程序最后需要发送日志消息到标准输出stdout或标准错误stderr并让systemd转发消息给journald和syslog就可以。这种情况下,甚至不需要捕捉异常,因为Python已经将异常写入到标准错误中。

import logging
import os

loggging.basicConfig(level=on.environ.get("LOGLEVEL","INFO"))

exit(main())

以上,应用程序现在将所有消息以level INFO或以上级别发送到stderr,使用的是简单格式:

ERROR:the.module.name:The log message

案例3:发送日志到文件

将日志记录到独立的文件,可以方便日志采集系统独立对不同应用采集日志,进行集中化分析处理。

import logging
import logging.handlers
import os

def main():
    handler = logging.handlers.WatchedFileHandler(
            os.environ.get("LOGFILE", "/var/log/myApp.log"))
    formatter = logging.Formatter(logging.BASIC_FORMAT)
    handler.setFormatter(formatter)
    root = logging.getLogger()
    root.setLevel(os.environ.get("LOGLEVEL", "INFO"))
    root.addHandler(handler)

    ...

    try:
        vmname = sys.argv[2]
        logging.info("vmname is %s" % vmname)
    except:
        logging.exception("Can't get vmname!")
        sys.exit(-2)

if __name__ == "__main__":
    main()

参考 When to use logging 可以看到不同级别日志方式,例如:

logging.warning()
logging.error()
logging.exception()
logging.critical()

注意 (参考 TypeError: unsupported operand type(s) for %: 'NoneType' and 'str')

正确的格式是:

logging.info("vmname is %s" % vmname)

不要写成

logging.info("vmname is %s") % vmname

错误的格式会导致提示:TypeError: unsupported operand type(s) for %: 'NoneType' and 'str'

类似 print("I said: %r" % x)

参考

Last updated