Jak mogę uzyskać bieżący ślad stosu w Javie?

1001

Jak uzyskać bieżące śledzenie stosu w Javie, na przykład jak w .NET możesz to zrobić Environment.StackTrace?

Znalazłem, Thread.dumpStack()ale nie tego chcę - chcę odzyskać ślad stosu, a nie go wydrukować.

ripper234
źródło
73
Zawsze używam „new Exception (). PrintStackTrace ()” jako wyrażenia obserwacyjnego podczas debugowania w Eclipse. Jest to przydatne, gdy zawieszasz się w punkcie przerwania i chcesz wiedzieć, skąd pochodzisz.
Tim Büthe
22
@ TimBüthe: czy Eclipse nie mówi ci już śladu stosu, gdy jesteś w trybie debugowania? Chyba tak.
Luigi Massa Gallerano
41
Arrays.toString (Thread.currentThread (). GetStackTrace ()); wystarczająco proste.
Ajay,
2
@Arkanon Odnosi się do java i pokazuje, jak zrobiliby to w .net i jaki jest odpowiednik w java.
Pokechu22,
9
Aby ładnie go wydrukować, możesz użyć apache StringUtils: StringUtils.join (currentThread (). GetStackTrace (), "\ n");
Art.

Odpowiedzi:

1158

Możesz użyć Thread.currentThread().getStackTrace().

Zwraca tablicę StackTraceElements reprezentującą bieżący ślad stosu programu.

jjnguy
źródło
48
Fajny one-liner, jeśli już używasz wspólnego apache, aby uzyskać ciąg: String fullStackTrace = org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace(e); stackoverflow.com/a/10620951/11236
ripper234
5
Pierwszy element (indeks 0) w tablicy to metoda java.lang.Thread.getStackTrace, drugi (indeks 1) zwykle jest pierwszym zainteresowanym.
lilalinux
175
Jeden linijka do konwersji śledzenia stosu na ciąg znaków, który działa, gdy nie masz wyjątku i nie używasz Apache Arrays.toString (Thread.currentThread (). GetStackTrace ())
Tony
9
Rozwiązanie @Tony jest lepsze, ponieważ nie wymaga rzucania
mvd
3
Jak wskazano w wielu odpowiedziach poniżej - new Throwable().getStackTrace()jest znacznie szybszy, ponieważ nie musi sprawdzać this != Thread.currentThread()i omija potencjalnych obciążeń JVM związanych z wywoływaniem go przez child-class ( Exception extends Throwable)
Vlad
264
Thread.currentThread().getStackTrace();

jest w porządku, jeśli nie obchodzi cię, jaki jest pierwszy element stosu.

new Throwable().getStackTrace();

będzie miał zdefiniowaną pozycję dla twojej obecnej metody, jeśli to ma znaczenie.

Yishai
źródło
35
(new Throwable()).getStackTrace()jest także szybszy w wykonywaniu (patrz bugs.sun.com/bugdatabase/view_bug.do?bug_id=6375302 )
MightyE
2
Czy możesz wyjaśnić bardziej szczegółowo, w jaki sposób wyniki Thread.currentThread (). GetStackTrace () mogą się różnić od nowych Throwable (). GetStackTrace ()? Próbowałem obu i okazało się, że Thread.currentThread (). GetStackTrace () zwraca tablicę z Thread.getStackTrace w indeksie 0, a metoda wywołująca ma indeks 1. Nowa metoda Throwable (). Metoda getStackTrace () zwraca tablicę z wywołaniem metoda o indeksie 0. Wygląda na to, że obie metody mają określoną pozycję dla bieżącej metody, ale pozycje są różne. Czy wskazujesz inną różnicę?
Ryan
9
@Ryan, nie ma gwarancji, że Thread.currentThread (). GetStackTrace () nie obiecuje utrzymania tej pozycji w stosunku do różnych wersji metody. Więc kiedy zdarzyło ci się sprawdzić, czy był to indeks 1. W niektórych wersjach JVM (1.5 lub 1.6, nie pamiętaj) dla Sun, był to indeks 2. To właśnie rozumiem przez zdefiniowaną pozycję - specyfikacja wymaga być tam i pozostać tam w przyszłości.
Yishai
5
@MightyE Błąd jest teraz zgłaszany jako zamknięty jako naprawiony w Javie 6. Patrząc na elementy wewnętrzne, wygląda to tak, jak teraz, (new Exception()).getStackTrace()gdy żądany ślad stosu znajduje się w bieżącym wątku (co zawsze będzie miało miejsceThread.currentThread().getStackTrace();
M. Justin
3
@MJustin: błąd nie jest „teraz” zgłaszany jako naprawiony, został naprawiony nawet sześć lat przed napisaniem komentarza przez MightyE. W rzeczywistości zostało to naprawione jeszcze przed założeniem
Stackoverflow
181
for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
    System.out.println(ste);
}
Leif Gruenwoldt
źródło
20
java ma metodę printStackTrace zdefiniowaną w Throwable, możesz zrobić: new Exception().printStackTrace(System.out)co pamiętam, pętla for prawdopodobnie ma mniejszy narzut, ale i tak powinieneś używać jej tylko jako funkcji debugowania ...
Jaap,
2
Najbardziej podoba mi się ten, ponieważ masz szansę na wyeliminowanie hałasu, owijając swoje oświadczenie println, np.for (StackTraceElement ste : Thread.currentThread().getStackTrace()) { if(ste.toString().contains("mypackages")){ System.out.println(ste); } }
nb
61
Thread.currentThread().getStackTrace();

