软件测试|Python神器logging,你真的了解吗?

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 软件测试|Python神器logging,你真的了解吗?

在这里插入图片描述

logging 是 python 标准模块,用于记录和处理程序中的日志。

功能很强大,官方文档很详细,网上也有大量的说明和教程,但是对很多初次接触的同学来说,存在一些障碍。

一是因为标准库文档太过繁琐,需要较高的理论基础,着急用时,常常被文档搞晕。

二是大部分说明材料要么是官方文档的罗列,要么是简单的应用,对实际应用帮助不大。

今天,我们从应用上的一些问题开始,探讨一下日志神器 logging 极其背后的原理,让它能真正的帮助到我们。

logging.debug or logger.debug?

debug 是日志模块中的一个日志等级为 DEBUG 的日志生成方法,还有 info、warning、error、critial,这里用 debug 做为代表进行说明。

我们常会看到,一会儿用 logging.debug 记录日志,一会儿又用 logger.debug 记录日志,到底该用什么?

先看代码:

import logging

logging.debug('调试信息')

logger = logging.getLogger()

logger.debug('调试信息')

首先 logging 是作为一个模块被引入的。logging.debug 用的是 logging 模块的模块方法。

logger 是用 logging.getLogger() 生成的,是一个 日志对象,logger.debug 调用的是 logger 这个日志对象的方法。

上面的代码中 logging.debug 和 logger.debug 的效果完全是一样的。

这是因为,为了让开发者方便使用,logging 模块提供了一些列模块方法,如 debug,在引入模块后,就可以直接使用。这样开发者就不必关心日志模块的细节,像用 print 一样输出日志。

如果需要对日志输出进行定制化,比如将日志输出到文件中,过滤某些级别的日志,就需要创建或者得到一个实际的日志对象来处理,如上面代码中通过 getLogger 方法得到的日志对象。

我们知道,程序设计里要避免重复的设计,如果模块方法采用一套机制,日志对象上的方法采用另一套机制,就会出现重复造轮子的问题。

所以在使用模块方法,logging 其实创建了一个日志对象 —— root logger。

也就是 logging.debug 这个调用,实质上是调用 root logger 的日志方法。

相当于默认情况下 root logger 会作为日志处理对象。

如何获得 root logger 对象呢?

通过不带参数的 logging.getLogger() 方法获得。

那么 logging.debug 和 rootLogger.debug 是一会事,可以理解(但不严谨)为 logging.debug 是 rootlogger.debug 的快捷方式。

日志层级

稍加留意就会观察到,程序是有层次结构的,通过相互引用,调用形成一个树状结构。

程序加载的地方是树根,比如 python 中要运行的代码文件,我们称之为 main。从树根开始长出其他枝叶。对于一个模块来说,又会形成一个自己的树。

如何用日志清楚地记录层次结构呢?

虽然直接打印出调用堆栈也可以看到调用结构,不过不太直观,缺乏业务逻辑描述。

而用 print 来打印出层次结构,需要编写大量的代码才能反射出(通过运行状态获取代码状态的一种方式)调用环境。

logging 提供了完毕的解决方案。

前面提到的 root logger 就是整个日志树的根,其他所有的 logger 都是从 root logger 伸展出来的枝叶。只要通过 getLogger(loggername) 方法获得的 logger 对象,都是伸展自 root logger 的。

如何向下伸展呢?

很简单,就像引用模块的层次关系一样,用 . 分隔层次就好了,例如:

logger = logging.getLogger('mod1.mod2.mod3')

logger.debug("调试信息")

语句 logging.getLogger('mod1.mod2.mod3') 实际上创建了三个 logger,名称分别是 mod1、mod1.mod2 和 mod1.mod2.mod3

mod1 为根,mod1.mod2 为子,mod1.mod2.mod3 为孙。

如果在 mod1 上设置了日志处理器(handler),那么其他两个的日志对象都会用到这个处理器。

这样不但记录的日志更清晰而且,可以为同一个根的日志对象设置可以共享的日志处理方式。

这样感觉也不方便,需要些那么多层次,如何才能更方便呢?在下面的 实践参考 里会有说明。

实践参考

了解了日志模块的一下特性,和其中的原理之后,这里有几条实践参考。

  1. 不要在子模块中使用 logging.basicConfig 设置日志模式
  2. 强烈建议在任何模块中通过 logger = logging.getLogger(__name__) 来创建日志对象 因为 name 代表的就是模板被加载的引用名称。

例如 from a.b.c import b 模块 c 中的 name 值就为 a.b.c。
而且这个引用名称刚好符合 logger 定义的层次结构。

  1. 通过命令行参数设置不同类型的日志,见代码:
import logging
import argparse
logger = logging.getLogger(__name__)

def create_args_parse():
    parser = argparse.ArgumentParser(description="参数列表")
    parser.add_argument('-d', '--debug', action='store_true', help='调试模式')
    # 加入其他命令行参数
        
    return parser

