0. 引言
我们在GitHub下载别人的代码时,总会发现logger
这个包的使用,仔细观察后发现,logger
代替了print
,下文就是为了搞明白logger
的具体用法和使用的场景。
1. logging模块的介绍
logging
就是日志,一般用来记录程序在运行过程中发生的状况,在程序开发过程中添加日志模块能够帮助我们了解程序运行过程中发生了哪些事件。当然不是说所有的事件都是需要我们关注的,所以事件也有轻重之分。
logging
可以设置输出日志的等级、日志保存路径、日志文件回滚等;相比 print
,具备如下优点:
- 可以通过设置不同的日志等级,在 release 版本中只输出重要信息,而不必显示大量的调试信息;
print
将所有信息都输出到标准输出中,严重影响开发者从标准输出中查看其它数据;logging
则可以由开发者决定将信息输出到什么地方,以及怎么输出;- 和
print
相比,logging
是线程安全的。
线程安全:线程执行一段代码,不会产生不确定的结果,那这段代码就是线程安全的。
多线程的时候执行
说明,
- 第一步打印字符串
- 第二步打印换行符,就在这之间,发生了线程的切换。
这说明
2. 事件及其(event)的级别介绍
下面是事件的级别分类:
事件名称 | 说明 | 适用场景 | Level | 是否被输出到控制台 |
---|---|---|---|---|
DEBUG | 我们在进行debug时,想让某一个东西被输出 | 诊断问题时使用 | 10 | 不会 |
INFO | 运行的该程序时打印的日志,目的是确认该程序是否如期进行 | 程序运行时正常的记录 | 20 | 不会 |
WARNING | WARNING并不影响程序运行,但是出现了一些小的问题 | 部分程序没有如期运行 | 30 | 会 |
ERROR | 某些程序不能执行特定的功能 | 该功能很重要,我们需要进行记录 | 40 | 会 |
CRITICAL | 发生了严重的错误,程序无法运行 | 发生致命错误时的记录 | 50 | 会 |
虽然事件是有轻重缓急之分,但是应该有一个具体数值来衡量,这里使用[10, 20, 30, 40, 50]
的数值来区分不同的日志类型。
logging
默认的级别是WARNING,也就意味着只有级别大于等于的才会被看到- 跟踪日志的方式可以是①写入到文件中,也可以②直接输出到控制台
3. 日志输出级别调整 —— logging.basicConfig(**kwargs)
使用默认格式化程序创建 StreamHandler
并将其添加到根日志记录器中,从而完成日志系统的基本配置。如果没有为根日志程序定义处理程序,debug()
、info()
、warning()
、error()
和 critical()
函数将自动调用 basicConfig()
。
如果根日志记录器已经为其配置了处理程序,则此函数不执行任何操作。
logging.basicConfig(**kwargs)
支持以下关键字参数:
参数名 | 描述 |
---|---|
filename | 日志的保存文件,即不在控制台输出而是将日志输入到该文件中 |
filemode | 该日志存储文件的打开模式,可选:①r (只读);②w (只写);③a (追加)。默认为a |
format | 日志的字符串格式 |
datefmt | 使用time.strftime() 所接收的指定日期/时间格式 |
style | 如果指定了格式,则对格式化字符串使用此样本。% 用于printf 样式;{
用于str.format() 样式;$ 用于string 样式。默认为% |
level | 记录的最低日志级别,低于该级别的就不记录了(等于该级别的还会记录)。默认为logging.WARNING 。 |
举个例子:
import logging # 导入日志包
# 控制输出日志的等级
logging.basicConfig(level=logging.WARNING,
format="%(asctime)s %(filename)s %(levelname)s %(message)s",
datefmt="%a %d %b %Y %H:%M:%S")
"""
format="%(asctime)s %(filename)s %(levelname)s %(message)s":表示输出日志的格式:时间 文件名 日志级别 具体的信息
datefmt="%a %d %b %Y %H:%M:%S"):时间的格式
a: 本地简化星期名称
d: 月内中的一天(0-31)
b: 本地简化的月份名称
Y: 四位数的年份表示(000-9999)
H: 24小时制小时数(0-23)
M: 分钟数(00-59)
S: 秒(00-59)
"""
logging.debug("[debug]Debug时使用的日志")
logging.info("[info]日常运行的日志")
logging.warning("[warning]记录警告!")
logging.error("[error]发生部分错误时的日志")
logging.critical("[critical]发生致命错误时的日志")
"""
Fri 13 May 2022 09:38:36 432319022.py WARNING [warning]记录警告!
Fri 13 May 2022 09:38:36 432319022.py ERROR [error]发生部分错误时的日志
Fri 13 May 2022 09:38:36 432319022.py CRITICAL [critical]发生致命错误时的日志
"""
4. 跟踪日志的方法
跟踪日志的方式有两种:
- 写入到文件中
- 直接输出到控制台
接下来使用小例子对这两种方式进行说明。
4.1 输出到控制台
import logging # 导入日志包
# 1. 跟踪日志的第一种方式 —— 输出到控制台
logging.debug("[debug] Debug时使用的日志")
logging.info("[info] 日常运行的日志")
logging.warning("[warning] 记录警告!")
logging.error("[error] 发生部分错误时的日志")
logging.critical("[critical] 发生致命错误时的日志")
"""
WARNING:root:[warning] 记录警告!
ERROR:root:[error] 发生部分错误时的日志
CRITICAL:root:[critical] 发生致命错误时的日志
"""
可以看到,当不指定最低的日志等级,默认为logging.WARNING
。
4.2 输出到文件
需要输出到文件则首先需要修改logging的基础配置文件,如下:
logging.basicConfig(filename="存储文件的位置/文件名/格式", level=输出日志的最低级别)
- 格式一般是
.log
- 输出日志的最低级别有:
logging.DEBUG
logging.INFO
logging.WARNING
logging.ERROR
logging.CRITICAL
例子1:最低输出日志的级别为logging.DEBUG
import logging # 导入日志包
# 2. 跟踪日志的第一种方式 —— 输出到文件
logging.basicConfig(filename='./example.log', level=logging.DEBUG)
logging.debug("[debug] Debug时使用的日志")
logging.info("[info] 日常运行的日志")
logging.warning("[warning] 记录警告!")
logging.error("[error] 发生部分错误时的日志")
logging.critical("[critical] 发生致命错误时的日志")
此时控制台并没有输出,而是在当前文件夹下生成了一个example.log
的文件,文件内容如下:
DEBUG:root:[debug] Debug时使用的日志
INFO:root:[info] 日常运行的日志
WARNING:root:[warning] 记录警告!
ERROR:root:[error] 发生部分错误时的日志
CRITICAL:root:[critical] 发生致命错误时的日志
可以看到,这个文件中保存了所有的日志信息。
例子2:最低输出日志的级别为logging.WARNING
import logging # 导入日志包
# 2. 跟踪日志的第一种方式 —— 输出到文件
logging.basicConfig(filename='./example.log', level=logging.WARNING)
logging.debug("[debug] Debug时使用的日志")
logging.info("[info] 日常运行的日志")
logging.warning("[warning] 记录警告!")
logging.error("[error] 发生部分错误时的日志")
logging.critical("[critical] 发生致命错误时的日志")
example.log
文件内容如下:
WARNING:root:[warning] 记录警告!
ERROR:root:[error] 发生部分错误时的日志
CRITICAL:root:[critical] 发生致命错误时的日志
这个日志中只保留等级≥WARNING的日志信息。
5. 自定义日志的记录内容
5.1 直接在logger.日志类型()中自定义
举个例子:
import logging
a = [1, 2, 3]
logger = logging.getLogger()
logger.setLevel(logging.WARNING)
logger.warning(f"此时a的值为: {
a}")
"""
此时a的值为: [1, 2, 3]
"""
5.2 通过设置format
来统一日志的记录格式
import logging
import time
logging.basicConfig(filename="./logging信息.log",
filemode="a",
level=logging.NOTSET,
format="%(asctime)s [%(filename)s]: [%(levelname)s] -> %(message)s",
datefmt="[%a %d %Y %H:%M:%S]"
)
a = [1, 2, 3]
logging.debug("这是一条debug信息")
logging.info(f"程序正常运行时的信息,此时a为: {
a}")
"""
[Fri 13 2022 10:04:47] [1339390831.py]: [DEBUG] -> 这是一条debug信息
[Fri 13 2022 10:04:47] [1339390831.py]: [INFO] -> 程序正常运行时的信息,此时a为: [1, 2, 3]
"""
6. 神经网络训练时的使用
这里只是举个简单的小例子,根据实际代码进行修改。
import logging
import random
import time
# 定义logging输出的格式和内容
logging.basicConfig(level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
datefmt="[%a %d %Y %H:%M:%S]"
)
def net(input):
return input * random.randint(10, 100) + random.randint(100, 200)
loss_function = lambda x: x/200
epochs = 30
for epoch in range(0, epochs):
start = time.time()
input = random.randint(1, 10)
output = net(input)
loss = loss_function(output)
# 记录
logging.info(f"\tEpoch: {
epoch+1}/{
epochs}\tcost: {
time.time() - start:.4f}\tloss: {
loss:.4f}\toutput: {
output:.4f}")
"""
[Fri 13 2022 10:19:34] INFO Epoch: 1/30 cost: 0.0000 loss: 3.0600 output: 612.0000
[Fri 13 2022 10:19:34] INFO Epoch: 2/30 cost: 0.0000 loss: 1.2100 output: 242.0000
[Fri 13 2022 10:19:34] INFO Epoch: 3/30 cost: 0.0000 loss: 3.9100 output: 782.0000
[Fri 13 2022 10:19:34] INFO Epoch: 4/30 cost: 0.0000 loss: 1.1200 output: 224.0000
[Fri 13 2022 10:19:34] INFO Epoch: 5/30 cost: 0.0000 loss: 2.9450 output: 589.0000
[Fri 13 2022 10:19:34] INFO Epoch: 6/30 cost: 0.0000 loss: 4.1850 output: 837.0000
[Fri 13 2022 10:19:34] INFO Epoch: 7/30 cost: 0.0000 loss: 0.8100 output: 162.0000
[Fri 13 2022 10:19:34] INFO Epoch: 8/30 cost: 0.0000 loss: 3.2900 output: 658.0000
[Fri 13 2022 10:19:34] INFO Epoch: 9/30 cost: 0.0000 loss: 5.0300 output: 1006.0000
[Fri 13 2022 10:19:34] INFO Epoch: 10/30 cost: 0.0000 loss: 1.2900 output: 258.0000
[Fri 13 2022 10:19:34] INFO Epoch: 11/30 cost: 0.0000 loss: 3.9050 output: 781.0000
[Fri 13 2022 10:19:34] INFO Epoch: 12/30 cost: 0.0000 loss: 2.3500 output: 470.0000
[Fri 13 2022 10:19:34] INFO Epoch: 13/30 cost: 0.0000 loss: 1.5350 output: 307.0000
[Fri 13 2022 10:19:34] INFO Epoch: 14/30 cost: 0.0000 loss: 0.8800 output: 176.0000
[Fri 13 2022 10:19:34] INFO Epoch: 15/30 cost: 0.0000 loss: 1.2850 output: 257.0000
[Fri 13 2022 10:19:34] INFO Epoch: 16/30 cost: 0.0000 loss: 1.0450 output: 209.0000
[Fri 13 2022 10:19:34] INFO Epoch: 17/30 cost: 0.0000 loss: 1.9250 output: 385.0000
[Fri 13 2022 10:19:34] INFO Epoch: 18/30 cost: 0.0000 loss: 2.2750 output: 455.0000
[Fri 13 2022 10:19:34] INFO Epoch: 19/30 cost: 0.0000 loss: 2.7200 output: 544.0000
[Fri 13 2022 10:19:34] INFO Epoch: 20/30 cost: 0.0000 loss: 1.9450 output: 389.0000
[Fri 13 2022 10:19:35] INFO Epoch: 21/30 cost: 0.0000 loss: 1.1500 output: 230.0000
[Fri 13 2022 10:19:35] INFO Epoch: 22/30 cost: 0.0000 loss: 2.4850 output: 497.0000
[Fri 13 2022 10:19:35] INFO Epoch: 23/30 cost: 0.0000 loss: 1.3800 output: 276.0000
[Fri 13 2022 10:19:35] INFO Epoch: 24/30 cost: 0.0000 loss: 0.9550 output: 191.0000
[Fri 13 2022 10:19:35] INFO Epoch: 25/30 cost: 0.0000 loss: 1.0350 output: 207.0000
[Fri 13 2022 10:19:35] INFO Epoch: 26/30 cost: 0.0000 loss: 3.1400 output: 628.0000
[Fri 13 2022 10:19:35] INFO Epoch: 27/30 cost: 0.0000 loss: 2.4550 output: 491.0000
[Fri 13 2022 10:19:35] INFO Epoch: 28/30 cost: 0.0000 loss: 1.0750 output: 215.0000
[Fri 13 2022 10:19:35] INFO Epoch: 29/30 cost: 0.0000 loss: 4.5150 output: 903.0000
[Fri 13 2022 10:19:35] INFO Epoch: 30/30 cost: 0.0000 loss: 4.5950 output: 919.0000
"""
参考
- https://www.jb51.net/article/126681.htm
- https://blog.csdn.net/colinlee19860724/article/details/90965100