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


1501

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


6
因为jqno的答案实际上使用了您在问题中指定的Throwable.getStackTrace()方法,而Brian却没有。他改用Throwable.printStackTrace()。
Stijn de Witt

10
几乎每个Java项目都应包含Apache commons-lang。它包括许多实现极其常见的开发需求的便捷方法。
罗素·席尔瓦

20
@StijndeWitt这三行代码几乎肯定需要从您调用它们的地方考虑在内。由于您不知道将它们放在哪里,因此它们会与所有其他有用的片段一起放入您的实用程序工具箱。答对了!您刚刚重新发明了番石榴/ commons-lang /等等...只是不太好。而是导入一个明智的实用程序库,并省去重新发明轮子的麻烦。新手的真正标志是认为您可以比图书馆作家做得更好。
安德鲁·斯宾塞

6
1.番石榴具有-Throwables.getStackTraceAsString(e)2. Apache Commons Lang-ExceptionUtils.getFullStackTrace 3.编写我们自己的自定义方法
user2323036 2014年

8
@AndrewSpencer我不明白为什么你们这么努力地抨击StijndeWitt想要用一些小片段来实现这一目标。编写一个小的实用程序方法确实没有太大的危险(我不认为它是“ SHEER ARROGANCE oh nooooo !!他认为自己比Apache更好!”)。有很多项目,尤其是使用非Java JVM语言的项目,它们确实不想仅仅为了记录堆栈跟踪就包括Guava或Commons Lang。我写了Scala&Clojure库,当然不会仅仅因为一种方法就使Apache Commons Lang成为传递依赖。
jm0

Answers:


1039

可以使用以下方法将Exception堆栈跟踪转换为String。此类可在Apache commons-lang中找到,它是最常见的依赖库,具有许多流行的开源代码

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


88
@Stijn-公平的说(我在下面写下了目前投票最高的答案),值得关注commons-lang以获得更多功能
Brian Agnew 2012年

54
@StijndeWitt Commons Lang很普通。我在工作的大多数项目/项目中都已经存在它。
WhyNotHugo 2012年

16
@Hugo谢谢,原打算使用StringWriter避免添加新库-事实证明它已经是我依赖项中的3个。因此,剩下的,检查是否已经拥有。
Nathanial

33
@MartinAsenov-按照您的逻辑,您永远不会使用这样的库,对吧?除非您已经在使用它,否则您将不使用它?
Brian Agnew

15
Fyi,程序包已更改,现在的课程是在:org.apache.commons.lang3.exception.ExceptionUtils
schmmd

2200

使用Throwable.printStackTrace(PrintWriter pw)堆栈跟踪发送到合适的作家。

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
如果您不喜欢为此类小而简单的事情添加外部库,请使用此答案。
Stijn de Witt 2012年

8
这确实会修剪堆栈跟踪,就像printStackTrace()一样。堆栈中的所有异常都是可见的,但是对于每个异常,堆栈可能会被修剪。任何需要完整跟踪的人都应该考虑这一点。
Laila Agaev

5
事实证明,这几乎与apache的ExceptionUtils.getStackTrace()方法的作用完全相同。实际上几乎是信。
壁虱2014年

6
@BrianAgnew,您不应该关闭StringWriterPrintWriter吗?
Muhammad Gelbana

13
@BrianAgnew,感谢您的回复。这让我感到好奇,这是我发现的东西:stackoverflow.com/questions/14542535/…–
穆罕默德·盖尔巴纳

459

这应该工作:

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

69
Java上下文中的简洁总是很荒唐的。那printStackTrace应该只是返回字符串,而决定是否打印给用户:)
dmitry

35
控制台中的printStackTrace打印是可以接受的,但是默认情况下至少应有一个以String形式返回的getStackTrace
Mateus Viccari 16-10-13

5
哪一部分是简洁的?您必须构造2个对象,这些对象除了将堆栈跟踪放入字符串外没有其他目的。
ArtOfWarfare

7
@dmitry称为的方法printXXX()应打印XXX。
user207421

2
@Greg实际上,这甚至没有讽刺意味,只需要简单地阅读他的话即可。
安德鲁(Andrew)

224

如果您是为Android开发的,则更简单的方法是使用此方法:

import android.util.Log;

String stackTrace = Log.getStackTraceString(exception); 

格式与getStacktrace相同,例如

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
谢谢。在这种情况下,日期和标签不会像在printStackTrace()中那样写在每一行中。
CoolMind

1
实际上,Log.getStackTraceString的核心使用上面提到的(D. Wroblewski的)StringWriter和Printwriter。
MikeL

2
在Android日志中打印它的最简单方法是: Log.e("TAG", "My message", new Throwable());
Erick M. Sprengel '18


113

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

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
如果您想修剪跟踪,例如,通过maxLines参数并仅向跟踪添加那么多的行,我将对该方法进行扩展
Rich Seller

e.getStackTrace之后的括号丢失。公共静态字符串stackTraceToString(Exception e){StringBuilder sb = new StringBuilder(); 对于(StackTraceElement元素:e.getStackTrace()){sb.append(element.toString()); sb.append(“ <br />”); } return sb.toString(); }
rlc

