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

这个问题是由围绕 这篇文章的讨论引发的,在那里我没有得到任何好的答案。

如果无法以其他方式处理异常,为什么记录异常然后重新引发(当然是保留原始堆栈跟踪)是一个坏主意呢?

57219 次浏览

I guess the simplest reason is that you'd typically have a single top-level handler doing this for you, so there's no need to pollute the code with this exception handling.

The cross-cutting concerns argument is basically that it is a waste of time handling errors that don't concern you. Far better to let the error bubble up the call stack until an appropriate handler can be found.

In my opinion, the only time you should catch an exception is when you can do something useful with the results. Catching simply to log is not useful, because you could centralize that work further up.

I assume the answer is largely because why are you catching it if you can't handle it? Why not let whomever can handle it (or whomever is left with no choice but to handle it) log it, if they feel that it is log-worthy?

If you catch it and log it and rethrow it, then there's no way for the upstream code to know that you've already logged the exception, and so the same exception might get logged twice. Or worse, if all the upstream code follows this same pattern, the exception might be logged an arbitrary number of times, once for each level in the code that decides to catch it, log it, and then throw it again.

Also some might argue that since throwing and catching exceptions are relatively costly operations, all this catching and rethrowing isn't helping your runtime performance. Nor is it helping your code in terms of conciseness or maintainability.

Log-and-throw is a good pattern iff the entity catching and rethrowing the exception has reason to believe that it contains information which will not get logged further up the call stack--at least not in the most-desired fashion. A couple of reasons this may occur:

  1. The exception may be caught and rethrown at an application-layer boundary, and may contain privileged information. It would be bad for a database layer to allow an exception saying, e.g. "Attempt to add duplicate key 'fnord' to field 'users'" to reach the outer application layer (which might in turn expose it to a user), but it could be useful for inner portions of the database to throw such an exception and the application interface to catch it, log it securely, and rethrow a somewhat less descriptive exception.
  2. The exception may be one that the outer layer would likely be expecting to handle without logging, but the inner layer may know something that the outer layer doesn't which would suggest that logging may be useful. As a crude example, a middle application layer might be programmed to try connecting to one server and, if that doesn't work, try another. Flooding the application's log with 'connection failed' messages while a server is down for maintenance might not be helpful, especially since--from the application's perspective, everything worked fine. It may be useful to forward information about the connection failure to a logging resource associated with servers, which could then filter the logs so as to produce a report of when the server went up and down, as opposed to a log of every single connection attempt.
If an outer layer would cause information from an exception to get lost, having an inner layer log and rethrow the exception may help ensure that it gets retained.

IMO log and throw is a clear violation of the Principle of Least Surprise.

If the exception is handled properly further up the call stack, it might not be worth an error log entry at all. And then it is confusing to find an error log entry.