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


当前回答

将堆栈跟踪打印到字符串

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更快、更便宜。

其他回答

Scala版本

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

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

我不知道最初的海报是什么原因让堆栈跟踪成为字符串。当堆栈跟踪应该以SLF4J/Logback LOG结尾,但没有或应该抛出异常时,我会这样做:

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

如果你不想使用外部库,而且你不是为Android开发的,你可以创建这样的“扩展”方法:

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

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

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

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

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

例如,printStackTrace()产生:

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时,跟踪字符串保持

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)