Java / Android - jak wydrukować pełny ślad stosu?

124

W systemie Android (Java) jak wydrukować pełny ślad stosu? Jeśli moja aplikacja ulegnie awarii z powodu nullPointerException lub czegoś podobnego, wydrukuje (prawie) pełny ślad stosu, jak poniżej:

java.io.IOException: Attempted read from closed stream.
com.android.music.sync.common.SoftSyncException: java.io.IOException: Attempted read from closed stream.
    at com.android.music.sync.google.MusicSyncAdapter.getChangesFromServerAsDom(MusicSyncAdapter.java:545)
    at com.android.music.sync.google.MusicSyncAdapter.fetchDataFromServer(MusicSyncAdapter.java:488)
    at com.android.music.sync.common.AbstractSyncAdapter.download(AbstractSyncAdapter.java:417)
    at com.android.music.sync.common.AbstractSyncAdapter.innerPerformSync(AbstractSyncAdapter.java:313)
    at com.android.music.sync.common.AbstractSyncAdapter.onPerformLoggedSync(AbstractSyncAdapter.java:243)
    at com.google.android.common.LoggingThreadedSyncAdapter.onPerformSync(LoggingThreadedSyncAdapter.java:33)
    at android.content.AbstractThreadedSyncAdapter$SyncThread.run(AbstractThreadedSyncAdapter.java:164)
Caused by: java.io.IOException: Attempted read from closed stream.
    at org.apache.http.impl.io.ChunkedInputStream.read(ChunkedInputStream.java:148)
    at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:159)
    at java.util.zip.GZIPInputStream.readFully(GZIPInputStream.java:212)
    at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:81)
    at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:64)
    at android.net.http.AndroidHttpClient.getUngzippedContent(AndroidHttpClient.java:218)
    at com.android.music.sync.api.MusicApiClientImpl.createAndExecuteMethod(MusicApiClientImpl.java:312)
    at com.android.music.sync.api.MusicApiClientImpl.getItems(MusicApiClientImpl.java:588)
    at com.android.music.sync.api.MusicApiClientImpl.getTracks(MusicApiClientImpl.java:638)
    at com.android.music.sync.google.MusicSyncAdapter.getChangesFromServerAsDom(MusicSyncAdapter.java:512)
    ... 6 more

Jednak czasami do celów debugowania chcę rejestrować pełny ślad stosu z miejsca, w którym jestem w kodzie. Pomyślałem, że mogę to zrobić:

StackTraceElement trace = new Exception().getStackTrace();
Log.d("myapp", trace.toString());

Ale to tylko wypisuje wskaźnik do obiektu ... Czy muszę iterować przez wszystkie elementy śledzenia stosu, aby je wydrukować? A może istnieje prosta metoda, aby to wszystko wydrukować?

Jake Wilson
źródło
1
Możesz użyć tej metody Thread.currentThread (). GetStackTrace () stackoverflow.com/questions/1069066/ ...
user2024270

Odpowiedzi:

119

Istnieją zastąpienia wszystkich metod dziennika z (String tag, String msg, Throwable tr)podpisami.

Przekazanie wyjątku jako trzeciego parametru powinno dać pełny ślad stosu w logcat.

Philipp Reichart
źródło
5
FYI, getStackTraceString()trzyargumentowe metody dziennika używają metody, o której wspomniał @Thomas za kulisami.
Philipp Reichart
6
Tworzyłem Androida przez dwa lata i nigdy wcześniej tego nie zauważyłem. Dziękuję Ci bardzo.
Anh Tuan
Patrząc na kod źródłowy, tak, masz rację @PhilippReichart. Oto kod dla Log.d na AOSP 4.2.2_r1
Ehtesh Choudhury
152

Poniższe powinny załatwić sprawę:

Log.d("myapp", Log.getStackTraceString(new Exception()));

Zauważ, że ...x morena końcu nie odcina żadnych informacji ze śladu stosu:

(Wskazuje to), że pozostała część śladu stosu dla tego wyjątku odpowiada wskazanej liczbie ramek od dołu śladu stosu wyjątku, który został spowodowany przez ten wyjątek (wyjątek „obejmujący”).

... lub innymi słowy, zamień x morena ostatnie xwiersze pierwszego wyjątku.

Michael Berry
źródło
1
Skąd pochodzi getStackTraceString()? Nie pojawia się w Eclipse? To nie jest część Throwableani Exception...
Jake Wilson
9
„... 6 więcej” na końcu nie ucina żadnych informacji. Mówi ci, że reszta śladu stosu jest taka sama, jak dla najwyższego wyjątku. Wystarczy spojrzeć na ostatnie 6 wierszy z pierwszego wyjątku.
Tomasz
Prawdopodobnie musiałbyś zaimportować bibliotekę rejestrowania (używając import android.util.Log;).
Dan
10

Użyj Log.getStackTraceString (Throwable t). Możesz uzyskać dłuższe ślady stosu, kopiąc głębiej. Na przykład:

try {
    ...
} catch(Exception e) {
    Log.d("Some tag", Log.getStackTraceString(e.getCause().getCause()));
}

