在自动化测试脚本运行的过程中,在控制台都会对应的输出运行的日志,如果测试的项目是在 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')
- 执行结果
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)
- 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的采集功能,并设定日志的输出格式,并将其保存到指定的文件中
- 代码实现
- 在自动化测试中,考虑到有很多的模块脚本会用到日志,我们不可能为每一个脚本都配置一下日志,就像前面用的那个 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()