日志
软件开发中通过日志记录程序的运行情况是一个开发的好习惯,对于错误排查和系统运维都有很大帮助。
Python 标准库自带了强大的 logging 日志模块,在各种 python 模块中得到广泛应用。
一、简单使用
1. 入门小案例
输出:
2. 日志级别
根据不同情况设置了五种日志等级,不同情况输出不同等级的日志。
日志器设置的级别会过滤掉低于这个级别的日志
输出
3. 配置basicConfig方法支持一下关键字参数进行配置。
4. 格式化规则
日志的输出格式可以通过下面格式自由组合输出
常用格式:%(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:%(message)s
5.日志写到文件
只需要配置 filename 参数即可
输出
二、高级用法
简单的代码通过 logging 直接使用即可,如果要深入使用需要按照面向对象的方式使用 logging。

1. 日志组件
logging 模块包含一下几个组件。
2.步骤
2.1 创建日志记录器
import logging
第一步创建一个 logger,用来产生日志
通过 setLevel 设置日志记录器的等级。
2.2 创建日志处理器
创建一个文本处理器用来将日志写入到文件
-
FileHandler 将日志发送到文件
-
StreaHandler将它可将日志记录输出发送到数据流例如 sys.stdout, sys.stderr 或任何文件类对象默认 sys.stdout 即控制台。
-
RotatingFileHandler支持根据日志文件大小进行轮转
-
TimedRotatingFileHandler 支持根据时间进行轮转日志文件
更多详情见官方文档
2.3 创建格式化器
格式化器需要设置到处理器上
2.4 创建过滤器
过滤器用来过滤指定日志。具体使用略,一般用不到。
详情见官方文档
2.5 将处理器添加到记录器上
2.6 记录日志
三、日志模块封装
1. 功能分析
-
能够自定义日志器名
-
能够自定义日志文件名和路径
-
能够自定义日志文件编码方式
-
能够自定义日志格式
-
使用时间轮转处理器,并能够配置
2.封装成函数
在 common 目录下创建模块 log_handler.py 在其中创建如下函数。

四、应用到项目中
1. 导入
日志器生成函数的导入不能像 Excel 数据读取函数一样,每个用例模块里都导入一遍。因为它返回一个日志器对象,当多次调用日志器生成函数,且日志器名称相同时,会给同一个日志器添加多个日志处理器,从而出现重复记录日志器的问题。
为了解决上面的问题,在 common 文件夹下创建一个名为 __init__.py 的文件,在 common 模块被导入时会自动执行这个文件里的代码,且只会执行一次。
在 __init__.py 文件编写如下代码:
那么在项目中的其他模块中就可以通过如下代码导入
from common import logger从而可以保证在项目执行过程中,get_logger 方法只会执行一遍。
2. 记录日志
日志的作用是记录程序的运行状态和当程序出现问题时能提供定位分析错误的依据。
什么时候需要记录日志,记录什么日志,根据每个人对程序的理解,以及经验。
我们的项目中,在用例执行的过程是核心,所以我们的日志也是围绕着用例的执行。
使用日志记录每个用例的测试数据,和测试结果,代码如下:
3.测试数据
传入进来的 case 参数
4. 测试步骤
5. 断言
整体代码如下
import sys
import time
import unittest
import warnings
from ddt import ddt, data
from common.base_datas import BaseDates
sys.path.append("../../")
sys.path.append("../../common")
from test_script.auto_script.login import login
from common.files_tools.get_excel_init import get_init
from common.extractor.dependent_parameter import DependentParameter
from common.extractor.data_extractor import DataExtractor
from common.encryption.encryption_main import do_encrypt
from common.do_sql.do_mysql import DoMysql
from common.tools.req import req
from common.tools.logger import MyLog
from common.comparator import loaders
from common.dependence import Dependence
from common.comparator.validator import Validator
warnings.simplefilter('ignore', ResourceWarning)
test_file = BaseDates.test_api # 获取 excel 文件路径
excel_handle, init_data, test_case = get_init(test_file)
databases = init_data.get('databases') # 获取数据库配置信息
mysql = DoMysql(databases) # 初始化 mysql 链接
dep = Dependence
dep.set_dep(eval(init_data.get("initialize_data"))) # 初始化依赖表
dep_par = DependentParameter() # 参数提取类实例化
logger = MyLog()
@ddt
class TestProjectApi(unittest.TestCase):
maxDiff = None
@classmethod
def setUpClass(cls) -> None:
loaders.set_bif_fun() # 加载内置方法
# 获取初始化基础数据
cls.host = init_data.get('host')
cls.path = init_data.get("path")
username = dep.get_dep("{{account}}")
password = dep.get_dep("{{passwd}}")
cls.headers = login(cls.host + cls.path, username, password)
dep.update_dep("headers", cls.headers)
# 加载内置方法
logger.my_log(f"内置方法:{dep.get_dep()}", "info")
def setUp(self) -> None:
logger.my_log(f"获取当前依赖参数表:{dep.get_dep()}")
logger.my_log("-----------------------------------start_test_api-----------------------------------", "info")
@data(*test_case) # {"":""}
def test_api(self, item): # item = {測試用例}
# f"""用例描述:{item.get("name")}_{item.get("desc")}"""
sheet = item.get("sheet")
item_id = item.get("Id")
name = item.get("name")
description = item.get("description")
host = self.host
path = self.path
headers = self.headers
url = item.get("Url")
run = item.get("Run")
method = item.get("Method")
sql_variable = item.get("sql变量")
sqlps = item.get("SQL")
item_headers = item.get("Headers") if item.get("Headers") else {}
parameters = item.get("请求参数")
parameters_key = item.get("提取请求参数")
encryption = item.get("参数加密方式")
regex = item.get("正则表达式")
keys = item.get("正则变量")
deps = item.get("绝对路径表达式")
jp_dict = item.get("Jsonpath")
sql_key = item.get("sql变量")
expect = item.get("预期结果")
if run.upper() != "YES":
return
if method == "TIME":
try:
time.sleep(int(url))
logger.my_log(f"暂存成功:{url}", "info")
return
except Exception as e:
MyLog().my_log(f'暂停时间必须是数字')
raise e
# 首先执行sql替换,将sql替换为正确的sql语句
sql = dep_par.replace_dependent_parameter(sqlps)
if method == "SQL" and mysql:
try:
execute_sql_results = mysql.do_mysql(sql)
logger.my_log(f'sql执行成功:{execute_sql_results}', "info")
if execute_sql_results and sql_variable:
# 执行sql数据提取
DataExtractor(execute_sql_results).substitute_data(jp_dict=sql_variable)
logger.my_log(f'sql 提取成功', "info")
return
except Exception as e:
logger.my_log(f'sql:{sql},异常:{e}')
raise e
try:
# 执行 sql 操作
sql_res = mysql.do_mysql(sql)
# 执行sql数据提取
DataExtractor(sql_res).substitute_data(jp_dict=sql_key)
except:
sql_res = "想啥呢?数据库都没有配置还想执行数据库操作?"
logger.my_log(sql_res)
# 替换 URL, PARAMETERS, HEADER,期望值
url = dep_par.replace_dependent_parameter(url)
parameters = dep_par.replace_dependent_parameter(parameters)
item_headers = dep_par.replace_dependent_parameter(item_headers)
headers = {**headers, **item_headers}
expected = dep_par.replace_dependent_parameter(expect)
# 提取请求参数信息
DataExtractor(parameters).substitute_data(jp_dict=parameters_key)
# 判断是否执行加密
if encryption:
parameters = do_encrypt(encryption, parameters) # 数据加密:MD5 or sha1
logger.my_log(f"当前用例所在的sheet--> {sheet}", "info")
logger.my_log(f"请求地址--> {host + path + url}", "info")
logger.my_log(f"请求头--> {headers}", "info")
logger.my_log(f"请求body--> {parameters}", "info")
logger.my_log(f"执行SQL语句--> {sql}", "info")
logger.my_log(f"执行sql结果--> {sql_res}", "info")
logger.my_log(f"预期结果--> {expected}", "info")
try:
# 执行请求操作
response = req(host + path, method, url, data=parameters, headers=headers)
logger.my_log(f"接口响应--> {response.text}", "info")
logger.my_log(f"接口耗时--> {response.elapsed}", "info")
except Exception as e:
result = "失败"
logger.my_log(f'{result}:{item_id}-->{name}_{description},异常:{e}')
raise e
result_tuple = Validator().run_validate(expected, response.json()) # 执行断言 返回结果元组
result = "PASS"
try:
self.assertNotIn("FAIL", result_tuple, "FAIL 存在结果元组中")
except Exception as e:
result = "FAIL"
raise e
finally:
pass
# 响应结果及测试结果回写 excel
# excel_handle.write_back(
# sheet_name=sheet,
# i=item_id,
# response_value=response.text,
# test_result=result,
# assert_log=str(result_tuple)
# )
try:
# 提取响应
DataExtractor(response.json()).substitute_data(regex=regex, keys=keys, deps=deps, jp_dict=jp_dict)
except:
logger.my_log(
f"提取响应失败:{name}_{description}:"
f"\nregex={regex},"
f" \nkeys={keys}, "
f"\ndeps={deps}, "
f"\njp_dict={jp_dict}")
def tearDown(self) -> None:
logger.my_log("-----------------------------------end_test_api-----------------------------------", "info")
@classmethod
def tearDownClass(cls) -> None:
pass
if __name__ == '__main__':
unittest.main()