【问题标题】:When to throw exceptions and when to log them?何时抛出异常以及何时记录它们?
【发布时间】:2010-12-25 13:46:58
【问题描述】:

我有一个三层结构
1.表现层
2.业务层
3. 数据层
表示层通过服务外观与业务层交互。现在我很困惑我应该在哪里抛出我的异常,我应该在哪里记录它们以及我应该在哪里捕获和吞下它们。目前,我正在记录后在我的表示层中吞下我的异常,而我正在记录并将它们扔到其他任何地方。这导致异常被记录三次。这不是一个大问题,但我想知道这方面的最佳做法。

例如: 表示层代码:

try 
{
  UserService.GetAllUsers() ;
}
catch(Exception ex )
{
  Logger.log(ex) // exception gets logged here
  // redirect to a friendly user error page 
}

用户服务层代码

try
{
  IUserDAO userDAO = ServiceRegistry.GetRegistry().GetDAOFactory().GetUserDAO() ;
  return userDao.GetAllUsers() ;
}
catch ( Exception ex)
{
  Logger.log(ex) ;  // and here !
  throw;
}

DAOL层代码

try
{
  // All db interaction code 
}
catch(Exception ex)
{
  Logger.log(ex) //and here !!
  throw ;
}

【问题讨论】:

  • 这一切都在同一台物理机器上吗?或者你有服务器和客户端吗?在我们的应用程序中,我们通常(尝试在所有物理机上记录)一次错误消息...
  • 目前这些都在同一台物理机上,但将来我们可能实际上将它们放在不同的物理机上。这有关系吗?
  • 我认为确实如此。如果服务器上出现异常,您希望将其记录到那里,但也希望在调用导致异常发生的方法的客户端上记录,因此可以在两个位置跟踪错误。可能看起来您记录了很多/太多,但由于它而出现性能或存储问题的可能性非常小,所以我会这样......

标签: .net exception-handling


【解决方案1】:

绝对没有必要将每段代码都放在一个 try-catch 块中。例如,如果您的“DAOLayer”中发生异常,您的日志文件将包含相同的异常 3 次...

记住例外的黄金法则 - 例外应该是例外的!这通常意味着您应该只捕获您能够处理的异常 - 所有其他您应该忽略的异常 - 更高级别的处理程序应该处理这些。

如果您担心未处理的异常会使您的应用程序崩溃(这是正确的),您应该查看 .Net 为处理未处理的异常而公开的 2 个事件。第一个是 AppDomain.CurrentDomain.UnhandledException,第二个是 Application.ThreadException(位于 Windows.Forms 命名空间中)。

【讨论】:

    【解决方案2】:

    我真的不明白您为什么要记录异常并重新抛出它。当然,无论哪个进程最终吞下它都可以进行日志记录。如果您想记录该信息,您可以简单地使用 ex.StackTrace 来获取调用堆栈。

    通常,当导致异常的问题无法在代码所在级别解决时,您会重新抛出异常。您通常会使用更具体的 catch 块来捕获这些并做出适当的响应(例如,如果由于网络超时而出现异常,您可以重试连接或尝试连接到备份服务器)。您希望在修复异常时记录异常并继续进行。

    如果在遇到异常时您无能为力,我只会费心去捕获并记录它,如果我不能指望调用堆栈更下方的东西来为我处理日志记录。否则它只会创建一个相当无意义的重复消息。

    【讨论】:

    • 图层的客户端可能会改变,如果我没有登录我的图层,我有点依赖客户端进行日志记录,如果他们不这样做..我在汤。
    • 嗯,这是在多个地方登录的合理理由 - 因为您不能指望客户负责记录。不过,可解决的错误仍应单独捕获。
    【解决方案3】:

    当异常情况发生时,您应该抛出异常 - 在任何层...如果您希望定期出现一些错误情况,请为它们编写代码 - 向用户显示错误以及修复错误的方法。

    记录所有异常,无一例外!有数据很好,如果你按照我之前的建议,你不会有太多的。

    您应该永远吞下异常。往上看。否则你怎么知道事情是否出错了?

    【讨论】:

    • 吞下异常而不是让应用程序失败有时可能是正确的选择。它是用正确性换取鲁棒性,有时鲁棒性更重要。
    • 始终记录。您可以根据要求决定是否要传播异常。
    【解决方案4】:

    我倾向于为每个级别创建一个异常,如果下一层无法处理异常,我会包装异常并传递它。

    例如,如果您尝试更新数据库,但主数据库已关闭,您可能只需要转到辅助数据库,因此您尝试这样做,如果成功则记录,但不要通过它开。

    我倾向于传递无法处理的异常,直到我到达顶层。在那里,对于一些错误将它们传递给客户端,如果这是一个异常用户可以做一些事情,否则,记录并吞下。

    如果是内存不足,请让用户知道,并准备在应用程序崩溃之前优雅地死掉。

    【讨论】:

      【解决方案5】:

      当您在最顶层记录异常时,您可以使用ex.Data 在传递异常之前添加有关异常的更多信息,而不是在自定义异常中包装异常或仅依靠 StackTrace 为您提供所需的所有信息上链,例如ex.Data.Add("filename", filename)。然后,您的日志记录代码可以转储所有这些附加属性。这可能会为您提供足够的信息来了解引发异常的原因并重现它。

      【讨论】:

        猜你喜欢
        • 2017-07-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-26
        • 2019-12-27
        • 2010-10-17
        • 2011-04-08
        • 1970-01-01
        相关资源
        最近更新 更多