如何理解python接口自动化之logging日志模块


Posted in Python onJune 15, 2021

一、logging模块介绍

​前言:我们之前运行代码时都是将日志直接输出到控制台,而实际项目中常常需要把日志存储到文件,便于查阅,如运行时间、描述信息以及错误或者异常发生时候的特定上下文信息。

​Python中自带的logging模块提供了标准的日志接口,在debug时使用往往会事半功倍。为什么不直接使用print去输出呢?这种方式对简单的脚本来说有用,对于复杂的系统来说相当于一个花瓶摆设,大量的print输出很容易被遗忘在代码里,并且print是标准输出,这很难从一堆信息里去判断哪些是你需要重点关注的。

​logging的优势就在于可以控制日志的级别,把不需要的信息进行过滤,且可以决定它输出到什么地方、如何输出,还可以通过控制等级把特定等级的信息输出到特定的位置等。logging一共分为四个部分:

  • Loggers:日志收集器,可供程序直接调用的接口,app通过调用提供的api来记录日志
  • Handlers:日志处理器, 决定将日志记录分配至正确的目的地
  • Filters:日志过滤器,对日志信息进行过滤, 提供更细粒度的日志是否输出的判断
  • Formatters:日志格式器,制定最终记录打印的格式布局

二、日志等级

​logging将logger的等级划分成5个level,由低到高分别是DEBUG、INFO、WARNING、ERROE、CRITICAL,默认是WARNING级别,CRITICAL最高,相关等级说明如下:

Level 说明
DEBUG 输出详细的运行信息,主要用于调试,追踪问题时使用
INFO 输出正常的运行的信息,一切按预期进行的情况
WARNING 一些意想不到的或即将会发生的情况,比如警告:内存空间不足,但不影响程序运行
ERROR 由于某些问题,程序的一些功能会受到影响,还可以继续运行
CRITICAL 一个严重的错误,表明程序本身可能无法继续运行

​这些等级的日志中低包含高,比如INFO,会收集INFO及以上等级的日志,DEBUG等级的日志将不进行收集。下面我们来输出这5个等级的日志:

import logging

"""
logging模块默认收集的日志是warning以上等级的

"""

a = 100
logging.debug(a)
logging.info('这是INFO等级的信息')
logging.warning('这是WARNING等级的信息')
logging.error('这是ERROR等级的信息')
logging.critical('这是CRITICAL等级的信息')

​输出结果:

C:\software\python\python.exe D:/learn/test.py

WARNING:root:这是WARNING等级的信息

ERROR:root:这是ERROR等级的信息

CRITICAL:root:这是CRITICAL等级的信息

Process finished with exit code 0

三、日志收集器

​日志是怎么被收集和输出的呢?答案就是日志收集器,设置一个收集器,把指等级的日志信息输出到指定的地方,控制台或文件等,其工作过程大致如下:

如何理解python接口自动化之logging日志模块

​logging中默认的日志收集器是root,收集等级默认是WARNING,我们可以通过setLevel来改变它的收集等级。

# 获取默认的日志收集器root
my_log = logging.getLogger()
# 设置默认的日志收集器等级
my_log.setLevel("DEBUG")  # 日志将全部被收集

a = 100
logging.debug(a)
logging.info('这是INFO等级的信息')
logging.warning('这是WARNING等级的信息')
logging.error('这是ERROR等级的信息')
logging.critical('这是CRITICAL等级的信息')

​输出结果:

C:\software\python\python.exe D:/learn/test.py

DEBUG:root:100

INFO:root:这是INFO等级的信息

WARNING:root:这是WARNING等级的信息

ERROR:root:这是ERROR等级的信息

CRITICAL:root:这是CRITICAL等级的信息

 

Process finished with exit code 0

 

​除了使用默认的日志收集器,我们也可以自己创建一个收集器logging.getLogger,如下:

import logging

