为什么将“日志和抛出”视为反模式?[关闭]


85

这个问题是由围绕本文的讨论引发的,当时我没有得到任何好的答案。

如果不能以其他方式处理异常,为什么应该记录异常然后将其重新抛出(当然,保留原始堆栈跟踪)是一个坏主意呢?


Answers:


121

我认为答案主要是因为如果无法处理,为什么要抓住它?如果他们认为值得使用日志,为什么不让任何人可以处理(或者什么也别无选择,只能处理)。

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

也可能有人认为,由于抛出和捕获异常是相对昂贵的操作,因此所有这些捕获和重新抛出都无助于提高运行时性能。简洁或可维护性也无济于事。


为进一步阐述,看到杰夫的答案的评论
马努

8
记录并抛出异常的原因可能是缩小范围并记录特定消息。
Mike Argyriou 2014年

11
答案:可能有用的调试信息在堆栈的当前点可用,而在堆栈跟踪的另一点将不可用
rtconner

如果新异常在更多信息或更具体的方面更有用,则捕获和重新抛出有时会有所帮助。
borjab

要花费@rtconner评论的内容:在异步代码中,捕获器可能没有抛出器可用的上下文。
Nir Alfasi

53

如果实体捕获并重新抛出异常有理由认为,它包含的信息将不会在调用堆栈的更远的地方进行记录-至少不是以最期望的方式,则日志记录是一种很好的模式。可能发生的几种原因:

  1. 异常可能在应用层边界被捕获并重新抛出,并且可能包含特权信息。数据库层不允许出现异常说明,例如“试图将重复的键“ fnord”添加到字段“用户”中”到达外部应用程序层(可能反过来将其公开给用户),这很不好。对于数据库的内部部分抛出这样的异常以及应用程序接口捕获它,安全地对其进行记录并重新抛出描述性较小的异常可能很有用。
  2. 例外情况可能是外层可能希望在不进行日志记录的情况下进行处理,但是内层可能知道外层不知道的东西,这表明日志记录可能有用。作为一个粗略的例子,中间的应用程序层可能被编程为尝试连接到一台服务器,如果不起作用,则尝试另一台。在服务器停机以进行维护时,使用“连接失败”消息填充应用程序日志可能没有帮助,尤其是因为从应用程序的角度来看,一切正常。将有关连接失败的信息转发到与服务器关联的日志记录资源可能很有用,然后该日志记录资源可以过滤日志,以便生成有关服务器启动和关闭时间的报告,而不是每次连接尝试的日志。

4
#1确实是一个明智的通用用例,但是OP明确询问了“将其扔掉(当然,保留原始堆栈跟踪)”,因此#1不是正确的答案。
郑浩

@GeoffreyZheng:这取决于是否安全地记录原始堆栈跟踪将被视为“保留”。
2015年

2
关于#1:公开异常内容(如e.getMessage()在UI上显示/ stacktrace以及将其作为REST响应发送)应视为漏洞本身,因为运行时异常可能包含任何敏感信息。关于#2:您可以重新引发异常,添加希望客户端知道的所有信息(+根本原因),而无需记录任何内容。
Nikita Bosik

@NikitaBosik:不管是否应该在客户端公开异常,这样做的应用程序非常普遍,以至于“深度安全”原则将建议清除异常消息中的敏感信息。此外,如果外层不希望丢弃特定类型的异常而不将其记录下来,也不希望将信息转发给可能感兴趣的外部实体,则让中间层添加外层要忽略的信息将无济于事任何东西。
超级猫

20

我想最简单的原因是,通常您需要一个顶级处理程序为您执行此操作,因此无需使用此异常处理来污染代码。

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

我认为,唯一可以捕获异常的情况是可以对结果进行有用的处理。仅捕获日志是没有用的,因为您可以进一步集中工作。


1
我同意您应该只有一个顶级处理程序为您执行此操作。但是我认为,顶级处理程序应该“记录并抛出”。因此,争论围绕着“记录并抛出”的点,而不是根本不这样做吗?!
马努

@Manu,我想指出的是,如果它是单个处理程序(集中式),那很好。如果要复制代码,那就不好了。我认为没有比这更多的了!
杰夫·福斯特

5
@Manu-在这种情况下,顶级处理程序将抛出什么?在我看来,如果有可扔给它的东西,那么它实际上并不是顶级处理程序。而且您绝对不希望在运行时环境本身上抛出异常。这将使大多数应用程序崩溃。
aroth

1
@aroth:我知道,您的意思是“日志并处理”(如果您是顶级处理程序),或者“不要日志并处理(否则处理)”。感谢您指出了这一点。
马努

9

IMO的日志和投掷显然违反了“最小惊讶原则”。

如果在调用堆栈的更深处正确处理了异常,则可能根本不值得输入错误日志。然后很难找到错误日志条目。


那么非错误日志呢?
Su Zhang

6
@SuZhang我不太明白你的问题。如果没有错误,则不会抛出任何错误。当然,您可以并且应该编写非错误日志。
巴斯蒂安·福伊特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.