接口自动化测试(4):logging模块

文章目录

    • 日志级别
    • format参数格式
    • 日志输出到文件
    • logging日志模块四大组件
      • 日志器:Logger
      • 处理器:Handler
      • 格式器:Formatter
      • 过滤器:Filter
      • 日志流处理的简单流程
    • 捕获异常信息

想想我们要做一个接口自动化测试框架的话,肯定需要用到日志输出功能,来查看测试过程中的结果信息。
logging模块是Python内置的一个强大易用的日志模块。

日志级别

日志一共分成5个等级,从低到高分别是:DEBUGINFOWARNINGERRORCRITICAL
在输出log的时候,不能所有日志都输出出来,而是有所选择,比如有时候我们只希望看到warning以上严重程度的,如果有太多info、debug会让log可读性变得很差。

级别
CRITICAL/FATAL 50
ERROR 40
WARNING 30
INFO 20
DEBUG 10
NOTSET 0

说明:
DEBUG: 详细的信息,通常只出现在诊断问题上
INFO: 确认一切按预期运行
WARNING: 一个迹象表明,一些意想不到的事情发生了,或表明一些问题在不久的将来(例如。磁盘空间低”)。这个软件还能按预期工作。
ERROR: 更严重的问题,软件没能执行一些功能
CRITICAL: 一个严重的错误,这表明程序本身可能无法继续运行

注意
默认只打印WARNING及其以上等级的log,要想打印WARNING以下等级的log,此时就可以引入NOTSET级别来显示。

简单示例:

import logging   # 引入日志模块
logging.debug("I am   message.")
logging.info("I am   message.")
logging.warning("I am   message.")
logging.error("I am   message.")
logging.critical("I am   message.")

运行结果:
接口自动化测试(4):logging模块_第1张图片
说明:

  1. 从运行结果可见,默认情况下python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志。
  2. 默认的日志格式为: 日志级别:Logger名称:输出日志消息。默认的Logger名称为root。
  3. 如果要显示低于WARNING级别的内容,则需要引入NOTSET级别来显示,如下:
import logging
logging.basicConfig(level=logging.NOTSET)    # 设置日志级别
logging.debug("I am   message.")
logging.info("I am   message.")
logging.warning("I am   message.")
logging.error("I am   message.")
logging.critical("I am   message.")

运行结果:
接口自动化测试(4):logging模块_第2张图片
说明:
logging.basicconfig() 函数
该函数可以用来修改日志的输出格式和方式,作用是将修改后的日志内容,写入到一个文件中。
可选参数如下:

  • filename 指定日志文件名
  • filemode 指定日志文件打开的模式,w 或 a
  • level 指定日志级别,默认 logging.WARNING
  • format 指定输出的格式和内容,format 的参考信息如下
  • datefmt 使用指定的时间格式,需要注意的是,该选项要在format中包含时间字段%(asctime)s时才有效

format参数格式

参数 含义
%(name)s Logger的名字
%(levelno)s 打印日志级别的数值
%(levelname)s 打印日志级别名称
%(pathname)s 打印当前执行程序的路径,其实就是sys.argv[0]
%(filename)s 打印当前执行程序名
%(funcName)s 打印日志的当前函数
%(lineno)d 打印日志的当前行号
%(asctime)s 打印日志的时间,默认格式是 “2001-08-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 打印线程ID
%(threadName)s 打印线程名称
%(process)d 打印进程ID
%(message)s 打印日志信息

例如:

import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] - %(levelname)s:  %(message)s', datefmt='%Y-%m-%d %H:%M:%S')   
logging.debug("I am   message.")
logging.info("I am   message.")
logging.warning("I am   message.")
logging.error("I am   message.")
logging.critical("I am   message.")

运行结果:
接口自动化测试(4):logging模块_第3张图片

日志输出到文件

上面我们看到,日志都是输出在控制台的,name如何将日志输出到日志文件呢?