jest dostępna od wersji JDK1.5.

W przypadku starszej wersji możesz przekierować exception.printStackTrace()do StringWriter():

StringWriter sw = new StringWriter();
new Throwable("").printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();
RealHowTo
źródło
3
new Throwable().getStackTrace()jest dostępna od JDK1.4…
Holger
40

Możesz użyć do tego wspólnego Apache:

String fullStackTrace = org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace(e);
stikkos
źródło
13
To miłe rozwiązanie jednoliniowe. Do Twojej wiadomości, dla każdego używającego org.apache.commons.lang3, pełna linia to:org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace(e);
fileoffset
2
Wystarczy powtórzyć komentarz fileoffset podczas przeprowadzki org.apache.commons.lang3, który początkowo przeoczyłem - import używa lang3zamiast langi zastępuje getFullStackTracego getStackTrace.
ggkmath
To bardzo przydatne! Podczas debugowania za pomocą IDE (na przykład IntelliJ IDEA) dobrze jest użyć ExceptionUtils.getStackTrace(new Throwable())wyrażenia, aby uzyskać ślad stosu.
Siergiej Brunow
24

Na Androidzie znacznie łatwiejszym sposobem jest użycie tego:

import android.util.Log;
String stackTrace = Log.getStackTraceString(exception); 
Vicky Kapadia
źródło
4
Gdzie jest zdefiniowane getStackTraceString? Czy pochodzi to z zewnętrznej biblioteki rejestrowania?
skiphoppy
6
android.util.Log.getStackTraceString developer.android.com/reference/android/util/Log.html
Ondrej Kvasnovsky
22

Aby uzyskać ślad stosu wszystkich wątków, możesz albo użyć narzędzia jstack, JConsole, albo wysłać sygnał zabicia-wyjścia (w systemie operacyjnym Posix).

Jeśli jednak chcesz to zrobić programowo, możesz spróbować użyć ThreadMXBean:

ThreadMXBean bean = ManagementFactory.getThreadMXBean();
ThreadInfo[] infos = bean.dumpAllThreads(true, true);

for (ThreadInfo info : infos) {
  StackTraceElement[] elems = info.getStackTrace();
  // Print out elements, etc.
}

Jak wspomniano, jeśli chcesz tylko śledzenia stosu bieżącego wątku, jest to o wiele łatwiejsze - wystarczy użyć Thread.currentThread().getStackTrace();

Adamski
źródło
2
Fragment kodu w tej odpowiedzi jest najbliższy programowemu generowaniu zrzutu, jaki widzisz po wysłaniu do JVM sygnału QUIT (w systemach uniksowych) lub <ctrl> <break> w systemie Windows. W przeciwieństwie do innych odpowiedzi, możesz zobaczyć monitory i synchronizatory, a także tylko ślady stosu (np. Jeśli iterujesz po tablicy StackTraceElement i robisz System.err.println(elems[i])na każdym z nich). Druga linia fragmentu kodu jest krótka bean.przed, dumpAllThreadsale myślę, że większość ludzi może to rozwiązać.
George Hawkins
22

Inne rozwiązanie (tylko 35 31 znaków):

new Exception().printStackTrace();   
new Error().printStackTrace();
kukis
źródło
1
niesamowite rozwiązanie. Teraz nie muszę zapętlać wszystkich ramek stosu
Vineel Kovvuri,
17

