为什么"log and throw"被认为是反模式?

Man*_*anu 74 logging exception-handling anti-patterns

围绕这篇文章的讨论引发了这个问题,我没有收到任何好的答案.

为什么要记录你的异常然后重新抛出它(当然保留原始的堆栈跟踪)是一个坏主意,如果你不能处理它呢?

aro*_*oth 104

我认为答案很大程度上是因为如果你无法处理它,你为什么要抓住它?为什么不让任何人能够处理它(或者除了处理它之外别无选择)记录它,如果他们觉得它是值得记录的?

如果你捕获并记录它并重新抛出它,那么上游代码就无法知道你已经记录了异常,因此同样的异常可能会被记录两次.或者更糟糕的是,如果所有上游代码都遵循相同的模式,则异常可能被记录任意次数,一次针对决定捕获它的代码中的每个级别,记录它,然后再次抛出它.

也有人可能会说,既然抛出和捕获异常是相对昂贵的操作,这一切捕获和重新抛出是帮不了你的运行时性能.它也不会在简洁性或可维护性方面帮助您的代码.

  • 答:可能在堆栈中的当前点可用的有用调试信息在堆栈跟踪中的另一个点处不可用 (9认同)
  • 记录和重新抛出exdeption的原因应该是缩小原因并记录特定的消息. (8认同)
  • 这不是为什么您通常可以在再次重新抛出异常或将它们包装到另一个异常中之前将信息附加到异常吗? (3认同)
  • 详细说明 @rtconner 的评论:在异步代码中,捕获器可能没有抛出器可用的上下文。 (2认同)

sup*_*cat 45

如果实体捕获并重新抛出异常有理由相信它包含的信息不会在调用堆栈中进一步记录,那么Log-and-throw是一个很好的模式 - 至少不是以最理想的方式.可能出现以下几个原因:

  1. 异常可能在应用程序层边界捕获并重新抛出,并且可能包含特权信息.如果数据库层允许异常,例如"尝试将重复键'fnord'添加到字段'用户'"以到达外部应用程序层(可能会将其暴露给用户),这将是不利的,但它可能对数据库的内部部分有用,可以抛出这样的异常,应用程序接口可以捕获它,安全地记录它,并重新抛出一个描述性较小的异常.
  2. 例外可能是外层可能期望在没有记录的情况下处理的异常,但是内层可能知道外层没有的东西,这表明记录可能是有用的.作为一个粗略的例子,中间应用程序层可能被编程为尝试连接到一个服务器,如果这不起作用,请尝试另一个.当服务器停机进行维护时,使用"连接失败"消息充斥应用程序的日志可能没有用,特别是因为 - 从应用程序的角度来看,一切正常.将与连接失败有关的信息转发到与服务器关联的日志记录资源可能很有用,然后可以过滤日志以生成服务器上升和下降的报告,而不是每个连接尝试的日志. .

  • #1确实是一个明智的通用用例,但OP明确询问"重新抛出它(当然保留原始堆栈跟踪)",因此#1不是正确的答案. (4认同)
  • 关于#1:暴露异常内容(例如在 UI 上显示 `e.getMessage()`/stacktrace,以及将其作为 REST 响应发送)应该被视为漏洞本身,因为运行时异常可能包含任何类型的敏感信息。关于#2:您可以重新抛出异常,添加您希望客户知道的所有信息(+根本原因),无需记录任何内容。 (3认同)

Jef*_*ter 18

我想最简单的原因是你通常只有一个顶级处理程序为你做这个,所以没有必要用这个异常处理来污染代码.

横切关注的论点基本上是浪费时间处理与您无关的错误.最好让错误冒出调用堆栈,直到找到合适的处理程序.

在我看来,你应该捕获异常的唯一时间是你可以对结果做一些有用的事情.仅仅记录日志是没有用的,因为你可以进一步集中这项工作.

  • @Manu - 在这种情况下,顶级处理程序抛出的是什么?在我看来,如果有可用的东西可以投掷,那么它不是真正的顶级处理程序.并且您绝对不希望重新抛出异常直到运行时环境本身.这将使大多数应用程序崩溃. (5认同)
  • 我同意您应该有一个顶级处理程序为您执行此操作。但在我看来,顶级处理程序应该是“记录并抛出”-ing。所以争论围绕在什么时候“记录并​​抛出”,而不是或根本不做?!? (2认同)

Bas*_*igt 8

IMO登录和投掷明显违反了最低惊喜原则.

如果在调用堆栈中进一步正确处理异常,则可能根本不值得使用错误日志条目.然后找到错误日志条目很困惑.

  • @SuZhang我不太明白你的问题.如果没有错误,就没有什么可扔的.当然,您可以而且应该编写非错误日志. (5认同)