直接看如下代码:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import logging
import os

logfile_dir = os.path.dirname(os.path.abspath(__file__))  # log文件的目录
logfile_name = 'log.txt'   # log文件名
logfile_path = os.path.join(logfile_dir, logfile_name)  # log文件的全路径
logging.basicConfig(
                    level=logging.WARNING,
                    format='%(asctime)s  %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
                    datefmt = '%Y-%m-%d  %H:%M:%S %a ',
                    filename=logfile_path,
                    filemode='w'
                    )

logging.debug("I am   message.")
logging.info("I am   message.")
logging.warning("I am   message.")
logging.error("I am   message.")
logging.critical("I am   message.")

执行代码后,看到脚本目录下多了一个log.txt文件,打开后看到有如下日志:
在这里插入图片描述

logging日志模块四大组件

组件名 类名 功能描述
记录器 Logger 提供日志接口,供应用代码使用
处理器 Handler 将(记录器产生的)日志记录发送至合适的目的地输出
过滤器 Filter 提供了更细粒度的控制工具来决定输出哪条日志记录,丢弃哪条日志记录
格式化器 Formatter 决定日志记录的最终输出格式

这些组件之间的关系描述:

  • 日志器(logger)需要通过处理器(handler)将日志信息输出到目标位置,如:文件、sys.stdout、网络等;
  • 不同的处理器(handler)可以将日志输出到不同的位置;
  • 日志器(logger)可以设置多个处理器(handler)将同一条日志记录输出到不同的位置;
  • 每个处理器(handler)都可以设置自己的过滤器(filter)实现日志过滤,从而只保留感兴趣的日志;
  • 每个处理器(handler)都可以设置自己的格式器(formatter)实现同一条日志以不同的格式输出到不同的地方。

**简单点说就是:**日志器(logger)是入口,真正干活儿的是处理器(handler),处理器(handler)还可以通过过滤器(filter)和格式器(formatter)对要输出的日志内容做过滤和格式化等处理操作。

日志器:Logger

日志器Logger以工厂化的形式返回一个Logger类实例。一般而言,大多使用下面的方法获得Logger类实例:logging.getLogger(name)

说明: 参数name表示将要返回的日志器的名称标识。

在使用logging模块时,系统会自动实例化一个名为 root 的日志器(根日志器),当未指定name属性时,事实上就是将变量名指向根日志器。

另外, Logger实例具有层级继承的特点,层级之间已“.”连接,例如:“a.b”,“a.b.c”,a是父日志器,b是子日志器,在未对子日志器进行配置情况下,子日志器默认继承父日志器的配置,对子日志器重新配置不会影响父日志器。

Logger类有以下的常用方法:

方法 描述
Logger.setLevel() 设置日志器将会处理的日志消息的最低严重级别
Logger.addHandler() 为该logger对象添加一个handler对象
Logger.removeHandler() 为该logger对象添加移除一个handler对象
Logger.addFilter() 为该logger对象添加一个filter对象
Logger.removeFilter() 为该logger对象移除一个filter对象
Logger.debug(), Logger.info(), Logger.warning(), Logger.error(), Logger.critical() 创建一个与它们的方法名对应等级的日志记录
Logger.exception() 创建一个类似于Logger.error()的日志消息
Logger.log() 需要获取一个明确的日志level参数来创建一个日志记录

处理器:Handler

Handler实例用于将日志记录发送到指定的位置进行输出。

一个logger对象可以添加多个handler(例如既要在控制台输出日志,又要将日志写入到文件A,还要讲日志写入文件B,这就可以配置3个handler),每个handler又可以定义不同日志级别,以实现日志分级过滤显示。

Handler类有以下的常用方法:

