ChatGPT解决这个技术问题 Extra ChatGPT

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

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

因为 jqno 的答案实际上使用了您在问题中指定的 Throwable.getStackTrace() 方法,而 Brian 没有。他改用 Throwable.printStackTrace()。
几乎每个 Java 项目都应该包含 Apache commons-lang。它包括许多实现极其常见的开发需求的便捷方法。
@StijndeWitt 这三行代码几乎肯定需要从您调用它们的地方考虑。由于您不知道将它们放在哪里,它们将与所有其他有用的片段一起进入您的实用工具箱。答对了!您刚刚重新发明了番石榴 / commons-lang / 任何东西......只是不太好。而是导入一个合理的实用程序库,并节省重新发明轮子。新手的真正标志是认为您可以比图书馆作家做得更好。
1. Guava 有 - Throwables.getStackTraceAsString(e) 2. Apache Commons Lang - ExceptionUtils.getFullStackTrace 3. 编写我们自己的自定义方法
@AndrewSpencer 我不明白你们为什么要如此努力地抨击 StijndeWitt 想要通过一些小片段来实现这一目标。编写一个小小的实用方法确实没有太大的危险(我不认为它是“纯粹的傲慢哦不!他认为他比 Apache 更好!!”)。有很多项目,尤其是在非 Java JVM 语言中,它们真的不想仅仅为了记录堆栈跟踪而包含 Guava 或 Commons Lang。我编写了 Scala 和 Clojure 库,当然不会让 Apache Commons Lang 成为一种传递依赖项。

N
Neuron

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

这确实修剪了堆栈跟踪,与 printStackTrace() 相同。堆栈中的所有异常都是可见的,但对于每个异常,堆栈可能会被修剪。任何需要整个跟踪的人都应该考虑这一点。
事实证明,这几乎正是 apache 的 ExceptionUtils.getStackTrace() 方法所做的。实际上几乎是信。
@BrianAgnew,您不应该关闭 StringWriterPrintWriter 吗?
@MuhammadGelbana - 是的。以上是为了方便起见。我怀疑如果你不这样做不会造成问题,但我会以良好实践为由提倡它
@BrianAgnew,感谢您的回复。这让我很好奇,这就是我的发现:stackoverflow.com/questions/14542535/…
a
amar

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

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


@Stijn - 公平地说(我在下面写了当前投票率最高的答案)值得查看 commons-lang 以获得更多功能
@StijndeWitt Commons Lang 很常见。它已经存在于我工作中的大多数项目/项目中。
@Hugo 谢谢,打算使用 StringWriter 来避免添加新库——结果它已经是我的 3 个依赖项的依赖项。所以剩下的,检查你是否已经拥有它。
@MartinAsenov-按照您的逻辑,您永远不会使用这样的库,对吗?除非您已经在使用它,否则您不会使用它?
仅供参考,包已更改,课程现在位于:org.apache.commons.lang3.exception.ExceptionUtils
D
D. Wroblewski

这应该有效:

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

Java 上下文中的简洁总是令人毛骨悚然。该 printStackTrace 应该只返回字符串,将是否打印它的决定留给用户:)
控制台中的 printStackTrace 打印是可以接受的,但默认情况下至少应该可以使用 getStackTrace 将其作为字符串返回
这其中的哪一部分是简洁的?除了将堆栈跟踪放入字符串之外,您必须构造 2 个没有任何目的而存在的对象。
@dmitry 一个名为 printXXX() 的方法应该打印 XXX。
@Greg实际上这甚至不是讽刺,只是需要简单地阅读他所说的话。
P
Pokechu22

如果你正在为 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): 在 com.temp.ttscancel.MainActivity。 onCreate(MainActivity.java:43) 09-24 16:09:07.042: I/System.out(4844): 在 android.app.Activity.performCreate(Activity.java:5248) 09-24 16:09:07.043: I/System.out(4844): 在 android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1110) 09-24 16:09:07.043: I/System.out(4844): 在 android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2162) 09-24 16:09:07.043: I/System.out(4844): 在 android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2257) 09-24 16:09:07.043: 我/System.out(4844): 在 android.app.ActivityThread.access$800(ActivityThread.java:139) 09-24 16:09:07.043: I/System.out(4844): 在 android.app.ActivityThread$H .handleMessage(ActivityThread.java:1210) 09-24 16:09:07.043: I/System.out(4844): 在 android.os.Handler.dispatchMessage(Handler.java:102) 09-24 16:09:07.043 : I/System.out(4844): 在 android.os.Loo per.loop(Looper.java:136) 09-24 16:09:07.044: I/System.out(4844): 在 android.app.ActivityThread.main(ActivityThread.java:5097) 09-24 16:09: 07.044: I/System.out(4844): 在 java.lang.reflect.Method.invokeNative(Native Method) 09-24 16:09:07.044: I/System.out(4844): 在 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): 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)


