Python学习之logging (日志模块)

一、概述

🐿️ 1. 什么是日志

生活日志:生活中,我们可以把日志等同于日记。日记记录着我们生活中的点点滴滴。我们可以通过查看日记,回忆某年某月某日我们做了什么有意义的事情。

程序日志:在编程中我们可以通过日志来记录代码的运行轨迹。跟生活日记是一个意思,就是记录代码运行时发生了什么。在Python中,日志是对程序执行时所发生事件的一种追踪方式。

🐿️ 2. 日志的作用

  • 记录系统或应用程序的运行情况,错误信息,警告信息,调试信息
  • 可以快速定位问题,分析系统或应用程序的运行情况
  • 分析用户的操作行为,类型喜好,地域分布等信息
  • 监控系统资源使用情况,统计访问量等

二、Python的Logging模块简单简介

🐿️ 1. 引入

我们在碰到自己辛辛苦苦书写的代码报错的时候,通常最多的解决方法都是通过加很多的print()函数,一点点缩小范围,直到找到BUG所在位置并解决之!比如下面这段代码:

#🌾 通过 print 打印 排查问题所在
result1 = '第一个函数运行OK'
print(result1)
result2 = '第二个函数运行OK'
print(result2)
result3 = '第三个函数运行不OK'
print(result3)
result4 = '第四个函数运行OK'
print(result4)
result5 = '第五个函数运行OK'
print(result5)
💁:不可否认,这确实也是一种解决BUG的方法!但是,当你找到BUG所在位置并解决之后,那么多的print() 语句,一个个删除,也是一个非常麻烦的事情!

我们将上面的所有print语句都改成为logging.debug()语句,观察输出,看看是什么结果?

import logging

#🌾 通过 logging 打印 排查问题所在
#配置日志等级为INFO
logging.basicConfig(level=logging.INFO)

result1 = '第一个函数运行OK'
logging.debug(result1)

result2 = '第二个函数运行OK'
logging.debug(result2)

result3 = '第三个函数运行不OK'
logging.debug(result3)

result4 = '第四个函数运行OK'
logging.debug(result4)

result5 = '第五个函数运行OK'
logging.debug(result5)

观察输出、此时并无任何输出----也就是目前对我们的程序是没有任何影响的!

🙋🏻:我们设置日志等级为DEBUG,即将level的值设置为logging.DEBUG, 再观察输出
import logging

#🌾 通过 logging 打印 排查问题所在
#配置日志等级为INFO
logging.basicConfig(level=logging.DEBUG)

result1 = '第一个函数运行OK'
logging.debug(result1)

result2 = '第二个函数运行OK'
logging.debug(result2)

result3 = '第三个函数运行不OK'
logging.debug(result3)

result4 = '第四个函数运行OK'
logging.debug(result4)

result5 = '第五个函数运行OK'
logging.debug(result5)
💁:我们会发现,此时就会以DEBUG级别输出信息。这样我们就可以简单的通过改变日志的等级(仅仅改变一个参数值)来控制是否输出显示----以达到查错的作用,而不再需要反复加删print()函数来查错。是不是挺方便!

输出结果:

DEBUG:root:第一个函数运行OK
DEBUG:root:第二个函数运行OK
DEBUG:root:第三个函数运行不OK
DEBUG:root:第四个函数运行OK
DEBUG:root:第五个函数运行OK

🐿️ 2. 模块简介 

logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径、日志文件回滚等。logging模块中定义的函数和类为应用程序和库的开发实现了一个灵活的事件日志系统。Python logging 的配置由四个部分组成:LoggerHandlersFilterFormatter

相比print,具备如下优点:

  • logging 可以通过设置不同的日志等级,在 release 版本中只输出重要信息,而不必显示大量的调试信息;
  • print 将所有信息都输出到标准输出中,严重影响开发者从标准输出中查看其它数据;logging则可以由开发者决定将信息输出到什么地方,以及怎么输出; 
🤔 那么,什么时候我们才有必要使用logging模块,什么时候抛出异常(raise Exception),什么时候使用简单的print函数即可?,其实官方文档给我们总结了一个表格

