Gibt es eine Möglichkeit, einen Stack-Trace zu sichern, ohne eine Ausnahme in Java auszulösen?

159

Ich denke darüber nach, ein Debug-Tool für meine Java-Anwendung zu erstellen.

Ich frage mich, ob es möglich ist, eine Stapelverfolgung zu erhalten, genau wie Exception.printStackTrace(), ohne tatsächlich eine Ausnahme auszulösen.

Mein Ziel ist es, in einer bestimmten Methode einen Stapel zu sichern, um zu sehen, wer der Methodenaufrufer ist.

corgrath
quelle

Antworten:

84

Sie können auch versuchen Thread.getAllStackTraces(), eine Karte mit Stapelspuren für alle aktiven Threads abzurufen.

Prabhu R.
quelle
250

Ja, einfach benutzen

Thread.dumpStack()
Rob Di Marco
quelle
44
... aber wirft es nicht.
Rob
5
Das ist die Antwort, die die Frage tatsächlich anspricht.
Bruno
7
Sehr einfach und elegant. Dies sollte die akzeptierte Antwort gewesen sein.
deLock
11
Fwiw, die Quelle dieser Methode: public static void dumpStack() { new Exception("Stack trace").printStackTrace(); }
JohnK
Dies ist bei weitem die beste Antwort auf die Frage, wenn Sie mit der Ausgabe zufrieden sind stderr, was die Implikation der Frage zu sein scheint.
Mike Nagetier
46

Wenn Sie die Ablaufverfolgung nur für den aktuellen Thread wünschen (und nicht für alle Threads im System, wie es Ram vorgeschlagen hat), gehen Sie wie folgt vor:

Thread.currentThread ().getStackTrace ()

Um den Anrufer zu finden, gehen Sie wie folgt vor:

private String getCallingMethodName() {
    StackTraceElement callingFrame = Thread.currentThread().getStackTrace()[4];
    return callingFrame.getMethodName();
}

Rufen Sie diese Methode innerhalb der Methode auf, die wissen muss, wer ihr Aufrufer ist. Ein Wort der Warnung: Der Index des aufrufenden Frames in der Liste kann je nach JVM variieren! Es hängt alles davon ab, wie viele Ebenen von Aufrufen sich in getStackTrace befinden, bevor Sie den Punkt erreichen, an dem die Ablaufverfolgung generiert wird. Eine robustere Lösung wäre, den Trace abzurufen und ihn zu durchlaufen, um nach dem Frame für getCallingMethodName zu suchen, und dann zwei Schritte weiter nach oben zu gehen, um den wahren Aufrufer zu finden.

Tom Anderson
quelle
2
Erstellen Sie einfach selbst eine neue Ausnahme und verwenden Sie [1] als Index!
Daniel
3
Der Titel dieser Frage lautet "ohne Ausnahme". Zugegeben, Sie schlagen vor, die Ausnahme zu erstellen, aber nicht zu werfen, aber ich denke immer noch, dass dies nicht im Sinne der Frage ist.
Tom Anderson
1
Natürlich ist es das. Das Erstellen einer Ausnahme ist der einfachste Weg, um einen Stacktrace zu erhalten. Sogar dein Thread.currentThread (). GetStackTrace () macht es, aber mein Weg macht es schneller :).
Daniel
@Daniel Es gibt noch eine weitere Überlegung: Bei vielen Test-Frameworks, z. B. Spock, reicht es aus, nur eine Ausnahme zu erstellen (ohne sie auszulösen), damit das Framework sie erkennt: Der Test berücksichtigt dann, dass eine Ausnahme "aufgetreten" ist, und der Test wird Ende mit einem Fehler.
Mike Nagetier
@mikerodent: Bist du sicher? Spock würde dafür Agenten einsetzen müssen. Da ich nur die Klasse aufrufen musste, habe ich Reflection.getCallerClass (2) in einer Utility-Funktion verwendet. Auf diese Weise erhalten Sie die Funktion und die Zeilennummer nicht, dachte ich.
Daniel
37

Sie können eine Stapelverfolgung wie folgt erhalten:

Throwable t = new Throwable();
t.printStackTrace();

Wenn Sie auf den Frame zugreifen möchten, können Sie mit t.getStackTrace () ein Array von Stack-Frames abrufen.

