java - ¿Cómo puedo convertir un trazado de pila en una cadena?


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


Answers






Utilice Throwable.printStackTrace (PrintWriter pw) para enviar el seguimiento de la pila a un escritor adecuado.

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



Esto debería funcionar:

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



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)



ADVERTENCIA: no incluye causa (¡que generalmente es la parte ú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();
}






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

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



El siguiente código le permite obtener el stackTrace completo con un formato de String , 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.
}



Inspirado por @Brian Agnew:

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



Aquí hay una versión que puede copiarse 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 catch

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



Imprimir el seguimiento de la pila en una 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 la creación de nuevos Throwable y la obtención del seguimiento de la pila desde allí es más rápida y económica que llamar a Thread.getStackTrace .




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

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

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

Y luego la variable de trace contiene su traza de pila. La salida también tiene la causa inicial, la salida es idéntica a printStackTrace()

Ejemplo, printStackTrace() produce:

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)

El trace String se mantiene, cuando se imprime en stdout

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)



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 usa autoFlush en PrintWriter .




Kotlin

La extensión de 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()
  }



El francotirador inteligente en el primer conjunto de comentarios fue muy divertido, pero realmente depende de lo que estás tratando de hacer. Si todavía no tienes la biblioteca correcta, entonces 3 líneas de código (como en la respuesta de D. Wroblewski) son perfectas. OTOH, si ya tienes 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 tomar diez minutos obtener la biblioteca e instalarla correctamente (menos de uno si sabe lo que está haciendo). Pero el reloj sigue corriendo, 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 necesito los rastros de pila completos, anidados y todo? En ese caso, el secreto es usar getFullStackTrace de apache.common (ver http://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/exception/ExceptionUtils.html # getFullStackTrace% 28java.lang.Throwable% 29 )

Salvó mi tocino. Gracias, Amar, por la pista!




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



una exageració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;
}



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



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

En lugar de un PrintWriter use un 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 clase SelectivePrintWriter 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, contains u otros criterios. También tenga en cuenta que depende de los detalles de implementación Throwable (no es probable que cambie, pero aún así).