在java中重新抛出异常时,不会完全维护堆栈跟踪

Stack Trace is not fully maintained when rethrowing exception in java

本问题已经有最佳答案,请猛点这里访问。

当异常在下面的代码中重新引发时,原始堆栈跟踪不会保留。

在第148行和第150行中引发异常。重新刷新后,第150行是指定的异常源。

我必须做什么来保持原始堆栈跟踪?

代码:

1
2
3
4
5
6
7
8
9
    try {

        content = (InputStream) conn.getContent(); //line 148

    } catch (IOException e) {

        throw new RuntimeException(e); //line 150

    }

原始堆栈跟踪:

1
 (java.lang.StackTraceElement[]) [sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source), java.net.URLConnection.getContent(Unknown Source), com.mycompany.myapp.client.services.impl.AbstractClientService.getResponseEle(AbstractClientService.java:148), com.mycompany.myapp.client.services.impl.AbstractClientService.getResponseEleWithCheck(AbstractClientService.java:162), com.mycompany.myapp.client.services.impl.AbstractClientService.getResponseEleWithCheck(AbstractClientService.java:158), com.mycompany.myapp.client.services.impl.InfoQueryClientServiceImpl.getFileOnTmpList(InfoQueryClientServiceImpl.java:84), com.mycompany.myapp.client.myappClient.getFileOnTmpList(myappClient.java:196), com.mycompany.myapp.client.model.Model.updateStudyInfos(Model.java:96), com.mycompany.myapp.client.model.Model.instantiateSingleton(Model.java:46), com.mycompany.myapp.applet.MainApplet.addMainPanel(MainApplet.java:106), com.mycompany.myapp.applet.MainApplet.createUIPanel(MainApplet.java:76), com.mycompany.myapp.applet.MainApplet.init(MainApplet.java:58), com.sun.deploy.uitoolkit.impl.awt.AWTAppletAdapter.init(Unknown Source), sun.plugin2.applet.Plugin2Manager$AppletExecutionRunnable.run(Unknown Source), java.lang.Thread.run(Unknown Source)]

重发后的堆栈跟踪:

1
 (java.lang.StackTraceElement[]) [com.mycompany.myapp.client.services.impl.AbstractClientService.getResponseEle(AbstractClientService.java:150), com.mycompany.myapp.client.services.impl.AbstractClientService.getResponseEleWithCheck(AbstractClientService.java:162), com.mycompany.myapp.client.services.impl.AbstractClientService.getResponseEleWithCheck(AbstractClientService.java:158), com.mycompany.myapp.client.services.impl.InfoQueryClientServiceImpl.getFileOnTmpList(InfoQueryClientServiceImpl.java:84), com.mycompany.myapp.client.myappClient.getFileOnTmpList(myappClient.java:196), com.mycompany.myapp.client.model.Model.updateStudyInfos(Model.java:96), com.mycompany.myapp.client.model.Model.instantiateSingleton(Model.java:46), com.mycompany.myapp.applet.MainApplet.addMainPanel(MainApplet.java:106), com.mycompany.myapp.applet.MainApplet.createUIPanel(MainApplet.java:76), com.mycompany.myapp.applet.MainApplet.init(MainApplet.java:58), com.sun.deploy.uitoolkit.impl.awt.AWTAppletAdapter.init(Unknown Source), sun.plugin2.applet.Plugin2Manager$AppletExecutionRunnable.run(Unknown Source), java.lang.Thread.run(Unknown Source)]


您的代码不会重新引发原始异常,而是抛出RuntimeException的新实例,该实例在创建时已填充其堆栈跟踪。

当捕获RuntimeException或在引发RuntimeException之前,将RuntimeException的堆栈跟踪设置为来自原始异常的堆栈跟踪。


"问题"是您没有重新引发异常。相反,你提出了一个新的例外。如果你真的再次提出这样的例外:

1
2
3
4
5
try {
    content = (InputStream) conn.getContent();
} catch (IOException e) {
    throw e;
}

您将发现堆栈跟踪被保留。

如果您在现代JVM上使用Throwable.printStackTrace(),它将显示链异常及其(唯一)堆栈帧。换句话说,保留原始异常的信息。

有一种方法可以将堆栈跟踪从一个异常拼接到另一个异常。

1
2
3
4
5
6
7
try {
    content = (InputStream) conn.getContent();
} catch (IOException e) {
    RuntimeException re = new RuntimeException(e);
    re.setStackTrace(e.getStackTrace());
    throw re;
}

然而,我个人认为这不是一个好主意。虽然RuntimeException的行号现在与原始异常相同,但您会得到一个连接对象的异常情况,该对象似乎抛出了一个RuntimeException,代码表示这不可能发生。我认为最好是以正常的方式链接异常,并让程序员正确地读取链接异常的堆栈跟踪。


您必须遵循引起丢弃的引用才能获得原始stacktrace。例如

1
2
3
4
5
6
7
8
9
public static StackTraceElement[] getCausingStacktrace(Throwable throwable) {
  if (throwable == null) {
    throw new NullPointerException();
  }
  while (throwable != null) {
    throwable = throwable.getCause();
  }
  return throwable.getStacktrace();
}

StackTrace保留。

你必须从RuntimeException中找出原因,然后从那里得到stacktrace来获得来源。

此功能是调用异常链接。


如果只需要原始stacktrace,请不要捕获它。另一方面,出于某种原因,我假设您将它包装在运行时异常中。您可以使用getCause()从运行时异常中获取原始异常。