【问题标题】:Extracting user-friendly exception details in Java在 Java 中提取用户友好的异常详细信息
【发布时间】:2011-02-16 22:41:41
【问题描述】:

我正在处理一个 J2EE Web 应用程序,当发生异常时,我想获取有关异常的一些基本详细信息并记录它。我正在记录的消息应该是非常基本的,对于运行 Web 服务器的人来说可能意味着什么。

使用e.getMessage() 是最好的记录方式吗?谢谢。

【问题讨论】:

    标签: java logging jakarta-ee exception


    【解决方案1】:

    我默认获取整个堆栈跟踪,这样我可以看到错误发生时发生的更多上下文。

    如果您使用 log4j 并将异常传递到其日志记录方法之一,它将打印您提供的任何消息,以及异常的消息及其堆栈跟踪:

    log.error("caught an exception", throwable);
    

    如果您有容易出错的事情,例如对 Web 服务的调用,您可以将抛出的异常包装在一个异常中,并为其分配一个非常清晰的消息,这样当管理员看到它时,该消息就会告诉他们发生了什么。

    就此而言,您可以保留单独的日志,一个带有用于开发人员的堆栈跟踪,另一个用于编写管理员友好的消息。您可以有一个异常处理程序,尝试将异常映射到友好消息,然后将该消息写入管理日志。

    【讨论】:

    • 您假设异常表明存在错误。对未检查异常的合理假设,但对已检查异常完全错误的假设。
    • @Raedwald:通过检查的不是错误的异常,您是指 InterruptedException 还是旧的 ejb FinderExceptions?当然,我主张根本不记录它们。我在这里的意思是处理意外异常的基本默认计划。
    • 已检查的异常通常并不表示存在错误。例如,尝试打开文件时未找到文件并不表示程序中存在错误。它们表示不可预测的运行时问题。
    • @Raedwald:我同意尽可能明智地处理事情会更好。但这个问题是专门关于记录和收集信息的,所以我认为这超出了范围。
    • 好吧,这样做是对的 log.error("caught an exception", throwable);但这会将整个堆栈跟踪打印到日志不是吗?是否有一种简单的方法可以仅打印异常名称、其来源和 log4j 中的本地化消息?但保留发送完整堆栈跟踪的选项?
    【解决方案2】:

    可能。问题是至少没有调用方法信息,这不是很有帮助

    考虑类似以下的事情

    /**
     * Returns <i>class.method:linenumber</i> of the caller (or, more accurately
     * the caller of the caller).
     * </p>
     *
     * <p>
     * Returns unknown if stacktrace is mucked up. Uses reflection and string
     * concatenation, so don't overuse this for trivial matters. For exception
     * handling and for logging, on the other hand, it is useful.
     *
     * @return method name of caller's caller and line number (String)
     */
    public static String returnCaller( Class ignoreMe )
    {
    
        String              ignoreClass = ignoreMe.getName();
        StackTraceElement[] steArr      = new Throwable().getStackTrace();
    
        if (steArr != null)
        {
            // subscript 1 is returnCaller().
            // subscript 2 is the caller of returnCaller()
            // subscript 3 is the caller of the caller of returnCaller()...
            for( int i = 0; i < steArr.length; i++)
            {
                if (steArr[i] == null)
                    break;
    
                String myclass = steArr[i].getClassName();
    
                if (myclass.equals(ErrorHandle.class.getName()))
                    continue;
    
                if (ignoreClass.equals(myclass))
                    continue;
    
                return steArr[i].getClassName()
                        + "."
                        + steArr[i].getMethodName()
                        + ":"
                        + steArr[i].getLineNumber();
            }
        }
    
        return "unknown";
    }
    

    【讨论】:

    • 我认为这对系统管理员来说很好,但你应该有一个单独的调试日志来记录整个堆栈跟踪。假设您将空值传递给Integer.valueOf(String)。实际抛出异常的类对你没有多大好处。
    • 义晒——当然可以。实际上,我最终同时记录了堆栈跟踪和这个 - 它们都有自己的位置。
    【解决方案3】:

    只记录异常消息不是一个好主意;您丢失了异常发生在哪一行,这使得在代码中查找记录的异常大多数时候都是一场噩梦。

    记录整个异常/堆栈跟踪。

    【讨论】:

    • “你丢失了发生异常的哪一行”:确实如此。但为什么这很重要?只有当异常表明存在错误并且您正在尝试定位和修复该错误时,它才会变得重要。对于未检查的异常是合理的假设,但对于已检查的异常则完全错误。
    【解决方案4】:

    如果您只是想通知系统管理员,出现了问题,但没有开发人员需要查看它,因为这是一些暂时的已知问题,或者可以由系统管理员解决,一个好的描述性消息就足够了。在所有其他情况下,每个开发人员都会非常感谢获得完整的堆栈跟踪以及异常发生时的系统状态(如果可用)(输入参数等...)。

    【讨论】:

      【解决方案5】:

      我做过非常相似的事情。但首先,您根本不需要向用户传达错误是什么。告诉他们错误是如何发生的是没有意义的,因为无论如何您都会记录它。 (如果用户知道由于 NPE 或 IO 错误而发生错误是否有意义,即使您将其重命名为更“友好”的术语)?

      首先您可能需要考虑re-throwing all of your Exceptions as runtime (unchecked) exceptions。这将强制集中和一致地记录错误。否则,您将不得不希望错误处理代码的每个实例都能正确记录它。重新抛出你的 SQLExceptions 例如:

          try{
              conn.close();
          } catch(SQLException ex){
              //logger.error("Cannot close connection"); //don't log this here, do it centrally
              throw new RuntimeException(ex);
          }
      

      您将希望将用户转发到一般错误页面,只需告诉他们发生了错误。这向他们表明,他们所做的任何事情都没有得到处理。这非常重要,这样他们就不会反击并一遍又一遍地尝试输入付款。让他们知道出了点问题,然后 (a) 联系帮助台或 (b) 在默认错误页面中放置一个表格,该页面可以记录故障单或向帮助台发送电子邮件。为此,您需要在 web.xml 中定义它:

      <error-page>
        <error-code>500</error-code>
        <location>/Error.jsp</location>
      </error-page>
      

      接下来,在 error.jsp 页面(或 servlet)中,如果您的容器允许您将堆栈跟踪引用为 request.getAttribute("javax.servlet.error.message"));,则在此处记录错误。

      OR,创建一个仅作用于响应的过滤器,如下所示:

      try {
          chain.doFilter(req, res);
       } catch (Exception e) {
          logger.error("pass any Mapped Diagnostic Context here", e); //log here, centrally
          throw new RuntimeException(e); //throws back to the container so that the error page will be seen
      }
      

      这将捕获所有传播的异常(未检查和未捕获),记录它们,然后重新抛出它们,以便它们调用错误页面。这样会报错

      1. 准确(每个错误实例一个日志)
      2. 可靠(如果用户收到错误,您会收到日志记录详细信息
      3. 一致(标准化,如果您将重新抛出已检查异常实施为未检查,您将能够查看所有异常并防止用户进入容易出错的区域,因为他们插入废话,您冒着灾难性的数据清理任务的风险放入未填充的表格或左键表格中)

      最后,您可能需要考虑使用 log4j 并使用一个附加程序,该附加程序将输出到您可以查看错误的地方,而无需等待运营团队将它们发送给您。如果您定期维护应用程序,这种方式会容易得多。

      【讨论】:

      • 这些是一些很棒的技巧,谢谢!我会检查它们,看看它们是否对我们的网络应用有用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-31
      • 2011-03-20
      • 2013-07-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多