my_logger = logging.getLogger('my_logger')	# 创建logging对象
my_logger.setLevel('INFO')	# 设置日志收集等级

a = 100
logging.debug(a)
logging.info('这是INFO等级的信息')
logging.warning('这是WARNING等级的信息')
logging.error('这是ERROR等级的信息')
logging.critical('这是CRITICAL等级的信息')

​输出结果:

C:\software\python\python.exe D:/learn/test.py

WARNING:root:这是WARNING等级的信息

ERROR:root:这是ERROR等级的信息

CRITICAL:root:这是CRITICAL等级的信息

 

Process finished with exit code 0

四、日志处理器

​上面例子中设置的收集器都是输出到控制台,除此我们还可以输出到文件中。

​Handlers(处理器)的作用就是将logger发过来的信息进行准确地分配,送往正确的地方。比如,送往控制台、文件或者是两者。它决定了每个日志收集器的行为,是创建收集器之后需要配置的重点区域。每个Handler同样有一个日志级别,一个logger可以拥有多个handler也就是说logger可以根据不同的日志级别将日志传递给不同的handler。当然也可以相同的级别传递给多个handler,这就根据需求来灵活的配置了。

​下面实例中设置了两个handler,一个是输出到控制台,一个是输出到文件中。关键代码:

  • logging.StreamHandler:输出到控制台的处理器
  • logging.FileHandler:输出到文件的处理器
  • addHandler:添加处理器
  • removeHandler:移除处理器
import logging

my_logger = logging.getLogger('my_logger')
my_logger.setLevel('INFO')

# 创建一个输出到控制台的处理器
sh = logging.StreamHandler()
sh.setLevel("ERROR")    # 设置处理器的输出等级
my_logger.addHandler(sh)    # 将处理器绑定到日志收集器上

# 创建一个输出到文件的处理器
fh = logging.FileHandler("logs.logs", encoding="utf8")
fh.setLevel("INFO")
my_logger.addHandler(fh)
# my_logger.removeHandler(fh)	# 移除处理器

a = 100
my_logger.debug(a)
my_logger.info('这是INFO等级的信息')
my_logger.warning('这是WARNING等级的信息')
my_logger.error('这是ERROR等级的信息')
my_logger.critical('这是CRITICAL等级的信息')

运行结果:

C:\software\python\python.exe D:/learn/test.py

这是ERROR等级的信息

这是CRITICAL等级的信息

 

Process finished with exit code 0

如何理解python接口自动化之logging日志模块

五、日志过滤器

​Filters可以实现比level更复杂的过滤功能,限制只有满足过滤规则的日志才会被输出。比如我们定义了filter = logging.Filter('A.B'),并将这个Filter添加到了一个Handler上,则使用该Handler的Logger中只有名字带A.B前缀的Logger才能输出其日志。下面是一个简单实例:

import logging

# 这是logger1
my_logger = logging.getLogger('A.C,B')
my_logger.setLevel('INFO')

# 这是logger2
my_logger2 = logging.getLogger('A.B')
my_logger2.setLevel('INFO')

# 创建一个处理器,两个logger都使用这个处理器
sh = logging.StreamHandler()
sh.setLevel("ERROR")
my_logger.addHandler(sh)
my_logger2.addHandler(sh)

# 创建一个过滤器绑到处理器上
my_filter = logging.Filter(name='A.B')
sh.addFilter(my_filter)    # 把过滤器添加到处理器上
# sh2.removeFilter(my_filter)   # 移除过滤器

my_logger.debug('这是logger1-DEBUG等级的信息')
my_logger.info('这是logger1-INFO等级的信息')
my_logger.warning('这是logger1-WARNING等级的信息')
my_logger.error('这是logger1-ERROR等级的信息')
my_logger.critical('这是logger1-CRITICAL等级的信息')