Głupie ja, to jest Thread.currentThread().getStackTrace();

ripper234
źródło
17

Tony, jako komentarz do zaakceptowanej odpowiedzi, udzielił najlepszej odpowiedzi, która faktycznie odpowiada na pytanie PO :

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

... OP NIE zapytał, jak uzyskać Stringślad stosu z pliku Exception. I chociaż jestem wielkim fanem Apache Commons, kiedy jest coś tak prostego jak powyższe, nie ma logicznego powodu, aby korzystać z zewnętrznej biblioteki.

gryzoń mike
źródło
Mam listę wątków i muszę wydrukować ślady stosu.
Daneel S. Yaitskov
W takim przypadku upewnij się, że używasz, Thread.getAllStackTraces()co daje ci Map<Thread, StackTraceElement[]>. Hotspot zabezpiecza wszystkie wątki, ilekroć będzie to potrzebne. Zing może przenosić pojedyncze wątki do bezpiecznego punktu, nie przeszkadzając innym. Zdobycie stacktrace wymaga bezpiecznego punktu. Thread.getAllStackTraces()pobiera wszystkie ślady stosu i zatrzymuje wszystkie wątki tylko raz. Oczywiście musisz dowiedzieć się, które mapowanie Cię interesuje.
Ralf H
14

sugeruję to

  Thread.dumpStack()

jest prostszym sposobem i ma tę zaletę, że nie tworzy wyjątku ani nie można go rzucać, gdy może nie być żadnego problemu, i jest znacznie bardziej istotny.

Thomas Adkins
źródło
1
Dobra, faktycznie, dumpStack () jest metodą statyczną i powinno być dostępne w sposób statyczny. Dzięki, zaćmienie!
Thomas Adkins
1
Uwielbiam ten skrót, ale kod źródłowy tej metody brzmi: new Exception("Stack trace").printStackTrace();tak naprawdę buduje Throwable
Florent
13

Pobieranie stacktrace:

StackTraceElement[] ste = Thread.currentThread().getStackTrace();

Drukowanie stacktrace (JAVA 8+):

Arrays.asList(ste).forEach(System.out::println);

Drukowanie stacktrage (JAVA 7):

StringBuilder sb = new StringBuilder();

for (StackTraceElement st : ste) {
    sb.append(st.toString() + System.lineSeparator());
}
System.out.println(sb);
Witold Kaczurba
źródło
12

W Javie 9 istnieje nowy sposób:

public static void showTrace() {

  List<StackFrame> frames =
    StackWalker.getInstance( Option.RETAIN_CLASS_REFERENCE )
               .walk( stream  -> stream.collect( Collectors.toList() ) );

  for ( StackFrame stackFrame : frames )
    System.out.println( stackFrame );
}
Christian Ullenboom
źródło
Ciekawy. :-)
djangofan
10

Mam metodę narzędzia, która zwraca ciąg znaków z stacktrace:

static String getStackTrace(Throwable t) {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw, true);
    t.printStackTrace(pw);
    pw.flush();
    sw.flush();
    return sw.toString();
}

I po prostu loguj jak ...

