关于Java:如何将堆栈跟踪转换为字符串?

How can I convert a stack trace to a string?

Throwable.getStackTrace()的结果转换为描述stacktrace的字符串最简单的方法是什么?


使用Throwable.printStackTrace(PrintWriter pw)将堆栈跟踪发送到适当的编写器。

1
2
3
4
5
6
7
8
9
10
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);


可以使用以下方法将Exception堆栈跟踪转换为String。这个类在ApacheCommonsLang中可用,它是最常见的依赖库,具有许多流行的开放源代码。

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


这应该有效:

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


如果您正在为Android开发,一个更简单的方法是使用它:

1
2
3
import android.util.Log;

String stackTrace = Log.getStackTraceString(exception);

格式与GetStackTrace相同,例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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)


番石榴的Throwables

如果你有实际的Throwable实例,google guava会提供Throwables.getStackTraceAsString()

例子:

1
String s = Throwables.getStackTraceAsString ( myException ) ;

警告:不包括原因(通常是有用的位!)

1
2
3
4
5
6
7
8
9
public String stackTraceToString(Throwable e) {
    StringBuilder sb = new StringBuilder();
    for (StackTraceElement element : e.getStackTrace()) {
        sb.append(element.toString());
        sb.append("
"
);
    }
    return sb.toString();
}


对我来说,最干净和最简单的方法是:

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


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


以下代码允许您使用String格式获取整个stacktrace,而不使用log4j甚至java.util.Logger等API:

1
2
3
4
5
6
7
8
9
10
catch (Exception e) {
    StackTraceElement[] stack = e.getStackTrace();
    String exception ="";
    for (StackTraceElement s : stack) {
        exception = exception + s.toString() +"
\t\t"
;
    }
    System.out.println(exception);
    // then you can send the exception string to a external file.
}


下面是一个可直接复制到代码中的版本:

1
2
3
4
5
6
7
8
9
10
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:
"
+ sw.toString());

或者,在接球区

1
2
3
4
5
6
} catch (Throwable t) {
    StringWriter sw = new StringWriter();
    t.printStackTrace(new PrintWriter(sw));
    logger.info("Current stack trace is:
"
+ sw.toString());
}


将堆栈跟踪打印到字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
        }
    }
}

如果您想打印当前线程堆栈跟踪而不创建Throwable的实例,这是很有用的,但是请注意,创建新的Throwable并从中获取堆栈跟踪实际上比调用Thread.getStackTrace更快、更便宜。


将stacktrace打印到打印流,然后将其转换为字符串

1
2
3
4
5
6
7
8
9
10
// ...

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

    System.out.println(str);
}

1
2
3
4
5
6
private String getCurrentStackTraceString() {
    StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
    return Arrays.stream(stackTrace).map(StackTraceElement::toString)
            .collect(Collectors.joining("
"
));
}


1
Arrays.toString(thrown.getStackTrace())

是将结果转换为字符串的最简单方法我在程序中使用这个来打印堆栈跟踪

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


来自ApacheCommonsLang3.4(JavaDoc)的代码:

1
2
3
4
5
6
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();
}

与其他答案不同的是,它在PrintWriter上使用了autoFlush


第一组评论中巧妙的剪接非常有趣,但这真的取决于你想做什么。如果您还没有正确的库,那么3行代码(如D.Wroblewski的答案)是完美的。Otoh,如果您已经有了apache.commons库(大多数大型项目都有),那么Amar的答案就更短了。好的,您可能需要10分钟才能找到库并正确安装它(如果您知道自己在做什么,则不到一分钟)。但是时钟在滴答滴答地响,所以你可能没有时间休息。JarekPrzyg_dzki有一个有趣的警告——"如果不需要嵌套异常的话"。

但是如果我确实需要完整的堆栈跟踪,嵌套的和全部的呢?在这种情况下,秘密是使用apache.common的getfullstacktrace(请参见http://common s.apache.org/proper/common s lang/javadocs/api-2.6/org/apache/common s/lang/exception/exceptionutils.html getfullstacktrace%28java.lang.throwable%29)

它救了我的培根。谢谢,阿马尔,给我提示!


科特林

扩展throwable类将为您提供字符串属性error.stackTraceString

1
2
3
4
5
6
7
val Throwable.stackTraceString: String
  get() {
    val sw = StringWriter()
    val pw = PrintWriter(sw)
    this.printStackTrace(pw)
    return sw.toString()
  }

如果没有java.io.*,可以这样做。

1
2
3
4
5
6
7
String trace = e.toString() +"
"
;                    

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

然后,trace变量保存堆栈跟踪。输出也包含初始原因,输出与printStackTrace()相同。

例如,printStackTrace()产生:

1
2
3
4
5
6
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)

