¿Cómo puedo convertir un seguimiento de pila en una cadena?

1502

¿Cuál es la forma más fácil de convertir el resultado de Throwable.getStackTrace()a una cadena que representa el stacktrace?

ripper234
fuente
66
Debido a que la respuesta de jqno en realidad usa el método Throwable.getStackTrace () que especificó en su pregunta, mientras que Brian no. Utiliza Throwable.printStackTrace () en su lugar.
Stijn de Witt
10
Casi todos los proyectos de Java deberían incluir Apache commons-lang. Incluye muchos métodos de conveniencia que implementan necesidades de desarrollo extremadamente comunes.
Russell Silva
20
@StijndeWitt Esas tres líneas de código casi seguramente necesitan factorizarse del lugar que las ha llamado. Como no sabe dónde colocarlos, irán a su caja de herramientas de utilidad con todos los demás fragmentos útiles. ¡Bingo! acabas de reinventar la guayaba / commons-lang / lo que sea ... solo que no tan bien. En su lugar, importe una biblioteca de utilidades sensata y ahorre reinventando la rueda. El verdadero signo de un novato es pensar que puedes hacer un mejor trabajo que los escritores de la biblioteca.
Andrew Spencer
66
1. La guayaba tiene - Throwables.getStackTraceAsString (e) 2. Apache Commons Lang - ExceptionUtils.getFullStackTrace 3. Escribe nuestros propios métodos personalizados
user2323036
8
@ AndrewSpencer No entiendo por qué ustedes se esfuerzan tanto por golpear a StijndeWitt por querer lograr esto con un pequeño fragmento. Realmente no hay mucho peligro al escribir un pequeño método de utilidad (no lo veo como "¡¡¡¡ARROGANCIA BRILLANTE, ¡oh nooooo! ¡Él piensa que es mejor que Apache!"). Hay toneladas de proyectos, especialmente en lenguajes JVM que no son Java, que realmente no quieren incluir Guava o Commons Lang solo para registrar un seguimiento de pila. Escribo las bibliotecas Scala y Clojure y ciertamente no convertiré a Apache Commons Lang en una dependencia transitiva solo por un método.
jm0

Respuestas:

1039

Se puede usar el siguiente método para convertir un Exceptionseguimiento de pila String. Esta clase está disponible en Apache commons-lang, que es la biblioteca dependiente más común con muchas fuentes abiertas populares

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

amar
fuente
88
@Stijn - para ser justos (escribí la respuesta más votada actualmente a continuación) vale la pena mirar commons-lang para obtener mucha más funcionalidad
Brian Agnew
54
@StijndeWitt Commons Lang es bastante común. Ya está presente en la mayoría de mis proyectos / proyectos en el trabajo.
WhyNotHugo
16
@Hugo, gracias, iba a usar StringWriter para evitar agregar una nueva biblioteca; resulta que ya es una dependencia de 3 de mis dependencias. Entonces, para el resto, verifique si ya lo tiene.
Nathanial
33
@MartinAsenov: siguiendo tu lógica, nunca usarías una biblioteca así, ¿verdad? ¿No lo usarías a menos que ya lo estés usando?
Brian Agnew
16
Para su información, el paquete ha cambiado y la clase es ahora en: org.apache.commons.lang3.exception.ExceptionUtils.
schmmd
2201

Se usa Throwable.printStackTrace(PrintWriter pw)para enviar el seguimiento de la pila a un escritor apropiado.

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);
Brian Agnew
fuente
505
Si no le gusta incluir una biblioteca externa para algo tan pequeño y simple como esto, use esta respuesta.
Stijn de Witt
8
Esto recorta el seguimiento de la pila, de la misma manera que printStackTrace (). Todas las excepciones en la pila son visibles, pero para cada excepción la pila puede recortarse. Cualquiera que requiera el rastro completo debería considerar esto.
Laila Agaev
55
Resulta que esto es casi exactamente lo que hace el método ExceptionUtils.getStackTrace () de apache. Casi al pie de la letra en realidad.
ticktock
66
@BrianAgnew, ¿no deberías cerrar el StringWritery PrintWriter?
Muhammad Gelbana
13
@BrianAgnew, gracias por la respuesta. Me dio curiosidad y esto es lo que encontré: stackoverflow.com/questions/14542535/…
Muhammad Gelbana
459