my_logger2.debug('这是logger2-DEBUG等级的信息')
my_logger2.info('这是logger2-INFO等级的信息')
my_logger2.warning('这是logger2-WARNING等级的信息')
my_logger2.error('这是logger2-ERROR等级的信息')
my_logger2.critical('这是logger2-CRITICAL等级的信息')

​因为只有logger2满足过滤器的条件,因此只会输出logger2的日志,运行结果如下:

C:\software\python\python.exe D:/learn/test.py

这是logger2-ERROR等级的信息

这是logger2-CRITICAL等级的信息

 

Process finished with exit code 0

​filter方法用于具体控制传递的record记录是否能通过过滤,如果该方法返回值为0表示不能通过过滤,非0表示可以通过过滤。

六、日志格式器

​顾名思义,对日志进行格式化,因为常规的日志输出并不直观美观,通过美化日志的输出格式,可以让我们阅读起来更加舒服。

​format常用格式如下:

  • %(name)s: 打印收集器名称
  • %(levelno)s: 打印日志级别的数值
  • %(levelname)s: 打印日志级别名称
  • %(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0]
  • %(filename)s: 打印当前执行程序名
  • %(funcName)s: 打印日志的当前函数
  • %(lineno)d: 打印日志的当前行号
  • %(asctime)s: 打印日志的时间
  • %(thread)d: 打印线程ID
  • %(threadName)s: 打印线程名称
  • %(process)d: 打印进程ID
  • %(message)s: 打印日志信息
import logging

my_logger = logging.getLogger('A.C,B')
my_logger.setLevel('INFO')

# 创建一个处理器
sh = logging.StreamHandler()
sh.setLevel("ERROR")
my_logger.addHandler(sh)
# 设置一个格式,并设置到处理器上
formatter = logging.Formatter('%(asctime)s - [%(filename)s-->line:%(lineno)d] - %(levelname)s: %(message)s')
sh.setFormatter(formatter)

my_logger.debug('这是logger1-DEBUG等级的信息')
my_logger.info('这是logger1-INFO等级的信息')
my_logger.warning('这是logger1-WARNING等级的信息')
my_logger.error('这是logger1-ERROR等级的信息')
my_logger.critical('这是logger1-CRITICAL等级的信息')

​运行结果:

C:\software\python\python.exe D:/learn/test.py

2020-08-01 18:28:43,645 - [test.py-->line:17] - ERROR: 这是logger1-ERROR等级的信息

2020-08-01 18:28:43,645 - [test.py-->line:18] - CRITICAL: 这是logger1-CRITICAL等级的信息

 

Process finished with exit code 0

 

七、日志滚动

​如果你用 FileHandler 存储日志,文件的大小会随着时间推移而不断增大,最终有一天它会占满你所有的磁盘空间。Python 的 logging 模块提供了两个支持日志滚动的 FileHandler 类,分别是 RotatingFileHandler 和 TimedRotatingFileHandler,它就可以解决这个尴尬的问题。

  • RotatingFileHandler 的滚动时刻是日志文件的大小达到一定值,当达到指定值的时候,RotatingFileHandler会将日志文件重命名存档,然后打开一个新的日志文件。
  • TimedRotatingFileHandler 是当某个时刻到来时就进行滚动,同 RotatingFileHandler 一样,当滚动时机来临时,TimedRotatingFileHandler 会将日志文件重命名存档,然后打开一个新的日志文件。

​在实际应用中,我们通常会根据时间进行滚动,以下实例也以时间滚动为例,按大小滚动的同理:

import logging
from logging.handlers import TimedRotatingFileHandler

my_logger = logging.getLogger('A.C,B')
my_logger.setLevel('INFO')

# 创建一个处理器,使用时间滚动的文件处理器
log_file_handler = TimedRotatingFileHandler(filename='log.log', when='D', interval=1, backupCount=10)
# log_file_handler.suffix = "%Y-%m-%d"
# log_file_handler.extMatch = re.compile(r"^\d{4}-\d{2}-\d{2}.log$")
log_file_handler.setLevel("ERROR")
my_logger.addHandler(log_file_handler)

