Python 程序员必须要学会的「日志」记录

写在之前

在我们的现实生活中,「日志记录」其实是一件非常重要的事情,比如银行的转账记录,汽车的行车记录仪记录行驶过程中的一切,如果出现了什么问题,我们可以通过「日志记录」来搞清楚到底发生了什么事情。

除了在生活中,在日常的系统开发以及调试等过程中,记录日志同样是一件很重要的事情。很多编程初学者并没有「记录日志」的习惯,认为记录日志是一件可有可无的事情,出现问题的时候只要使用 print 函数打印一下程序的中间结果即可,真是 too young too naive。只是 print 的话对于简单的脚本程序来说或许可行,可是到了碰到复杂的系统,你如果还是只用 print 这种方式的话,你会看到大量的消息,看到吐也不一定能找到其中有用的消息。

「日志」是一个系统的重要组成部分,用来记录用户操作、系统运行状态和错误信息,它的好坏直接影响到系统出现问题时定位的速度,有日志记录,我们可以在服务崩溃的时候很快的通过查看日志来发现问题出现的地方,同样也可以通过对日志的观察和分析,提前发现系统可能存在的风险。

Python 的标准日志模块

上面我们说了「日志」是如此的重要,作为无所不能的 Python 当然也有日志相关的功能,Python 标准库中提供了 logging 模块供我们使用。在最简单的使用中,默认情况下 logging 将日志打印到屏幕终端,我们可以直接导入 logging 模块,然后调用 debug,info,warn,error 和 critical 等函数来记录日志,默认日志的级别为 warning,级别比 warning 高的日志才会被显示(critical > error > warning > info > debug),「级别」是一个逻辑上的概念,用来区分日志的重要程度。

import logging

logging.debug('debug message')

logging.info("info message")

logging.warn('warn message')

logging.error("error message")

logging.critical('critical message')

1

2

3

4

5

6

7

上述代码的执行结果如下所示:

WARNING:root:warn message

ERROR:root:error message

CRITICAL:root:critical message

1

2

3

我在上面说过,用 print 的话会产生大量的信息,从而很难从中找到真正有用的信息。而 logging 中将日志分成不同的级别以后,我们在大多数时间只保存级别比较高的日志信息,从而提高了日志的性能和分析速度,这样我们就可以很快速的从一个很大的日志文件里找到错误的信息。

配置日志格式

我们在用 logging 来记录日志之前,先来进行一些简单的配置:

import logging

logging.basicConfig(filename= 'test.log', level= logging.INFO)

logging.debug('debug message')

logging.info("info message")

logging.warn('warn message')

logging.error("error message")

logging.critical('critical message')

1

2

3

4

5

6

7

8

9

运行上面的代码以后,会在当前的目录下新建一个 test.log 的文件,这个文件中存储 info 以及 info 以上级别的日志记录。运行一次的结果如下所示:

INFO:root:info message

WARNING:root:warn message

ERROR:root:error message

CRITICAL:root:critical message

1

2

3

4

5

上面的例子中,我是用 basicConfig 对日志进行了简单的配置,其实我们还可以进行更为复杂些的配置,在此之前,我们先来了解一下 logging 中的几个概念:

Logger:日志记录器,是应用程序中可以直接使用的接口。

Handler:日志处理器,用以表明将日志保存到什么地方以及保存多久。

Formatter:格式化,用以配置日志的输出格式。

上述三者的关系是:一个 Logger 使用一个 Handler,一个 Handler 使用一个 Formatter。那么概念我们知道了,该如何去使用它们呢?我们的 logging 中有很多种方式来配置文件,简单的就用上面所说的 basicConfig,对于比较复杂的我们可以将日志的配置保存在一个配置文件中,然后在主程序中使用 fileConfig 读取配置文件。

基本的知识我们知道了,下面我们来做一个小的题目:日志文件保存所有 debug 及其以上级别的日志,每条日志中要有打印日志的时间,日志的级别和日志的内容。请先自己尝试着思考一下,如果你已经思考完毕请继续向下看:

import logging

logging.basicConfig(

level= logging.DEBUG,

format = '%(asctime)s : %(levelname)s : %(message)s',

filename= "test.log"

)

logging.debug('debug message')

logging.info("info message")

logging.warn('warn message')

logging.error("error message")

logging.critical('critical message')

1

2

3

4

5

6

7

8

9

10

11

12

13

14

上述代码的一次运行结果如下:

2018-10-19 22:50:35,225 : DEBUG : debug message

2018-10-19 22:50:35,225 : INFO : info message

2018-10-19 22:50:35,225 : WARNING : warn message

2018-10-19 22:50:35,225 : ERROR : error message

2018-10-19 22:50:35,225 : CRITICAL : critical message

1

2

3

4

5

我刚刚在上面说过,对于比较复杂的我们可以将日志的配置保存在一个配置文件中,然后在主程序中使用 fileConfig 读取配置文件。下面我们就来看一个典型的日志配置文件(配置文件名为 logging.conf):

[loggers]

keys = root

[handlers]

keys = logfile

[formatters]

keys = generic

[logger_root]

handlers = logfile

[handler_logfile]

class = handlers.TimedRotatingFileHandler

args = ('test.log', 'midnight', 1, 10)

level = DEBUG

formatter = generic

[formatter_generic]

format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s] %(message)s

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

在上述的日志配置文件中,首先我们在 [loggers] 中声明了一个叫做 root 的日志记录器(logger),在 [handlers] 中声明了一个叫 logfile 的日志处理器(handler),在 [formatters] 中声明了一个名为 generic 的格式化(formatter)。之后在 [logger_root] 中定义 root 这个日志处理器(logger) 所使用的日志处理器(handler) 是哪个,在 [handler_logfile] 中定义了日志处理器(handler) 输出日志的方式、日志文件的切换时间等。最后在 [formatter_generic] 中定义了日志的格式,包括日志的产生时间,级别、文件名以及行号等信息。

有了上述的配置文件以后,我们就可以在主代码中使用 logging.conf 模块的 fileConfig 函数加载日志配置:

import logging

import logging.config

logging.config.fileConfig('logging.conf')

logging.debug('debug message')

logging.info("info message")

logging.warn('warn message')

logging.error("error message")

logging.critical('critical message')

1

2

3

4

5

6

7

8

9

10

11

上述代码的运行一次的结果如下所示:

2018-10-19 23:00:02,809 WARNI [root:8] warn message

2018-10-19 23:00:02,809 ERROR [root:9] error message

2018-10-19 23:00:02,809 CRITI [root:10] critical message

1

2

3

写在之后

正如标题中所说的那样,我认为「日志记录」是每个 Python 程序员必须要知道且学会的东西,也是每个程序员必须具备的意识。如果你之前没有使用过日志亦或者说不知道该怎么去使用日志记录,这篇文章我相信会给你带来一些帮助。

Python 的日志库设计之好,用起来之灵活,可以说是 Python 标准库中相当优秀的存在。当然上面我们所说的只是 Python 日志库中很少的一部分,更多的操作和内容还需要你在今后的学习和实践中自己去发掘和运用。

Python 程序员必须要学会的「日志」记录

相关推荐