2016-09-26 76 views
6

爲了更好的調試,我會經常想有:如何動態地生成與調試日誌信息的堆棧幀

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()是堆棧跟蹤結束了一個真正的方法,​​是動態生成的方法,而World.foo()又是一個真正的方法。

是的,我知道這會污染已經很深的堆棧痕跡。不要擔心。我有我的理由。

有沒有一種(簡單)的方法來做到這一點或類似於上述?

+1

我對這個領域並不是非常熟悉,但是我知道類似的東西可以在字節碼級完成,但我不確定在語言中是否可行。 –

+0

@ Meguy26:如果字節碼級別的解決方案可以在JDK之外沒有特殊的依賴關係實現,那麼我很好。雖然,我也會接受使用像Bytebuddy –

回答

8

無需生成代碼來解決這個問題:

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這樣的其他實用程序可以通過重寫異常堆棧框架來隱藏它們的魔力。

使用Java 9時,使用Stack Walker API進行此類操作會更有效。

+0

*「Stack Walker」*這樣的依賴項的工作解決方案......您的意思是,而不是'Stream.of(stackTraceElements)'? :) –

+1

Stack Walker API的一個優點是它的性能,因爲它不需要收集有關整個堆棧的信息,而且成本相當高。 –

+0

是這樣嗎? [我以前在'Files.walk()'API中看過「走路」,它沒有達到你期望的效果)(https://blog.jooq.org/2014/01/24/java-8 -friday-goodies-the-new-new-io-apis /) –