2
不能确定,但​​是我认为这不会显示根本原因异常的堆栈跟踪。
apoorv020

7
无论您做什么,都不要修剪痕迹。在我看来,很多次我都在看GlassFish日志中的堆栈轨迹,其中将有用的部分剪掉了。
cayhorstmann 2012年

使用String.format("%n")或类似的方法代替"\n"使DOS胶合剂满意。
ceving 2013年

89

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

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

37
该代码是干净的,但输出不是。最后,您必须执行.replaceAll(“,”,“ \ n”)。但是,您会丢失printStackTrace建议的缩进。
fury 2012年

4
当您在单行中记录跟踪时,这很有用
webjockey

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

1
嗨,我想指出的是,被引用答案的注释部分指出StringWriterand PrintWriter对象应该是closed...。(或者我猜只PrintWriter需要关闭,因为关闭它也应该关闭StringWriter
埃里克

8
灵感是指复制吗?您只需将其放入方法即可...在这里阅读答案就像观看“土拨鼠日”
Murmel

26

以下代码允许您使用某种String格式获取整个stackTrace ,而无需使用log4J或什至这样的API 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
是的,但您认为它有点笨重吗?在任何大小合理的项目日志记录框架中都是必须的,为什么要打扰
mzzzzb 2012年

这是一开始没有给出错误信息。可以添加
kommradHomer

您可能要使用StringBuffer而不是简单的串联。
dedda1994 '19

当出于隐私原因不允许您记录消息时,此功能很有用。
A1m

要知道,虽然这种解决方案将不记录内在原因
A1M

19

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

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 (Throwable t) {
    StringWriter sw = new StringWriter();
    t.printStackTrace(new PrintWriter(sw));
    logger.info("Current stack trace is:\n" + sw.toString());
}

这将继续超出当前功能的范围,即。是在哪里Throwable定义的,对不对?
n611x007

16
Arrays.toString(thrown.getStackTrace())

将结果转换为String的最简单方法是在程序中使用它来打印堆栈跟踪

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

为我工作,看起来什么都没有修剪!没有任何外部库也比其他答案简单,很好的答案!
Shaung诚

16

将堆栈跟踪打印到PrintStream,然后将其转换为String

// ...

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

    System.out.println(str);
}

1
这是最直接的答案
DaSqy Stc,

11

将堆栈跟踪打印到字符串

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

科特林

扩展Throwable类将为您提供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
谢谢eddyrkokr!它非常适合我的所有需求。我刚刚将其添加到基本TestNG类中,并且它从我的所有测试中一一收集了堆栈跟踪信息!
Krzysztof Walczewski

9

第一组评论中的巧妙剪裁非常有趣,但这实际上取决于您要执行的操作。如果您还没有正确的库,那么三行代码(如D. Wroblewski的答案)非常理想。OTOH,如果您已经拥有apache.commons库(就像大多数大型项目一样),那么Amar的答案会更短。好的,可能需要十分钟才能获得该库并正确安装它(如果您知道自己在做什么,则少于一分钟)。但是时钟在滴答作响,因此您可能没有时间空闲。JarekPrzygó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

它保存了我的培根。谢谢,阿马尔,提示!


9

来自Apache Commons Lang 3.4JavaDoc)的代码:

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

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


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)

5

Scala版本

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

5

如果您使用的是Java 8,请尝试以下操作

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

您可以找到as getStackTrace()提供的功能代码Throwable.java

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” 加入。


此解决方案不遵守堆栈跟踪选项卡缩进,否则效果很好!
krizajb

4

盛大地回答了Gala的问题,其中还包括导致异常的原因:

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

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

import java.util.Arrays;

try{

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

3

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

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

易于按原样传递给记录器。


您得到的内容与printStackTrace()此处的内容有所不同:1)引发了异常;2)原因及其
堆栈跟踪

差异是可以预期的,因为printStackTrace()从不转换是问题的一部分。
安德烈·列别登科

3

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

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

它将使用堆栈跟踪元素数组,将它们转换为字符串,然后加入多行字符串。


2
此解决方案正在删除邮件,并且忽略了所有父级跟踪
judovana

2

如果您不想使用外部库并且不打算为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();
}

2

这是个老问题,但是我想添加一种特殊情况,即您不想打印所有堆栈只需删除一些您实际上不感兴趣的部分,就可以排除某些类或程序包。

代替PrintWriter使用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);

SelectivePrintWriter类由下式给出:

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

请注意,此类可能很容易通过正则表达式contains或其他条件进行过滤。另请注意,这取决于Throwable实现细节(不太可能更改,但仍会更改)。


1
是的,这很好用,实际上是一个很有用的想法。
gil.fernandes

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

当您运行该程序时,输出将类似于以下内容:

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

1

我不知道为什么没人提到 ExceptionUtils.getStackFrames(exception)

对我来说,这是将stacktrace及其所有原因转储到最后的最方便的方法:

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

0

警告:这可能有点题外话了,但是好吧...;)

我不知道最初的张贴者为什么要首先将堆栈跟踪作为字符串。当堆栈跟踪应该以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
}

我喜欢它,因为它不需要外部库(当然,除了您的日志记录后端外,大多数时候它都将保留在后台)。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.