如何将堆栈跟踪转换为字符串?

1708

如何最简单地将Throwable.getStackTrace()的结果转换为描述堆栈跟踪的字符串?


8
因为jqno的回答实际上使用了你在问题中指定的Throwable.getStackTrace()方法,而Brian没有使用它。他改用了Throwable.printStackTrace()方法。 - Stijn de Witt
10
几乎每个 Java 项目都应该包含 Apache commons-lang。它包含了许多实现极为常见的开发需求的方便方法。 - Russell Silva
20
这三行代码几乎肯定需要从您调用它们的位置中分离出来。由于您不知道将它们放在哪里,因此它们将与所有其他有用的片段一起放在您的实用工具箱中。瞧!你刚刚重新发明了guava / commons-lang /等等……只是没有那么好。相反,导入一个明智的实用程序库,并节省重新发明轮子。真正的新手标志是认为您可以比库编写者做得更好。 - Andrew Spencer
9
  1. Guava库提供了Throwables.getStackTraceAsString(e)方法。
  2. Apache Commons Lang库提供了ExceptionUtils.getFullStackTrace方法。
  3. 我们可以编写自己的自定义方法。
- user2323036
16
我不明白为什么你们要这样努力地抨击StijndeWitt想通过一些小片段来实现这个目标。编写一个小型实用程序并没有什么危险性(我不认为他是“绝对自大,哦不!他认为自己比Apache优秀!”)。有很多项目,特别是在非Java JVM语言中,真的不想包含Guava或Commons Lang只为了记录堆栈跟踪。我编写Scala和Clojure库,肯定不会为了一个方法而将Apache Commons Lang作为传递性依赖项。 - jm0
显示剩余7条评论
32个回答

24

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

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

或者,在catch块中

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

21
Arrays.toString(thrown.getStackTrace())

将结果转换成字符串的最简单方法,我在我的程序中使用它来打印堆栈跟踪。

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

20

Kotlin >= 1.4

Throwable上使用内置函数stackTraceToString()

Kotlin < 1.4

扩展Throwable类将给您提供字符串属性error.stackTraceString

val Throwable.stackTraceString: String
  get() {
    val sw = StringWriter()
    val pw = PrintWriter(sw)
    this.printStackTrace(pw)
    return sw.toString()
  }

从 Kotlin 1.4 开始,现在有一个内置的 API 来执行此操作 https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/stack-trace-to-string.html - Aguragorn

14

如果你正在使用Java 8,尝试这个:

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

你可以在 Throwable.java 提供的 getStackTrace() 函数中找到代码:

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

对于 StackTraceElement,它提供了以下的toString()方法:

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

所以只需将StackTraceElement与"\n"连接即可。


如果你想要限制它,你可以在流上使用 limit(5) 方法。 - Maik

13

将堆栈跟踪打印为字符串

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


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

10

代码来源于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();
}

与其他答案的区别在于,它使用了PrintWriter上的autoFlush


9
第一组评论中聪明的抨击非常有趣,但这实际上取决于你想要做什么。 如果你还没有正确的库,那么像D. Wroblewski的答案中的3行代码就足够了。 另一方面,如果你已经有了apache.commons库(大多数大型项目都会有),那么Amar的答案就更短了。 好吧,可能需要花费十分钟来获取库并正确安装它(如果你知道你在做什么,则不到一分钟)。但时间正在流逝,所以你可能没有多余的时间。 Jarek Przygódzki提出了一个有趣的警告--"如果你不需要嵌套异常"。
但是,如果我确实需要完整的堆栈跟踪,包括所有嵌套的内容怎么办?在这种情况下,秘密是使用apache.common的getFullStackTrace(请参见http://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/exception/ExceptionUtils.html#getFullStackTrace%28java.lang.Throwable%29
它救了我的麻烦。感谢阿玛尔的提示!

8

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

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

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

然后trace变量将保存您的堆栈跟踪信息。输出还包含初始原因,输出与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)
trace 字符串保存了当被打印到 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)

8

使用Java 8的Stream API,您可以进行以下操作:

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

它将接收一个堆栈跟踪元素的数组,将它们转换为字符串并连接成多行字符串。

8
此解决方案将删除信息并忽略所有父级追踪。 - judovana

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接