Come posso convertire una traccia dello stack in una stringa?


1502

Qual è il modo più semplice per convertire il risultato Throwable.getStackTrace()in una stringa che raffigura lo stacktrace?


6
Perché la risposta di jqno utilizza effettivamente il metodo Throwable.getStackTrace () specificato nella domanda, mentre Brian no. Usa invece Throwable.printStackTrace ().
Stijn de Witt,

10
Quasi tutti i progetti Java dovrebbero includere Apache commons-lang. Include molti metodi di praticità che implementano esigenze di sviluppo estremamente comuni.
Russell Silva,

20
@StijndeWitt Quelle tre righe di codice hanno quasi certamente bisogno di essere prese in considerazione dal posto in cui le hai chiamate. Poiché non sai dove metterli, andranno nella tua casella degli strumenti di utilità con tutti gli altri frammenti utili. Bingo! hai appena reinventato guava / commons-lang / qualunque cosa ... solo non così bene. Importa invece una libreria di utilità sensibili e risparmia reinventando la ruota. Il vero segno di un principiante è pensare di poter fare un lavoro migliore degli scrittori della biblioteca.
Andrew Spencer,

6
1. Guava has - Throwables.getStackTraceAsString (e) 2. Apache Commons Lang - ExceptionUtils.getFullStackTrace 3. Scrivi i nostri metodi personalizzati
user2323036

8
@AndrewSpencer Non capisco perché voi ragazzi proviate così duramente a colpire StijndeWitt per aver voluto raggiungere questo obiettivo con qualche piccolo frammento. Non c'è davvero molto pericolo nello scrivere un piccolo metodo di utilità (non lo vedo come "SHEER ARROGANCE oh nooooo !! pensa di essere migliore di Apache !!"). Ci sono tonnellate di progetti, specialmente in linguaggi JVM non Java, che in realtà non vogliono includere Guava o Commons Lang solo per registrare uno stacktrace. Scrivo librerie Scala & Clojure e certamente non farò di Apache Commons Lang una dipendenza transitiva solo per un metodo.
jm0

Risposte:


1039

Si può usare il seguente metodo per convertire una Exceptiontraccia dello stack in String. Questa classe è disponibile in Apache commons-lang che è la libreria dipendente più comune con molti open source popolari

org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(Throwable)


