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

Python生产环境报错时如何自动记录相关局部变量排查KeyError问题

可行的三种实现方案

方案1:全局异常钩子自动捕获栈上下文

所有未捕获异常触发时自动打印对应栈帧的局部变量,不需要修改业务代码:

import sys
import logging
from types import FrameType

logger = logging.getLogger(__name__)
# 配置logger输出格式、handler等省略,按生产环境现有配置即可

def custom_excepthook(exc_type, exc_value, exc_tb):
    # 先输出标准异常栈
    logger.critical("Uncaught exception occurred", exc_info=(exc_type, exc_value, exc_tb))
    # 遍历栈帧提取局部变量
    frame: FrameType = exc_tb.tb_frame
    while frame:
        logger.critical(f"=== Local variables at {frame.f_code.co_filename}:{frame.f_lineno} in {frame.f_code.co_name} ===")
        for var_name, var_val in frame.f_locals.items():
            try:
                val_repr = repr(var_val)
                # 生产环境可调整截断长度,避免日志过大
                val_repr = val_repr[:2000] + "[TRUNCATED]" if len(val_repr) > 2000 else val_repr
                logger.critical(f"  {var_name} = {val_repr}")
            except Exception as e:
                logger.critical(f"  {var_name} = <repr failed: {repr(e)}>")
        frame = frame.f_back

sys.excepthook = custom_excepthook

触发KeyError时你可以直接在日志里找到报错行所在栈帧的res变量完整内容,不需要提前插入日志代码。

注意:异步应用的协程异常不会被sys.excepthook捕获,需要额外配置异步事件循环的异常处理器。

方案2:封装响应解析通用方法

仅针对HTTP响应解析场景做统一处理,侵入性低:

import logging
import requests

logger = logging.getLogger(__name__)

def safe_parse_response(res: requests.Response):
    try:
        json_data = res.json()
        return json_data["data"]
    except (KeyError, ValueError) as e:
        # 报错时自动输出完整响应信息
        logger.error(
            "Response parse failed, status=%d, raw content=%s",
            res.status_code,
            res.text[:2000],
            exc_info=True
        )
        # 保留原有异常抛出逻辑,不影响业务流程
        raise

原有业务代码只需要把res.json()["data"]替换为safe_parse_response(res)即可,所有解析异常都会自动记录完整响应内容。

方案3:自定义日志过滤器注入上下文

如果需要给所有请求相关日志统一加上下文,可以使用logging的过滤器实现:

class RequestContextFilter(logging.Filter):
    def filter(self, record):
        # 从当前上下文(比如threadlocal、async context var)获取最近的请求响应对象
        current_res = getattr(request_context, "current_res", None)
        if current_res:
            record.res_status = current_res.status_code
            record.res_content = current_res.text[:2000]
        return True

# 给logger添加过滤器
logger.addFilter(RequestContextFilter())
# 日志格式里可以直接用添加的字段:%(res_status)s %(res_content)s

生产环境注意事项

  • 所有打印逻辑需要添加敏感信息脱敏规则,避免token、用户隐私等数据泄露到日志中
  • 严格控制内容打印长度,避免大响应体导致日志文件异常膨胀
  • 可根据业务需求过滤不需要打印的变量,减少无效日志输出

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 06:15:07