• 3分钟通过日志定位bug,这个技能测试人必须会!


    ♥  前  言

    软件开发中通过日志记录程序的运行情况是一个开发的好习惯,对于错误排查和系统运维都有很大帮助。

    Python 标准库自带了强大的 logging 日志模块,在各种 python 模块中得到广泛应用。

    一、简单使用

    1. 入门小案例

    1. import logging
    2. logging.basicConfig(level=logging.DEBUG, #设置级别,根据等级显示
    3. format='%(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:% (message)s') # 设置输出格式
    4. logging.debug('This is a debug log')
    5. logging.info('This is a info log')
    6. logging.warning('This is a warning log')
    7. logging.error('This is a error log')
    8. logging.critical('This is a critical log')

    如果你想学习自动化测试,我这边给你推荐一套视频,这个视频可以说是B站播放全网第一的自动化测试教程,同时在线人数到达1000人,并且还有笔记可以领取及各路大神技术交流:798478386    

    【已更新】B站讲的最详细的Python接口自动化测试实战教程全集(实战最新版)_哔哩哔哩_bilibili【已更新】B站讲的最详细的Python接口自动化测试实战教程全集(实战最新版)共计200条视频,包括:1、接口自动化之为什么要做接口自动化、2、接口自动化之request全局观、3、接口自动化之接口实战等,UP主更多精彩视频,请关注UP账号。icon-default.png?t=N7T8https://www.bilibili.com/video/BV17p4y1B77x/?spm_id_from=333.337

     

    2. 日志级别

    根据不同情况设置了五种日志等级,不同情况输出不同等级的日志。

    日志等级

    (level)

    描述

    DEBUG 

    调试信息,通常在诊断问题的时候用得着 

    INFO

    普通信息,确认程序按照预期运行 

    WARNING 

    警告信息,表示发生意想不到的事情,或者指示接下来可能会出现一些问题,但是程序还是继续运行 

    ERROR 

    错误信息,程序运行中出现了一些问题,程序某些功能不能执行 

    CRITICAL

    危险信息,一个严重的错误,导致程序无法继续运行 

    日志器设置的级别会过滤掉低于这个级别的日志

    1. import logging
    2. logging.basicConfig(level=logging.WARNING, #设置级别,根据等级显示
    3. format='%(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:% (message)s') # 设置输出格式
    4. logging.debug('This is a debug log')
    5. logging.info('This is a info log')
    6. logging.warning('This is a warning log')
    7. logging.error('This is a error log')
    8. logging.critical('This is a critical log')

    2020-09-11 17:39:26,667-WARNING-This is a warning log

    2020-09-11 17:39:26,669-ERROR-This is a error log

    2020-09-11 17:39:26,669-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

    1. import logging
    2. logging.basicConfig(level=logging.DEBUG, #设置级别,根据等级显示
    3. format='%(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:% (message)s') # 设置输出格式
    4. logging.debug('This is a debug log')

    [DEBUG]-2020-09-11 17:36:50,125--4:This is a debug log

    5.日志写到文件

    只需要配置 filename 参数即可

    1. import logging
    2. logging.basicConfig(
    3. level=logging.WARNING, #设置级别,根据等级显示
    4. filename='example.log'
    5. format='%(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:% (message)s') # 设置输出格式
    6. logging.debug('This is a debug log')
    7. logging.info('This is a info log')
    8. logging.warning('This is a warning log')
    9. logging.error('This is a error log')
    10. logging.critical('This is a critical log')

    注意,配置了 fielname 后,日志将不会输出在控制台。

    二、高级用法

    简单的代码通过 logging 直接使用即可,如果要深入使用需要按照面向对象的方式使用 logging。

    1. 日志组件

    logging 模块包含一下几个组件。

    组件

    说明

    Loggers(日志记录器) 

    提供程序直接使用的接口 

    Handlers(日志处理器) 

    将记录的日志发送到指定的位置 

    Filters(日志过滤器) 

    用于过滤特定的日志记录 

    Formatters(日志格式器) 

    用于控制日志信息的输出格式 

    2.步骤

    2.1 创建日志记录器

    1. import logging
    2. # 第一步创建一个logger,用来产生日志
    3. logger = logging.getLogger('%s_log' % __name__)
    4. logger.setLevel(logging.DEBUG) # 设置日志等级

    通过 getLogger 这个方法可以创建一个日志记录器,注意要给名字否则返回根日志记录器。

    通过 setLevel 设置日志记录器的等级。

    2.2 创建日志处理器

    1. # 创建一个文本处理器用来将日志写入到文件
    2. file_handler = logging.FileHandler(filename='py34.log',encoding='utf-8')
    file_handler.setLevel('WARNING')  # 设置处理器的日志等级
    1. # 创建一个控制台处理器用来将日志输出到控制台
    2. console_handler = logging.StreamHandler()
    3. 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')

    格式化器需要设置到处理器上

    1. file_handler.setFormatter(formatter)
    2. 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 将处理器添加到记录器上

    1. logger.addHandler(file_handler)
    2. logger.addHandler(console_handler)

    2.6 记录日志

    logger.info('This is a info')

    2020-09-11 22:22:44,095-[-->line:1]-INFO:This is a info

    logger.warning('This is a warning')

    2020-09-11 22:23:20,337-[-->line:1]-WARNING:This is a warning

    三、日志模块封装

    1. 功能分析

    1. 能够自定义日志器名 

    2. 能够自定义日志文件名和路径 

    3. 能够自定义日志文件编码方式 

    4. 能够自定义日志格式 

    5. 使用时间轮转处理器,并能够配置 

    2.封装成函数

    在 common 目录下创建模块 log_handler.py 在其中创建如下函数。

    1. import logging
    2. from logging.handlers import TimedRotatingFileHandler
    3. def get_logger(name, filename, encoding='utf-8', fmt=None, when='d', interval=1, backup_count=7, debug=False):
    4. """
    5. :param name: 日志器的名字
    6. :param filename: 日志文件名(包含路径)
    7. :param encoding: 字符编码
    8. :param fmt: 日志格式
    9. :param when: 日志轮转时间单位
    10. :param interval: 间隔
    11. :param backup_count: 日志文件个数
    12. :param debug: 调试模式
    13. :return:
    14. """
    15. logger = logging.getLogger(name)
    16. logger.setLevel(logging.DEBUG)
    17. # 文件处理器的等级一般情况一定比控制台要高
    18. if debug:
    19. file_level = logging.DEBUG
    20. console_level = logging.DEBUG
    21. else:
    22. file_level = logging.WARNING
    23. console_level = logging.INFO
    24. if fmt is None:
    25. fmt = '%(levelname)s %(asctime)s [%(filename)s-->line:%(lineno)d]:%(message)s'
    26. file_handler = TimedRotatingFileHandler(
    27. filename=filename, when=when, interval=interval, backupCount=backup_count, encoding=encoding)
    28. file_handler.setLevel(file_level)
    29. console_handler = logging.StreamHandler()
    30. console_handler.setLevel(console_level)
    31. formatter = logging.Formatter(fmt=fmt)
    32. file_handler.setFormatter(formatter)
    33. console_handler.setFormatter(formatter)
    34. logger.addHandler(file_handler)
    35. logger.addHandler(console_handler)
    36. return logger
    37. if __name__ == '__main__':
    38. log = get_logger(name='py41', filename='py41.log', debug=True, when='s')
    39. log.info('我是普通信息')
    40. import time
    41. time.sleep(3)
    42. log.warning('我是警告信息')

    四、应用到项目中

    1. 导入

    日志器生成函数的导入不能像 Excel 数据读取函数一样,每个用例模块里都导入一遍。因为它返回一个日志器对象,当多次调用日志器生成函数,且日志器名称相同时,会给同一个日志器添加多个日志处理器,从而出现重复记录日志器的问题。

    为了解决上面的问题,在 common 文件夹下创建一个名为 __init__.py 的文件,在 common 模块被导入时会自动执行这个文件里的代码,且只会执行一次。

    在 __init__.py 文件编写如下代码:

    1. from .log_handler import get_logger
    2. logger = get_logger('py41', 'py38.log')

    那么在项目中的其他模块中就可以通过如下代码导入

    from common import logger

    从而可以保证在项目执行过程中,get_logger 方法只会执行一遍。

    2. 记录日志

    日志的作用是记录程序的运行状态和当程序出现问题时能提供定位分析错误的依据。

    什么时候需要记录日志,记录什么日志,根据每个人对程序的理解,以及经验。

    我们的项目中,在用例执行的过程是核心,所以我们的日志也是围绕着用例的执行。

    使用日志记录每个用例的测试数据,和测试结果,代码如下:

    1. ...
    2. @list_data(*cases)
    3. def test_login(self, case):
    4. """
    5. 登陆测试
    6. """
    7. logger.info('测试用例【{}】开始测试'.format(case['title']))
    8. # 1. 测试数据
    9. # 传入进来的case参数
    10. logger.info('测试用例【{}】的测试数据是:{}'.format(case['title'], case))
    11. # 2. 测试步骤
    12. res = login_check(case['username'], case['password'])
    13. logger.info('测试用例【{}】的测试结果是:{}'.format(case['title'], res))
    14. # 3. 断言
    15. try:
    16. self.assertEqual(res, case['expect'])
    17. except AssertionError as e:
    18. logger.error('测试用例【{}】断言失败'.format(case['title']))
    19. raise e
    20. else:
    21. logger.info('测试用例【{}】断言成功'.format(case['title']))
    22. finally:
    23. logger.info('测试用例【{}】测试结束')

  • 相关阅读:
    【2022蓝帽杯】file_session && 浅入opcode
    这个好玩又实用的jupyter插件我真的爱了
    区间素数 马蹄集
    MySQL中最左匹配相关实践总结
    vue移动端适配
    绘画系统(01):【纲】Paint System[官翻]
    我们用到的3种Mock测试方案​
    Jupyter Notebook 快捷键
    远程仓库(一)之git 小乌龟安装教程
    江苏MES
  • 原文地址:https://blog.csdn.net/m0_73409141/article/details/133385135