def set_logger(debug):
    formatter = logging.Formatter('%(asctime)s - %(levelname)8s - %(name)s - %(filename)s:%(lineno)d - %(thread)d- %(funcName)s:\t%(message)s')
    if debug:
        hd = logging.StreamHandler()
        logger.setLevel(logging.DEBUG)
        hd.setFormatter(formatter)
    else:
        hd = logging.FileHandler(f'{__name__}.log', 'a', encoding='utf-8')
        logger.setLevel(logging.INFO)
        hd.setFormatter(formatter)
    logger.addHandler(hd)

if __name__ == '__main__':
   parser = create_args_parse()
   args = parser.parse_args()
   debug = args.debug
   set_logger(debug)
   ...
  • create_args_parse 方法用于解析命令行参数,其中定义了一个 debug 参数,表示开启调试模式
  • set_logger 方法接收一个是否为调试模式的参数,根据是否为调试模式,设置不同的日志模式
  • main 中,首先调用 create_args_parse 获得命令行参数对象,然后从中解析出参数,提取 debug 模式,传送给 set_logger 方法,设置日志模式

这样只需要在运行程序时,加上参数 -d 就可以让日志打印到终端上,不加,日志就会自动去 __main__.log 日志文件中去了。

总结

python 为我们提供了很多便利的功能,有些需要真的用到才能有所体会,所以在遇到问题时,需要多研究一下,找到其中的特点和内在的原理或机制,这样就能更好的应用了。

理解了 logging 的原理之后,已经在我的很多项目中发挥了巨大作用,而且再也不必纠结于怎么用,如何更合理等这些问题了。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
1月前
|
安全 Linux 网络安全
Kali 渗透测试:基于结构化异常处理的渗透-使用Python编写渗透模块(一)
Kali 渗透测试:基于结构化异常处理的渗透-使用Python编写渗透模块(一)
|
1月前
|
Python Windows 网络安全
Kali 渗透测试:基于结构化异常处理的渗透-使用Python编写渗透模块(二)
Kali 渗透测试:基于结构化异常处理的渗透-使用Python编写渗透模块(二)
|
23天前
|
JSON 测试技术 持续交付
自动化测试与脚本编写:Python实践指南
自动化测试与脚本编写:Python实践指南
25 1
|
14天前
|
Web App开发 测试技术 数据安全/隐私保护
自动化测试的魔法:使用Python进行Web应用测试
【10月更文挑战第32天】本文将带你走进自动化测试的世界,通过Python和Selenium库的力量,展示如何轻松对Web应用进行自动化测试。我们将一起探索编写简单而强大的测试脚本的秘诀,并理解如何利用这些脚本来确保我们的软件质量。无论你是测试新手还是希望提升自动化测试技能的开发者,这篇文章都将为你打开一扇门,让你看到自动化测试不仅可行,而且充满乐趣。
|
1月前
|
存储 Python
Python Logging 限制文件大小
Python Logging 限制文件大小
|
1月前
|
测试技术 数据安全/隐私保护 开发者
自动化测试的奥秘:如何用Selenium和Python提升软件质量
【9月更文挑战第35天】在软件开发的海洋中,自动化测试是那艘能引领我们穿越波涛的帆船。本文将揭开自动化测试的神秘面纱,以Selenium和Python为工具,展示如何构建一个简单而强大的自动化测试框架。我们将从基础出发,逐步深入到高级应用,让读者能够理解并实现自动化测试脚本,从而提升软件的质量与可靠性。
|
2月前
|
存储 Python
Python Logging 限制文件大小
Python Logging 限制文件大小
|
2月前
|
Web App开发 测试技术 持续交付
自动化测试的利器:Selenium与Python的完美结合
【9月更文挑战第21天】在软件开发的世界里,测试是确保产品质量的关键步骤。随着敏捷开发和持续集成的流行,自动化测试工具变得尤为重要。本文将介绍如何使用Selenium和Python进行高效的自动化测试,不仅提供代码示例,还深入探讨如何设计测试用例、选择正确的测试框架、以及如何整合到CI/CD流程中。无论你是初学者还是有经验的开发者,这篇文章都将为你提供宝贵的见解和实用的技巧。
46 3
|
2月前
|
安全 JavaScript 前端开发
自动化测试的魔法:如何用Python编写你的第一个测试脚本
【8月更文挑战第41天】在软件的世界里,质量是王道。而自动化测试,就像是维护这个王国的骑士,确保我们的软件产品坚不可摧。本文将引导你进入自动化测试的奇妙世界,教你如何使用Python这把强大的魔法杖,编写出能够守护你代码安全的第一道防护咒语。让我们一起开启这场魔法之旅吧!
|
2月前
|
存储 Python
Python Logging 限制文件大小
Python Logging 限制文件大小