Java/Android-如何打印出完整的堆栈跟踪?

在 Android (Java)中,如何打印出完整的堆栈跟踪?如果我的应用程序由于 nullPointerException 或其他原因崩溃,它会打印出一个(几乎)完整的堆栈跟踪,如下所示:

java.io.IOException: Attempted read from closed stream.
com.android.music.sync.common.SoftSyncException: java.io.IOException: Attempted read from closed stream.
at com.android.music.sync.google.MusicSyncAdapter.getChangesFromServerAsDom(MusicSyncAdapter.java:545)
at com.android.music.sync.google.MusicSyncAdapter.fetchDataFromServer(MusicSyncAdapter.java:488)
at com.android.music.sync.common.AbstractSyncAdapter.download(AbstractSyncAdapter.java:417)
at com.android.music.sync.common.AbstractSyncAdapter.innerPerformSync(AbstractSyncAdapter.java:313)
at com.android.music.sync.common.AbstractSyncAdapter.onPerformLoggedSync(AbstractSyncAdapter.java:243)
at com.google.android.common.LoggingThreadedSyncAdapter.onPerformSync(LoggingThreadedSyncAdapter.java:33)
at android.content.AbstractThreadedSyncAdapter$SyncThread.run(AbstractThreadedSyncAdapter.java:164)
Caused by: java.io.IOException: Attempted read from closed stream.
at org.apache.http.impl.io.ChunkedInputStream.read(ChunkedInputStream.java:148)
at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:159)
at java.util.zip.GZIPInputStream.readFully(GZIPInputStream.java:212)
at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:81)
at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:64)
at android.net.http.AndroidHttpClient.getUngzippedContent(AndroidHttpClient.java:218)
at com.android.music.sync.api.MusicApiClientImpl.createAndExecuteMethod(MusicApiClientImpl.java:312)
at com.android.music.sync.api.MusicApiClientImpl.getItems(MusicApiClientImpl.java:588)
at com.android.music.sync.api.MusicApiClientImpl.getTracks(MusicApiClientImpl.java:638)
at com.android.music.sync.google.MusicSyncAdapter.getChangesFromServerAsDom(MusicSyncAdapter.java:512)
... 6 more

但是有时候,出于调试的目的,我希望从代码中的位置记录完整的堆栈跟踪。我想我可以这么做:

StackTraceElement trace = new Exception().getStackTrace();
Log.d("myapp", trace.toString());

但是这只是打印出指向对象的指针... ... 我是否必须迭代所有的堆栈跟踪元素才能打印出来?或者有一个简单的方法把它们全部打印出来?

104303 次浏览

(String tag, String msg, Throwable tr)签名覆盖了所有 记录方法

将异常作为 第三参数传递应该会在 logcat 中提供完整的堆栈跟踪。

使用 Log.getStackTraceString (Throwable t)。通过深入挖掘可以获得更长的堆栈跟踪。例如:

try {
...
} catch(Exception e) {
Log.d("Some tag", Log.getStackTraceString(e.getCause().getCause()));
}

http://developer.android.com/reference/android/util/Log.html#getStackTraceString%28java.lang.Throwable%29恢复

下面的方法应该可以解决这个问题:

Log.d("myapp", Log.getStackTraceString(new Exception()));

注意,不会从堆栈跟踪中切断任何信息:末尾的 ...x more

(这表明) ,该异常的堆栈跟踪的其余部分与该异常(“封闭”异常)引起的堆栈跟踪底部指定的帧数相匹配。

... 或者换句话说,用第一个异常的最后一行代替 x more

你可以这样做:

public static String toString(StackTraceElement[] stackTraceElements) {
if (stackTraceElements == null)
return "";
StringBuilder stringBuilder = new StringBuilder();
for (StackTraceElement element : stackTraceElements)
stringBuilder.append(element.toString()).append("\n");
return stringBuilder.toString();
}
private static String buildStackTraceString(final StackTraceElement[] elements) {
StringBuilder sb = new StringBuilder();
if (elements != null && elements.length > 0) {
for (StackTraceElement element : elements) {
sb.append(element.toString());
}
}
return sb.toString();
}




// call this at your check point
Log.d(TAG, buildStackTraceString(Thread.currentThread().getStackTrace()));

您需要使用 Throwable Object 来获得完整的 stackTrace。

try{
// code here
}catch(Exception e){
String exception = getStackTrace(e);
}


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

档号: https://stackoverflow.com/a/18546861

还可以使用 Thread.dumpStack()等方法在应用程序代码中的任何位置打印堆栈跟踪

请浏览 更多细节的链接

我现在快速执行了一个递归函数,它将迭代 throwable 和 throwable.getCause ()。

这是因为每个“ throwable.getCause ()”返回一个新的异常消息,其中有一些行是重复的,有一些行是新的。 所以这个概念是: 如果有一个“原因”有一行“ n 更多”。所以我得到了“ n more”之前的最后一行。然后我得到了 cause 消息,最后我给 cause 消息添加子字符串,只得到最后一个重复行之后的部分(同时出现在两个行上的最后一行: main throwable 和 cause throwable)。

然后,当我得到原因消息时,我使用递归,因此重新调用相同的函数以从主抛出程序获得原因消息,我将得到一个已经替换的消息。如果主抛出的原因还有另外一个原因,那么主抛出的原因有三个级别(main-> cause-> cause-of-cause) ,在主抛出的原因上我会得到一个“ cause message”,一个已经被替换的消息(使用相同的主要概念

public static <T extends Throwable> String printStackTraceString(T ex) {     // Recursive
Throwable tr = ex;
if (tr != null) {
String st = Log.getStackTraceString(tr);
if (tr.getCause() != null) {
// Recursion...
String cs = printStackTraceString(tr.getCause());


String r1 = st.subSequence(0x0, st.lastIndexOf("\n", st.lastIndexOf("\n") - "\n".length())).toString();
String replace = r1.substring(r1.lastIndexOf("\n"));
if (cs.contains(replace)) {
return r1.concat(cs.subSequence(cs.indexOf(replace) + replace.length(), cs.length()).toString());
}
}
return st;
}
return "";
}

我只用了2级(main-> cause) ,没有用更多的:/ 如果有任何错误,请编辑该函数并写一条注释: D

有一个美好的编码和一个美好的一天

重要提示:

如果“ st”不包含“ n”或类似的内容,这段代码有时会出现异常(我发现一些异常堆栈跟踪存在这个问题)。 为了解决这个问题,您需要在代码行之前添加一些检查: “ String r1 = ...”

您需要检查: “ st”包含“ n”,并且“ st.subSequence”的开始和结束索引都是有效的。

无论如何,我建议把它放在 try-catch 中,并返回一个空字符串以防异常。(它是递归的,因此返回的空字符串将连接到以前处理的字符串)。

对于 kotlin android 用户,我们不需要创建扩展函数来遍历整个堆栈消息,我们在 kotlin 的可抛出类中内置了一些东西。

这是 Kotlin v > 1.4的引擎盖下的数据

@SinceKotlin("1.4")
public actual fun Throwable.stackTraceToString(): String {
val sw = StringWriter()
val pw = PrintWriter(sw)
printStackTrace(pw)
pw.flush()
return sw.toString()
}

用法:

 Log.d("ERROR_STACKTRACE","${t.stackTraceToString()}") // 't' is the throwable obj