# 设置一个格式,并设置到处理器上
formatter = logging.Formatter('%(asctime)s - [%(filename)s-->line:%(lineno)d] - %(levelname)s: %(message)s')
log_file_handler.setFormatter(formatter)

my_logger.debug('这是logger1-DEBUG等级的信息')
my_logger.info('这是logger1-INFO等级的信息')
my_logger.warning('这是logger1-WARNING等级的信息')
my_logger.error('这是logger1-ERROR等级的信息')
my_logger.critical('这是logger1-CRITICAL等级的信息')

​参数说明:

filename:日志文件名;

when:是一个字符串,用于描述滚动周期的基本单位,字符串的值及意义如下:

  • S - Seconds
  • M - Minutes
  • H - Hours
  • D - Days
  • midnight - roll over at midnight
  • W{0-6} - roll over on a certain day; 0 - Monday

interval: 滚动周期,单位由when指定,比如:when='D',interval=1,表示每天产生一个日志文件;

backupCount: 表示日志文件的保留个数;

​除了上述参数之外,TimedRotatingFileHandler还有两个比较重要的成员变量,它们分别是suffix和extMatch。suffix是指日志文件名的后缀,suffix中通常带有格式化的时间字符串,filename和suffix由“.”连接构成文件名(例如:filename="test", suffix="%Y-%m-%d.log",生成的文件名为test.2020-08-01.log。extMatch是一个编译好的正则表达式,用于匹配日志文件名的后缀,它必须和suffix是匹配的,如果suffix和extMatch匹配不上的话,过期的日志是不会被删除的。比如,suffix=“%Y-%m-%d.log”, extMatch的只能是re.compile(r"^\d{4}-\d{2}-\d{2}.log$")。默认情况下,在TimedRotatingFileHandler对象初始化时,suffxi和extMatch会根据when的值进行初始化:

S:suffix="%Y-%m-%d_%H-%M-%S",extMatch=r"\^d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}";
M:suffix="%Y-%m-%d_%H-%M",extMatch=r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}";
H:suffix="%Y-%m-%d_%H",extMatch=r"^\d{4}-\d{2}-\d{2}_\d{2}";
D:suffxi="%Y-%m-%d",extMatch=r"^\d{4}-\d{2}-\d{2}";
MIDNIGHT:"%Y-%m-%d",extMatch=r"^\d{4}-\d{2}-\d{2}";
W:"%Y-%m-%d",extMatch=r"^\d{4}-\d{2}-\d{2}";

​如果对日志文件名没有特殊要求的话,可以不用设置suffix和extMatch,如果需要,一定要让它们匹配上。

八、模块封装

​一次封装,一劳永逸,之后直接调用即可,封装内容按需。

import logging
from logging.handlers import TimedRotatingFileHandler


class MyLogger(object):

    @staticmethod
    def create_logger():
        my_logger = logging.getLogger("my_logger")
        my_logger.setLevel("DEBUG")
        # 控制台处理器
        stream_handler = logging.StreamHandler()
        stream_handler.setLevel("ERROR")
        my_logger.addHandler(stream_handler)
        # 使用时间滚动的文件处理器
        log_file_handler = TimedRotatingFileHandler(filename='log.log', when='D', interval=1, backupCount=10)
        log_file_handler.setLevel("INFO")
        my_logger.addHandler(log_file_handler)
        
        formatter = logging.Formatter('%(asctime)s - [%(filename)s-->line:%(lineno)d] - %(levelname)s: %(message)s')
        stream_handler.setFormatter(formatter)
        log_file_handler.setFormatter(formatter)

        return my_logger