N
Neuron

Guava 的 Throwables 类

如果您有实际的 Throwable 实例,Google Guava 将提供 Throwables.getStackTraceAsString()

例子:

String s = Throwables.getStackTraceAsString ( myException ) ;

s
samthebest

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

public String stackTraceToString(Throwable e) {
    StringBuilder sb = new StringBuilder();
    for (StackTraceElement element : e.getStackTrace()) {
        sb.append(element.toString());
        sb.append("\n");
    }
    return sb.toString();
}

V
Vladas Diržys

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

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

代码是干净的,但输出不是。最后你必须做一个 .replaceAll(", ", "\n") 。但是,您会丢失 printStackTrace 建议的缩进。
当您在单行中记录跟踪时,这很有用
A
Akira Yamamoto
public static String getStackTrace(Throwable t) {
    StringWriter sw = new StringWriter();
    t.printStackTrace(new PrintWriter(sw));
    return sw.toString();
}

嗨,我只想指出,引用的答案的评论部分指出 StringWriterPrintWriter 对象应该是 closed....(或者我猜只有 PrintWriter需要关闭,因为关闭它也应该关闭 StringWriter
M
Murilo Vasconcelos

以下代码允许您以 String 格式获取整个 stackTrace,而无需使用 log4J 甚至 java.util.Logger 之类的 API:

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.
}

I
Ihor Patsian

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

// ...

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

    System.out.println(str);
}

r
rouble

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

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

P
Panu Haaramo
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});

v
vonox7

科特林 >= 1.4

Throwable 上使用内置函数 stackTraceToString()

科特林 < 1.4

扩展 Throwable 类将为您提供 String 属性 error.stackTraceString

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

从 Kotlin 1.4 开始,现在有一个内置 API 可以执行此操作kotlinlang.org/api/latest/jvm/stdlib/kotlin/…
B
Brooklyn99

如果你使用的是 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)")));
}

所以只需用“\n”加入 StackTraceElement


如果你想限制它,你可以在流上使用方法 limit(5) 。
J
Jarek Przygódzki

将堆栈跟踪打印到字符串

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


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

I
IvanRF

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


T
Tihamer

第一组评论中的巧妙狙击非常有趣,但这实际上取决于您要做什么。如果您还没有正确的库,那么 3 行代码(如 D. Wroblewski 的回答)是完美的。 OTOH,如果您已经拥有 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

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


G
Gala

如果没有 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)

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

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)

i
ivanjermakov

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

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

它将获取堆栈跟踪元素数组,将它们转换为字符串并加入多行字符串。


此解决方案正在删除消息并忽略所有父跟踪
i
ido flax

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

s
samthebest

斯卡拉版本

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

J
Jorge Santos

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

import java.util.Arrays;

try{

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

C
Community

如果您不想使用外部库并且不开发 for Android,您可以像这样创建一个 'extension' method

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

A
Andrey Lebedenko

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

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

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


您会得到与 printStackTrace() 不同的东西,在这里您将失去:1)抛出的异常; 2)原因及其堆栈跟踪
这种差异在意料之中,因为转换 printStackTrace() 从来都不是问题的一部分。
P
Prakhar Nigam
 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)

A
Andrey Sarul

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

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

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

什么是ExceptionUtils?
这是一个公共语言工具类
g
gil.fernandes

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

使用 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 或其他标准进行过滤。另请注意,它取决于 Throwable 实施细节(不太可能改变,但仍然)。


A
Alexander Wessel

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

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

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


E
Eric

几个选项

StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw));字符串异常AsString = sw.toString();使用 Google Guava lib String stackTrace = Throwables.getStackTraceAsString(myException); org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(Throwable)