Esto debería funcionar:

StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionAsString = sw.toString();
D. Wroblewski
fuente
69
Conciso en el contexto de Java es siempre hilarante. Eso printStackTracedebería devolver la cadena, dejando la decisión de si imprimirla o no al usuario :)
dmitry
35
La impresión printStackTrace en la consola es aceptable, pero al menos un getStackTrace que lo devuelve como una Cadena debería estar disponible de forma predeterminada
Mateus Viccari el
55
¿Qué parte de esto es conciso? Tienes que construir 2 objetos que existen para otro propósito que no sea poner el rastro de la pila en una cadena.
ArtOfWarfare
77
@dmitry Un método llamado printXXX()debería imprimir XXX.
Marqués de Lorne
2
@ Greg En realidad, eso ni siquiera fue sarcasmo, solo requirió una lectura clara de lo que dijo.
Andrew
225

Si está desarrollando para Android, una forma mucho más fácil es usar esto:

import android.util.Log;

String stackTrace = Log.getStackTraceString(exception); 

El formato es el mismo que getStacktrace, por ejemplo

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)
Vicky Kapadia
fuente
2
Gracias. En este caso, una fecha y una etiqueta no se escriben en cada fila como en printStackTrace ().
CoolMind
1
En realidad, Log.getStackTraceString en su núcleo utiliza los StringWriter y Printwriter mencionados anteriormente (D. Wroblewski).
MikeL
2
La forma más fácil de imprimirlo en el registro de Android es: Log.e("TAG", "My message", new Throwable());
Erick M. Sprengel
113

ADVERTENCIA: no incluye la causa (que generalmente es el bit útil)

public String stackTraceToString(Throwable e) {
    StringBuilder sb = new StringBuilder();
    for (StackTraceElement element : e.getStackTrace()) {
        sb.append(element.toString());
        sb.append("\n");
    }
    return sb.toString();
}
jqno
fuente
1
Optaría por una extensión de este enfoque si desea recortar la traza, por ejemplo, pasar un parámetro maxLines y solo agregar esa cantidad de líneas a la traza
Rich Seller
Faltan los paréntesis después de e.getStackTrace. Cadena estática pública stackTraceToString (Excepción e) {StringBuilder sb = new StringBuilder (); for (elemento StackTraceElement: e.getStackTrace ()) {sb.append (element.toString ()); sb.append ("<br />"); } return sb.toString (); }
rlc
2
No estoy seguro, pero creo que esto no imprimirá el seguimiento de la pila de la excepción de causa raíz.
apoorv020
77
Hagas lo que hagas, no recortes el rastro. Me ha sucedido muchas, muchas veces que miré un rastro de pila en los registros de GlassFish que tenían las partes útiles recortadas.
cayhorstmann
Use String.format("%n")algo similar en lugar de "\n"hacer felices a los lamers de DOS.
ceving
89

Para mí, la forma más limpia y fácil fue:

import java.util.Arrays;
Arrays.toString(e.getStackTrace());
Vladas Diržys
fuente
37
El código está limpio, pero la salida no. Tienes que hacer un .replaceAll (",", "\ n") al final. Sin embargo, pierde la sangría que printStackTrace propone.
furia el
44
Esto es útil cuando registra el seguimiento en una sola línea
webjockey
28
public static String getStackTrace(Throwable t) {
    StringWriter sw = new StringWriter();
    t.printStackTrace(new PrintWriter(sw));
    return sw.toString();
}
Akira Yamamoto
fuente
1
Hola, me gustaría señalar que la sección de comentarios de la respuesta que se cita señala que los objetos StringWritery PrintWriterdeberían ser closed ... (o supongo que solo PrintWriteres necesario cerrarlos, ya que cerrarlos también debería cerrarlos StringWriter)
Eric
8
¿Por inspirado te refieres a copiado? Solo lo pones en un método ... Leer las respuestas aquí es como ver "Groundhog Day"
Murmel
26

