日志记录是一种跟踪软件在运行时所发生的任何事件的方法。也就是说,在日常开发过程中,我们可以将程序所产生的事件用日志的形式记录下来,以此来查看程序逻辑执行得是否合理。这对于开发人员来说十分的重要。本篇文章使用Python中的logging来对日志的一些操作展开说明。

image.png

logging为我们提供了许多记录日志的方法,如 debug()info()warning()error()critical()。根据处理事件的情况不同,又可以分为不同的等级,如 DEBUGINFOWARNINGERRORCRITICAL。如下表格:

等级 啥时候用?
DEBUG 输出详细信息,通常仅在诊断问题的时候使用。
INFO 按照预期正在进行的并且是确认的事情。
WARNING 预示着发生了意想不到的事情,或者在不久的将来预示着一些问题(例如“磁盘空间低”)。软件仍在按预期运行。
ERROR 由于更严重的问题,该软件无法执行某些功能。
CRITICAL 表明程序本身可能无法继续运行的严重的错误。

下表给出了日志记录的等级:

等级 数值
CRITICAL 50
ERROT 40
WARNING 30
INFO 20
DEBUG 10
NOTSET 0

然而,默认情况下是 WARNING,这种情况下它会追踪此级别及以上级别的事件。

可以以不同方式处理被跟踪的事件。处理跟踪事件的最简单方法是将它们打印到控制台,另一种常见方法是将它们写入磁盘文件。

先来看一个简单的例子,首先导入 logging 模块,通过模块的 basicConfig 方法设置了日志文件的名称以及将跟踪阈值的日志记录级别设置为 DEBUG,最后输出三条日志信息,如下所示:

1
2
3
4
5
6
import logging

logging.basicConfig(filename='example.log', level=logging.DEBUG)
logging.debug('This message should go to the log file.')
logging.info('So should this')
logging.warning('And this, too.')

执行程序后会生成一个日志文件example.log,打开它就会看到日志信息,如下图:

你可以看到,单单是输出了日志信息,而没有时间,不方便查看。接下来通过另一个例子将日志时间、日志等级等信息一起记录下来。

下面的例子用于计算一元二次方程的根,计算完成后将日志记录下来,如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import logging
import math

# 规定打印日志的格式
LOG_FORMAT = '%(levelname)s %(asctime)s - %(name)s - %(message)s'
# 增加以及初始化logger
logging.basicConfig(filename='f:\\Python_test\\BasicKnowledge\\test.log',
                    level=logging.DEBUG,
                    format=LOG_FORMAT,
                    filemode='w')
# getLogger(name)用于获取logger对象,如果不指定记录器的名称name,则返回root对象
logger = logging.getLogger('Carol')

def quadratic_formula(a, b, c):
    """
    返回方程 ax^2 + bx + c = 0 的根
    """
    logger.info('quadratic_formula({0}, {1}, {2})'.format(a, b, c))

    # 计算判别式
    logger.debug('# Compute the discriminant')
    disc = b ** 2 - 4 * a * c

    # 计算两个根
    logger.debug('# compute the two roots')
    root1 = (-b + math.sqrt(disc)) / (2 * a)
    root2 = (-b - math.sqrt(disc)) / (2 * a)

    # 返回根
    logger.debug('# return the roots')
    return root1, root2

roots = quadratic_formula(1, 0, -4)
print(roots)

输出结果如下:

1
(2.0, -2.0)

同时,执行程序后会生成一个日志文件 test.log,打开它就会看到日志信息,如下图:

你可以看到,在有解的情况下不仅返回了方程的根,而且也将计算顺序以日志的形式保存了下来。

下面我们给它一个无解的参数,看看它的日志文件会保存什么东西。修改下列语句:

1
roots = quadratic_formula(1, 0, 1)

你可以看到控制台报错,同时日志文件如下图:

这是因为它在执行完 logger.debug('# compute the two roots') 这条语句之后就不再执行了,也就是我们修改的参数不满足方程根的求解。

再最后一个例子,我们结合 try-except-else-finally 来说明 logging 的使用方法。

对于不熟悉 try-except-else-finally 的同学,你可以看下面的图片:

该例子用于统计打开一个文件所需要的时间,并将其记录在日志文件中,如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import logging
import time

logging.basicConfig(filename="F:\\Python_test\\BasicKnowledge\\problems.log",
                    level=logging.DEBUG)
logger = logging.getLogger()

def read_file_timed(path):
    """Return the contents of the file at 'path' and measure time required"""
    start_time = time.time()
    try:
        f = open(path, mode='rb')
        data = f.read()
        return data
    except FileNotFoundError as err:
        logger.error(err)
        raise
    else:
        f.close()
    finally:
        stop_time = time.time()
        dt = stop_time - start_time
        logger.info('Time required for {file} = {time}'.format(file=path, time=dt))

path = read_file_timed('F:\\Python_test\\BasicKnowledge\\google_stock_data.csv')

如果 google_stock_data.csv 文件存在的话,你可以看到日志文件中有如下内容:

image.png

如果打开一个不存在的文件,执行程序后,会出现如下的内容:

image.png

好了,介绍完了。

对于一个比较有意思的谷歌股票数据文件,你可以在 这里 下载。