当打印到stdout时,trace字符串保持不变。

1
2
3
4
5
6
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)

如果您正在使用Java 8,请尝试

1
2
3
4
Arrays.stream(e.getStackTrace())
                .map(s->s.toString())
                .collect(Collectors.joining("
"
));

您可以找到throwable.java提供的getstacktrace()函数的代码,如下所示:

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

对于stacktraceelement,它提供程序将字符串设置为:

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

所以只需将stacktraceelement与


对Gala的回答的一个严格审查,也将包括例外的原因:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private String extrapolateStackTrace(Exception ex) {
    Throwable e = ex;
    String trace = e.toString() +"
"
;
    for (StackTraceElement e1 : e.getStackTrace()) {
        trace +="\t at" + e1.toString() +"
"
;
    }
    while (e.getCause() != null) {
        e = e.getCause();
        trace +="Cause by:" + e.toString() +"
"
;
        for (StackTraceElement e1 : e.getStackTrace()) {
            trace +="\t at" + e1.toString() +"
"
;
        }
    }
    return trace;
}

我的OneLiner将堆栈跟踪转换为封闭的多行字符串:

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

很容易"照原样"传递给记录器。


解决方案是将数组的stacktrace转换为字符串数据类型。请参见以下示例:

1
2
3
4
5
6
7
8
import java.util.Arrays;

try{

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

如果您不想使用外部库,而不是为Android开发,您可以创建一个"扩展"方法,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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("
"
);

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

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

旧问题,但我想添加一个特殊情况,您不想打印所有堆栈,通过删除一些您实际上不感兴趣的部分,不包括某些类或包。

SelectivePrintWriter代替PrintWriter

1
2
3
4
5
6
7
8
// 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);

其中SelectivePrintWriter类由以下公式给出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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);
        }
    }
}

请注意,这一类可以很容易地通过regex、contains或其他标准过滤掉。还请注意,这取决于Throwable的实施细节(不太可能更改,但仍然如此)。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 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);
        }
    }
}

运行程序时,输出将类似:

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

斯卡拉版本

1
2
3
4
5
6
def stackTraceToString(e: Exception): String = {
  import java.io.PrintWriter
  val sw = new StringWriter()
  e.printStackTrace(new PrintWriter(sw))
  sw.toString
}

警告:这可能有点离题,但哦,好吧…;)

我不知道最初的海报为什么要把堆栈跟踪作为字符串放在首位。当堆栈跟踪应该在slf4j/logback日志中结束时,但没有异常发生或应该被抛出,我要做的是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
}

我喜欢它,因为它不需要外部库(除了您的日志记录后端,当然,大多数情况下,它都会在适当的位置)。