El siguiente código le permite obtener todo el stackTrace con un Stringformato, sin utilizar API como log4J o incluso 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.
}
dumonderic
fuente
1
ya, pero es un poco voluminoso, ¿no te parece? en cualquier marco de registro de proyectos de tamaño razonable es una necesidad por qué molestarse
mzzzzb
Este no está dando el mensaje de error al principio. aunque puede agregarse
kommradHomer
Es posible que desee utilizar un StringBuffer en lugar de una simple concatenación.
dedda1994
Esto es útil cuando no puede registrar el mensaje por razones de privacidad.
A1m
Sin embargo, tenga en cuenta que esta solución no registrará las causas internas
A1m
19

Aquí hay una versión que se puede copiar y pegar directamente en el código:

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

O, en un bloque de captura

} catch (Throwable t) {
    StringWriter sw = new StringWriter();
    t.printStackTrace(new PrintWriter(sw));
    logger.info("Current stack trace is:\n" + sw.toString());
}
rublo
fuente
esto continuará más allá del alcance de la función actual, es decir. donde Throwablese define el, ¿verdad?
n611x007
16
Arrays.toString(thrown.getStackTrace())

Es la forma más fácil de convertir el resultado en Cadena. Estoy usando esto en mi programa para imprimir el seguimiento de la pila.

LOGGER.log(Level.SEVERE, "Query Builder Issue Stack Trace : {0} ,Message : {1} objid {2}", new Object[]{Arrays.toString(e.getStackTrace()), e.getMessage(),objId});
Ramanan Durairaj
fuente
Funcionó para mí, ¡nada parece recortado! Más simple que otra respuesta también sin ninguna biblioteca externa, ¡gran respuesta!
Shaung Cheng
16

Imprima el seguimiento de la pila en a PrintStream, luego conviértalo a String:

// ...

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

    System.out.println(str);
}
Inhailed al horno
fuente
1
esta es la respuesta más directa
DaSqy Stc
11

Impresión de seguimiento de pila a cadena

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

Es útil cuando desea imprimir el seguimiento de la pila de subprocesos actual sin crear una instancia de Throwable, pero tenga en cuenta que crear un nuevo Throwabley obtener el seguimiento de la pila desde allí en realidad es más rápido y más barato que llamar Thread.getStackTrace.

Jarek Przygódzki
fuente
11

Kotlin

Extender la clase Throwable le dará la propiedad String error.stackTraceString:

val Throwable.stackTraceString: String
  get() {
    val sw = StringWriter()
    val pw = PrintWriter(sw)
    this.printStackTrace(pw)
    return sw.toString()
  }
vonox7
fuente
10
private String getCurrentStackTraceString() {
    StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
    return Arrays.stream(stackTrace).map(StackTraceElement::toString)
            .collect(Collectors.joining("\n"));
}
eddyrokr
fuente
1
Gracias eddyrkokr! Funciona perfecto para todas mis clases. ¡Lo acabo de agregar en la clase base de TestNG y recopila los rastros de pila uno por uno de todas mis pruebas!
Krzysztof Walczewski
9

Los disparos inteligentes en el primer conjunto de comentarios fueron muy divertidos, pero realmente dependen de lo que intentes hacer. Si aún no tiene la biblioteca correcta, entonces 3 líneas de código (como en la respuesta de D. Wroblewski) son perfectas. OTOH, si ya tiene la biblioteca apache.commons (como lo harán la mayoría de los proyectos grandes), entonces la respuesta de Amar es más corta. De acuerdo, puede llevarle diez minutos obtener la biblioteca e instalarla correctamente (menos de uno si sabe lo que está haciendo). Pero el tiempo corre, por lo que es posible que no tenga tiempo de sobra. Jarek Przygódzki tenía una advertencia interesante: "Si no necesita excepciones anidadas".

