做过企业级开发的同学应该都懂得,日志的重要性,所以 Python 当然也内置了日志处理模块 logging 来处理日志内容。

1 基本日志使用

对于简单的日志使用来说日志功能提供了一系列便利的函数。它们是 debug()info()warning()error()critical()。当然一次执行的简单脚本,其实使用 print 就可以了,但如果执行过程复杂或者需要输出到文件的场景中,logging 模块就会显示其威力了。

日志功能应以所追踪事件级别或严重性而定,严重程度:DEBUG<INFO<WARNING<ERROR<CRITICAL,默认的级别是WARNING,意味着只会追踪该级别及以上的事件,除非更改日志配置。

一个非常简单的例子

import logging
logging.warning('Watch out!')  # will print a message to the console
logging.info('I told you so')  # will not print anything

如果你在命令行中输入这些代码并运行,你将会看到命令行中输出:

WARNING:root:Watch out!

结果中可以看到,INFO 消息并没有出现,因为默认级别是 WARNING 。打印的信息包含事件的级别以及在日志调用中的对于事件的描述,例如“Watch out!”。暂时不用担心“root”部分,后面马上可以看到输出格式可按需要进行调整。

记录日志到文件
一种非常常见的情况是将日志事件记录到文件:

import logging
logging.basicConfig(filename='run.log')# , format='%(asctime)s - %(levelname)s - %(message)s', filemode='w')
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')

当前目录会生成一个文件,打开文件会看到内容:

2019-10-18 18:04:39,697 - DEBUG - debug message
2019-10-18 18:04:39,697  - INFO - info message
2019-10-18 18:04:39,697 - WARNING - warn message
2019-10-18 18:04:39,697 - ERROR - error message
2019-10-18 18:04:39,697 - CRITICAL - critical message

方法 logging.basicConfig 参数中 format 指定了输出内容的格式,filemode 指定了输出模式,模式规则参考 open 操作文件模式,更多格式化形式参数,参考官方文档列表:日志格式化变量

记录变量数据
要记录变量数据,请使用格式字符串作为事件描述消息,并将变量数据作为参数附加。 例如:

import logging
logging.warning('%s before you %s', 'Look', 'leap!')

将显示:

WARNING:root:Look before you leap!

如上所见,将可变数据合并到事件描述消息中使用旧的 %-s 形式的字符串格式化。 这是为了向后兼容,毕竟日志模块几乎从 python 已发布就有的模块,其他新增字符串格式化形式如 string.format() 或者 string.Tempalte 可以自从嵌套拼接日志内容。

2 日志进阶

日志模块中提供了一种叫记录器的模块,通过调用 Logger 类(以下称为 loggers , 记录器)的实例来执行日志记录,每个记录器都有一个名称,用了标示当前日志的模块来源。
在命名记录器时使用的一个好习惯是在每个使用日志记录的模块中使用模块级记录器,命名如下:

logger = logging.getLogger(__name__)

这意味着记录器名称跟踪包或模块的层次结构,并且直观地从记录器名称显示记录事件的位置。
Logger 对象有三重任务。首先,它们向应用程序代码公开了几种接收日志内容的接口方法,以便应用程序可以在运行时记录消息,如info()error()等。其次,记录器对象根据严重性(默认过滤工具)或过滤器对象确定要处理的日志消息。第三,记录器对象将相关的日志消息传递给所有感兴趣的日志处理程序 (Handler)。

先来个简单的例子:

import logging

# 设定名称
logger = logging.getLogger('simple_example')

# 设置日志级别
logger.setLevel(logging.DEBUG)

# 设置 Handler
ch = logging.FileHandler(filename='run.log')

# 设置 Handler 的日志级别,会覆盖 logger 本身的日志级别
ch.setLevel(logging.INFO)

# 创建一个格式化对象并传给 Handler
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)

# 把 Handler 传给 logger
logger.addHandler(ch)

logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')

在上面的例子中,创建了一个名叫 simple_examplelogger,然后构建了一个 FileHandlerlogger,在这个 FileHandler 中定义格式化、日志级别等,大部分情况下,选择或者构建一个合适的 Handler 是定义一个 Logger 的关键。

Handler 对象负责将适当的日志消息(基于日志消息的严重性)分派给处理程序的指定目标。 Logger 对象可以使用 addHandler() 方法向自己添加零个或多个处理程序对象。比如一个场景中,应用程序可能希望将所有日志消息发送到日志文件,将错误或更高的所有日志消息发送到标准输出,以及将所有关键消息发送至一个邮件地址。 那么此方案就需要三个单独的处理程序,其中每个处理程序负责将特定级别的消息发送到特定位置。

标准库包含很多处理程序(Handler)类型,参考官方文档:有用的处理程序 (Handler),可以从中按需选取合适的 Handler,当然也可以自定义。

日志在企业级应和复杂的场景中是必不可少并且非常重要的组成部分,本教程只是作为入门级别示例,使用过程中请更多参考所使用框架的的日志最佳实践。

本文参考:
官方:日志基础教程官方:Handler介绍官方:日志模块