如何将堆栈跟踪转换为字符串?
将Throwable.getStackTrace()
的结果转换为描述堆栈跟踪的字符串的最简单方法是什么?
使用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);
StringWriter
和PrintWriter
吗?
- Muhammad Gelbana 2015-08-02
可以使用以下方法将Exception
堆栈跟踪转换为String
。此类在 Apache commons-lang 中可用,这是具有许多流行开源的最常见的依赖库
org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(Throwable)
org.apache.commons.lang3.exception.ExceptionUtils
.
- schmmd 2013-11-13
这应该是可行的。
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionAsString = sw.toString();
printStackTrace
应该只返回字符串,把是否打印的决定权留给用户:)
- dmitry 2015-07-14
printXXX()
的方法应该打印出XXX。
- user207421 2017-08-11
如果你是为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)
Guava's Throwables
class
如果你有实际的Throwable
实例,Google Guava提供了Throwables.getStackTraceAsString()
.
举例说明。
String s = Throwables.getStackTraceAsString ( myException ) ;
警告:不包括原因(这通常是有用的部分!)。
public String stackTraceToString(Throwable e) {
StringBuilder sb = new StringBuilder();
for (StackTraceElement element : e.getStackTrace()) {
sb.append(element.toString());
sb.append("\n");
}
return sb.toString();
}
对我来说,最干净、最简单的方法是。
import java.util.Arrays;
Arrays.toString(e.getStackTrace());
public static String getStackTrace(Throwable t) {
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
return sw.toString();
}
StringWriter
和PrintWriter
对象应该是close
d....(或者我想只有PrintWriter
需要关闭,因为关闭它也应该关闭StringWriter
)。
- Eric 2016-08-16
下面的代码允许你以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.
}
将堆栈跟踪打印成PrintStream
,然后将其转换为String
:
// ...
catch (Exception e)
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
e.printStackTrace(new PrintStream(out));
String str = new String(out.toByteArray());
System.out.println(str);
}
这里有一个可以直接复制粘贴到代码中的版本。
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());
}
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});
Kotlin >= 1.4
在一个Throwable
上使用内置的函数stackTraceToString()
。
Kotlin < 1.4
扩展Throwable类将给你提供String属性error.stackTraceString
:
val Throwable.stackTraceString: String
get() {
val sw = StringWriter()
val pw = PrintWriter(sw)
this.printStackTrace(pw)
return sw.toString()
}
如果您使用的是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
即可。
将堆栈跟踪记录打印成字符串
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
更快、更便宜。
private String getCurrentStackTraceString() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
return Arrays.stream(stackTrace).map(StackTraceElement::toString)
.collect(Collectors.joining("\n"));
}
代码来自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
。
第一组评论中的巧妙狙击非常有趣,但这真的取决于你想做什么。如果你还没有正确的库,那么3行代码(如D.Wroblewski的答案)就很完美了。 相反,如果你已经有了apache.commons库(大多数大型项目都会有),那么Amar的答案就更短了。 好吧,你可能需要10分钟来获取库并正确安装它(如果你知道自己在做什么,那么不到一分钟)。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)
它拯救了我的培根。谢谢你,阿玛尔,谢谢你的提示!
如果没有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)
使用Java 8 流媒体API,您可以做这样的事情。
Stream
.of(throwable.getStackTrace())
.map(StackTraceElement::toString)
.collect(Collectors.joining("\n"));
它将接收堆栈跟踪元素的数组,将它们转换为字符串,并连接成多行字符串。
对笪瞵岔语的回答,也将包括例外情况的原因。
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;
}
Scala版本
def stackTraceToString(e: Exception): String = {
import java.io.PrintWriter
val sw = new StringWriter()
e.printStackTrace(new PrintWriter(sw))
sw.toString
}
如果您不想使用外部库,并且您没有开发for 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();
}
解决方案是将数组的 stackTrace 转换为字符串数据类型。请看下面的例子。
import java.util.Arrays;
try{
}catch(Exception ex){
String stack = Arrays.toString(ex.getStackTrace());
System.out.println("stack "+ stack);
}
我的oneliner,将堆栈跟踪转换为所附的多行字符串。
Stream.of(e.getStackTrace()).map((a) -> a.toString()).collect(Collectors.joining("\n", "[", "]"))
很容易 "按原样 "传递给记录器。
printStackTrace()
不同,在这里你将失去:1)被抛出的异常;2)原因和它们的堆栈跟踪。
- valijon 2018-12-11
printStackTrace()
从来都不是问题的一部分。
- Andrey Lebedenko 2018-12-11
少量的选择
StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); String exceptionAsString = sw.toString();
使用Google Guava lib
String stackTrace = Throwables.getStackTraceAsString ( myException ) ;
org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(Throwable)
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)
我想知道为什么没有人提到ExceptionUtils.getStackFrames(exception)
对我来说,这是最方便的方式,可以将堆栈跟踪及其所有原因转储到最后。
String.join("\n", ExceptionUtils.getStackFrames(exception));
老问题了,但我只想补充一个特殊的情况,即你不想打印所有的堆栈,通过删除一些你实际上不感兴趣的部分,排除某些类或包。
使用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);
}
}
}
请注意,此类可以很容易地适应正则表达式、contains
或其他条件进行过滤。另请注意,这取决于Throwable
实现细节(不太可能改变,但仍然如此)。
警告。这可能有点偏离主题,但是哦,好吧...;)
我不知道最初的发帖者reason是什么原因想把堆栈跟踪变成字符串。当堆栈跟踪应该在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
}
我喜欢它,因为它不需要外部库(除了你的日志后端,当然,大部分时间都会在这里)。
我不久前写了一些这方面的方法,所以我想为什么不把我的两分钱扔在这里呢。
/** @param stackTraceElements The elements to convert
* @return The resulting string */
public static final String stackTraceElementsToStr(StackTraceElement[] stackTraceElements) {
return stackTraceElementsToStr(stackTraceElements, "\n");
}
/** @param stackTraceElements The elements to convert
* @param lineSeparator The line separator to use
* @return The resulting string */
public static final String stackTraceElementsToStr(StackTraceElement[] stackTraceElements, String lineSeparator) {
return stackTraceElementsToStr(stackTraceElements, lineSeparator, "");
}
/** @param stackTraceElements The elements to convert
* @param lineSeparator The line separator to use
* @param padding The string to be used at the start of each line
* @return The resulting string */
public static final String stackTraceElementsToStr(StackTraceElement[] stackTraceElements, String lineSeparator, String padding) {
String str = "";
if(stackTraceElements != null) {
for(StackTraceElement stackTrace : stackTraceElements) {
str += padding + (!stackTrace.toString().startsWith("Caused By") ? "\tat " : "") + stackTrace.toString() + lineSeparator;
}
}
return str;
}
/** @param stackTraceElements The elements to convert
* @return The resulting string */
public static final String stackTraceCausedByElementsOnlyToStr(StackTraceElement[] stackTraceElements) {
return stackTraceCausedByElementsOnlyToStr(stackTraceElements, "\n");
}
/** @param stackTraceElements The elements to convert
* @param lineSeparator The line separator to use
* @return The resulting string */
public static final String stackTraceCausedByElementsOnlyToStr(StackTraceElement[] stackTraceElements, String lineSeparator) {
return stackTraceCausedByElementsOnlyToStr(stackTraceElements, lineSeparator, "");
}
/** @param stackTraceElements The elements to convert
* @param lineSeparator The line separator to use
* @param padding The string to be used at the start of each line
* @return The resulting string */
public static final String stackTraceCausedByElementsOnlyToStr(StackTraceElement[] stackTraceElements, String lineSeparator, String padding) {
String str = "";
if(stackTraceElements != null) {
for(StackTraceElement stackTrace : stackTraceElements) {
str += (!stackTrace.toString().startsWith("Caused By") ? "" : padding + stackTrace.toString() + lineSeparator);
}
}
return str;
}
/** @param e The {@link Throwable} to convert
* @return The resulting String */
public static final String throwableToStrNoStackTraces(Throwable e) {
return throwableToStrNoStackTraces(e, "\n");
}
/** @param e The {@link Throwable} to convert
* @param lineSeparator The line separator to use
* @return The resulting String */
public static final String throwableToStrNoStackTraces(Throwable e, String lineSeparator) {
return throwableToStrNoStackTraces(e, lineSeparator, "");
}
/** @param e The {@link Throwable} to convert
* @param lineSeparator The line separator to use
* @param padding The string to be used at the start of each line
* @return The resulting String */
public static final String throwableToStrNoStackTraces(Throwable e, String lineSeparator, String padding) {
if(e == null) {
return "null";
}
String str = e.getClass().getName() + ": ";
if((e.getMessage() != null) && !e.getMessage().isEmpty()) {
str += e.getMessage() + lineSeparator;
} else {
str += lineSeparator;
}
str += padding + stackTraceCausedByElementsOnlyToStr(e.getStackTrace(), lineSeparator, padding);
for(Throwable suppressed : e.getSuppressed()) {
str += padding + throwableToStrNoStackTraces(suppressed, lineSeparator, padding + "\t");
}
Throwable cause = e.getCause();
while(cause != null) {
str += padding + "Caused by:" + lineSeparator + throwableToStrNoStackTraces(e.getCause(), lineSeparator, padding);
cause = cause.getCause();
}
return str;
}
/** @param e The {@link Throwable} to convert
* @return The resulting String */
public static final String throwableToStr(Throwable e) {
return throwableToStr(e, "\n");
}
/** @param e The {@link Throwable} to convert
* @param lineSeparator The line separator to use
* @return The resulting String */
public static final String throwableToStr(Throwable e, String lineSeparator) {
return throwableToStr(e, lineSeparator, "");
}
/** @param e The {@link Throwable} to convert
* @param lineSeparator The line separator to use
* @param padding The string to be used at the start of each line
* @return The resulting String */
public static final String throwableToStr(Throwable e, String lineSeparator, String padding) {
if(e == null) {
return "null";
}
String str = padding + e.getClass().getName() + ": ";
if((e.getMessage() != null) && !e.getMessage().isEmpty()) {
str += e.getMessage() + lineSeparator;
} else {
str += lineSeparator;
}
str += padding + stackTraceElementsToStr(e.getStackTrace(), lineSeparator, padding);
for(Throwable suppressed : e.getSuppressed()) {
str += padding + "Suppressed: " + throwableToStr(suppressed, lineSeparator, padding + "\t");
}
Throwable cause = e.getCause();
while(cause != null) {
str += padding + "Caused by:" + lineSeparator + throwableToStr(e.getCause(), lineSeparator, padding);
cause = cause.getCause();
}
return str;
}
举例说明。
try(InputStream in = new FileInputStream(file)) {
...
} catch(IOException e) {
String exceptionToString = throwableToStr(e);
someLoggingUtility.println(exceptionToString);
...
}
印刷品:
java.io.FileNotFoundException: C:\test.txt (The system cannot find the file specified)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(Unknown Source)
at java.io.FileInputStream.<init>(Unknown Source)
at com.gmail.br45entei.Example.main(Example.java:32)