代码之家  ›  专栏  ›  技术社区  ›  Lukas Eder

如何使用调试日志信息动态生成堆栈帧

  •  6
  • Lukas Eder  · 技术社区  · 9 年前

    Exception 
      at com.example.blah.Something.method()
      at com.example.blah.Xyz.otherMethod()
      at com.example.hello.World.foo()
      at com.example.debug.version_3_8_0.debug_info_something.Hah.method() // synthetic method
      at com.example.x.A.wrappingMethod()
    

    如上所示的调试堆栈帧将动态生成,就像 java.lang.reflect.Proxy ,但我想完全控制最终出现在代理上的整个完全限定的方法名。

    在通话现场,我会做一些愚蠢而简单的事情,比如:

    public void wrappingMethod() {
        run("com.example.debug.version_3_8_0.debug_info_something.Hah.method()", () -> {
            World.foo();
        });
    }
    

    如您所见 wrappingMethod() Hah.method() 是动态生成的方法,而 World.foo() 也是一种真正的方法。

    有没有一种(简单的)方法可以做到这一点,或者类似于上面的方法?

    1 回复  |  直到 9 年前
        1
  •  8
  •   Rafael Winterhalter    9 年前

    无需生成代码来解决此问题:

    static void run(String name, Runnable runnable) {
      try {
        runnable.run();
      } catch (Throwable throwable) {
        StackTraceElement[] stackTraceElements = throwable.getStackTrace();
        StackTraceElement[] currentStackTrace = new Throwable().getStackTrace();
        if (stackTraceElements != null && currentStackTrace != null) { // if disabled
          int currentStackSize = currentStackStrace.length;
          int currentFrame = stackTraceElements.length - currentStackSize - 1;
          int methodIndex = name.lastIndexOf('.');
          int argumentIndex = name.indexOf('(');
          stackTraceElements[currentFrame] = new StackTraceElement(
              name.substring(0, methodIndex),
              name.substring(methodIndex + 1, argumentIndex),
              null, // file name is optional
              -1); // line number is optional
          throwable.setStackTrace(stackTraceElements);
        }
        throw throwable;
      }
    }
    

    通过代码生成,您可以添加一个具有名称的方法,在方法中重新定义调用位置,展开框架并调用生成的方法,但这将需要更多的工作,而且永远不会同样稳定。

    此策略是测试框架中相当常见的方法, we do it a lot in Mockito 还有其他实用程序,如JRebel,通过重写异常堆栈帧来隐藏其魔力。

    当使用Java9时,使用 Stack Walker API .