3分钟通过日志定位bug,这个技能测试人必须会
♥ 前 言
软件开发中通过日志记录程序的运行情况是一个开发的好习惯,对于错误排查和系统运维都有很大帮助。
Python 标准库自带了强大的 logging 日志模块,在各种 python 模块中得到广泛应用。
一、简单使用
1. 入门小案例
import logging
logging.basicConfig(level=logging.DEBUG, #设置级别,根据等级显示format='%(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:% (message)s') # 设置输出格式
logging.debug('This is a debug log')
logging.info('This is a info log')
logging.warning('This is a warning log')
logging.error('This is a error log')
logging.critical('This is a critical log')
2. 日志级别
根据不同情况设置了五种日志等级,不同情况输出不同等级的日志。
日志等级 (level) | 描述 |
DEBUG | 调试信息,通常在诊断问题的时候用得着 |
INFO | 普通信息,确认程序按照预期运行 |
WARNING | 警告信息,表示发生意想不到的事情,或者指示接下来可能会出现一些问题,但是程序还是继续运行 |
ERROR | 错误信息,程序运行中出现了一些问题,程序某些功能不能执行 |
CRITICAL | 危险信息,一个严重的错误,导致程序无法继续运行 |
日志器设置的级别会过滤掉低于这个级别的日志
import logging
logging.basicConfig(level=logging.WARNING, #设置级别,根据等级显示format='%(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:% (message)s') # 设置输出格式
logging.debug('This is a debug log')
logging.info('This is a info log')
logging.warning('This is a warning log')
logging.error('This is a error log')
logging.critical('This is a critical log')
3. 配置
basicConfig 方法支持一下关键字参数进行配置。
参数 | 描述 |
filename | 使用指定的文件名而不是 StreamHandler 创建 FileHandler。 |
filemode | 如果指定了 filename,则用此 模式 打开该文件。默认模式为 'a'。 |
format | 处理器使用的指定格式字符串。 |
datefmt | 使用指定的日期/时间格式,与 time.strftime() 所接受的格式相同。 |
style | 如果指定了 format,将为格式字符串使用此风格。'%', '{' 或 '$' 分别对应于 printf 风格,str.format() 或 string.Template。默认为 '%'。 |
level | 设置根记录器级别去指定 level。 |
stream | 使用指定的流初始化 StreamHandler。请注意此参数与 filename 是不兼容的 - 如果两者同时存在,则会引发 ValueError。 |
handlers | 如果指定,这应为一个包含要加入根日志记录器的已创建处理程序的可迭代对象。任何尚未设置格式描述符的处理程序将被设置为在此函数中创建的默认格式描述符。请注意此参数与 filename 或 stream 不兼容 —— 如果两者同时存在,则会引发 ValueError。 |
force | 如果将此关键字参数指定为 true,则在执行其他参数指定的配置之前,将移除并关闭附加到根记录器的所有现有处理器。 |
4. 格式化规则
日志的输出格式可以通过下面格式自由组合输出
规则 | 描述 |
%(asctime)s | 日志事件发生的时间 |
%(levelname)s | 该日志记录的日志级别 |
%(message)s | 日志记录的文本内容 |
%(name)s | 所使用的日志器名称,默认是'root' |
%(pathname)s | 调用日志记录函数的文件的全路径 |
%(filename)s | 调用日志记录函数的文件 |
%(module)s | 模块 (filename 的名称部分)。 |
%(funcName)s | 调用日志记录函数的函数名 |
%(lineno)d | 调用日志记录函数的代码所在的行号 |
常用格式:%(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:% (message)s
import logging
logging.basicConfig(level=logging.DEBUG, #设置级别,根据等级显示format='%(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:% (message)s') # 设置输出格式
logging.debug('This is a debug log')
5.日志写到文件
只需要配置 filename 参数即可
import logging
logging.basicConfig(level=logging.WARNING, #设置级别,根据等级显示filename='example.log'format='%(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:% (message)s') # 设置输出格式
logging.debug('This is a debug log')
logging.info('This is a info log')
logging.warning('This is a warning log')
logging.error('This is a error log')
logging.critical('This is a critical log')
注意,配置了 fielname 后,日志将不会输出在控制台。
二、高级用法
简单的代码通过 logging 直接使用即可,如果要深入使用需要按照面向对象的方式使用 logging。
1. 日志组件
logging 模块包含一下几个组件。
组件 | 说明 |
Loggers(日志记录器) | 提供程序直接使用的接口 |
Handlers(日志处理器) | 将记录的日志发送到指定的位置 |
Filters(日志过滤器) | 用于过滤特定的日志记录 |
Formatters(日志格式器) | 用于控制日志信息的输出格式 |
2.步骤
2.1 创建日志记录器
import logging
# 第一步创建一个logger,用来产生日志
logger = logging.getLogger('%s_log' % __name__)
logger.setLevel(logging.DEBUG) # 设置日志等级
通过 getLogger 这个方法可以创建一个日志记录器,注意要给名字否则返回根日志记录器。
通过 setLevel 设置日志记录器的等级。
2.2 创建日志处理器
# 创建一个文本处理器用来将日志写入到文件
file_handler = logging.FileHandler(filename='py34.log',encoding='utf-8')
file_handler.setLevel('WARNING') # 设置处理器的日志等级
# 创建一个控制台处理器用来将日志输出到控制台
console_handler = logging.StreamHandler()
console_handler.setLevel('INFO') # 设置控制台处理器的日志等级
日志处理器就是将日志发送到指定的位置。
- FileHandler 将日志发送到文件
- StreaHandler 将它可将日志记录输出发送到数据流例如 sys.stdout, sys.stderr 或任何文件类对象默认 sys.stdout 即控制台。
- RotatingFileHandler 支持根据日志文件大小进行轮转
- TimedRotatingFileHandler 支持根据时间进行轮转日志文件
更多详情见官方文档
(https://docs.python.org/zh-cn/3/library/logging.handlers.html?utm_source=testingpai.com#module-logging.handlers)
2.3 创建格式化器
formatter = logging.Formatter(fmt='%(levelname)s %(asctime)s [%(filename)s-->line:%(lineno)d]:%(message)s')
格式化器需要设置到处理器上
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
2.4 创建过滤器
过滤器用来过滤指定日志。具体使用略,一般用不到。
详情见官方文档
(https://docs.python.org/zh-cn/3/howto/logging-cookbook.html?utm_source=testingpai.com#filters-contextual)
2.5 将处理器添加到记录器上
logger.addHandler(file_handler)
logger.addHandler(console_handler)
2.6 记录日志
logger.info('This is a info')
logger.warning('This is a warning')
三、日志模块封装
1. 功能分析
- 能够自定义日志器名
- 能够自定义日志文件名和路径
- 能够自定义日志文件编码方式
- 能够自定义日志格式
- 使用时间轮转处理器,并能够配置
2.封装成函数
在 common 目录下创建模块 log_handler.py 在其中创建如下函数。
import logging
from logging.handlers import TimedRotatingFileHandlerdef get_logger(name, filename, encoding='utf-8', fmt=None, when='d', interval=1, backup_count=7, debug=False):""":param name: 日志器的名字:param filename: 日志文件名(包含路径):param encoding: 字符编码:param fmt: 日志格式:param when: 日志轮转时间单位:param interval: 间隔:param backup_count: 日志文件个数:param debug: 调试模式:return:"""logger = logging.getLogger(name)logger.setLevel(logging.DEBUG)# 文件处理器的等级一般情况一定比控制台要高if debug:file_level = logging.DEBUGconsole_level = logging.DEBUGelse:file_level = logging.WARNINGconsole_level = logging.INFOif fmt is None:fmt = '%(levelname)s %(asctime)s [%(filename)s-->line:%(lineno)d]:%(message)s'file_handler = TimedRotatingFileHandler(filename=filename, when=when, interval=interval, backupCount=backup_count, encoding=encoding)file_handler.setLevel(file_level)console_handler = logging.StreamHandler()console_handler.setLevel(console_level)formatter = logging.Formatter(fmt=fmt)file_handler.setFormatter(formatter)console_handler.setFormatter(formatter)logger.addHandler(file_handler)logger.addHandler(console_handler)return loggerif __name__ == '__main__':log = get_logger(name='py41', filename='py41.log', debug=True, when='s')log.info('我是普通信息')import timetime.sleep(3)log.warning('我是警告信息')
四、应用到项目中
1. 导入
日志器生成函数的导入不能像 Excel 数据读取函数一样,每个用例模块里都导入一遍。因为它返回一个日志器对象,当多次调用日志器生成函数,且日志器名称相同时,会给同一个日志器添加多个日志处理器,从而出现重复记录日志器的问题。
为了解决上面的问题,在 common 文件夹下创建一个名为 __init__.py 的文件,在 common 模块被导入时会自动执行这个文件里的代码,且只会执行一次。
在 __init__.py 文件编写如下代码:
from .log_handler import get_logger
logger = get_logger('py41', 'py38.log')
那么在项目中的其他模块中就可以通过如下代码导入
from common import logger
从而可以保证在项目执行过程中,get_logger 方法只会执行一遍。
2. 记录日志
日志的作用是记录程序的运行状态和当程序出现问题时能提供定位分析错误的依据。
什么时候需要记录日志,记录什么日志,根据每个人对程序的理解,以及经验。
我们的项目中,在用例执行的过程是核心,所以我们的日志也是围绕着用例的执行。
使用日志记录每个用例的测试数据,和测试结果,代码如下:
...
@list_data(*cases)def test_login(self, case):"""登陆测试"""logger.info('测试用例【{}】开始测试'.format(case['title']))# 1. 测试数据# 传入进来的case参数logger.info('测试用例【{}】的测试数据是:{}'.format(case['title'], case))# 2. 测试步骤res = login_check(case['username'], case['password'])logger.info('测试用例【{}】的测试结果是:{}'.format(case['title'], res))# 3. 断言try:self.assertEqual(res, case['expect'])except AssertionError as e:logger.error('测试用例【{}】断言失败'.format(case['title']))raise eelse:logger.info('测试用例【{}】断言成功'.format(case['title']))finally:logger.info('测试用例【{}】测试结束')
相关文章:

3分钟通过日志定位bug,这个技能测试人必须会
♥ 前 言 软件开发中通过日志记录程序的运行情况是一个开发的好习惯,对于错误排查和系统运维都有很大帮助。 Python 标准库自带了强大的 logging 日志模块,在各种 python 模块中得到广泛应用。 一、简单使用 1. 入门小案例 import logging logging.ba…...

【论文总结】V-Shuttle:可扩展和语义感知的 Hypervisor 虚拟设备模糊测试
介绍 这是来自2021 CCS的一篇论文,作者有GaoningPan, Xingwei Lin, Xuhong Zhang, Yongkang Jia, Shouling Ji, Chunming Wu, Xinlei Ying, Jiashui Wang, Yanjun Wu。该论文提出V-shuttle的新框架来执行管控程序的模糊测试,该框架执行可扩展和语义感知…...

一篇文章让你搞懂TypeScript中的typeof()、keyof()是什么意思
TypeScript中的typeof()、keyof()是什么意思? 知识回调(不懂就看这儿!)场景复现核心干货👇👇👇举例引入字面量类型(literal types&…...

【机会约束、鲁棒优化】机会约束和鲁棒优化研究优化【ccDCOPF】研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

4月想跳槽的同学,没有更好的选择,可以去美团
在美团干了半年,说一下自己的感受,美团是一家福利中等,工资待遇中上,高层管理团队强大,加班强度一般,技术不错,办公环境一般,工作氛围中上,部门差距之间工作体验差距巨大…...

从输入url到页面展现(一)从浏览器解析url开始
前端面试有一道很考验人的问题,那就是:请你说一下从用户从输入url到页面展现的过程是怎样的?在接下来的一段时间呢,狗哥会从这一问题出发,开始剖析这个过程,希望可以让更多的小伙伴掌握到这个过程ÿ…...
购物 · 礼物
标题 前言必学场景词汇及用法书店花店玩具店讨价还价情境常用单词书店花店玩具店前言 加油 必学场景词汇及用法 书店 1.book store / book shop 书店 I browsed through the book store, but I didn’t find the book I was looking for. 我把书店里的书浏览了一番,但是没…...
可视化图表API格式要求有哪些?Sugar BI详细代码示例(2)
Sugar BI中的每个图表可以对应一个数据 API,用户浏览报表时,选定一定的过滤条件,点击「查询」按钮将会通过 API 拉取相应的数据;前面说过,为了确保用户数据的安全性,Sugar BI上的所有数据请求都在Sugar BI的…...

153. 寻找旋转排序数组中的最小值
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums [0,1,2,4,5,6,7] 在变化后可能得到: 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2] 若旋转 7 次࿰…...

Linux 文件描述符
Linux 文件描述符 Linux 中一切皆文件,比如 C 源文件、视频文件、Shell脚本、可执行文件等,就连键盘、显示器、鼠标等硬件设备也都是文件。 一个 Linux 进程可以打开成百上千个文件,为了表示和区分已经打开的文件,Linux 会给每个…...

第17章_反射机制
第17章_反射机制 讲师:尚硅谷-宋红康(江湖人称:康师傅) 官网:http://www.atguigu.com 本章专题与脉络 1. 反射(Reflection)的概念 1.1 反射的出现背景 Java程序中,所有的对象都有两种类型:编…...
使用VBA小程序提高资产清查效率
资产清查是一件相当烦人的工作,去年使用LayUIPHPMS SQL Server 2014写了一个资产清查的程序,可惜写完了,LayUI已经停止更新了,就没有再完善下去,数据也没有更新,等于就废了。 今年又要进行资产清查…...

JavaSE学习进阶day07_02 异常
第三章 异常 3.1 异常概念 异常,就是不正常的意思。在生活中:医生说,你的身体某个部位有异常,该部位和正常相比有点不同,该部位的功能将受影响.在程序中的意思就是: 异常 :指的是程序在执行过程中,出现的非正常的情况࿰…...

操作系统学习笔记
文章目录 操作系统虚拟内存锁缓存机制CPU性能指标进程、线程文件管理系统 操作系统 操作系统是控制应用程序的执行,并充当应用程序和计算机硬件之间的接口。在计算机系统中,处于最外层的是(应用软件) 。 面向用户的就是外层的&am…...

【Spring Boot】SpringBoot设计了哪些可拓展的机制?
文章目录 前言SpringBoot核心源码拓展Initializer拓展监听器ApplicationListenerBeanFactory的后置处理器 & Bean的后置处理器AOP其他的拓展点 前言 当我们引入注册中心的依赖,比如nacos的时候,当我们启动springboot,这个服务就会根据配置…...

《程序员面试金典(第6版)》面试题 10.10. 数字流的秩
题目描述 假设你正在读取一串整数。每隔一段时间,你希望能找出数字 x 的秩(小于或等于 x 的值的个数)。请实现数据结构和算法来支持这些操作,也就是说: 实现 track(int x) 方法,每读入一个数字都会调用该方法; 实现 g…...

智能洗地机好用吗?值得入手的洗地机推荐
洗地机是一款高效的地面清洁设备,不仅可以很好清理地面不同形态的干湿垃圾,还减少了人工和水资源的浪费,是我们日常生活中必不可少的清洁工具。作为以一位评测博主,很多朋友咨询我在选购洗地机时应该注意哪些要点,有哪…...

Spring Security实战(一)——基于内存和数据库模型的认证与授权
目录 简介 一、初识Spring Security(入门案例) (1)新建project (2)选择依赖 (3)编写一个 HelloController (4)启动项目,访问localhost:8080…...
轻松掌握FFmpeg编程:从架构到实践
轻松掌握FFmpeg编程:从架构到实践 (Master FFmpeg Programming with Ease: From Architecture to Practice 引言 (Introduction)FFmpeg简介与应用场景 (Brief Introduction and Application Scenarios of FFmpeg)为什么选择FFmpeg进行音视频处理 (Why Choose FFmpeg…...
桌面应用程序开发攻略(初步了解)
什么是桌面应用程序? 桌面应用开发是指为桌面计算机或其他类似设备(如服务器)开发软件应用程序的过程。桌面应用通常是独立于浏览器运行的,并且可以在操作系统的桌面或应用程序菜单中找到。桌面应用可以使用各种编程语言开发&…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...