... 
catch (FileNotFoundException e) {
    logger.config(getStackTrace(e));
}
Salvador Valencia
źródło
Wygląda to na jeden automatycznie wygenerowany przez Netbeans.
Leif Gruenwoldt,
to logger to wbudowany program rejestrujący lub program utworzony i zapisany w pliku.
Doug Hauf
@Doug Hauf, logger to odniesienie do wbudowanego programu Java Logger. Upewnij się, że skonfigurowałeś plik logging.properties. Dodaj go na początku zajęć w następujący sposób: prywatny statyczny końcowy Logger logger = Logger.getLogger (Your_Class_Name.class.getName ());
Salvador Valencia,
1
java.util.logging.Logger#log(Level, String, Throwable)już to robi. Jest to niepotrzebne przepisywanie rzeczy, które już istnieją w standardowej bibliotece Java, i wskazuje nowym programistom w złym kierunku, który jest z dala od dokumentacji. Robiąc to teraz, wymusiłeś sformatowanie Stacktrace w określony sposób, gdzie jako loggingAPI pozwala na dynamiczne zmienianie formatowania instrukcji dziennika podczas jej określania. O ile mi wiadomo, log4jma również podobne zachowanie. Nie wymyślaj na nowo koła, bo w końcu tracisz rzeczy, które wyjaśniłem.
searchengine27,
1
Istniał również w 2012 roku. Niestety data nie ma znaczenia w twoim przypadku. Zobacz [link] (docs.oracle.com/javase/1.5.0/docs/api/java/util/logging/Logger.html#log (java.util.logging.Level, java.lang.String, java.lang .Throwable)), [link] (docs.oracle.com/javase/1.5.0/docs/api/java/util/logging/Logger.html#log (java.util.logging.LogRecord)) itp. (Tam kilka funkcji, które przechodzą Throwableod do Handlers przez do FormatterS w 2012 roku, który był Java7, bardziej niż ja tutaj wymienione A funkcje te sięgają znacznie dłużej niż Java7 została wokół;. Stąd J2SE 5.0 Javadocs)
searchengine27
10

Aby sznurować za pomocą guawy:

Throwables.getStackTraceAsString(new Throwable())
Zitrax
źródło
8
try {
}
catch(Exception e) {
    StackTraceElement[] traceElements = e.getStackTrace();
    //...
}

lub

Thread.currentThread().getStackTrace()
kurczaka w maśle
źródło
Czy twój najlepszy przykład nie da ci tylko śladu stosu w stosunku do bloku try / catch?
Dan Monego,
1
jakie są różnice między nimi
twlkyao
5

Może mógłbyś spróbować:

catch(Exception e)
{
    StringWriter writer = new StringWriter();
    PrintWriter pw = new PrintWriter(writer);
    e.printStackTrace(pw);
    String errorDetail = writer.toString();
}

Ciąg „errorDetail” zawiera ślad stosu.

użytkownik1782556
źródło
5
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();

Ostatni element tablicy reprezentuje spód stosu, który jest najnowszym wywołaniem metody w sekwencji.

A StackTraceElement has getClassName(), getFileName(), getLineNumber() and getMethodName().

wykonaj pętlę przez StackTraceElement i uzyskaj pożądany wynik.

for (StackTraceElement ste : stackTraceElements ) 
{
    //do your stuff here...
}
Jawad Zeb
źródło
Dziękujemy za wspomnienie, że ostatni element zawiera najnowszy. Przeciw intuicyjny i zaoszczędził mi trochę czasu.
jasne
również .toString () ładnie wypisze wszystkie te dane w łańcuch
Vlad
4

Użyłem odpowiedzi z góry i dodałem formatowanie

public final class DebugUtil {

    private static final String SEPARATOR = "\n";

    private DebugUtil() {
    }

    public static String formatStackTrace(StackTraceElement[] stackTrace) {
        StringBuilder buffer = new StringBuilder();
        for (StackTraceElement element : stackTrace) {
            buffer.append(element).append(SEPARATOR);
        }
        return buffer.toString();
    }

    public static String formatCurrentStacktrace() {
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        return formatStackTrace(stackTrace);
    }
}

źródło
2

Możesz użyć narzędzia jstack, jeśli chcesz sprawdzić bieżący stos wywołań swojego procesu.

Usage:
    jstack [-l] <pid>
        (to connect to running process)
    jstack -F [-m] [-l] <pid>
        (to connect to a hung process)
    jstack [-m] [-l] <executable> <core>
        (to connect to a core file)
    jstack [-m] [-l] [server_id@]<remote server IP or hostname>
        (to connect to a remote debug server)

Options:
    -F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)
    -m  to print both java and native frames (mixed mode)
    -l  long listing. Prints additional information about locks
    -h or -help to print this help message
Sampath
źródło
1

To jest stary post, ale oto moje rozwiązanie:

Thread.currentThread().dumpStack();

Więcej informacji i więcej metod: http://javarevisited.blogspot.fr/2013/04/how-to-get-current-stack-trace-in-java-thread.html

Manow
źródło
3
Ponieważ Thread.dumpStack () jest statyczny, powinieneś po prostu wywołać go bezpośrednio.
David Avendasora
2
OP wskazał, że chce odwołania w kodzie do śladu stosu, a nie do wydrukowania.
Taylor R
1
jak wspomniano @DavidAvendasora, powinieneś wywoływać Thread.dumpStack()bezpośrednio, ponieważ jest to metoda statyczna.
M-WaJeEh
Tak, java.lang.Thread.dumpStack () działa na Javie 11.
Park JongBum
-3

System.out.println(Arrays.toString(Thread.currentThread().getStackTrace()));

Pomoże to wydrukować ślad stosu bez pętli.

Bhuvanwaitz
źródło
1
Twoja odpowiedź jest taka sama jak moja wysłana 3 miesiące wcześniej.
Mike Rodent