88
@Stijn - per essere onesti (ho scritto l'attuale risposta più votata di seguito) vale la pena guardare commons-lang per molte più funzionalità
Brian Agnew

54
@StijndeWitt Commons Lang è piuttosto comune. È già presente nella maggior parte dei miei progetti / progetti sul lavoro.
WhyNotHugo,

16
@Hugo grazie, avrei usato StringWriter per evitare di aggiungere una nuova libreria - risulta che è già una dipendenza di 3 delle mie dipendenze. Quindi, per il resto, controlla se ce l'hai già.
Nathanial

33
@MartinAsenov - seguendo la tua logica non avresti mai usato una simile libreria, vero? Non lo useresti se non lo stai già usando?
Brian Agnew,

16
Cordiali saluti, il pacchetto è cambiato e la classe è ora a: org.apache.commons.lang3.exception.ExceptionUtils.
schmmd,

2201

Utilizzare Throwable.printStackTrace(PrintWriter pw)per inviare la traccia dello stack a un writer appropriato.

import java.io.StringWriter;
import java.io.PrintWriter;

// ...

StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
String sStackTrace = sw.toString(); // stack trace as a string
System.out.println(sStackTrace);

505
Se non ti piace includere una libreria esterna per qualcosa di così piccolo e semplice come questo, usa questa risposta.
Stijn de Witt,

8
Questo fa tagliare la traccia dello stack, allo stesso modo printStackTrace (). Tutte le eccezioni nella pila sono visibili ma per ogni eccezione la pila può essere tagliata. Chiunque richieda l'intera traccia dovrebbe considerare questo.
Laila Agaev,

5
Questo, a quanto pare, è praticamente esattamente ciò che fa il metodo ExceptionUtils.getStackTrace () di apache. Quasi alla lettera in realtà.
tickector

6
@BrianAgnew, non dovresti chiudere il StringWritere PrintWriter?
Muhammad Gelbana,

13
@BrianAgnew, grazie per la risposta. Mi ha fatto curioso e qui è quello che ho trovato: stackoverflow.com/questions/14542535/...
Muhammad Gelbana

459

Questo dovrebbe funzionare:

StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionAsString = sw.toString();

69
Conciso nel contesto java è sempre collinare. Questo printStackTracedovrebbe solo restituire una stringa, lasciando la decisione se stamparla o meno all'utente :)
dmitry

35
La stampa printStackTrace nella console è accettabile, ma almeno un getStackTrace che lo restituisce come stringa dovrebbe essere disponibile per impostazione predefinita
Mateus Viccari

5
Quale parte di questo è concisa? Devi costruire 2 oggetti che esistono per nessun altro scopo se non quello di mettere la traccia dello stack in una stringa.
ArtOfWarfare il

7
@dmitry Un metodo chiamato printXXX()dovrebbe stampare XXX.
Marchese di Lorne,

2
@Greg In realtà non era nemmeno il sarcasmo, richiedeva solo una lettura chiara di ciò che diceva.
Andrew,

225

Se stai sviluppando per Android, un modo molto più semplice è usare questo:

import android.util.Log;

String stackTrace = Log.getStackTraceString(exception); 

Il formato è lo stesso di getStacktrace, ad es

09-24 16:09:07.042: I/System.out(4844): java.lang.NullPointerException
09-24 16:09:07.042: I/System.out(4844):   at com.temp.ttscancel.MainActivity.onCreate(MainActivity.java:43)
09-24 16:09:07.042: I/System.out(4844):   at android.app.Activity.performCreate(Activity.java:5248)
09-24 16:09:07.043: I/System.out(4844):   at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1110)
09-24 16:09:07.043: I/System.out(4844):   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2162)
09-24 16:09:07.043: I/System.out(4844):   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2257)
09-24 16:09:07.043: I/System.out(4844):   at android.app.ActivityThread.access$800(ActivityThread.java:139)
09-24 16:09:07.043: I/System.out(4844):   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1210)
09-24 16:09:07.043: I/System.out(4844):   at android.os.Handler.dispatchMessage(Handler.java:102)
09-24 16:09:07.043: I/System.out(4844):   at android.os.Looper.loop(Looper.java:136)
09-24 16:09:07.044: I/System.out(4844):   at android.app.ActivityThread.main(ActivityThread.java:5097)
09-24 16:09:07.044: I/System.out(4844):   at java.lang.reflect.Method.invokeNative(Native Method)
09-24 16:09:07.044: I/System.out(4844):   at java.lang.reflect.Method.invoke(Method.java:515)
09-24 16:09:07.044: I/System.out(4844):   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
09-24 16:09:07.044: I/System.out(4844):   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)

2
Grazie. In questo caso, una data e un tag non sono scritti in ogni riga come in printStackTrace ().
CoolMind

1
In realtà Log.getStackTraceString nel suo core utilizza i sopra citati StringWriter e Printwriter (di D. Wroblewski).
MikeL,

2
Il modo più semplice per stamparlo nel registro Android è: Log.e("TAG", "My message", new Throwable());
Erick M. Sprengel,


113

ATTENZIONE: non include la causa (che di solito è il bit utile!)

public String stackTraceToString(Throwable e) {
    StringBuilder sb = new StringBuilder();
    for (StackTraceElement element : e.getStackTrace()) {
        sb.append(element.toString());
        sb.append("\n");
    }
    return sb.toString();
}

1
Vorrei aggiungere un'estensione di questo approccio se si desidera tagliare la traccia, ad esempio passare un parametro maxLines e aggiungere solo quelle righe alla traccia
Rich Seller