以上就是如何理解python接口自动化之logging日志模块的详细内容,更多关于python 接口自动化 logging日志模块的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
Python开发实例分享bt种子爬虫程序和种子解析
May 21 Python
Python3基础之函数用法
Aug 13 Python
Python中SOAP项目的介绍及其在web开发中的应用
Apr 14 Python
Python字符串匹配算法KMP实例
Jul 18 Python
Linux上安装Python的PIL和Pillow库处理图片的实例教程
Jun 23 Python
python实现自动化上线脚本的示例
Jul 01 Python
Python实现平行坐标图的两种方法小结
Jul 04 Python
django做form表单的数据验证过程详解
Jul 26 Python
python读取Excel表格文件的方法
Sep 02 Python
python如何获取apk的packagename和activity
Jan 10 Python
opencv 查找连通区域 最大面积实例
Jun 04 Python
聊一聊python常用的编程模块
May 14 Python
python基于turtle绘制几何图形
详解Flask开发技巧之异常处理
Jun 15 #Python
Python Pandas常用函数方法总结
Jun 15 #Python
深入理解python协程
Jun 15 #Python
2021年最新用于图像处理的Python库总结
Python中的xlrd模块使用整理
Jun 15 #Python
浅谈python中的多态
Jun 15 #Python
You might like
重置版游戏视频
2020/04/09 魔兽争霸
使用session判断用户登录用户权限(超简单)
2013/06/08 PHP
php编写的一个E-mail验证类
2015/03/25 PHP
使用Yii2实现主从数据库设置
2016/11/20 PHP
PHP中迭代器的简单实现及Yii框架中的迭代器实现方法示例
2020/04/26 PHP
Javascript 同时提交多个Web表单的方法
2009/02/19 Javascript
javascript 必知必会之closure
2009/09/21 Javascript
jquery 元素控制(追加元素/追加内容)介绍及应用
2013/04/21 Javascript
JavaScript语言核心数据类型和变量使用介绍
2013/08/23 Javascript
防止登录页面出现在frame中js代码
2014/07/22 Javascript
JavaScript数组去重的3种方法和代码实例
2015/07/01 Javascript
JS+DIV+CSS实现的经典标签切换效果代码
2015/09/14 Javascript
BootStrap中Tab页签切换实例代码
2016/05/30 Javascript
深入剖析JavaScript面向对象编程
2016/07/12 Javascript
原生javascript实现读写CSS样式的方法详解
2017/02/20 Javascript
彻底搞懂JavaScript中的apply和call方法(必看)
2017/09/18 Javascript
在vue项目中引用Iview的方法
2018/09/14 Javascript
vue.js的双向数据绑定Object.defineProperty方法的神奇之处
2019/01/18 Javascript
js中arguments对象的深入理解
2019/05/14 Javascript
微信小程序实现搜索历史功能
2020/03/26 Javascript
JS实现前端路由功能示例【原生路由】
2020/05/29 Javascript
python 反向输出字符串的方法
2018/07/16 Python
Python3.5内置模块之time与datetime模块用法实例分析
2019/04/27 Python
Python实现从N个数中找到最大的K个数
2020/04/02 Python
HTML5 input新增type属性color颜色拾取器的实例代码
2018/08/27 HTML / CSS
Marc Jacobs彩妆官网:Marc Jacobs Beauty
2017/07/03 全球购物
Omio法国:全欧洲低价大巴、火车和航班搜索和比价
2017/11/13 全球购物
MAC Cosmetics官方网站:魅可专业艺术彩妆
2019/04/10 全球购物
承诺书怎么写
2014/03/26 职场文书
人代会标语
2014/06/30 职场文书
员工试用期自我鉴定范文
2014/09/15 职场文书
班主任师德师风自我剖析材料
2014/10/02 职场文书
幼儿园老师个人总结
2015/02/28 职场文书
《打电话》教学反思
2016/02/22 职场文书
thinkphp 获取控制器及控制器方法
2021/04/16 PHP
Java实现斗地主之洗牌发牌
2021/06/14 Java/Android