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…...
桌面应用程序开发攻略(初步了解)
什么是桌面应用程序? 桌面应用开发是指为桌面计算机或其他类似设备(如服务器)开发软件应用程序的过程。桌面应用通常是独立于浏览器运行的,并且可以在操作系统的桌面或应用程序菜单中找到。桌面应用可以使用各种编程语言开发&…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
微服务通信安全:深入解析mTLS的原理与实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言:微服务时代的通信安全挑战 随着云原生和微服务架构的普及,服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...
