Python程序日志输出

日志在问题分析时必不可少,好的程序应该输出足够的日志信息。

那么,用 Python 语言开发程序时,如何输出日志呢?答案是—— logging 模块!

1
2
3
4
>>> import logging
>>> logging.warning('something wrong happended.')
WARNING:root:Something wrong happended.
>>> logging.info('seems good!')

这是一个入门级例子,导入 logging 模块后,调用 warning 方法即可输出警告信息。

日志级别

日志输出视轻重缓急,可分为多个级别。例子还调用 info 方法输出一条普通信息,级别比警告信息低。 Python 支持的日志级别包括:

名称 数值 方法 含义
CRITICAL logging.CRITICAL logger.critical 严重错误
ERROR logging.ERROR logger.error 错误
WARNING logging.WARNING logger.warning 警告
INFO logging.INFO logger.info 普通信息
DEBUG logging.DEBUG logger.debug 调试信息
NOTSET logging.NOTSET 未指定

注意到,例子中 info 输出的普通信息并没真正打印到屏幕上,这是因为默认的 logger 对象只输出较高级别日志。为了输出想要的级别,我们需要自行定制 Logger 对象。

日志对象

Logger 对象通过 getLogger 方法创建:

1
2
3
4
>>> logger = logging.getLogger(__name__)
>>> logger.setLevel(logging.INFO)
>>> logger.info('you must be able to see me!')
INFO:__main__:you must be able to see me!

调用 Logger 对象 setLevel 方法设置日志输出级别,后续将忽略比该级别低的日志。调用 debug 方法输出日志将被忽略:

1
>>> logger.debug('you cant see me now!')

更多定制方法,请查看 帮助文档

1
>>> help(logger)

日志处理器

前面几个例子,日志均输出到标准输出,因此我们可以在屏幕中看到。当然了,日志还可以输出到文件,甚至可以通过网络发送出去,这都是通过 Handler 对象控制的。

标准输出

日志对象默认输出到标准输出,等价于:

1
2
3
4
5
logger_handler = logging.StreamHandler(sys.stdout)

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.addHandler(logger_handler)

可以进一步控制处理器可以输出的 日志级别

1
2
logger_handler = logging.StreamHandler(sys.stdout)
logger_handler.setLevel(logging.WARNING)

这样一来,只有不低于 WARNING 级别的日志才会被该处理器处理。需要注意的是, Logger 对象以及 Handler 对象级别一起决定日志输出行为。

文件

相比标准输出,将日志输出到文件更为妥当,文件可以保留一定的历史待查。

日志格式

采用默认格式输出的日志非常丑陋,信息量也不够,甚至连时间都没有! 因此,需要根据应用需求,灵活调整日志输出格式。 为 Handler 对象自定义格式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import logging, sys

FMT = '%(asctime)s %(levelname) 8s: [%(filename)s:%(lineno)d] [%(processName)s:%(process)d %(threadName)s] - %(message)s'
DATEFMT = '[%Y-%m-%d %H:%M:%S]'

logger_handler = logging.StreamHandler(sys.stdout)
logger_handler.setLevel(logging.DEBUG)

formatter = logging.Formatter(fmt=FMT, datefmt=DATEFMT)
logger_handler.setFormatter(formatter)

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.addHandler(logger_handler)

关键代码是 9-10 行,初始化一个格式化器,并设置到 Handler 对象上。初始化器需要两个参数, fmt 指定日志格式, datefmt 指定日期格式。除了日志内容,日志记录可以注入 时间级别代码文件名代码行数进程名进程ID线程名 等信息。

例子输出大致如下,可以看到 格式更为美观,信息更为丰富 了:

1
2
>>> logger.info('Hello, world!')
[2019-01-08 11:25:50]     INFO: [<stdin>:1] [MainProcess:99324 MainThread] - Hello, world!

【小菜学Python】系列文章首发于公众号【小菜学编程】,敬请关注:

【小菜学Python】系列文章首发于公众号【小菜学编程】,敬请关注: