在Java中重新抛出异常时,堆栈跟踪并没有完全维护。

3

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

异常在第148行抛出,并在第150行重新抛出。在重新抛出后,第150行是异常的指定源。

我该怎么做才能保留原始堆栈跟踪?

代码:

    try {

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

    } catch (IOException e) {

        throw new RuntimeException(e); //line 150

    }

原始堆栈跟踪:

 (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)]

重新抛出后的堆栈跟踪:

 (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上调用getCause()方法了吗? - David Grant
5个回答

3

你的代码没有重新抛出原始异常,而是抛出了一个新的RuntimeException实例,该实例在创建时填充了其堆栈跟踪。

当你捕获RuntimeException时,你可以调用Throwable.getCause().getStackTrace()或者在抛出RuntimeException之前将RuntimeException的堆栈跟踪设置为原始异常的堆栈跟踪。


最佳实践是什么? - user1826324
由于在catch块中除了抛出原始异常之外没有做任何事情,最好的做法是根本不在这里捕获它。如果您正在将其包装在RuntimeException中以规避定义已检查的异常,则只需将其添加为原因即可。替换堆栈跟踪仍将导致信息丢失,因为您只有堆栈跟踪,而不知道它是IOException。 - Janoz
也许OP是将异常包装在运行时异常中,以避免在方法签名中添加'throws'子句,从而'污染'代码? - monojohnny
仅仅调用getCause可能不足够,因为可能存在多层包装的异常。请参考我的回答:https://dev59.com/bGrXa4cB1Zd3GeqPDuOx#27076790 - Flow

2
“问题”在于你没有重新抛出异常,而是抛出了一个“新”的异常。如果你真的像这样重新抛出异常:
try {
    content = (InputStream) conn.getContent();
} catch (IOException e) {
    throw e;
}

你会发现堆栈跟踪信息被保留下来。如果在现代JVM上使用Throwable.printStackTrace(),它将显示链接的异常及其(唯一的)堆栈帧。换句话说,原始异常的信息得到了保留。
有一种方法可以将一个异常的堆栈跟踪信息插入到另一个异常中。
try {
    content = (InputStream) conn.getContent();
} catch (IOException e) {
    RuntimeException re = new RuntimeException(e);
    re.setStackTrace(e.getStackTrace());
    throw re;
}

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


看起来他正在尝试将一个已检查的异常包装成未检查的异常。我怀疑这样做并没有什么帮助。 - Dunes
1
@Dunes - 我已经知道了。 - Stephen C

0

你必须遵循引起 Throwables 的参考以获取原始的堆栈跟踪。例如

public static StackTraceElement[] getCausingStacktrace(Throwable throwable) {
  if (throwable == null) {
    throw new NullPointerException();
  }
  while (throwable != null) {
    throwable = throwable.getCause();
  }
  return throwable.getStacktrace();
}

0

如果你只想要原始的堆栈跟踪信息,那就不要捕获它。另一方面,我假设你将其包装在运行时异常中是有原因的。你可以使用getCause()从运行时异常中获取原始异常。


0

堆栈跟踪被保留。

您必须从 RuntimeException 获取原因,并从那里获取堆栈跟踪以获取起源。

此功能称为异常链接。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接