在自动化测试脚本运行的过程中,在控制台都会对应的输出运行的日志,如果测试的项目是在 Linux服务器上运行的话,没有控制台可供日志的输出,看不到日志或者收集不到日志该如何去查找问题呢?

1、日志存在的作用

 无论是在开发或者测试中,如果在项目运行过程中出现问题时,日志就会显得非常重要,它会帮我们快速准确的定位到问题的所在

2、日志的级别

日志的信息有很多种,如调式信息、报错异常信息等,根据日志信息的不同可以对日志进行分级来管理,日志一般定位的级别有:

      这些日志的级别在使用过程中都需要我们自己去进行设置,在正常的情况下,普通的日志输出直接用的info类型,调试时输出的日志信息用的是debug类型,如果预计会有什么错误时用的是error类型。

3、日志的格式

对日志进行格式化的输出是为了提高他的可阅读性,如: 时间+模块+行数+日志的具体信息 ,如果输出的日志信息非常混乱的话不利于定位出现的问题。

4、logging 模块

可参考:logging官方文档

  • logging的构成

logging的模块有:logger、Handler、Filter、Formatter 四个部分

4.1  logger 记录器

        暴露程序代码能直接使用的接口,logger 是一个树形的层级结构,在使用接口 debug、info、warn、error、critical 之前必须要创建出一个 logger 实例出来,也就是要创建一个记录器,如果没有明确的创建的话,会默认创建出来一个 root logger,并应用默认的日志级别(WARN),处理器(Handler)和格式化器(Formatter)

  • 方法
def basicConfig(**kwargs):   # 为日志的记录系统做基本的配置
  • 对应参数介绍

filename:指定日志文件的名称

format:为处理程序指定格式化的字符串

datefmt:使用指定的日期/时间格式,如果指定了格式字符串的样式的话,则使用指定的格式字符串类型

level:将根记录器级别设置为指定的级别

filemode:指定打开文件的模式(用过Python的就知道这个模式和Python中对文件操作中的模式非常的相似),如果没有指定模式,会以 追加(即: a)的模式执行

  • 文件的读写模式
  • logging_test.py
import logging

# logging.basicConfig(level=logging.DEBUG)
logging.basicConfig(level=logging.WARNING)   # level 后面设置什么样的日志级别,就会输出对应级别以上的信息

logging.debug('log debug info')
logging.info('hello world')
logging.warning('warning message')
logging.error(' error message')
logging.critical('critical message')
  • 执行结果

软件测试之 Logging 日志收集、log 配置文件及结合实例介绍 logging 日志的使用-LMLPHP

4.2  Handler 处理器

说到 handler 处理器,它的类型有很多种,但是比较常用的有三个:Stream Handler、File Handler、Null Handler

  • Stream Handler 

将日志记录输出发送到sys.stdout,sys.stderr或者任何类似文件流中的对象

  • File Handler

将日志记录输出发送到指定的磁盘文件,它继承了Stream Handler的输出功能

logging.basicConfig(filename='log_tst.log', level=logging.DEBUG)

软件测试之 Logging 日志收集、log 配置文件及结合实例介绍 logging 日志的使用-LMLPHP

  • Null Handler

不做任何格式化或者输出

4.3  Filter 过滤器

Handlers 和 Loggers 可以使用 Filters来完成比级别更为复杂的过滤

4.4  Formatter 格式化器

使用 Formatter 对象可以设置日志信息最后的规则、结构和内容,默认的日期时间格式为:%Y-%m-%d %H:%M:%S

Format 格式如下:

  • 使用的方法
logging.basicConfig(filename='log_tst.log', level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d]%(levelname)s %(message)s')
  • 输出的结果:
2019-08-30 15:06:23,491 log_ts.py[line:9]DEBUG log debug info
2019-08-30 15:06:23,491 log_ts.py[line:10]INFO hello world
2019-08-30 15:06:23,491 log_ts.py[line:11]WARNING warning message
2019-08-30 15:06:23,491 log_ts.py[line:12]ERROR error message
2019-08-30 15:06:23,492 log_ts.py[line:13]CRITICAL critical message

