15.9. logging — 记录状态、错误和提醒消息
使用目的: 报告状态,错误和有用的信息(不同于直接输出,可有选择的屏蔽信息)
logging
模块定义了一个用于报告来自应用程序与库的错误和状态信息的标准 API。
使用标准库模块提供的日志 API 的主要好处是所有 Python 模块都可以参与日志记录,因此应用程序的日志可以包含第三方模块的消息。
Logging 组件
logging
日志记录系统由四种交互类型的对象组成。 每个想要记录的模块或应用程序都使用 Logger
实例将信息添加到日志中。 调用 logger
会创建一个 LogRecord
,它会将信息保存在内存中,直到它被处理为止。 Logger
可能有一些 `Handler` 对象被配置用来接收和处理日志记录。 Handler
使用 Formatter
将日志记录转换为输出消息。
应用中使用 logging vs. 库中使用 logging
应用程序开发人员和库作者都可以使用 logging
模块,但不同的人有不同的考虑因素需要记住。
应用程序开发人员配置 logging
模块,将消息引导到适当的输出流。可以将具有不同级别的消息记录到不同的输出流。用于将日志消息写入文件的处理程序,HTTP GET / POST记录,通过 SMTP 的电子邮件的记录,通用套接字或特定于操作系统的日志记录机制都包含在内,并且可以为特殊需求创建自定义日志目标类,而不只是使用内置类。
库的开发人员也可以使用 logging
,并减少工作量。只需使用适当的名称为每个上下文创建一个记录器实例,然后使用标准级别记录消息。只要库使用具有一致的命名和级别选择的日志记录 API,就可以根据需要将应用程序配置为显示或隐藏库中的消息。
将日志记录到文件中
大多数应用程序都配置为将日志记录到文件。 使用 basicConfig()
函数设置默认处理程序,可使调试消息写入文件。
logging_file_example.py
import logging
LOG_FILENAME = 'logging_example.out'
logging.basicConfig(
filename=LOG_FILENAME,
level=logging.DEBUG,
)
logging.debug('This message should go to the log file')
with open(LOG_FILENAME, 'rt') as f:
body = f.read()
print('FILE:')
print(body)
运行了这段脚本后,记录的信息会杯写到 logging_example.out
文件中去.
$ python3 logging_file_example.py
FILE:
DEBUG:root:This message should go to the log file
轮换日志文件
重复运行此脚本会导致有更多的信息追加到文件中。要想每次运行时都创建一个新的文件,我们需要给 basicConfig()
传递 filemode
为 w
模式。除了用此种方法来管理,用 RotatingFileHandler
是一个更好的选择,它会自动创建新文件同时也不会丢失旧文件。
logging_rotatingfile_example.py
import glob
import logging
import logging.handlers
LOG_FILENAME = 'logging_rotatingfile_example.out'
# 为 logger 设置我们想要的输出等级
my_logger = logging.getLogger('MyLogger')
my_logger.setLevel(logging.DEBUG)
# 给 logger 添加合适的日志消息处理器
handler = logging.handlers.RotatingFileHandler(
LOG_FILENAME,
maxBytes=20,
backupCount=5,
)
my_logger.addHandler(handler)
# 写入些日志
for i in range(20):
my_logger.debug('i = %d' % i)
# 我们查看下哪些文件被创建了
logfiles = glob.glob('%s*' % LOG_FILENAME)
for filename in sorted(logfiles):
print(filename)
运行后的结果会创建6个独立的文件,每个里都包含部分应用程序的日志信息。
$ python3 logging_rotatingfile_example.py
logging_rotatingfile_example.out
logging_rotatingfile_example.out.1
logging_rotatingfile_example.out.2
logging_rotatingfile_example.out.3
logging_rotatingfile_example.out.4
logging_rotatingfile_example.out.5
最近的文件总是 logging_rotatingfile_example.out
,每次都会尝试写入到它的上限然后加一个后缀 .1
。之后就会一直累加这个后缀(.1
会变成 .2
)直到变成 .5
,再之后如果还有内容就会循环这个过程。
注意
本例用的日志长度非常小。
maxBytes
的实际长度需要根据真实程序来确定。
日志等级
logging
另一个有用的功能是可以根据不同的 日志等级 生成不同的消息。也就是说可以在代码中根据调试信息进行不同程度的检测,比如可以设置在生产系统中不显示调试信息。下表列出了 logging
的日志等级。
Logging Levels
Level | Value |
---|---|
CRITICAL | 50 |
ERROR | 40 |
WARNING | 30 |
INFO | 20 |
DEBUG | 10 |
NOTSET | 0 |
相应的日志消息只会在处理器或 logger 设置日志等级为此消息的等级或此消息等级更高时发出。比如,如果某消息等级为 CRITICAL
,logger 设置的等级是 ERROR
(50 > 40),这时该消息就会发出。如果消息的等级为 WARNING
,但 logger 设置的等级是 ERROR
(30 < 40),这时消息就不会发出了。
logging_level_example.py
import logging
import sys
LEVELS = {
'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL,
}
if len(sys.argv) > 1:
level_name = sys.argv[1]
level = LEVELS.get(level_name, logging.NOTSET)
logging.basicConfig(level=level)
logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical error message')
以 debug
或 warning
参数运行脚本来查看不同等级下的输出结果。
$ python3 logging_level_example.py debug
DEBUG:root:This is a debug message
INFO:root:This is an info message
WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical error message
$ python3 logging_level_example.py info
INFO:root:This is an info message
WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical error message
命名 Logger 实例
之前我们所有的日志信息都带有 root
标记,因为我们的代码都是使用的 root
logger。我们如果想知道来自不同模块的代码的日志信息的话我们可以给每个模块都设置一个单独的 logger 对象。这样 logger 发送日志信息时就会是它们自己的名字。下面是一个例子:
logging_modules_example.py
import logging
logging.basicConfig(level=logging.WARNING)
logger1 = logging.getLogger('package1.module1')
logger2 = logging.getLogger('package2.module2')
logger1.warning('This message comes from one module')
logger2.warning('This comes from another module')
下面是输出,我们可以看到不同的 logger 写入消息时的名字是不一样的。
$ python3 logging_modules_example.py
WARNING:package1.module1:This message comes from one module
WARNING:package2.module2:This comes from another module
日志树
Logger
实例是以树的形式配置的,不同的 logger 会以不同的名字表示。一般每个应用程序会定义一个总名称,不同模块中的 logger 都是它的孩子。根 logger 没有名字。看图:
树结构用来配置日志非常方便,因为这意味着每个 logger 不需要都有自己的处理器。没有处理器的 logger 所发出的消息会被父处理器接受处理。也就意味着大部分应用程序只需要配置一个根 logger 的处理器,所有的日志信息都会被收集并发送到同一个地方,看图:
树结构也允许我们在应用程序的不同部分使用不同的日志等级,处理器和日志格式来管理不同的日志该发送到什么地方,看图:
集成 warning 模块
logging 模块通过 captureWarnings()
来集成 warnings
,它会让 warnings
将消息通过 logging 系统发送而不是直接输出。
logging_capture_warnings.py
import logging
import warnings
logging.basicConfig(
level=logging.INFO,
)
warnings.warn('This warning is not sent to the logs')
logging.captureWarnings(True)
warnings.warn('This warning is sent to the logs')
警告信息会以 py.warnings
的 logger 发送,等级为 WARNING
。
$ python3 logging_capture_warnings.py
logging_capture_warnings.py:13: UserWarning: This warning is not
sent to the logs
warnings.warn('This warning is not sent to the logs')
WARNING:py.warnings:logging_capture_warnings.py:17: UserWarning:
This warning is sent to the logs
warnings.warn('This warning is sent to the logs')
参阅
- logging 标准库文档 – 内容更丰富的
logging
文档,更强大的指南和更详尽的参考。- Python 2 到 3 logging 迁移注意事项
warnings
– 非致命警告.- logging_tree – 展示应用程序日志树的第三方包 – Brandon Rhodes 编写
- Logging Cookbook – 标准库文档 – 不同任务中的
logging
。
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。