5

假设我有以下代码:

void foo() {
    /* ... */
    try {
        bar(param1);
    } catch (MyException e) {
        /* ??? */
    }
}

void bar(Object param1) throws MyException {
    /* ... */
    try {
       baz(param2);
    } catch (MyException e) {
        /* ??? */
    }
}

void baz(Object param2) throws MyException {
    /* ... */
    if (itsAllATerribleMistakeOhNo) {
        /* ??? */
        throw new MyException("oops, error.");
    }
}

我想知道应该在哪里以及如何记录错误。

  • 在下面的 baz() 中发生错误的地方,我确切地知道什么操作出了差错,并且可以记录该事实。
  • 在顶部,我有最一般的上下文(例如,在处理过程中我们遇到错误的连接的 IP 是什么。)
  • 一路上,我可能有一些在顶部或底部都不知道的上下文。

另一个复杂情况是,当您从顶部查看时,底部的错误可能不会真正被视为错误(例如,在数据库中查找某些内容失败;也许您不确定) - 所以我可能会选择.logger.WARN()而不是logger.ERROR().

所以,上面我描述了 3 个位置(底部、顶部和沿途)——但这不仅仅是在哪里记录的问题,还有要扔什么的问题。在中间的每个级别,您都有 2x2 选项:

  • 记录/不记录消息
  • 抛出原始异常/将异常包装在带有添加消息的新异常中。

关于这些复杂的选择,最佳实践或一些常识是什么?

注意:我不是在问一般的错误处理/异常使用,只是关于上面描述的困境。

4

4 回答 4

2

我倾向于遵循的一些建议:

一些最佳实践的链接

1)跟踪发生的异常。如果类或 API 知道异常发生的上下文,则作为异常发生的点,那么跟踪并提供适当的日志会更好。但是,如果 API 无法处理或评论确切的上下文,则 API 不应记录事件并将其留给调用者。

2)包装异常:当有很多异常可以抛出并且所有异常形成一个类似的组(SQLException)时,它提供单个异常并让您在需要时提取信息。否则,调用者需要处理的异常将会激增。

3)重新抛出异常:如果 API 记录异常并且用户可以对其采取一些操作,则必须重新抛出异常以告诉用户发生了一些错误情况。

4)正确的异常原因:异常消息不应该过于技术性让调用者理解,消息本身应该引导用户理解异常的根本原因。

更新: Java 中的异常管理

于 2013-03-04T08:23:34.247 回答
2

在日志记录方面,我更喜欢将所有日志记录保留在应用程序边界的顶部。通常我使用拦截器或过滤器以一般方式处理所有日志记录。通过这个概念,我可以保证所有内容都只记录一次。

在这种情况下,您将登录您的foo()方法或应用程序的任何入口点(您提到了 IP 地址,我想我们正在谈论 servlet 容器或应用程序服务器)。

然后,在过滤器/拦截器中捕获您自己的异常并根据您的需要记录它。添加 acatch throwable以捕获您未在代码中处理的所有其他异常并将它们记录为错误,因为显然您错过了堆栈跟踪中更下方的某些内容。

这个概念需要提前计划。您可能会使用自己的ApplicationException存储错误消息(字符串)以及一些严重级别(可能在枚举中)。当您进行实际日志记录时,您需要它来选择正确的日志级别。

这适用于所有情况,并且具有所有日志记录仅发生一次的优点。但是,有一种情况您仍然需要登录代码:如果您可以完全处理代码中某处的错误(即发生异常并且您可以做一些允许您继续工作而无需(重新)抛出一个例外)。由于您没有引发异常,因此不会记录任何内容。

把它们加起来:

  • 以一般方式记录在最高位置,最好使用拦截器或过滤器。
  • 将异常包装在您自己的 ApplicationExceptions 中,并添加严重性以及其他感兴趣的内容以登录您的应用程序。
于 2013-03-04T09:27:46.360 回答
1

当我在代码中抛出异常时,我通常不会记录任何内容。例外是足够的信息。唯一的例外是,当我在我的系统边界时,也就是说,当异常将离开我的系统边界时,我会登录,因为我不确定其他系统会如何处理该错误。

当我处理异常时,我会在主动处理它们时记录它们,这意味着当我处于一个 catch 子句中时,它会做更多的事情,然后只是重新抛出异常。通常这是在顶部,但这取决于情况。

于 2013-03-04T08:22:07.500 回答
1

在测试阶段抛出异常时,你应该记住:

  • 使异常消息尽可能清晰。堆栈跟踪在最好的情况下可能会令人困惑,因此请确保您正在阅读的内容至少对您有意义。
  • 确保异常与事件相关。如果用户输入了错误的值并且您抛出了 NullPointerException,那么您的代码是不合逻辑的并且失去了它的价值。
  • 确保它包含尽可能多的关于该事件的信息。也就是说,保持消息的相关性。如果数据库调用出错,则将连接字符串打印到数据库,然后尝试 SQL 查询。当前使用的每个变量的状态不是必需的。
  • 不要胡说八道。输入技术术语以使其看起来像是在侵入矩阵是很诱人的。在压力大的情况下它对你没有帮助,当然也对使用你的代码的其他人没有帮助。简单的英语单词总是更可取。
  • 最后,永远不要忽略一个例外。始终确保您处理异常,并且按照我上面所述的规则以某种方式输出详细信息。
于 2013-03-04T08:22:11.530 回答