Mancano le parentesi dopo e.getStackTrace. public static String stackTraceToString (Exception e) {StringBuilder sb = new StringBuilder (); for (elemento StackTraceElement: e.getStackTrace ()) {sb.append (element.toString ()); sb.append ("<br />"); } return sb.toString (); }
rlc,

2
Non sono sicuro, ma penso che questo non stamperà la traccia dello stack dell'eccezione causa radice.
apoorv020,

7
Qualunque cosa tu faccia, non tagliare la traccia. Mi è successo molte, molte volte che ho guardato una traccia dello stack nei log di GlassFish che aveva tagliato le parti utili.
cayhorstmann,

Usa String.format("%n")o qualcosa di simile invece di "\n"rendere felici i lamer DOS.
ceving

89

Per me il modo più semplice e pulito era:

import java.util.Arrays;
Arrays.toString(e.getStackTrace());

37
Il codice è pulito, ma l'output no. Devi fare un .replaceAll (",", "\ n") alla fine. Tuttavia, si perde il rientro proposto da printStackTrace.
furia il

4
Ciò è utile quando si registra la traccia in un'unica riga
webjockey il

28
public static String getStackTrace(Throwable t) {
    StringWriter sw = new StringWriter();
    t.printStackTrace(new PrintWriter(sw));
    return sw.toString();
}

1
Ciao, vorrei solo sottolineare che la sezione commenti della risposta citata sottolinea che gli oggetti StringWritere PrintWriterdovrebbero essere closed .... (o immagino che solo i PrintWriterbisogni debbano essere chiusi poiché chiudendolo dovrebbe chiudere StringWriteranche quello)
Eric

8
Per ispirato intendi copiato? Lo metti solo in un metodo ... Leggere le risposte qui è come guardare "Giorno della marmotta"
Murmel

26

Il codice seguente consente di ottenere l'intero stackTrace con un Stringformato, senza utilizzare API come log4J o addirittura java.util.Logger:

catch (Exception e) {
    StackTraceElement[] stack = e.getStackTrace();
    String exception = "";
    for (StackTraceElement s : stack) {
        exception = exception + s.toString() + "\n\t\t";
    }
    System.out.println(exception);
    // then you can send the exception string to a external file.
}

1
ma è un po 'ingombrante non credi? in qualsiasi framework di registrazione dei progetti di dimensioni ragionevoli è un must, quindi perché preoccuparsi
mzzzzb

Questo non sta dando il messaggio di errore all'inizio. può essere aggiunto però
kommradHomer il

È possibile che si desideri utilizzare StringBuffer anziché una semplice concatenazione.
dedda1994,

Ciò è utile quando non è consentito registrare il messaggio per motivi di privacy.
A1m,

Ricorda però che questa soluzione non registrerà le cause interne
A1m

19

Ecco una versione che è copiabile in pasta direttamente nel codice:

import java.io.StringWriter; 
import java.io.PrintWriter;

//Two lines of code to get the exception into a StringWriter
StringWriter sw = new StringWriter();
new Throwable().printStackTrace(new PrintWriter(sw));

//And to actually print it
logger.info("Current stack trace is:\n" + sw.toString());

Oppure, in un blocco di cattura

} catch (Throwable t) {
    StringWriter sw = new StringWriter();
    t.printStackTrace(new PrintWriter(sw));
    logger.info("Current stack trace is:\n" + sw.toString());
}

ciò continuerà oltre l'ambito della funzione attuale, ad es. dove Throwableè definito, giusto?
n611x007,

16
Arrays.toString(thrown.getStackTrace())

È il modo più semplice per convertire il risultato in String. Lo sto usando nel mio programma per stampare la traccia dello stack

LOGGER.log(Level.SEVERE, "Query Builder Issue Stack Trace : {0} ,Message : {1} objid {2}", new Object[]{Arrays.toString(e.getStackTrace()), e.getMessage(),objId});

Ha funzionato per me, nulla sembra essere tagliato! Più semplice delle altre risposte anche senza alcuna libreria esterna, ottima risposta!
Shaung Cheng,

