编辑:还有其他库可以使用本机 VM api 来实现此目的:https://github.com/cretz/stackparam https://github.com/cretz/stackparam它还修改 Throwable 类以始终打印修改后的堆栈跟踪。
我能想到的唯一可能的方法是使用代理和工具化,但是代理需要添加到启动命令行中。
然后,我将使用 ASM 库注册转换器来转换每个类(请记住,一些基本的 java 类可能已经加载),并将代码添加到每个方法调用的开头,以手动跟踪每个方法类并将其传递给我的库来跟踪它们:
// note that parameters names might not exist in runtime if code was compiled without a flag to include them.
public void doSomething(String name, int something) {
MyLib.enterMethod(ThisClass.class, new MethodSignature(void.class, String.class, int.class), new Argument("name", name), new Argument("something", something));
try {
// original code
} finally { // so we don't need to care about return in the middle of original code or exceptions
MyLib.exitMethod();
}
}
enterMethod
会将调用帧添加到某个队列中并且exitMethod
将删除最后添加的帧。请注意,每个线程应该有单独的队列,使用一些Map<Thread, MyFrame>
or ThreadLocal
对线程使用一些弱引用可能是个好主意。
然后您可以使用该队列中的帧来创建自己的堆栈跟踪。
但是这样做可能会大大降低性能 - 不仅仅是因为此代码的成本,而是将其添加到每个 setter/getter 可能会导致该方法永远不会被内联并进一步影响性能。
所以这是可能的,但我真的不建议这样做。
另外,其他库添加的一些其他转换器可能会影响结果,最好将您的堆栈跟踪与原始堆栈跟踪进行比较,以找到您未转换的任何丢失的方法(例如本机方法),并将它们添加到您的堆栈跟踪中,但没有该方法附加数据。
如果您确实也需要支持本机方法 - 那么您可以创建更高级的转换器来添加enterMethod
/exitMethod
调用本机方法之前和之后。
另外,如果这仅用于调试,您可以使用调试 API,因此它只能用作调试器。