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


当前回答

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

我不知道最初的海报是什么原因让堆栈跟踪成为字符串。当堆栈跟踪应该以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
}

我喜欢它,因为它不需要外部库(当然,除了您的日志后端,它大部分时间都会在那里)。

其他回答

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

将堆栈跟踪打印到PrintStream,然后将其转换为字符串:

// ...

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

    System.out.println(str);
}

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

使用SelectivePrintWriter代替PrintWriter:

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

请注意,这个类可能很容易被Regex、contains或其他条件过滤掉。还要注意,这取决于可丢弃的实现细节(不太可能改变,但仍然如此)。

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。

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

我不知道最初的海报是什么原因让堆栈跟踪成为字符串。当堆栈跟踪应该以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
}

我喜欢它,因为它不需要外部库(当然,除了您的日志后端,它大部分时间都会在那里)。