Pero ¿y si yo no necesito los seguimientos de pila completos, anidados y todo? En ese caso, el secreto es usar getFullStackTrace de apache.common (consulte http://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/exception/ExceptionUtils.html # getFullStackTrace% 28java.lang.Throwable% 29 )

Me salvó el tocino. Gracias, Amar, por la pista!

Tihamer
fuente
9

Código de 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 diferencia con las otras respuestas es que se usa autoFlush en el PrintWriter.

IvanRF
fuente
8

Sin java.io.*eso se puede hacer así.

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

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

Y luego la tracevariable contiene su rastro de pila. La salida también tiene la causa inicial, la salida es idéntica aprintStackTrace()

Ejemplo, printStackTrace()rendimientos:

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 tracecadena se mantiene, cuando se imprime enstdout

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)
Gala
fuente
5

Versión Scala

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

si está utilizando Java 8, intente esto

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

Puede encontrar el código para la getStackTrace()función proporcionado por Throwable.java:

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

y para StackTraceElementproveedores toString()como:

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

Así que solo únete StackTraceElementcon "\ n".

Pengcheng Zhang
fuente
¡Esta solución no respeta la sangría de la pestaña de seguimiento de pila, de lo contrario funciona muy bien!
krizajb
4

una exapansión en la respuesta de Gala que también incluirá las causas de la excepción:

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;
}
lino ido
fuente
4

La solución es convertir el stackTrace de la matriz al tipo de datos de cadena . Vea el siguiente ejemplo:

import java.util.Arrays;

try{

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

Con Java 8 Stream API puedes hacer algo como esto:

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

Tomará una variedad de elementos traza de la pila, los convertirá en una cadena y se unirá en una cadena multilínea.

ivanjermakov
fuente
2
Esta solución está eliminando el mensaje e ignorando todo rastro de los padres
judovana
3

Mi línea para convertir el seguimiento de la pila a la cadena de varias líneas adjunta:

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

Fácil de pasar al registrador "tal cual".

Andrey Lebedenko
fuente
Obtienes algo diferente de printStackTrace()Aquí perderás: 1) Excepción que se lanzó; 2) Causas y su stacktrace
valijon
La diferencia es bastante esperada, ya que la conversión de printStackTrace()nunca fue parte de la pregunta.
Andrey Lebedenko
2

Si no desea utilizar una biblioteca externa y no está desarrollando para Android , puede crear un método de 'extensión' como este:

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();
}
Kapé
fuente
2

Antigua pregunta, pero me gustaría agregar el caso especial en el que no desea imprimir toda la pila , eliminando algunas partes que realmente no le interesan, excluyendo ciertas clases o paquetes.

En lugar de PrintWriterusar 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);

Donde la SelectivePrintWriterclase está dada por:

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

Tenga en cuenta que esta clase se puede adaptar fácilmente para filtrar por Regex containsu otros criterios. También tenga en cuenta que depende de los Throwabledetalles de implementación (no es probable que cambie, pero aún así).

MarcG
fuente
1
Sí, esto funciona bien y en realidad es una idea bastante útil.
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);
        }
    }
}

Cuando ejecutas el programa, la salida será algo similar:

java.lang.ArithmeticException: / by zero
at PrintStackTrace.main(PrintStackTrace.java:9)
Prakhar Nigam
fuente
1

Me pregunto por qué nadie mencionó ExceptionUtils.getStackFrames(exception)

Para mí es la forma más conveniente de volcar stacktrace con todas sus causas hasta el final:

String.join("\n", ExceptionUtils.getStackFrames(exception));
Andrey Sarul
fuente
0

Advertencia: Esto puede estar un poco fuera de tema, pero bueno ...;)

No sé cuál fue la razón original de los carteles para querer el seguimiento de la pila como cadena en primer lugar. Cuando la traza de la pila debería terminar en un REGISTRO SLF4J / Logback, pero no se produjo ni se debe hacer una excepción, esto es lo que hago:

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
}

Me gusta porque no requiere una biblioteca externa (que no sea su backend de registro, que de todos modos estará en su lugar la mayor parte del tiempo, por supuesto).

Alexander Wessel
fuente