16

Stampa la traccia dello stack in a PrintStream, quindi convertila in a String:

// ...

catch (Exception e)
{
    ByteArrayOutputStream out = new ByteArrayOutputStream(); 
    e.printStackTrace(new PrintStream(out));
    String str = new String(out.toByteArray());

    System.out.println(str);
}

1
questa è la risposta più diretta
DaSqy Stc

11

Stampa dello stack stack su stringa

import java.io.PrintWriter;
import java.io.StringWriter;

public class StackTraceUtils {
    public static String stackTraceToString(StackTraceElement[] stackTrace) {
        StringWriter sw = new StringWriter();
        printStackTrace(stackTrace, new PrintWriter(sw));
        return sw.toString();
    }
    public static void printStackTrace(StackTraceElement[] stackTrace, PrintWriter pw) {
        for(StackTraceElement stackTraceEl : stackTrace) {
            pw.println(stackTraceEl);
        }
    }
}

È utile quando si desidera stampare la traccia dello stack del thread corrente senza creare l'istanza di Throwable- ma si noti che la creazione di una nuova Throwabletraccia dello stack da lì è in realtà più veloce ed economica della chiamata Thread.getStackTrace.


11

Kotlin

L'estensione della classe Throwable ti darà la proprietà String error.stackTraceString:

val Throwable.stackTraceString: String
  get() {
    val sw = StringWriter()
    val pw = PrintWriter(sw)
    this.printStackTrace(pw)
    return sw.toString()
  }

10
private String getCurrentStackTraceString() {
    StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
    return Arrays.stream(stackTrace).map(StackTraceElement::toString)
            .collect(Collectors.joining("\n"));
}

1
Grazie eddyrkokr! Funziona perfettamente per tutti i miei clas. L'ho appena aggiunto nella classe TestNG di base e raccoglie stacktraces uno per uno dai miei tutti i test!
Krzysztof Walczewski,

9

L'intelligente sniping nella prima serie di commenti è stato molto divertente, ma dipende davvero da cosa stai cercando di fare. Se non hai già la libreria corretta, allora 3 righe di codice (come nella risposta di D. Wroblewski) sono perfette. OTOH, se hai già la libreria apache.commons (come faranno i progetti più grandi), la risposta di Amar è più breve. OK, potrebbero essere necessari dieci minuti per ottenere la libreria e installarla correttamente (meno di una se sai cosa stai facendo). Ma il tempo stringe, quindi potresti non avere il tempo di risparmiare. Jarek Przygódzki aveva un avvertimento interessante: "Se non hai bisogno di eccezioni nidificate".

