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

如何在pytest捕获的日志中显示logging extra字典中的动态键值对?

如何在pytest捕获的日志中显示logging extra字典中的动态键值对?

我完全理解你的痛点——因为structlog生成的extra字典里的键是动态变化的,没法提前硬编码到log_cli_format里,默认的pytest日志格式化器根本没法把这些动态上下文内容显示出来。下面给你一个精准解决这个问题的方案:

核心思路

我们需要自定义一个日志格式化器,它能自动遍历日志记录中所有来自extra的动态键值对,把它们转换成key=value的格式附加到日志消息末尾,不需要提前知道键名。然后让pytest使用这个自定义格式化器来处理捕获的日志。

具体实现步骤

1. 编写自定义格式化器(通过conftest.py复用)

在你的测试目录下创建conftest.py文件,添加以下代码:

import logging

class DynamicExtraFormatter(logging.Formatter):
    def format(self, record):
        # 先获取默认格式化后的日志消息
        original_message = super().format(record)
        
        # 筛选出所有来自extra的动态键值对(排除logging自带的默认属性)
        extra_props = {}
        for key, value in record.__dict__.items():
            if key not in logging.LogRecord.__dict__:
                extra_props[key] = value
        
        # 如果有动态属性,就拼接成key=value的字符串附加到消息后
        if extra_props:
            kv_string = ' '.join(f"{key}={repr(value)}" for key, value in extra_props.items())
            return f"{original_message} {kv_string}"
        
        return original_message

def pytest_configure(config):
    # 给pytest的CLI日志处理器设置自定义格式化器
    log_plugin = config.pluginmanager.get_plugin("logging")
    log_plugin.log_cli_handler.setFormatter(
        DynamicExtraFormatter(config.getini("log_cli_format"))
    )
    
    # 给测试捕获日志的处理器也设置自定义格式化器
    for handler in logging.getLogger().handlers:
        # 识别pytest的捕获日志文件处理器
        if hasattr(handler, "baseFilename") and "pytest" in handler.baseFilename:
            handler.setFormatter(
                DynamicExtraFormatter(config.getini("log_format"))
            )

2. 配置pytest日志格式(可选,推荐)

在项目根目录创建pytest.ini,设置默认的日志格式(也可以通过命令行参数传递):

[pytest]
log_cli_level = INFO
log_cli_format = %(levelname)s %(name)s:%(filename)s:%(lineno)s %(message)s

3. 验证你的测试代码

用你提供的 reproducer 测试:

import pytest
import logging

logger = logging.getLogger()

def test_logging():
    logger.info('message here', extra={"foo": "bar", "baz": "qux"})
    assert False, 'Trigger failure'

运行命令:

pytest --log-cli-level INFO

此时输出的捕获日志就会包含动态的extra内容:

----------------------------------------- Captured log call -----------------------------------------
INFO     root:test_me.py:8 message here foo='bar' baz='qux'
====================================== short test summary info ======================================
FAILED test_me.py::test_logging - AssertionError: Trigger failure
assert False
========================================= 1 failed in 0.08s =========================================

为什么这个方法有效?

当你调用logger.info(..., extra=dict)时,Python的logging模块会把extra字典里的所有键值对直接注入到日志记录对象(LogRecord)的属性中。我们的自定义格式化器通过对比LogRecord的默认属性,筛选出这些动态注入的键值,然后自动格式化成可读的字符串。

这个方案完美适配structlog的场景——不管structlog生成的extra里有什么动态键名,都会被自动捕获并显示。

备注:内容来源于stack exchange,提问作者Charles Duffy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 10:28:08