方法 描述
handler.setLevel() 设置handler处理的日志信息最低级别
handler.setFormatter( 给这个handler选择一个格式
handler.addFilter() 为handler添加一个过滤器对象
handler.removeFilter() 为handler删除一个过滤器对象

要注意的是,在实际开发中,最好不要直接使用Handler类,应根据实际的功能需要,实例化Handler类的子类。(因为Handler是一个基类,它只定义了素有handlers都应该有的接口,同时提供了一些子类可以直接使用或覆盖的默认行为)

Handler类的子类包括:

Handler 描述
logging.StreamHandler 将日志消息发送到输出到Stream,如std.out, std.err或任何file-like对象。
logging.FileHandler 将日志消息发送到磁盘文件,默认情况下文件大小会无限增长
logging.handlers.RotatingFileHandler 将日志消息发送到磁盘文件,并支持日志文件按大小切割
logging.hanlders.TimedRotatingFileHandler 将日志消息发送到磁盘文件,并支持日志文件按时间切割
logging.handlers.HTTPHandler 将日志消息以GET或POST的方式发送给一个HTTP服务器
logging.handlers.SMTPHandler 将日志消息发送给一个指定的email地址
logging.NullHandler 该Handler实例会忽略error messages,通常被想使用logging的library开发者使用来避免’No handlers could be found for logger XXX’信息的出现。

下面来看一下代码示例,目的:(1)在控制台输出日志(日志级别为info);(2)同时将日志写入到文件a.log文件(日志级别为debug);(3)还要将日志写入文件b.log文件(日志级别为warning)

import logging

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

# 控制台输出,日志级别为info
con_handler = logging.StreamHandler()
con_handler.setLevel(logging.INFO)
logger.addHandler(con_handler)

# 输出到文件a.log,日志级别为debug
file_a_handler = logging.FileHandler('./a.log', encoding='UTF-8')
file_a_handler.setLevel(logging.DEBUG)
logger.addHandler(file_a_handler)

# 输出到文件b.log,日志级别为warning
file_b_handler = logging.FileHandler('./b.log', encoding='UTF-8')
file_b_handler.setLevel(logging.WARNING)
logger.addHandler(file_b_handler)

if __name__ == '__main__':
    logger.debug('I am a debug msg')
    logger.info('I am a info msg')
    logger.warning('I am a warning msg')

运行结果:
(1)控制台输出如下:
接口自动化测试(4):logging模块_第4张图片
(2)a.log输出如下:
在这里插入图片描述
(3)b.log输出如下:
在这里插入图片描述

注意: 在一个日志器中添加多个handler时,最好通过logger.setLevel(logging.DEBUG)先设置一下logger本身的日志级别,如果某个handler的级别比logger的日志级别低,那么该handler的日志级别无效,handler会以logger的级别来处理。

格式器:Formatter

Formater对象用于配置日志信息的最终顺序、结构和内容。与logging.Handler基类不同的是,应用代码可以直接实例化Formatter类。另外,如果你的应用程序需要一些特殊的处理行为,也可以实现一个Formatter的子类来完成。

Formatter类的构造方法定义如下:
logging.Formatter.__init__(fmt=None, datefmt=None, style='%')

可见,该构造方法接收3个可选参数:

  • fmt:指定消息格式化字符串,如果不指定该参数则默认使用message的原始值
  • datefmt:指定日期格式字符串,如果不指定该参数则默认使用"%Y-%m-%d %H:%M:%S"
  • style:Python3.2新增的参数,可取值为 ‘%’, ‘{‘和 ‘$’,如果不指定该参数则默认使用’%’

过滤器:Filter

在我们已经知道的logging使用方法中,都是通过日志级别来控制日志是否输出,Filter能够实现更加强大的过滤功能,控制日志输出。自定义的过滤器中必须覆写filter方法,当filter的返回值判断为True则允许输出,反之不允许输出。例如过滤包含敏感信息的日志,

import logging
class CountryFilter(logging.Filter):
    def filter(self, record):
        return "America" not in record.getMessage()

logger = logging.getLogger()
handler = logging.StreamHandler()
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
logger.addFilter(CountryFilter())

logger.critical('I love America')
logger.debug('I love China')

运行结果:
接口自动化测试(4):logging模块_第5张图片
从上面的运行结果可以看到,虽然第一条日志记录的日志等级更高,但是因为包含了过滤器中设置的敏感信息,所以不被允许输出。

日志流处理的简单流程

  1. 创建一个logger
  2. 设置下logger的日志的等级
  3. 创建合适的Handler(FileHandler要有路径)
  4. 设置下每个Handler的日志等级
  5. 创建下日志的格式
  6. 向Handler中添加上面创建的格式
  7. 将上面创建的Handler添加到logger中
  8. 打印输出logger.debug\logger.info\logger.warning\logger.error\logger.critical

下面根据上面的流程来写一个示例代码,如下:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import logging

# 第1步:创建一个名为KD的日志器
logger = logging.getLogger("KD")

# 第2步:设置日志器的日志等级
logger.setLevel(logging.DEBUG)

# 第3步:创建适合的Handler(一个控制台,一个文件)
con_handler = logging.StreamHandler()
file_handler= logging.FileHandler('./test.log', encoding='UTF-8')

# 第4步:设置Handler的日志级别
con_handler.setLevel(logging.ERROR)
file_handler.setLevel(logging.INFO)

# 第5步:创建日志输出格式
formatter = logging.Formatter(
    fmt = "%(asctime)s %(name)s %(filename)s %(message)s",
    datefmt = '%Y-%m-%d  %H:%M:%S %a '
    )

# 第6步:向Handler中添加上面创建的格式
con_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)

# 第7步:将上面创建的Handler添加到logger中
logger.addHandler(con_handler)
logger.addHandler(file_handler)

# 第8步:输出不同级别的log
logger.debug("调试信息")
logger.info("提示信息")
logger.warning("警告信息")
logger.error("错误信息")

(1)控制台输出如下:
接口自动化测试(4):logging模块_第6张图片
(2)test.log文件输出如下:
接口自动化测试(4):logging模块_第7张图片

捕获异常信息

在日志中,如果只是记录发生了异常,那其实作用不大,还是不知道为什么发生了异常。如果我们在日志中能看到异常信息是什么,那么记录的日志就完美了。

Python的logging模块确实也提供了这一功能,代码示例如下:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import logging

# 第1步:创建一个名为KD的日志器
logger = logging.getLogger("KD")

# 第2步:设置日志器的日志等级
logger.setLevel(logging.DEBUG)

# 第3步:创建适合的Handler(一个控制台,一个文件)
con_handler = logging.StreamHandler()
file_handler= logging.FileHandler('./test.log', encoding='UTF-8')

# 第4步:设置Handler的日志级别
con_handler.setLevel(logging.ERROR)
file_handler.setLevel(logging.INFO)

# 第5步:创建日志输出格式
formatter = logging.Formatter(
    fmt = "%(asctime)s %(name)s %(filename)s %(message)s",
    datefmt = '%Y-%m-%d  %H:%M:%S %a '
    )

# 第6步:向Handler中添加上面创建的格式
con_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)

# 第7步:将上面创建的Handler添加到logger中
logger.addHandler(con_handler)
logger.addHandler(file_handler)

# 第8步:输出不同级别的log
logger.debug("调试信息")
logger.info("提示信息")
logger.warning("警告信息")

try:
    logger.error("错误信息: 除法运算异常....")
    1/0
except Exception as e:
    logger.exception(e)

运行结果:
(1)控制台输出如下:
接口自动化测试(4):logging模块_第8张图片
(2)test.log文件输出如下:
接口自动化测试(4):logging模块_第9张图片
参考链接:
[1] https://www.cnblogs.com/Nicholas0707/p/9021672.html
[2] http://www.likecs.com/show-57627.html

你可能感兴趣的:(零基础学习自动化测试)