正确打印Servlet异常的堆栈跟踪

4

所以我正在使用一个过滤器来捕获Servlet异常(因为我们同时使用JSF /普通Servlet)

当捕获ServletException并调用printstacktrace时,大部分信息都会丢失。

"真正"的根异常似乎隐藏在"有趣"的表达式后面。

((ServletException) e.getRootCause().getCause()).getRootCause().getCause().getCause().getCause()

这显然不是正确的方法。

有没有一种简单的方式来打印出此类异常的“完整”信息。有人能解释一下为什么异常会被这样包装吗?

4个回答

6

看一下commons-lang中的ExceptionUtils类。它包含了几个有用的方法,可以打印出所有异常链。


3

在查看了ExceptionUtils之后,问题得到了解决!

    final StringWriter stacktrace = new StringWriter();
    ExceptionUtils.printRootCauseStackTrace(throwable,new PrintWriter(stacktrace));
    msg.append(stacktrace.getBuffer());

这将打印出完整的堆栈跟踪,包括所有相关的信息。


1
嗯?Throwable.printStackTrace()是什么意思? - Lawrence Dol
1
Throwable.printStackTrace() 只会遵循 Throwable.getCause() 链。有很多奇怪的异常情况存在(例如 SQLException),它们使用不同的方法名称来存储异常的根本原因(由于在 Java 1.4 之前没有标准的方法来处理这种情况)。ExceptionUtils 遵循 getNextException、getTargetException、getRootCause 等方法(请参阅该类的完整列表)。 - David

1

这被称为异常链。通过在不同的异常中包装异常,您可以让异常在堆栈中向上冒泡,而无需让您的主应用程序类担心某些低级别的异常。

例如:

public void doStuff() throws StuffException {
    try {
        doDatabaseStuff();
    } catch (DatabaseException de1) {
        throw new StuffException("Could not do stuff in the database.", de1);
    }
}

这样,您的应用程序只需要处理StuffException,但如果确实需要,它可以访问底层的DatabaseException

要获取捕获的异常的最底层(和所有其他)异常,您可以迭代其根本原因:

    ...
} catch (SomeException se1) {
    Throwable t = se1;
    logger.log(Level.WARNING, "Top exception", se1);
    while (t.getCause() != null) {
        t = t.getCause();
        logger.log(Level.WARNING, "Nested exception", t);
    }
    // now t contains the root cause
}

这种方法的问题在于:关于更高级别异常(它们的消息)的大量信息丢失了。 - Andreas Petersson
我认为他不想删除异常链 - 只是要智能地记录它。 - matt b
投票支持公正。尽管它没有提供解决方案,但它解释了异常的嵌套。 - Andreas Petersson
没有什么可以阻止操作者在顶层和最底层异常之间使用所有异常。(答案中添加了日志记录。) - Bombe
哦,顺便说一下:JDK的日志记录框架在记录顶级异常时已经记录了所有嵌套异常。 - Bombe

0

ServletException的异常链接很棘手。根据使用的Web服务器实现和Web开发框架,在运行时链可能会使用cause和/或rootCause。这个link解释得非常好。为了使事情更加复杂,我见过一些异常情况,其中原因指向异常本身。 以下是我们使用的递归方法,涵盖了所有ServletExceptions的基础:

public static Throwable getDeepCause(Throwable ex) {
    if (ex == null) {
        return ex;
    }
    Throwable cause;
    if (ex instanceof ServletException) {
        cause = ((ServletException) ex).getRootCause();
        if (cause == null) {
            cause = ex.getCause();
        }
    } else {
        cause = ex.getCause();
    }
    if (cause != null && cause != ex) {
        return getDeepCause(cause);
    } else {
        // stop condition - reached the end of the exception chain
        return ex;
    }
}

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