🐿️ 3. 日志等级

💁:logging中级别大小:DEBUG < INFO < WARNING < ERROR < CRITICAL
⚠️ 注意:指定了日志等级后,只会显示大于等于所指定日志等级的日志信息!

🐿️ 4. formater 格式

Formatter用来规定Log record文本的格式,其使用python formatting string来规定具体格式。在默认情况下,logging模块的输出格式如下:

import logging

#🌾 输出:
logging.warning('%s before you %s','Look','leap!') 

# WARNING:root:Look before you leap!

但是,默认的输出格式不一定能满足我们的需求,我们可以通过Formatter自定义输出格式,在日志中添加更多丰富的信息,一些常见的项(实际上以下都是Log Record的属性)如下所示:

#日志级别的数值
%(levelno)s:打印日志级别的数值。

#日志等级名称
%(levelname)s:打印日志级别的名称。

#调用日志记录函数的文件的全路径
%(pathname)s:打印当前执行程序的路径,其实就是sys.argv[0]。

#调用日志记录函数的文件
%(filename)s:打印当前执行程序名。

#调用日志记录函数的函数名
%(funcName)s:打印日志的当前函数。

#调用日志记录函数的代码所在行号
%(lineno)d:打印日志的当前行号。

#日志事件发生的时间
%(asctime)s:打印日志的时间。

#当前线程的ID
%(thread)d:打印线程ID。

#当前线程名称
%(threadName)s:打印线程名称。

#当前进程ID
%(process)d:打印进程ID。

#当前进程名称
%(processName)s:打印线程名称。

#当前模块名称
%(module)s:打印模块名称。

#日志记录的文本内容
%(message)s:打印日志信息。

设置Formatter主要包括两种方式:

一种是通过Formatter类构建Formatter实例,并将其绑定到特定的handler上;
一种是通过logging.basicConfig设置:

#🌾:通过logging.basicConfig设置日志配置格式。
logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(lineno)d - %(funcName)s - %(message)s',
    datefmt='%Y/%m/%d %H:%M:%S',
    style='%' # '%', ‘{‘ or ‘$’
)

#🌾:通过Formatter类构建Formatter实例,并将其绑定到特定的handler上。
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s', 
    datefmt='%Y/%m/%d %H:%M:%S', 
    style='%'
)
formatter.converter = time.localtime()
formatter.converter = time.gmtime()

🐿️ 5. 日志等级&format格式模拟使用

import logging

#🌾:设置输出的格式
LOG_FORMAT = "时间:%(asctime)s - 日志等级:%(levelname)s - 日志信息:%(message)s"

#🌾:对logger进行配置---【日志等级】&【输出格式】
#⚠️
#【1】. 日志等级(WARNING,INFO,DEBUG,ERROR) “大写”;
#【2】. logging.basicConfig 只有一条!!!,如果写多条,也只有一条会生效。
logging.basicConfig(level = logging.WARNING, format = LOG_FORMAT)

#🌾:logging.level(message) 创建一条level级别的日志
logging.debug('This is debug log')
logging.info('This is a info log')
logging.warning('This is a warning log')
logging.error('This is error log')
logging.critical('This is critical log')

#👀 输出结果:观察可知,确实只有大于等于 WARNING 日志等级的日志信息输出了哦 !!!
# 时间:2024-12-23 11:26:25,297 - 日志等级:WARNING - 日志信息:This is a warning log
# 时间:2024-12-23 11:26:25,297 - 日志等级:ERROR - 日志信息:This is error log
# 时间:2024-12-23 11:26:25,297 - 日志等级:CRITICAL - 日志信息:This is critical log

🐿️ 6. 日志信息保存为文件

import logging

#🌾:设置输出的格式
LOG_FORMAT = "时间:%(asctime)s - 日志等级:%(levelname)s - 日志信息:%(message)s"

