You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pytest中结合Allure报告添加logging的优化方案咨询

问题描述

我用如下静态方法给Allure报告添加步骤:

import allure
from datetime import datetime


class Report:
    @staticmethod
    def report_step(step_title):
        with allure.step(f'[{datetime.now()}]: {step_title}'):
            pass

尝试加入logging功能后修改为:

def report_step(step_title):
    m = f'[{datetime.now()}]: {step_title}'
    with allure.step(m):
        logging.debug(m)

但存在两个问题:

  • 无法灵活指定日志级别(虽可添加参数,但仍需更优方式);
  • 日志格式为'[%(asctime)s] [ %(levelname)s ] [%(filename)s] [%(funcName)s] [%(lineno)d]: %(message)s',但所有日志条目均来自Report类,无法定位实际调用位置。

解决方案

1. 灵活配置日志级别

给report_step方法添加log_level参数,默认设为logging.DEBUG,直接映射logging的级别常量,调用时可按需传入不同级别。

2. 定位实际调用位置

利用inspect模块获取调用者的栈帧信息,跳过当前Report类的栈帧,将调用者的文件名、行号、函数名传递给日志系统,让日志格式显示实际调用代码的位置。

方案一:直接在方法内获取调用者信息

修改后的完整代码:

import allure
import logging
from datetime import datetime
import inspect


class Report:
    @staticmethod
    def report_step(step_title, log_level=logging.DEBUG):
        step_msg = f'[{datetime.now()}]: {step_title}'
        
        # 获取调用者的栈帧信息
        caller_frame = inspect.currentframe().f_back
        caller_filename = caller_frame.f_code.co_filename
        caller_lineno = caller_frame.f_lineno
        caller_func = caller_frame.f_code.co_name
        
        with allure.step(step_msg):
            logger = logging.getLogger(__name__)
            # 通过extra参数传递调用者信息,覆盖日志默认的位置信息
            logger.log(
                level=log_level,
                msg=step_msg,
                extra={
                    'filename': caller_filename,
                    'funcName': caller_func,
                    'lineno': caller_lineno
                }
            )

调用示例:

# 默认DEBUG级别
Report.report_step("初始化测试环境")
# 指定INFO级别
Report.report_step("用户登录操作", log_level=logging.INFO)

方案二:自定义日志过滤器(更优雅的全局处理)

如果多个地方需要处理这种调用者日志,可以自定义日志过滤器,统一跳过Report类的栈帧:

import logging
import inspect


class CallerFilter(logging.Filter):
    def filter(self, record):
        frame = inspect.currentframe()
        try:
            # 跳过filter、log方法、report_step三层栈帧,定位到实际调用者
            for _ in range(3):
                frame = frame.f_back
            record.filename = frame.f_code.co_filename
            record.funcName = frame.f_code.co_name
            record.lineno = frame.f_lineno
        finally:
            # 手动删除帧对象,避免内存泄漏
            del frame
        return True

# 给全局日志器添加过滤器
root_logger = logging.getLogger()
root_logger.addFilter(CallerFilter())

此时Report类的方法可以简化为:

import allure
import logging
from datetime import datetime


class Report:
    @staticmethod
    def report_step(step_title, log_level=logging.DEBUG):
        step_msg = f'[{datetime.now()}]: {step_title}'
        with allure.step(step_msg):
            logging.log(log_level, step_msg)

两种方案都能实现:

  • 按需指定日志级别,满足不同场景的日志输出需求;
  • 日志格式中的位置信息将显示实际调用report_step的代码位置,而非Report类内部。

内容的提问来源于stack exchange,提问作者Ran Pol

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 01:15:31