程序运行的日志能够有效的帮助我们查看程序运行的情况。python 内置的日志模块是 logging,通过它能够有效的记录 python 程序运行的情况。

日志例子

编写一个日志的例子,保存到 /workspace/proj-pf/common-scripts/monitor.py

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import logging
import time
from datetime import datetime
from logging import handlers


class MyLogger(object):
# 5个日志级别
levels = {
"debug": logging.DEBUG,
"info": logging.INFO,
"warning": logging.WARNING,
"error": logging.ERROR,
"critical": logging.CRITICAL,
}

def __init__(
self,
log_name,
level="debug",
when="D",
max_backup=3,
formatter="%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s",
):
formatter = logging.Formatter(formatter) # 日志输出格式

# 定义一个日志实例
self.logger = logging.getLogger() # 实例化一个日志(实例)
self.logger.setLevel(self.levels.get(level)) # 设置日志级别

# 定义控制台日志输出流
stream_handler = logging.StreamHandler() # 实例化一个输出日志到控制台的 handler
stream_handler.setFormatter(formatter) # 日志输出格式

# 定义文件日志输出流
file_handler = handlers.TimedRotatingFileHandler(
filename=log_name, when=when, backupCount=max_backup, encoding="utf-8"
) # 实例化一个输出日志到文件的 handler,以 when 为备份标识(D 天、H 小时、M 分、S 秒、W 每星期(interval==0时代表星期一)、midnight 每天凌晨),
# 最多存放备份 backupCount 个,超过的日志将会删除
file_handler.setFormatter(formatter) # 设置文件里写入的格式

# 把 handler 添加到日志实例中
self.logger.addHandler(stream_handler) # 把控制台输出流对象添加到 日志实例
self.logger.addHandler(file_handler) # 把文件输出流对象添加到 日志实例


def do(args, logger):
for j in args:
logger.info(f"j = {j}")


def main():
now = datetime.now().strftime("%Y%m%d-%H%M%S")
logger = MyLogger(log_name=f"run-{now}.log", level="debug").logger
i = 0
while i < 3:
logger.info(f"--- 监控到 i = {i} ---")
try:
do(range(2), logger)
except Exception as e:
# print(str(e))
logger.error(str(e))
i += 1
logger.info("Done!")


if __name__ == "__main__":
main()

执行该程序:

1
2
cd /workspace/proj-pf/common-scripts/
python monitor.py

控制台结果输出:

1
2
3
4
5
6
7
8
9
10
2023-01-19 15:49:09,026 - monitor.py[line:57] - INFO: --- 监控到 i = 0 ---
2023-01-19 15:49:09,026 - monitor.py[line:49] - INFO: j = 0
2023-01-19 15:49:09,026 - monitor.py[line:49] - INFO: j = 1
2023-01-19 15:49:09,026 - monitor.py[line:57] - INFO: --- 监控到 i = 1 ---
2023-01-19 15:49:09,026 - monitor.py[line:49] - INFO: j = 0
2023-01-19 15:49:09,026 - monitor.py[line:49] - INFO: j = 1
2023-01-19 15:49:09,026 - monitor.py[line:57] - INFO: --- 监控到 i = 2 ---
2023-01-19 15:49:09,026 - monitor.py[line:49] - INFO: j = 0
2023-01-19 15:49:09,026 - monitor.py[line:49] - INFO: j = 1
2023-01-19 15:49:09,026 - monitor.py[line:64] - INFO: Done!

同时,在目录 /workspace/proj-pf/common-scripts/ 下,有日志文件 run-20230119-154909.log 产生。对于监控程序,我们把 main 函数的 while 循环改为死循环,此时,生成的日志文件会最多包含 max_backup + 1 个,前 max_backup 个以我们设置的 when 标识,如: run-20230114-153542.log.2023-01-16, run-20230114-153542.log.2023-01-17, run-20230114-153542.log.2023-01-18 等。

字符串字段

输出的日志文件格式,可以根据需要自定义,常见字符段有:

字符段 格式(代码中输入该内容) 描述
asctime %(asctime)s 日志事件发生的时间–人类可读时间,如:2003-07-08 16:49:45,896
created %(created)f 日志事件发生的时间–时间戳,就是当时调用time.time()函数返回的值
relativeCreated %(relativeCreated)d 日志事件发生的时间相对于logging模块加载时间的相对毫秒数(目前还不知道干嘛用的)
msecs %(msecs)d 日志事件发生事件的毫秒部分
levelname %(levelname)s 该日志记录的文字形式的日志级别(’DEBUG’, ‘INFO’, ‘WARNING’, ‘ERROR’, ‘CRITICAL’)
levelno %(levelno)s 该日志记录的数字形式的日志级别(10, 20, 30, 40, 50)
name %(name)s 所使用的日志器名称,默认是’root’,因为默认使用的是 rootLogger
message %(message)s 日志记录的文本内容,通过 msg % args计算得到的
pathname %(pathname)s 调用日志记录函数的源码文件的全路径
filename %(filename)s pathname的文件名部分,包含文件后缀
module %(module)s filename的名称部分,不包含后缀
lineno %(lineno)d 调用日志记录函数的源代码所在的行号
funcName %(funcName)s 调用日志记录函数的函数名
process %(process)d 进程ID
processName %(processName)s 进程名称,Python 3.1新增
thread %(thread)d 线程ID
threadName %(thread)s 线程名称

参考文献

  1. Python之日志处理(logging模块)
  2. 日志常用指引