Pobrano z http://developer.android.com/reference/android/util/Log.html#getStackTraceString%28java.lang.Throwable%29

Tomasz
źródło
Log.getStackTraceString()nie rejestruje śladu stosu, po prostu zwraca go jako ciąg. Powyższy kod nie zarejestruje niczego, a także zakończy się niepowodzeniem z NPE, jeśli tak e.getCause()jest null.
Philipp Reichart
9
private static String buildStackTraceString(final StackTraceElement[] elements) {
    StringBuilder sb = new StringBuilder();
    if (elements != null && elements.length > 0) {
        for (StackTraceElement element : elements) {
            sb.append(element.toString());
        }
    }
    return sb.toString();
}


// call this at your check point
Log.d(TAG, buildStackTraceString(Thread.currentThread().getStackTrace()));
faywong
źródło
6

Możesz użyć tego:

public static String toString(StackTraceElement[] stackTraceElements) {
    if (stackTraceElements == null)
        return "";
    StringBuilder stringBuilder = new StringBuilder();
    for (StackTraceElement element : stackTraceElements)
        stringBuilder.append(element.toString()).append("\n");
    return stringBuilder.toString();
}
Mohsen Afshin
źródło
4

Możesz również wydrukować ślad stosu w dowolnym punkcie kodu aplikacji przy użyciu metod, takich jak Thread.dumpStack()

Przejdź przez łącze, aby uzyskać więcej informacji

ajaykoppisetty
źródło
2

Musisz użyć Throwable Object, aby uzyskać pełny stackTrace.

try{
 // code here
}catch(Exception e){
    String exception = getStackTrace(e);
}

public static String getStackTrace(final Throwable throwable) {
     final StringWriter sw = new StringWriter();
     final PrintWriter pw = new PrintWriter(sw, true);
     throwable.printStackTrace(pw);
     return sw.getBuffer().toString();
}

Odniesienie: https://stackoverflow.com/a/18546861

Houssin Boulla
źródło
0

Szybko wykonałem teraz funkcję rekurencyjną, która będzie iterować funkcje throwable i throwable.getCause ().

Dzieje się tak, ponieważ każda „throwable.getCause ()” zwraca do Ciebie nowy komunikat o wyjątku z kilkoma powtórzonymi wierszami i nowymi wierszami. Więc koncepcja jest taka: jeśli istnieje „przyczyna”, jest linia z „n więcej ..” na głównym rzucie, więc otrzymuję ostatnią linię przed tą z „n więcej ..”, wtedy otrzymuję komunikat o przyczynie i na koniec podłańcuch komunikatu o przyczynie, pobierając tylko część po ostatniej powtórzonej linii (ostatnia linia, która pojawia się w obu: główny rzut i przyczyna rzucana).

Następnie używam rekurencji, gdy otrzymuję komunikat o przyczynie, więc ponownie wywołując tę ​​samą funkcję, aby uzyskać komunikat o przyczynie z głównego elementu rzutującego, otrzymam już zastąpiony komunikat. Jeśli przyczyna wyrzucenia z głównego miotanego ma również inną przyczynę, więc główny rzut ma 3 poziomy (główny -> przyczyna -> przyczyna-przyczyny), na głównym wyrzucanym rzucie otrzymam "komunikat o przyczynie" już zastąpiony komunikat (używając tej samej koncepcji pliku main

public static <T extends Throwable> String printStackTraceString(T ex) {     // Recursive
    Throwable tr = ex;
    if (tr != null) {
        String st = Log.getStackTraceString(tr);
        if (tr.getCause() != null) {
            // Recursion...
            String cs = printStackTraceString(tr.getCause());

            String r1 = st.subSequence(0x0, st.lastIndexOf("\n", st.lastIndexOf("\n") - "\n".length())).toString();
            String replace = r1.substring(r1.lastIndexOf("\n"));
            if (cs.contains(replace)) {
                return r1.concat(cs.subSequence(cs.indexOf(replace) + replace.length(), cs.length()).toString());
            }
        }
        return st;
    }
    return "";
}

Próbowałem tylko z 2 poziomami (główna -> przyczyna) a nie z więcej: / Jeśli coś jest nie tak to edytuj funkcję i napisz komentarz: D

miłego kodowania i miłego dnia też: D

Ważny:

Ten kod czasami otrzymuje wyjątek, jeśli „st” nie zawiera „\ n” lub podobnego (znalazłem jakiś rodzaj wyjątków, w których stosowe ślady mają ten problem). Aby rozwiązać ten problem, musisz dodać czek przed wierszem kodu: „String r1 = ...”

Musisz sprawdzić: „st” zawiera „\ n” oraz czy indeksy początkowy i końcowy „st.subSequence” są prawidłowe.

W każdym razie proponuję umieścić to wewnątrz try-catch i zwrócić pusty ciąg w przypadku wyjątku. (Jest rekurencyjny, więc zwrócony pusty ciąg zostanie połączony z poprzednim przetworzonym ciągiem).

Z3R0
źródło