Ma cosa succede se io ne servono le tracce dello stack completi, nidificati e tutti? In tal caso, il segreto è utilizzare getFullStackTrace di apache.common (consultare http://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/exception/ExceptionUtils.html # getFullStackTrace% 28java.lang.Throwable% 29 )

Mi ha salvato la pancetta. Grazie, Amar, per il suggerimento!


9

Codice da Apache Commons Lang 3.4 ( JavaDoc ):

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

La differenza con le altre risposte è che utilizza autoFlush sul PrintWriter.


8

Senza di java.io.*essa si può fare così.

String trace = e.toString() + "\n";                     

for (StackTraceElement e1 : e.getStackTrace()) {
    trace += "\t at " + e1.toString() + "\n";
}   

E quindi la tracevariabile contiene la traccia dello stack. L'output contiene anche la causa iniziale, l'output è identico aprintStackTrace()

Esempio, printStackTrace()rese:

java.io.FileNotFoundException: / (Is a directory)
    at java.io.FileOutputStream.open0(Native Method)
    at java.io.FileOutputStream.open(FileOutputStream.java:270)
    at java.io.FileOutputStream.<init>(FileOutputStream.java:213)
    at java.io.FileOutputStream.<init>(FileOutputStream.java:101)
    at Test.main(Test.java:9)

La tracestringa regge, se stampata sustdout

java.io.FileNotFoundException: / (Is a directory)
     at java.io.FileOutputStream.open0(Native Method)
     at java.io.FileOutputStream.open(FileOutputStream.java:270)
     at java.io.FileOutputStream.<init>(FileOutputStream.java:213)
     at java.io.FileOutputStream.<init>(FileOutputStream.java:101)
     at Test.main(Test.java:9)

5

Versione Scala

def stackTraceToString(e: Exception): String = {
  import java.io.PrintWriter
  val sw = new StringWriter()
  e.printStackTrace(new PrintWriter(sw))
  sw.toString
}

5

se stai usando Java 8, prova questo

Arrays.stream(e.getStackTrace())
                .map(s->s.toString())
                .collect(Collectors.joining("\n"));

puoi trovare il codice per la getStackTrace()funzione fornito da Throwable.java:

public StackTraceElement[] getStackTrace() {
    return getOurStackTrace().clone();
}

e per StackTraceElement, fornisce toString()come:

public String toString() {
    return getClassName() + "." + methodName +
        (isNativeMethod() ? "(Native Method)" :
         (fileName != null && lineNumber >= 0 ?
          "(" + fileName + ":" + lineNumber + ")" :
          (fileName != null ?  "("+fileName+")" : "(Unknown Source)")));
}

Quindi unisciti StackTraceElementa "\ n".


Questa soluzione non rispetta il rientro della scheda di traccia dello stack, altrimenti funziona alla grande!
Krizajb,

4

una spiegazione sulla risposta di Gala che includerà anche le cause dell'eccezione:

private String extrapolateStackTrace(Exception ex) {
    Throwable e = ex;
    String trace = e.toString() + "\n";
    for (StackTraceElement e1 : e.getStackTrace()) {
        trace += "\t at " + e1.toString() + "\n";
    }
    while (e.getCause() != null) {
        e = e.getCause();
        trace += "Cause by: " + e.toString() + "\n";
        for (StackTraceElement e1 : e.getStackTrace()) {
            trace += "\t at " + e1.toString() + "\n";
        }
    }
    return trace;
}

4

La soluzione è convertire stackTrace dell'array in un tipo di dati stringa . Vedi il seguente esempio:

import java.util.Arrays;

try{

}catch(Exception ex){
    String stack = Arrays.toString(ex.getStackTrace());
    System.out.println("stack "+ stack);
}

4

Con l' API Java 8 Stream puoi fare qualcosa del genere:

Stream
    .of(throwable.getStackTrace())
    .map(StackTraceElement::toString)
    .collect(Collectors.joining("\n"));

Ci vorrà una serie di elementi traccia stack, li convertirà in stringa e si unirà in stringa multilinea.


2
Questa soluzione sta rimuovendo il messaggio e sta ignorando tutta la traccia del genitore
judovana

3

Il mio oneliner per convertire la traccia dello stack nella stringa multilinea inclusa:

Stream.of(e.getStackTrace()).map((a) -> a.toString()).collect(Collectors.joining("\n", "[", "]"))

Facile da passare al logger "così com'è".


Ottieni qualcosa che differisce da printStackTrace()Qui perderai: 1) Eccezione che è stata lanciata; 2) Cause e loro stacktrace
valijon l'

La differenza è abbastanza attesa, dal momento che la conversione del printStackTrace()mai è stata una parte della domanda.
Andrey Lebedenko,

2

Se non si desidera utilizzare una libreria esterna e non si sta sviluppando per Android , è possibile creare un metodo di 'estensione' come questo:

public static String getStackTraceString(Throwable e) {
    return getStackTraceString(e, "");
}

