我应该为了记录目的而捕获异常吗?

public foo(..)
{
   try
   {
     ...
   } catch (Exception ex) {
     Logger.Error(ex);
     throw;
   }
}

如果我在每个层(数据访问、业务和 Web 服务)中都有此设置,则意味着异常会被记录多次。

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

有帮助吗?

解决方案

当然不。你应该找到正确的地方 处理 异常(实际上做一些事情,比如捕获而不重新抛出),然后记录它。当然,您可以而且应该包含整个堆栈跟踪,但遵循您的建议会使代码中充满 try-catch 块。

其他提示

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

我的最佳实践是:

  1. 仅在公共方法中尝试/捕获(一般情况下;显然,如果您正在捕获特定错误,您会在那里检查它)
  2. 仅在抑制错误并重定向到错误页面/表单之前登录 UI 层。

一般的经验法则是,只有当您确实可以采取措施时,您才会捕获异常。因此,在业务或数据层,您只会在如下情况下捕获异常:

            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 - 而不是抛出异常。

好的做法是 翻译例外情况. 。不要只是记录它们。如果你想知道抛出异常的具体原因,抛出具体的异常:

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);
   }
}

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

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

在你的例子中,你什么也没做,只是捕获异常,记录它,然后再次抛出它。如果您所做的只是记录它,为什么不直接通过一次 try/catch 在最高级别捕获它,而不是在每个方法中捕获它?

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

有时您需要记录在处理异常时不可用的数据。在这种情况下,只需登录即可获取该信息。

例如(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
    }
}

这类似于追溯日志记录(我的术语),您可以缓冲事件日志,但除非存在触发事件(例如引发异常),否则不会写入它们。

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

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

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

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

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

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

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

public foo(..)
{
   try
   {
     ...
   }
   catch (NullReferenceException ex) {
     DoSmth(e);
   }
   catch (ArgumentExcetion ex) {
     DoSmth(e);
   }
   catch (Exception ex) {
     DoSmth(e);
   }
}

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

通过这种方式,您可以在服务器上记录异常情况,而无需在客户端计算机上查找发生的情况。

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

您需要制定处理异常的策略。我不建议捕获并重新抛出。除了多余的日志条目之外,它还使代码更难以阅读。考虑在构造函数中写入异常日志。这会为您想要恢复的异常保留 try/catch;使代码更易于阅读。为了处理意外或不可恢复的异常,您可能需要在程序最外层附近使用 try/catch 来记录诊断信息。

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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top