5、logging日志实例应用

  • 测试场景

将刚开始写的豌豆荚的启动app功能添加log的采集功能,并设定日志的输出格式,并将其保存到指定的文件中

  • 代码实现
  1. 在自动化测试中,考虑到有很多的模块脚本会用到日志,我们不可能为每一个脚本都配置一下日志,就像前面用的那个 yaml 文件中书写的内容一样,直接放入到一个文件中,用到的时候直接从文件中读取出来就可以
  • log.conf
[loggers]                   # 日志采集器
keys=root,infoLogger        # 默认的日志采集器,自定义的日志采集器

[logger_root]               # 默认日志采集器
level=DEBUG                 # 日志级别
handlers=consoleHandler,fileHandler    # handlers控制日志输出的路径,一个是控制台,一个是文件

[logger_infoLogger]         # 自定义日志采集器
handlers=consoleHandler,fileHandler    # 日志的输出路径,可以是控制台也可以是文件
qualname=infoLogger         # 引用名称
propagate=0                 # propagate的参数有0和1,0表示输出日志,但是消息不进行传递;1表示输出日志,同时将消息往更高级别的地方进行传递,root为最高的级别

[handlers]                  # 控制日志输出的流向
keys=consoleHandler,fileHandler

[handler_consoleHandler]    # 输出到控制台的具体配置
class=StreamHandler
level=INFO
formatter=form02
args=(sys.stdout,)          # 标准流输出

[handler_fileHandler]       # 输出到文件的具体配置
class=FileHandler
level=INFO
formatter=form01
args=('log_test.log', 'a')    # 输出到当前文件

[formatters]
keys=form01,form02

[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s

[formatter_form02]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
  • 然后在需要的模块脚本中增加如下代码就可以了
import logging, logging.config

log_file = '存放日志配置文件的路径/log.conf'
logging.config.fileConfig(log_file)
logging = logging.getLogger()
  • 实现测试场景中的代码

log_practice.py

from appium import webdriver
import yaml, logging, logging.config
from selenium.common.exceptions import NoSuchElementException

file = open('存放 yaml 文件的路径/appium_two/wdj_capability.yaml','r')
data = yaml.load(file,Loader=yaml.FullLoader)

# 日志
# logging.basicConfig(filename='log_tst.log', level=logging.DEBUG, format='%(asctime)s %(filename)s %(thread)d %(threadName)s [line:%(lineno)d]%(levelname)s %(message)s')
log_file = '存放日志配置文件的路径/log.conf'
logging.config.fileConfig(log_file)
logging = logging.getLogger()


xg_caps = {}
xg_caps['platformName'] = data['platformName']
xg_caps['platformVersion'] = data['platformVersion']
xg_caps['deviceName'] = data['deviceName']

xg_caps['appPackage'] = data['appPackage']
xg_caps['appActivity'] = data['appActivity']

xg_caps['noReset'] = data['noReset']
xg_caps['unicodeKeyboard'] = data['unicodeKeyboard']
xg_caps['resetKeyboard'] = data['resetKeyboard']

logging.info('start app ......')
driver = webdriver.Remote('http://'+str(data['ip'])+':'+str(data['port'])+'/wd/hub', xg_caps)

def check_jumpBtn():
    logging.info('check_jumpBtn')
    try:
        jumpBtn = driver.find_element_by_id('com.wandoujia.phoenix2:id/aio')
    except NoSuchElementException:
        logging.info('no jumpBtn')
    else:
        jumpBtn.click()
        logging.info('点击跳过')

def check_cancelBtn():
    logging.info('check_cancelBtn')
    try:
        cancelBtn = driver.find_element_by_id('com.wandoujia.phoenix2:id/s5')
    except NoSuchElementException:
        logging.info('no cancelBtn')
    else:
        cancelBtn.click()
        logging.info('点击取消更新')

if __name__ == '__main__':
    check_cancelBtn()
    driver.implicitly_wait(3)
    check_jumpBtn()

 

 

 

 

08-31 22:55