Como converter um rastreamento de pilha em uma string?


1502

Qual é a maneira mais fácil de converter o resultado Throwable.getStackTrace()em uma sequência que representa o rastreamento de pilha?


6
Como a resposta do jqno realmente usa o método Throwable.getStackTrace () que você especificou na sua pergunta, enquanto Brian não. Ele usa Throwable.printStackTrace ().
Stijn de Witt

10
Quase todos os projetos Java devem incluir o Apache commons-lang. Inclui muitos métodos de conveniência que implementam necessidades de desenvolvimento extremamente comuns.
Russell Silva

20
@StijndeWitt Essas três linhas de código quase certamente precisam ser fatoradas fora do lugar que você as chamou. Como você não sabe onde colocá-los, eles entrarão na sua caixa de ferramentas utilitárias com todos os outros trechos úteis. Bingo! você acabou de reinventar a goiaba / commons-lang / tanto faz ... só que não tão bem. Importe uma biblioteca de utilitários sensata e salve a reinvenção da roda. O verdadeiro sinal de um novato é pensar que você pode fazer um trabalho melhor do que os escritores da biblioteca.
Andrew Spencer

6
1. A goiaba possui - Throwables.getStackTraceAsString (e) 2. Apache Commons Lang - ExceptionUtils.getFullStackTrace 3. Escreva nossos próprios métodos personalizados
user2323036

8
@AndrewSpencer Não entendo por que vocês tentam atacar o StijndeWitt por querer conseguir isso com um pequeno trecho. Realmente não há muito perigo em escrever um método minúsculo de utilidade (não o vejo como "ARROGÂNCIA MAIS ESSA oh nãooooo !! ele pensa que é melhor que o Apache !!"). Existem muitos projetos, especialmente em linguagens JVM não Java, que realmente não desejam incluir o Guava ou o Commons Lang apenas para registrar um rastreamento de pilha. Escrevo as bibliotecas Scala & Clojure e certamente não fará do Apache Commons Lang uma dependência transitiva apenas por um método.
Jm0 2/16

Respostas:


1039

Pode-se usar o seguinte método para converter um Exceptionrastreamento de pilha em String. Esta classe está disponível no Apache commons-lang, que é a biblioteca dependente mais comum com muitos fontes abertas populares

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


88
@Stijn - para ser justo (eu escrevi a maior resposta atual votou abaixo) vale a pena olhar para commons-lang para muito mais funcionalidade
Brian Agnew

54
@StijndeWitt Commons Lang é bastante comum. Já está presente na maioria dos meus projetos / projetos em andamento.
precisa saber é o seguinte

16
@ Hugo, obrigado, ia usar o StringWriter para evitar adicionar uma nova biblioteca - acontece que já é uma dependência de três das minhas dependências. De resto, verifique se já o possui.
Nathanial

33
@ MartinAsenov - seguindo sua lógica, você nunca usaria uma biblioteca assim, usaria? Você não o usaria a menos que já o esteja usando?
Brian Agnew

16
FYI, o pacote foi alterado ea classe está agora em: org.apache.commons.lang3.exception.ExceptionUtils.
schmmd

2201

Use Throwable.printStackTrace(PrintWriter pw)para enviar o rastreamento de pilha para um gravador apropriado.

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 você não gosta de incluir uma biblioteca externa para algo tão pequeno e simples como esse, use esta resposta.
Stijn de Witt

8
Isso apara o rastreamento da pilha, da mesma forma que printStackTrace (). Todas as exceções na pilha são visíveis, mas para cada exceção a pilha pode ser cortada. Qualquer pessoa que exija o rastreamento inteiro deve considerar isso.
Laila Agaev #

5
Acontece que isso é exatamente o que o método ExceptionUtils.getStackTrace () do apache faz. Quase ao pé da letra, na verdade.
ticktock

6
@BrianAgnew, você não deveria fechar o StringWritere PrintWriter?
Muhammad Gelbana

13
@BrianAgnew, obrigado pela resposta. Isso me fez curioso e aqui está o que eu encontrei: stackoverflow.com/questions/14542535/...
Muhammad Gelbana

459

Isso deve funcionar:

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

69
Conciso no contexto java é sempre colunar. Isso printStackTracedeve apenas retornar a string, deixando a decisão de imprimí-la ou não para o usuário :) #
395

35
impressão printStackTrace no console é aceitável, mas pelo menos um getStackTrace devolvê-lo como uma String deve estar disponível por padrão
Mateus Viccari