private static String getStackTraceString(Throwable e, String indent) {
    StringBuilder sb = new StringBuilder();
    sb.append(e.toString());
    sb.append("\n");

    StackTraceElement[] stack = e.getStackTrace();
    if (stack != null) {
        for (StackTraceElement stackTraceElement : stack) {
            sb.append(indent);
            sb.append("\tat ");
            sb.append(stackTraceElement.toString());
            sb.append("\n");
        }
    }

    Throwable[] suppressedExceptions = e.getSuppressed();
    // Print suppressed exceptions indented one level deeper.
    if (suppressedExceptions != null) {
        for (Throwable throwable : suppressedExceptions) {
            sb.append(indent);
            sb.append("\tSuppressed: ");
            sb.append(getStackTraceString(throwable, indent + "\t"));
        }
    }

    Throwable cause = e.getCause();
    if (cause != null) {
        sb.append(indent);
        sb.append("Caused by: ");
        sb.append(getStackTraceString(cause, indent));
    }

    return sb.toString();
}

2

Vecchia domanda, ma vorrei solo aggiungere il caso speciale in cui non si desidera stampare tutta la pila , rimuovendo alcune parti che non sono realmente interessate, escludendo determinate classi o pacchetti.

Invece di un PrintWriteruso a SelectivePrintWriter:

// This filters out this package and up.
String packageNameToFilter = "org.springframework";

StringWriter sw = new StringWriter();
PrintWriter pw = new SelectivePrintWriter(sw, packageNameToFilter);
e.printStackTrace(pw);
String sStackTrace = sw.toString(); 
System.out.println(sStackTrace);

Dove la SelectivePrintWriterlezione è data da:

public class SelectivePrintWriter extends PrintWriter {
    private boolean on = true;
    private static final String AT = "\tat";
    private String internal;

    public SelectivePrintWriter(Writer out, String packageOrClassName) {
        super(out);
        internal = "\tat " + packageOrClassName;
    }

    public void println(Object obj) {
        if (obj instanceof String) {
            String txt = (String) obj;
            if (!txt.startsWith(AT)) on = true;
            else if (txt.startsWith(internal)) on = false;
            if (on) super.println(txt);
        } else {
            super.println(obj);
        }
    }
}

Si noti che questa classe può essere facilmente adattata per filtrare da Regex containso altri criteri. Si noti inoltre che dipende dai Throwabledettagli di implementazione (non è probabile che cambi, ma comunque).


1
Sì, funziona bene ed è in realtà un'idea abbastanza utile.
gil.fernandes,

2
 import java.io.PrintWriter;
import java.io.StringWriter;

public class PrintStackTrace {

    public static void main(String[] args) {

        try {
            int division = 0 / 0;
        } catch (ArithmeticException e) {
            StringWriter sw = new StringWriter();
            e.printStackTrace(new PrintWriter(sw));
            String exceptionAsString = sw.toString();
            System.out.println(exceptionAsString);
        }
    }
}

Quando esegui il programma, l'output sarà qualcosa di simile:

java.lang.ArithmeticException: / by zero
at PrintStackTrace.main(PrintStackTrace.java:9)

1

Mi chiedo perché nessuno ne abbia parlato ExceptionUtils.getStackFrames(exception)

Per me è il modo più conveniente per scaricare stacktrace con tutte le sue cause fino alla fine:

String.join("\n", ExceptionUtils.getStackFrames(exception));

0

Attenzione: questo potrebbe essere un po 'fuori tema, ma vabbè ...;)

In primo luogo, non so quale sia stata la ragione dei poster originali per volere la traccia dello stack come stringa. Quando la traccia dello stack dovrebbe finire in un LOG SLF4J / Logback, ma non è stata o deve essere generata alcuna eccezione, ecco cosa faccio:

public void remove(List<String> ids) {
    if(ids == null || ids.isEmpty()) {
        LOG.warn(
            "An empty list (or null) was passed to {}.remove(List). " +
            "Clearly, this call is unneccessary, the caller should " + 
            "avoid making it. A stacktrace follows.", 
            getClass().getName(),
            new Throwable ("Stacktrace")
        );

        return;
    }

    // actual work, remove stuff
}

Mi piace perché non richiede una libreria esterna (a parte il back-end di registrazione, che sarà comunque attivo nella maggior parte dei casi, ovviamente).


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.