日志(Logging)¶

日志模块自2.3版本开始便是Python标准库的一部分。它被简洁的描述在PEP 282。众所周知,除了基础日志指南 部分,该文档并不容易阅读。
日志的两个目的:
- 诊断日志 记录与应用程序操作相关的日志。例如,用户遇到的报错信息,可通过搜索诊断日志获得上下文信息。
- 审计日志 为商业分析而记录的日志。从审计日志中,可提取用户的交易信息,并结合其他用户资料构成用户报告或者用来优化商业目标。
... 或者打印?¶
当需要在命令行应用中显示帮助文档时,打印
是一个相对于日志更好的选择。而在其他时候,日志总能优于打印
,理由如下:
- 日志事件产生的日志记录 ,包含清晰可用的诊断信息,如文件名称、路径、函数名和行号等。
- 包含日志模块的应用,默认可通过根记录器对应用的日志流进行访问,除非您将日志过滤了。
- 可通过
logging.Logger.setLevel()
方法有选择地记录日志,或可通过设置logging.Logger.disabled
属性为True
来禁用。
库中的日志¶
日志指南 中含库日志配置 的说明。由于是用户 ,而非库来指明如何响应日志事件,因此这里有一个值得反复说明的忠告:
注解
强烈建议不要向您的库日志中加入除NullHandler外的其它处理程序。
在库中,声明日志的最佳方式是通过__name__
全局变量:logging
模块通过点(dot)运算符创建层级排列的日志,因此,用__name__
可以避免名字冲突。
以下是一个来自requests 资源 的最佳实践的例子 —— 把它放置在您的__init__.py
文件中
importlogginglogging.getLogger(__name__).addHandler(logging.NullHandler())
应用程序中的日志¶
应用程序开发的权威指南,应用的12要素 ,也在其中一节描述了日志的作用 。它特别强调将日志视为事件流,并将其发送至由应用环境所处理的标准输出中。
配置日志至少有以下三种方式:
- 使用INI格式文件:
- 优点: 使用
logging.config.listen()
函数监听socket,可在运行过程中更新配置 - 缺点: 通过源码控制日志配置较少(例如 子类化定制的过滤器或记录器)。
- 优点: 使用
- 使用字典或JSON格式文件:
- 优点: 除了可在运行时动态更新,在Python 2.6之后,还可通过
json
模块从其它文件中导入配置。 - 缺点: 很难通过源码控制日志配置。
- 优点: 除了可在运行时动态更新,在Python 2.6之后,还可通过
- 使用源码:
- 优点: 对配置绝对的控制。
- 缺点: 对配置的更改需要对源码进行修改。
通过INI文件进行配置的例子¶
我们假设文件名为logging_config.ini
。关于文件格式的更多细节,请参见日志指南 中的日志配置 部分。
[loggers]keys=root[handlers]keys=stream_handler[formatters]keys=formatter[logger_root]level=DEBUGhandlers=stream_handler[handler_stream_handler]class=StreamHandlerlevel=DEBUGformatter=formatterargs=(sys.stderr,)[formatter_formatter]format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s
然后在源码中调用logging.config.fileConfig()
方法:
importloggingfromlogging.configimportfileConfigfileConfig('logging_config.ini')logger=logging.getLogger()logger.debug('often makes a very good meal of%s','visiting tourists')
通过字典进行配置的例子¶
Python 2.7中,您可以使用字典实现详细配置。PEP 391 包含了一系列字典配置的强制和非强制的元素。
importloggingfromlogging.configimportdictConfiglogging_config=dict(version=1,formatters={'f':{'format':'%(asctime)s%(name)-12s%(levelname)-8s%(message)s'}},handlers={'h':{'class':'logging.StreamHandler','formatter':'f','level':logging.DEBUG}},root={'handlers':['h'],'level':logging.DEBUG,},)dictConfig(logging_config)logger=logging.getLogger()logger.debug('often makes a very good meal of%s','visiting tourists')
通过源码直接配置的例子¶
importlogginglogger=logging.getLogger()handler=logging.StreamHandler()formatter=logging.Formatter('%(asctime)s%(name)-12s%(levelname)-8s%(message)s')handler.setFormatter(formatter)logger.addHandler(handler)logger.setLevel(logging.DEBUG)logger.debug('often makes a very good meal of%s','visiting tourists')