5
Que parte disso é concisa? Você precisa construir 2 objetos que existem apenas para colocar o rastreamento de pilha em uma sequência.
ArtOfWarfare 14/11/16

7
@dmitry Um método chamado printXXX()deve imprimir XXX.
Marquês de Lorne

2
@ Greg Na verdade, isso não foi nem sarcasmo, apenas exigiu uma leitura clara do que ele disse.
Andrew

224

Se você está desenvolvendo para Android, uma maneira muito mais fácil é usar isso:

import android.util.Log;

String stackTrace = Log.getStackTraceString(exception); 

O formato é o mesmo que getStacktrace, por exemplo

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
Obrigado. Nesse caso, uma data e uma tag não são gravadas em todas as linhas, como em printStackTrace ().
CoolMind 01/06

1
Na verdade, o Log.getStackTraceString em seu núcleo usa o StringWriter e o Printwriter mencionados acima (de D. Wroblewski).
MikeL

2
A maneira mais fácil de apenas imprimi-lo no log Android é: Log.e("TAG", "My message", new Throwable());
Erick M. Sprengel


113

AVISO: Não inclui a causa (que geralmente é a 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();
}

1
Eu iria com uma extensão dessa abordagem se você deseja cortar o traço, por exemplo, passar um maxLines parâmetro e apenas acrescentar que muitas linhas ao traço
Rich Seller

Os parênteses após e.getStackTrace estão ausentes. public static String stackTraceToString (Exceção e) {StringBuilder sb = new StringBuilder (); para (elemento StackTraceElement: e.getStackTrace ()) {sb.append (element.toString ()); sb.append ("<br />"); } retornar sb.toString (); }
rlc 20/10/11

2
Não tenho certeza, mas acho que isso não imprimirá o rastreamento de pilha da exceção de causa raiz.
Apoorv020

7
Faça o que fizer, não apare o rastro. Aconteceu comigo muitas e muitas vezes que observei um rastreamento de pilha nos logs do GlassFish que tinha as partes úteis cortadas.
Cayhorstmann # 12/12

Use String.format("%n")ou algo semelhante em vez de "\n"fazer os lamers do DOS felizes.
ceving

89

Para mim, a maneira mais limpa e fácil era:

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

37
O código está limpo, mas a saída não está. Você precisa fazer um .replaceAll (",", "\ n") no final. No entanto, você perde o recuo proposto pelo printStackTrace.
Fury

4
Isto é útil quando você log trace em uma única linha
webjockey

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

1
Olá, gostaria apenas de salientar que a seção de comentários da resposta citada indica que os objetos StringWritere PrintWriterdevem ser closed .... (ou acho que apenas as PrintWriternecessidades precisam ser fechadas, pois o fechamento deve fechar StringWritertambém)
Eric Eric

8
Por inspirado você quer dizer copiado? Você só colocá-lo em um método ... Lendo as respostas aqui é como assistir a "Groundhog Day"
Murmel

26

O código a seguir permite obter o stackTrace inteiro com um Stringformato, sem usar APIs como log4J ou mesmo 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
sim, mas é um pouco volumoso, você não acha? em qualquer estrutura de log projeto de tamanho razoável é uma obrigação então porque se preocupar
mzzzzb

Este não está dando a mensagem de erro no começo. pode ser adicionado embora
kommradHomer 13/11

Você pode querer usar um StringBuffer em vez de uma concatenação simples.
precisa saber é o seguinte

Isso é útil quando você não tem permissão para registrar a mensagem por motivos de privacidade.
19m em A1m

Esteja ciente no entanto que esta solução não irá registrar as causas internas
A1m

19

Aqui está uma versão que pode ser copiada diretamente no 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());

Ou, em um bloco de captura

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

isso continuará além do escopo da função atual, ie. onde o Throwableé definido, certo?
N611x007

16
Arrays.toString(thrown.getStackTrace())

É a maneira mais fácil de converter o resultado em String? Estou usando isso no meu programa para imprimir o rastreamento de pilha

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

Trabalhou para mim, nada parece ser aparado! Mais simples que outras respostas também sem qualquer biblioteca externa, ótima resposta!
Shaung Cheng

16

Imprima o rastreamento de pilha em a PrintStreame converta-o em String:

// ...

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

    System.out.println(str);
}

1
esta é a resposta mais direta
DaSqy Stc

11

Rastreio da pilha de impressão em sequência

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

É útil quando você deseja imprimir o rastreamento atual da pilha de encadeamentos sem criar uma instância de Throwable- mas observe que criar novos Throwablee obter rastreamento de empilhamento a partir daí é realmente mais rápido e mais barato do que chamar Thread.getStackTrace.


