29

我应该捕获异常以进行日志记录吗?

公共 foo(..)
{
   尝试
   {
     ...
   } 捕捉(异常前){
     Logger.Error(ex);
     扔;
   }
}

如果我在每个层(DataAccess、Business 和 WebService)中都设置了这个,这意味着异常被记录了好几次。

如果我的层位于单独的项目中并且只有公共接口有 try/catch,这样做是否有意义?为什么?为什么不?我可以使用其他方法吗?

4

16 回答 16

37

当然不。您应该找到处理异常的正确位置(实际上是做一些事情,例如 catch-and-not-rethrow),然后记录它。当然,您可以而且应该包含整个堆栈跟踪,但是按照您的建议会在代码中乱扔 try-catch 块。

于 2008-09-18T18:47:10.043 回答
18

除非您要更改异常,否则您应该只在您要处理错误的级别记录,而不是重新抛出它。否则,您的日志只有一堆“噪音”,每层记录 3 条或更多相同的消息。

我的最佳做法是:

  1. 仅在公共方法中尝试/捕获(一般而言;显然,如果您要捕获特定错误,您会在那里检查它)
  2. 仅在抑制错误并重定向到错误页面/表单之前登录 UI 层。
于 2008-09-18T18:48:42.583 回答
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——而不是抛出异常。

于 2008-09-18T19:04:31.343 回答
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);
   }
}
于 2008-09-18T18:56:39.007 回答
1

使用您自己的异常来包装内置异常。这样,您可以在捕获异常时区分已知错误和未知错误。如果您有一个方法调用可能会引发异常以对预期和意外失败做出反应的其他方法,这将非常有用

于 2008-09-18T18:49:23.377 回答
1

您可能想查找标准异常处理样式,但我的理解是:在您可以向异常添加额外细节的级别处理异常,或者在您将向用户呈现异常的级别处理异常。

在您的示例中,您除了捕获异常、记录它并再次抛出它之外什么都不做。如果您所做的只是记录它,为什么不使用一个 try/catch 在最高级别捕获它,而不是在每个方法中捕获它?

如果您要在再次抛出异常之前向异常添加一些有用的信息,我只会在该层处理它 - 将异常包装在您创建的新异常中,该异常具有超出低级异常文本的有用信息,这通常对任何人都没有意义没有一些上下文..

于 2008-09-18T18:52:40.890 回答
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
    }
}

这类似于追溯日志(我的术语),您可以在其中缓冲事件日志,但除非有触发事件(例如抛出异常),否则不要写入它们。

于 2008-10-23T22:06:41.217 回答
0

如果您需要记录所有异常,那么这是一个绝妙的主意。也就是说,在没有其他原因的情况下记录所有异常并不是一个好主意。

于 2008-09-18T18:49:42.830 回答
0

您可能希望在最高级别登录,通常是您的 UI 或 Web 服务代码。多次记录是一种浪费。此外,当您查看日志时,您想了解整个故事。

在我们的一个应用程序中,我们所有的页面都派生自一个 BasePage 对象,该对象处理异常处理和错误日志记录。

于 2008-09-18T18:49:51.910 回答
0

如果这是它唯一做的事情,我认为最好从这些类中删除 try/catch,并将异常引发到负责处理它们的类。这样,每个异常您只会获得一个日志,从而为您提供更清晰的日志,甚至您可以记录堆栈跟踪,这样您就不会错过异常的起源。

于 2008-09-18T18:50:32.143 回答
0

我的方法是仅在处理程序中记录异常。可以说是“真正的”处理程序。否则,日志将很难阅读,代码的结构也很差。

于 2008-09-18T18:53:01.387 回答
0

这取决于异常:如果这实际上不应该发生,我肯定会记录它。另一方面:如果你期望这个异常,你应该考虑应用程序的设计。

无论哪种方式:您至少应该尝试指定要重新抛出、捕获或记录的异常。

public foo(..)
{
   try
   {
     ...
   }
   catch (NullReferenceException ex) {
     DoSmth(e);
   }
   catch (ArgumentExcetion ex) {
     DoSmth(e);
   }
   catch (Exception ex) {
     DoSmth(e);
   }
}
于 2008-09-18T18:54:56.220 回答
0

您将希望在层边界处登录。例如,如果您的业务层可以部署在 n 层应用程序中物理上独立的机器上,那么以这种方式记录并抛出错误是有意义的。

通过这种方式,您可以在服务器上记录异常日志,而无需在客户端机器上四处探查以找出发生了什么。

我在使用 Remoting 或 ASMX Web 服务的应用程序的业务层中使用此模式。使用 WCF,您可以使用附加到 ChannelDispatcher(完全是另一个主题)的 IErrorHandler 拦截并记录异常 - 因此您不需要 try/catch/throw 模式。

于 2008-09-18T18:59:32.573 回答
0

您需要制定处理异常的策略。我不推荐接球再扔。除了多余的日志条目之外,它还使代码更难阅读。考虑将异常写入构造函数中的日志。这为您想要恢复的异常保留了 try/catch;使代码更易于阅读。要处理意外或不可恢复的异常,您可能希望在程序最外层附近使用 try/catch 来记录诊断信息。

顺便说一句,如果这是 C++,您的 catch 块正在创建异常对象的副本,这可能是其他问题的潜在来源。尝试捕获对异常类型的引用:

  catch (const Exception& ex) { ... }

于 2008-09-18T19:48:55.433 回答
0

这个软件工程广播播客是错误处理最佳实践的一个很好的参考。实际上有2个讲座。

于 2008-09-18T20:37:04.977 回答
0

一般来说,这是不好的做法,除非您出于非常特定的原因需要登录。

就一般日志异常而言,应该在根异常处理程序中处理。

于 2021-02-04T16:10:33.930 回答