2016-09-26 4 views
6

Zum besseren Debuggen erzeugt, würde ich oft haben mag:Wie dynamisch einen Stapelrahmen mit Debug-Log-Informationen

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() 

Der Debug-Stack-Frame wie oben wie ein java.lang.reflect.Proxy dynamisch generiert werden, nur gezeigt würde, es sei denn dass ich die vollständige Kontrolle über den vollständigen, vollständigen Methodennamen, der auf dem Proxy endet, haben möchte.

Am Aufrufort, würde ich etwas dumm und einfach, wie dies tun:

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

Wie Sie sehen können, die wrappingMethod() ist eine echte Methode, die auf dem Stack-Trace endet, Hah.method() eine dynamisch generierte Methode, während World.foo() ist wieder eine echte Methode.

Ja, ich weiß, das verunreinigt die bereits tiefen Deep Stack Spuren. Mach dir keine Sorgen darüber. Ich habe meine Gründe.

Gibt es eine (einfache) Möglichkeit, dies oder etwas Ähnliches wie oben beschrieben zu machen?

+1

Ich bin nicht sehr vertraut mit der Gegend, aber ich weiß, dass so etwas auf Bytecode-Ebene getan werden kann, aber ich bin mir nicht sicher, ob es in der Sprache möglich ist. –

+0

@ Meguy26: Ich bin mit Bytecode-Level-Lösungen in Ordnung, wenn sie ohne besondere Abhängigkeiten außerhalb des JDK implementiert werden können. Obwohl ich auch funktionierende Lösungen akzeptiere, die eine Abhängigkeit wie Bytebuddy verwenden –

Antwort

8

Keine Notwendigkeit für die Codegenerierung, dieses Problem zu lösen:

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; 
    } 
} 

Mit Codegenerierung Sie eine Methode mit dem Namen hinzufügen könnte, die Aufrufstelle innerhalb der Methode neu zu definieren, um den Rahmen entspannen und die erzeugte Methode aufrufen aber das wäre viel mehr Arbeit und wäre niemals gleich stabil.

Diese Strategie ist ein ziemlich gängiger Ansatz beim Testen von Frameworks, we do it a lot in Mockito und auch andere Dienstprogramme wie JRebel tun es, um ihre Magie zu verstecken, indem sie Ausnahme-Stack-Frames umschreiben.

Wenn Java 9 verwendet wird, wäre es effizienter, solche Manipulationen unter Verwendung der Stack Walker API durchzuführen.

+0

* "Stack Walker" * ... meinst du, anstatt "Stream.of (stackTraceElements)"? :) –

+1

Ein Vorteil der Stack Walker API ist ihre Leistung, da sie keine Informationen über den gesamten Stack sammeln muss, was ziemlich teuer ist. –

+0

Ist es so? [Ich habe "Walk" vorher in der 'Files.walk()' API gesehen und es tut nicht, was Sie erwarten könnten] (https://blog.jooq.org/2014/01/24/java-8 - Freitag-Goodies-the-neu-neu-io-apis /) –

Verwandte Themen