我是否应该捕获用于记录目的的异常?
public foo(..) { try { ... } catch (Exception ex) { Logger.Error(ex); throw; } }
如果我在每个图层(DataAccess,Business和WebService)中都有这个,这意味着会多次记录异常。
如果我的图层在单独的项目中并且只有公共接口中有try / catch,那么这样做是否有意义? 为什么?为什么不?我可以使用不同的方法吗?
答案 0 :(得分:35)
绝对不是。您应该找到处理异常的正确位置(实际执行某些操作,例如catch-and-not-rethrow),然后记录它。您当然可以并且应该包括整个堆栈跟踪,但是根据您的建议会使用try-catch块来丢弃代码。
答案 1 :(得分:18)
除非您要更改异常,否则只应记录您要处理错误的级别,而不是重新抛出它。否则,您的日志只会有一堆“噪音”,记录3条或更多相同的消息,每层一次。
我的最佳做法是:
答案 2 :(得分:9)
一般的经验法则是,如果你真的可以做一些事情,你只能抓住一个例外。因此,在业务或数据层,您只能在这样的情况下捕获异常:
try
{
this.Persist(trans);
}
catch(Exception ex)
{
trans.Rollback();
throw ex;
}
我的业务/数据层尝试保存数据 - 如果生成异常,则回滚任何事务并将异常发送到UI层。
在UI层,您可以实现一个常见的异常处理程序:
Application.ThreadException + = new ThreadExceptionEventHandler(Application_ThreadException);
然后处理所有异常。它可能会记录异常,然后显示用户友好的响应:
static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
LogException(e.Exception);
}
static void LogException(Exception ex)
{
YYYExceptionHandling.HandleException(ex,
YYYExceptionHandling.ExceptionPolicyType.YYY_Policy,
YYYExceptionHandling.ExceptionPriority.Medium,
"An error has occurred, please contact Administrator");
}
在实际的UI代码中,如果要执行不同的操作,可以捕获个别异常 - 例如显示不同的友好消息或修改屏幕等。
另外,作为提醒,总是尝试处理错误 - 例如除以0 - 而不是抛出异常。
答案 3 :(得分:2)
优良做法是翻译例外。不要只记录它们。如果您想知道抛出异常的具体原因,请抛出特定的异常:
public void connect() throws ConnectionException {
try {
File conf = new File("blabla");
...
} catch (FileNotFoundException ex) {
LOGGER.error("log message", ex);
throw new ConnectionException("The configuration file was not found", ex);
}
}
答案 4 :(得分:1)
使用您自己的异常来包装inbuild异常。这样,您可以在捕获异常时区分已知错误和未知错误。如果您有一个方法可以调用其他方法,这些方法可能会引发激活以对预期和意外故障做出反应,这是非常有用的
答案 5 :(得分:1)
您可能希望查找标准异常处理样式,但我的理解是:在可以向异常添加额外详细信息的级别处理异常,或者在向用户显示异常的级别处理异常。
在你的例子中你除了捕获异常,记录它并再次抛出它之外什么也没做。为什么不只是在最高级别用一次try / catch而不是在每个方法中捕获它,如果你所做的就是记录它?
我只会在该层处理它,如果您要在再次抛出异常之前向异常添加一些有用的信息 - 将异常包装在您创建的新异常中,该异常包含超出低级异常文本的有用信息,这通常意味着没有一些背景的人很少。
答案 6 :(得分:1)
有时您需要记录处理异常时不可用的数据。在这种情况下,只记录以获取该信息是合适的。
例如(Java伪代码):
public void methodWithDynamicallyGeneratedSQL() throws SQLException {
String sql = ...; // Generate some SQL
try {
... // Try running the query
}
catch (SQLException ex) {
// Don't bother to log the stack trace, that will
// be printed when the exception is handled for real
logger.error(ex.toString()+"For SQL: '"+sql+"'");
throw ex; // Handle the exception long after the SQL is gone
}
}
这类似于追溯日志记录(我的术语),您可以在其中缓冲事件日志,但除非存在触发事件(例如抛出异常),否则不要写入事件。
答案 7 :(得分:0)
如果您需要记录所有异常,那么这是一个很棒的主意。也就是说,没有其他原因记录所有异常并不是一个好主意。
答案 8 :(得分:0)
您可能希望记录最高级别,通常是您的UI或Web服务代码。多次记录是一种浪费。此外,当您查看日志时,您想要了解整个故事。
在我们的一个应用程序中,我们的所有页面都是从BasePage对象派生的,该对象处理异常处理和错误记录。
答案 9 :(得分:0)
如果这是它唯一做的事情,我认为最好从这些类中删除try / catch,并将异常提升到负责处理它们的类。这样,每个异常只能获得一个日志,为您提供更清晰的日志,甚至您可以记录堆栈跟踪,这样您就不会错过发生异常的位置。
答案 10 :(得分:0)
我的方法是仅在处理程序中记录异常。可以说是“真正的”处理程序。否则,日志将很难阅读,代码结构化程度较低。
答案 11 :(得分:0)
这取决于异常:如果这实际上不应该发生,我肯定会记录它。另一方面:如果你期望这个例外,你应该考虑应用程序的设计。
无论哪种方式:您至少应该尝试指定要重新抛出,捕获或记录的异常。
public foo(..)
{
try
{
...
}
catch (NullReferenceException ex) {
DoSmth(e);
}
catch (ArgumentExcetion ex) {
DoSmth(e);
}
catch (Exception ex) {
DoSmth(e);
}
}
答案 12 :(得分:0)
您需要登录层边界。例如,如果您的业务层可以部署在n层应用程序中的物理上独立的计算机上,那么以这种方式记录和抛出错误是有意义的。
通过这种方式,您可以在服务器上记录异常日志,而无需在客户端计算机上查看发生的情况。
我在使用Remoting或ASMX Web服务的应用程序的业务层中使用此模式。使用WCF,您可以使用附加到ChannelDispatcher的IErrorHandler(完全是另一个主题)来拦截和记录异常 - 因此您不需要try / catch / throw模式。
答案 13 :(得分:0)
您需要制定处理异常的策略。我不建议捕获和重新抛出。除了多余的日志条目之外,它还使代码更难阅读。 考虑在构造函数中写入异常的日志。这保留了要从中恢复的异常的try / catch;使代码更容易阅读。要处理意外或不可恢复的异常,您可能需要在程序最外层附近的try / catch来记录诊断信息。
BTW,如果这是C ++,你的catch块就会创建一个异常对象的副本,这可能是其他问题的潜在来源。尝试捕获对异常类型的引用:
catch (const Exception& ex) { ... }
答案 14 :(得分:0)
This软件工程广播播客是错误处理最佳实践的非常好的参考。实际上有2个讲座。
答案 15 :(得分:0)
一般来说这是不好的做法,除非您出于非常具体的原因需要登录。
对于一般的日志异常,应该在根异常处理程序中处理。