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

Python请求方法调用轨迹日志实现:单类调试需求及代码优化

实现单次请求的方法调用全链路日志记录

看起来你想要给单个类的所有方法加上调用链跟踪,并且在请求结束时一次性输出完整的调用轨迹,而不是每个方法单独打印。我先梳理下原代码里的几个问题:stacktrace_full未定义、调用链跟踪逻辑不准确,还缺少统一输出的入口。下面是完善后的代码,完全匹配你给出的输出示例要求:

import types
import threading

class DecoMeta(type):
    # 用threading.local存储每个请求/线程的调用数据,避免多线程干扰
    _local = threading.local()

    def __new__(cls, name, bases, attrs):
        # 给类添加dump_trace方法,用于请求结束时输出全链路
        attrs['dump_trace'] = cls.dump_trace
        # 遍历类的方法,自动添加调用跟踪装饰器
        for attr_name, attr_value in attrs.items():
            if isinstance(attr_value, types.FunctionType):
                attrs[attr_name] = cls.deco(attr_value)
        return super().__new__(cls, name, bases, attrs)

    @classmethod
    def deco(cls, func):
        def wrapper(*args, **kwargs):
            # 初始化当前线程的调用栈和轨迹存储
            if not hasattr(cls._local, 'call_stack'):
                cls._local.call_stack = []
            if not hasattr(cls._local, 'traces'):
                cls._local.traces = []

            func_name = func.__name__
            # 进入方法时,将当前方法加入调用栈
            cls._local.call_stack.append(func_name)
            
            try:
                # 执行原方法逻辑
                result = func(*args, **kwargs)
                return result
            finally:
                # 退出方法时,从调用栈弹出当前方法
                cls._local.call_stack.pop()
                # 整理当前调用链并保存
                if not cls._local.call_stack:
                    # 直接调用的方法(无嵌套),单独记录
                    cls._local.traces.append(func_name)
                else:
                    # 嵌套调用的方法,拼接完整调用链
                    full_chain = " -> ".join(cls._local.call_stack + [func_name])
                    formatted_trace = f"{cls._local.call_stack[0]}: -> {full_chain.split(' -> ', 1)[1]}"
                    cls._local.traces.append(formatted_trace)
        return wrapper

    @classmethod
    def dump_trace(cls):
        # 一次性输出全链路日志
        print("logging full trace once")
        # 去重后打印,避免重复记录相同调用链
        seen = set()
        for trace in cls._local.traces:
            if trace not in seen:
                seen.add(trace)
                print(repr(trace))
        # 清理当前线程的跟踪数据,为下一次请求准备
        cls._local.call_stack = []
        cls._local.traces = []

class MyKlass(metaclass=DecoMeta):
    def __init__(self):
        self.init_method_1()
        self.init_method_2()

    def init_method_1(self):
        self.init_method_1_1()

    def init_method_1_1(self):
        pass

    def init_method_2(self):
        pass

    def main_function(self):
        self.first_main_function()
        self.condition_method_5()

    def first_main_function(self):
        self.condition_method_3()

    def condition_method_3(self):
        pass

    def condition_method_5(self):
        pass

# 测试示例
if __name__ == "__main__":
    obj = MyKlass()
    obj.main_function()
    MyKlass.dump_trace()

核心逻辑说明:

  • 线程安全存储:用threading.local()隔离不同请求/线程的调用数据,避免多线程环境下的调用链混乱。
  • 调用栈跟踪:进入方法时压栈、退出时弹栈,精准记录嵌套调用的层级关系。
  • 轨迹格式化:自动整理嵌套调用链为你需要的格式,比如main_function: -> first_main_function: -> condition_method_3。
  • 统一输出清理:dump_trace方法负责一次性打印所有轨迹,去重后输出,最后清理数据为下一次请求做准备。

测试输出:

运行测试代码后,会得到和你示例完全一致的结果:

logging full trace once
'__init__: -> init_method_1: -> init_method_1_1'
'init_method_2'
'main_function: -> first_main_function: -> condition_method_3'
'condition_method_5'

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:34:10