无需修改代码:Python函数调用栈、参数及返回值追踪方案咨询
Python运行时函数调用无侵入追踪方案
核心思路
利用Python内置的sys.settrace()钩子机制,在程序启动时注入外部追踪逻辑,无需修改目标代码即可捕获所有函数的调用、返回事件,收集调用栈、参数、返回值等信息并写入日志文件。
实现代码
创建trace_logger.py文件,内容如下:
import sys import logging import inspect # 配置日志输出到文件 logging.basicConfig( filename='function_trace.log', level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) def trace_calls(frame, event, arg): # 仅处理函数调用和返回事件 if event not in ('call', 'return'): return trace_calls func_name = frame.f_code.co_name # 跳过内置函数、匿名函数等系统级调用 if func_name.startswith('<'): return trace_calls # 构建当前调用栈(从顶层到当前函数) stack = [] current_frame = frame while current_frame: stack.append(f"{current_frame.f_code.co_name} ({current_frame.f_code.co_filename}:{current_frame.f_lineno})") current_frame = current_frame.f_back stack_str = " -> ".join(reversed(stack)) if event == 'call': # 解析函数参数 argspec = inspect.getfullargspec(frame.f_code) args_list = [] # 收集位置参数 for arg_name in argspec.args: args_list.append(f"{arg_name}={repr(frame.f_locals.get(arg_name))}") # 收集可变参数*args if argspec.varargs: args_list.append(f"*{repr(frame.f_locals.get(argspec.varargs, ()))}") # 收集关键字参数**kwargs if argspec.varkw: args_list.append(f"**{repr(frame.f_locals.get(argspec.varkw, {}))}") args_str = ", ".join(args_list) logging.info(f"CALL: {func_name}({args_str}) | CALL STACK: {stack_str}") elif event == 'return': logging.info(f"RETURN: {func_name} -> {repr(arg)} | CALL STACK: {stack_str}") return trace_calls def main(): if len(sys.argv) < 2: print("用法: python trace_logger.py <目标脚本路径> [脚本参数...]") sys.exit(1) # 调整命令行参数,让目标脚本接收自身参数 script_path = sys.argv[1] sys.argv = sys.argv[1:] # 启动追踪 sys.settrace(trace_calls) # 执行目标脚本 with open(script_path, 'r') as f: code = compile(f.read(), script_path, 'exec') exec(code, globals(), locals()) if __name__ == '__main__': main()
使用方法
- 将上述代码保存为
trace_logger.py - 在命令行中运行目标脚本,通过追踪器启动:
如果目标脚本需要传入参数,直接追加在后面即可:python trace_logger.py your_target_script.pypython trace_logger.py your_target_script.py arg1 arg2
注意事项
- 性能开销:运行时追踪会降低程序执行速度,对于性能敏感场景,可在
trace_calls中添加过滤逻辑(比如仅追踪指定目录下的代码) - 内置函数限制:该方案无法追踪Python内置函数(如
print、len),若需追踪需额外封装处理 - 对象序列化:使用
repr()序列化参数和返回值,自定义对象建议实现__repr__方法以获得清晰的日志输出 - 递归处理:递归函数的每一层调用都会被完整记录,日志会包含递归栈的所有层级
内容的提问来源于stack exchange,提问作者Yair M
相关产品推荐
相关产品推荐