Beachten Sie, dass bei diesem Stacktrace (genau wie bei jedem anderen) möglicherweise einige Frames fehlen, wenn der Hotspot-Compiler mit der Optimierung beschäftigt war.

Gerco trocknet
quelle
Ich mag diese Antwort, weil sie mir die Möglichkeit bietet, die Ausgabe zu steuern. Ich kann ersetzen t.printStackTracemit t.printStackTrace(System.out).
4
In einer Zeile:new Throwable().printStackTrace(System.out);
1
Dies sollte die akzeptierte Antwort sein. Es ist einfach und unkompliziert! Vielen Dank für das Teilen
Sam
2
und es ist einfach, an java.util.Loggerlog.log(Level.SEVERE, "Failed to do something", new Throwable());
MitchBroadhead
13

Beachten Sie, dass Thread.dumpStack () tatsächlich eine Ausnahme auslöst:

new Exception("Stack trace").printStackTrace();
Ariel T.
quelle
12
Es wird eine Ausnahme erstellt, aber nicht ausgelöst.
MatrixFrog
6

Sie können auch ein Signal an die JVM senden, um Thread.getAllStackTraces () auf einem laufenden Java-Prozess auszuführen, indem Sie ein QUIT-Signal an den Prozess senden.

Unter Unix / Linux verwenden Sie:

kill -QUIT process_idDabei ist process_id die Prozessnummer Ihres Java-Programms.

Unter Windows können Sie in der Anwendung Strg-Pause drücken, obwohl dies normalerweise nur angezeigt wird, wenn Sie einen Konsolenprozess ausführen.

JDK6 hat eine weitere Option eingeführt, den Befehl jstack, mit dem der Stapel von jedem laufenden JDK6-Prozess auf Ihrem Computer angezeigt wird:

jstack [-l] <pid>

Diese Optionen sind sehr nützlich für Anwendungen, die in einer Produktionsumgebung ausgeführt werden und nicht einfach geändert werden können. Sie sind besonders nützlich für die Diagnose von Laufzeit-Deadlocks oder Leistungsproblemen.

http://java.sun.com/developer/technicalArticles/Programming/Stacktrace/ http://java.sun.com/javase/6/docs/technotes/tools/share/jstack.html

Andrew Newdigate
quelle
6

Wenn Sie eine Capture-Ausgabe benötigen

StringWriter sw = new StringWriter();
new Throwable("").printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();
Ricardo Jl Rufino
quelle
6

Java 9 führte die StackWalker und unterstützenden Klassen zum Gehen des Stapels ein.

Hier einige Ausschnitte aus dem Javadoc:

Die walkMethode öffnet einen sequentiellen Stream StackFramesfür den aktuellen Thread und wendet dann die angegebene Funktion an, um den StackFrameStream zu durchlaufen . Der Stream meldet Stapelrahmenelemente in der Reihenfolge vom obersten Rahmen, der den Ausführungspunkt darstellt, an dem der Stapel generiert wurde, bis zum untersten Rahmen. Der StackFrameStream wird geschlossen, wenn die Walk-Methode zurückkehrt. Wenn versucht wird, den geschlossenen Stream wiederzuverwenden, IllegalStateExceptionwird geworfen.

...

  1. Um einen Schnappschuss der Top-10-Stapelrahmen des aktuellen Threads zu erstellen,

    List<StackFrame> stack = StackWalker.getInstance().walk(s ->
     s.limit(10).collect(Collectors.toList()));
mkobit
quelle
0

HPROF Java Profiler

Sie müssen dies nicht einmal im Code tun. Sie können Java HPROF an Ihren Prozess anhängen und jederzeit Control- \ drücken, um einen Heap-Dump auszugeben, Threads auszuführen usw. . . ohne Ihren Anwendungscode durcheinander zu bringen. Dies ist etwas veraltet, Java 6 wird mit der GUI-Jconsole geliefert, aber ich finde HPROF immer noch sehr nützlich.

Gandalf
quelle
0

Die Antwort von Rob Di Marco ist bei weitem die beste, wenn Sie gerne etwas ausgeben stderr.

Wenn Sie in a Stringmit neuen Zeilen gemäß einer normalen Ablaufverfolgung erfassen möchten , können Sie Folgendes tun:

Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );
Mike Nagetier
quelle