#🌾:对logger进行配置---【日志等级】&【输出格式
#注意:
# 【1】. 日志等级WARNINGINFODEBUGERROR) “大写”;
# 【2】. logging.basicConfig 只有一条!!!,如果写多条,也只有一条会生效。
# 【3】. 在 logging.basicConfig 中配置 “filename” 参数可以将日志保存在文件中。
logging.basicConfig(level = logging.WARNING, format = LOG_FORMAT,filename='myLoggerMessage')

#🌾:logging.level(message) 创建一条level级别的日志
logging.debug('This is debug log')
logging.info('This is a info log')
logging.warning('This is a warning log')
logging.error('This is error log')
logging.critical('This is critical log')

#👀 输出结果:观察可知,确实只有大于等于 WARNING 日志等级的日志信息输出了哦 !!!
# 时间:2024-12-23 11:26:25,297 - 日志等级:WARNING - 日志信息:This is a warning log
# 时间:2024-12-23 11:26:25,297 - 日志等级:ERROR - 日志信息:This is error log
# 时间:2024-12-23 11:26:25,297 - 日志等级:CRITICAL - 日志信息:This is critical log

三、Python的Logging模块进阶操作

✨ 如果只是简单的使用logging,那么上面介绍的方法就可以了,如果深度定制logging,那么就需要对它进行更深的了解!

🐿️ 1. 灵活配置日志器

各组件之间的关系

🐿️ 2. 模块化组件使用

🌾 使用步骤

1. 创建一个logger(日志记录器) 对象;

2. 定义handler(日志处理器),决定把日志发到哪里;

常用的是:
  StreamHandler --> 输出控制平台
  FileHandler --> 输出到文件

3. 把设置日志级别(level) 和 输出格式Formatters (日志格式器);

4. 把Handler添加到对应的logger中去

🌾 使用实例
#【示例一】
import logging

#🌾 1. 创建一个 logger(日志记录器) 对象;
my_logger = logging.Logger('first_logger')

#🌾 2. 定义handler(日志处理器),决定把日志发到哪里
my_handler = logging.FileHandler('test.log')

#🌾 3. 设置日志级别(level) 和 输出格式Formatters(日志格式器)
my_handler.setLevel(logging.INFO)
my_format = logging.Formatter("时间:%(asctime)s 日志信息:%(message)s 行号:%(lineno)d")

# 把handle 添加到对应的logger中
my_handler.setFormatter(my_format)
my_logger.addHandler(my_handler)

#🌾 4. 使用
my_logger.info("我的日志组件")

#🌾 5. 结果
# “test.log” 文件中。 时间:2024-12-23 13:21:52,635 日志信息:我的日志组件 行号:18
#【示例二】
import logging
#【第一个日志处理器】
#🌾 1. 创建一个 logger(日志记录器) 对象;
my_logger = logging.Logger('first_logger')

#🌾 2. 定义handler(日志处理器),决定把日志发到哪里
my_handler = logging.FileHandler('test.log')

#🌾 3. 设置日志级别(level) 和 输出格式Formatters(日志格式器)
my_handler.setLevel(logging.INFO)
my_format = logging.Formatter("时间:%(asctime)s 日志信息:%(message)s 行号:%(lineno)d")

# 把handle 添加到对应的logger中
my_handler.setFormatter(my_format)
my_logger.addHandler(my_handler)

#【第二个日志处理器】
#🌾 1. 创建一个 logger (日志记录器) 对象:
you_handler = logging.StreamHandler()

#🌾 2. 设置日志级别(level)和 输出格式 Formatters(日志格式器)
you_handler.setLevel(logging.DEBUG)
you_format = logging.Formatter("时间:%(asctime)s 日志信息:%(message)s 行号:%(lineno)d 这时streamHandler")
you_handler.setFormatter(you_format)
# 把handle 添加到对应的logger中
my_logger.addHandler(you_handler)

#【使用】
my_logger.info("我的日志组件")
# 【输出结果】
#“test.log”文件中。时间:2024-12-23 13:55:33,497 日志信息:我的日志组件 行号:32
#“控制台输出”:时间:2024-12-23 13:55:33,497 日志信息:我的日志组件 行号:32 这时streamHandler

posted on 2024-12-23 13:59  梁飞宇  阅读(2397)  评论(0)    收藏  举报