几乎没有选择

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

  • 使用google guava libString stackTrace = Throwables.getStackTraceAsString ( myException ) ;

  • org.apache.commons.lang.exception.exceptionutils.getstacktrace(可丢弃)


  • 不久前我为这个写了一些方法,所以我想为什么不把我的两分钱扔到这个上面。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    /** @param stackTraceElements The elements to convert
     * @return The resulting string */

    public static final String stackTraceElementsToStr(StackTraceElement[] stackTraceElements) {
        return stackTraceElementsToStr(stackTraceElements,"
    "
    );
    }

    /** @param stackTraceElements The elements to convert
     * @param lineSeparator The line separator to use
     * @return The resulting string */

    public static final String stackTraceElementsToStr(StackTraceElement[] stackTraceElements, String lineSeparator) {
        return stackTraceElementsToStr(stackTraceElements, lineSeparator,"");
    }

    /** @param stackTraceElements The elements to convert
     * @param lineSeparator The line separator to use
     * @param padding The string to be used at the start of each line
     * @return The resulting string */

    public static final String stackTraceElementsToStr(StackTraceElement[] stackTraceElements, String lineSeparator, String padding) {
        String str ="";
        if(stackTraceElements != null) {
            for(StackTraceElement stackTrace : stackTraceElements) {
                str += padding + (!stackTrace.toString().startsWith("Caused By") ?"\tat" :"") + stackTrace.toString() + lineSeparator;
            }
        }
        return str;
    }

    /** @param stackTraceElements The elements to convert
     * @return The resulting string */

    public static final String stackTraceCausedByElementsOnlyToStr(StackTraceElement[] stackTraceElements) {
        return stackTraceCausedByElementsOnlyToStr(stackTraceElements,"
    "
    );
    }

    /** @param stackTraceElements The elements to convert
     * @param lineSeparator The line separator to use
     * @return The resulting string */

    public static final String stackTraceCausedByElementsOnlyToStr(StackTraceElement[] stackTraceElements, String lineSeparator) {
        return stackTraceCausedByElementsOnlyToStr(stackTraceElements, lineSeparator,"");
    }

    /** @param stackTraceElements The elements to convert
     * @param lineSeparator The line separator to use
     * @param padding The string to be used at the start of each line
     * @return The resulting string */

    public static final String stackTraceCausedByElementsOnlyToStr(StackTraceElement[] stackTraceElements, String lineSeparator, String padding) {
        String str ="";
        if(stackTraceElements != null) {
            for(StackTraceElement stackTrace : stackTraceElements) {
                str += (!stackTrace.toString().startsWith("Caused By") ?"" : padding + stackTrace.toString() + lineSeparator);
            }
        }
        return str;
    }

    /** @param e The {@link Throwable} to convert
     * @return The resulting String */

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

    /** @param e The {@link Throwable} to convert
     * @param lineSeparator The line separator to use
     * @return The resulting String */

    public static final String throwableToStrNoStackTraces(Throwable e, String lineSeparator) {
        return throwableToStrNoStackTraces(e, lineSeparator,"");
    }

    /** @param e The {@link Throwable} to convert
     * @param lineSeparator The line separator to use
     * @param padding The string to be used at the start of each line
     * @return The resulting String */

    public static final String throwableToStrNoStackTraces(Throwable e, String lineSeparator, String padding) {
        if(e == null) {
            return"null";
        }
        String str = e.getClass().getName() +":";
        if((e.getMessage() != null) && !e.getMessage().isEmpty()) {
            str += e.getMessage() + lineSeparator;
        } else {
            str += lineSeparator;
        }
        str += padding + stackTraceCausedByElementsOnlyToStr(e.getStackTrace(), lineSeparator, padding);
        for(Throwable suppressed : e.getSuppressed()) {
            str += padding + throwableToStrNoStackTraces(suppressed, lineSeparator, padding +"\t");
        }
        Throwable cause = e.getCause();
        while(cause != null) {
            str += padding +"Caused by:" + lineSeparator + throwableToStrNoStackTraces(e.getCause(), lineSeparator, padding);
            cause = cause.getCause();
        }
        return str;
    }

    /** @param e The {@link Throwable} to convert
     * @return The resulting String */

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

    /** @param e The {@link Throwable} to convert
     * @param lineSeparator The line separator to use
     * @return The resulting String */

    public static final String throwableToStr(Throwable e, String lineSeparator) {
        return throwableToStr(e, lineSeparator,"");
    }

    /** @param e The {@link Throwable} to convert
     * @param lineSeparator The line separator to use
     * @param padding The string to be used at the start of each line
     * @return The resulting String */

    public static final String throwableToStr(Throwable e, String lineSeparator, String padding) {
        if(e == null) {
            return"null";
        }
        String str = padding + e.getClass().getName() +":";
        if((e.getMessage() != null) && !e.getMessage().isEmpty()) {
            str += e.getMessage() + lineSeparator;
        } else {
            str += lineSeparator;
        }
        str += padding + stackTraceElementsToStr(e.getStackTrace(), lineSeparator, padding);
        for(Throwable suppressed : e.getSuppressed()) {
            str += padding +"Suppressed:" + throwableToStr(suppressed, lineSeparator, padding +"\t");
        }
        Throwable cause = e.getCause();
        while(cause != null) {
            str += padding +"Caused by:" + lineSeparator + throwableToStr(e.getCause(), lineSeparator, padding);
            cause = cause.getCause();
        }
        return str;
    }

    例子:

    1
    2
    3
    4
    5
    6
    7
    try(InputStream in = new FileInputStream(file)) {
        ...
    } catch(IOException e) {
        String exceptionToString = throwableToStr(e);
        someLoggingUtility.println(exceptionToString);
        ...
    }

    印刷品:

    1
    2
    3
    4
    5
    java.io.FileNotFoundException: C:\test.txt (The system cannot find the file specified)
        at java.io.FileInputStream.open0(Native Method)
        at java.io.FileInputStream.open(Unknown Source)
        at java.io.FileInputStream.<init>(Unknown Source)
        at com.gmail.br45entei.Example.main(Example.java:32)