11

Kotlin

Estender a classe Throwable fornecerá a propriedade 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
Obrigado eddyrkokr! Funciona perfeito para todas as minhas classes. Acabei de adicioná-lo na classe TestNG base e ele coleta os stacktraces um por um em todos os meus testes!
Krzysztof Walczewski

9

O sniping inteligente no primeiro conjunto de comentários foi muito divertido, mas realmente depende do que você está tentando fazer. Se você ainda não possui a biblioteca correta, três linhas de código (como na resposta de D. Wroblewski) são perfeitas. OTOH, se você já possui a biblioteca apache.commons (como a maioria dos grandes projetos), a resposta de Amar é mais curta. OK, você pode levar dez minutos para obter a biblioteca e instalá-la corretamente (menos de um, se você souber o que está fazendo). Mas o tempo está passando, então você pode não ter tempo de sobra. Jarek Przygódzki tinha uma ressalva interessante - "Se você não precisa de exceções aninhadas".

Mas e se eu não precisar dos rastreamentos de pilha completos, aninhados e tudo? Nesse caso, o segredo é usar o getFullStackTrace do 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 )

Isso salvou meu bacon. Obrigado, Amar, pela dica!


9

Código do 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();
}

A diferença com as outras respostas é que ela usa autoFlush no PrintWriter.


8

Sem java.io.*isso pode ser feito assim.

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

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

E então a tracevariável mantém seu rastreamento de pilha. A saída também mantém a causa inicial, a saída é idêntica àprintStackTrace()

Exemplo, printStackTrace()produz:

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)

A traceString mantém, quando impressa emstdout

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

Versão Scala

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

5

se você estiver usando o Java 8, tente isso

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

você pode encontrar o código para a getStackTrace()função fornecida por Throwable.java:

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

e StackTraceElement, para isso, fornece toString()como:

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

Então, junte-se ao StackTraceElement"\ n".


Esta solução não respeita o recuo da guia de rastreamento de pilha, caso contrário, funciona muito bem!
Krizajb 28/0918

4

uma exapansão na resposta de Gala que também incluirá as causas da exceção:

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

A solução é converter o stackTrace da matriz em tipo de dados string . Veja o seguinte exemplo:

import java.util.Arrays;

try{

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

4

Com a Java 8 Stream API, você pode fazer algo assim:

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

Ele pegará uma matriz de elementos de rastreio de pilha, os converterá em sequência e ingressará em sequência multilinha.


2
Esta solução é a remoção de mensagem e está ignorando todos os vestígios dos pais
judovana

3

Meu oneliner para converter o rastreamento de pilha na sequência de várias linhas incluída:

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

Fácil de passar para o logger "como está".


Você obtém algo que difere printStackTrace()daqui. Você perderá: 1) Exceção que foi lançada; 2) Causas e seu rastreamento de pilha
valijon

A diferença é bastante esperada, uma vez que a conversão do printStackTrace()nunca fazia parte da questão.
Andrey Lebedenko

2

Se você não quiser usar uma biblioteca externa e não estiver desenvolvendo para o Android , poderá criar um método de 'extensão' 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();
}

2

Pergunta antiga, mas gostaria de adicionar um caso especial em que você não deseja imprimir toda a pilha , removendo algumas partes nas quais não está realmente interessado, excluindo determinadas classes ou pacotes.

Em vez de PrintWriterusar um 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);

Onde a SelectivePrintWriterclasse é 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);
        }
    }
}

Observe que esta classe pode ser facilmente adaptada para filtrar por Regex containsou outros critérios. Observe também que depende dos Throwabledetalhes da implementação (provavelmente não será alterado, mas ainda).


1
Sim, isso funciona bem e é realmente uma ideia bastante útil.
precisa saber é o seguinte

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 você executa o programa, a saída será algo semelhante:

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

1

Eu me pergunto por que ninguém mencionou ExceptionUtils.getStackFrames(exception)

Para mim, é a maneira mais conveniente de despejar o rastreamento de pilha com todas as suas causas até o fim:

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

0

Aviso: Este tópico pode ser um pouco estranho, mas tudo bem ...;)

Não sei qual era o motivo original dos pôsteres para querer que o rastreamento da pilha fosse string em primeiro lugar. Quando o rastreamento da pilha deve terminar em um SLF4J / Logback LOG, mas nenhuma exceção foi ou deve ser lançada, eis o que eu faço:

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
}

Gosto porque não requer uma biblioteca externa (exceto o seu back-end de log, que estará disponível na maioria das vezes, é claro).


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.