日志记录代码是否应该抛出异常?

时间:2015-02-10 15:45:18

标签: c# exception

我正在设计一个小型图书馆,它应该有助于登录不同的目的地(文件,数据库等)。但是,如果出现问题(例如无法写入文件),我不确定是否应该抛出异常?

如果我抛出异常(与日志记录相关),使用我的库的应用程序开发人员可以清楚地看到日志记录不会按预期发生。另一方面,它可能是另一个问题来源。

如果我禁止例外并且只是安静地不记录,那么开发人员可能会错过重要信息,例如:无法访问数据库。

是否有任何关于如何处理此问题的建议,一般指导原则(如果是b等)?


到目前为止我已阅读:

Microsofts Guidelines for Exceptions州:

  

✗不要让公共成员根据某种选择投掷或不投掷。

因此,是否抛出异常的选项将违反这些指南,但如果不确定异常的可能性,则不会发出抛出异常的声明。

我还在this question/answer中读到我不应该捕获异常,因为我不能对它们做任何有意义的事情,但问题是针对企业级系统,而我的问题则在更一般的层面上提出。

4 个答案:

答案 0 :(得分:3)

不要教条地遵循任何准则。请参阅指南,然后按照您认为最适合解决问题的方法进行操作。

记录器是一种特殊的软件:它有特殊的需求和考虑因素。因此,microsoft发布的指南不适用于您的记录器,其方式与应用于通用软件的方式完全相同。

在我的书中,根据" Is-This-A-Development-Environment"来改变记录器的行为是完全正确的。标志,并在true时抛出异常,或者抑制这些异常(可能将它们记录在一些不太容易出错的介质中),否则。

答案 1 :(得分:1)

有趣的是,就在本周,我在我的一个应用程序中添加了一个新的NLog目标,它没有记录,但也没有抱怨。因此,让我感到震惊的是,NLog团队已经针对失败的日志目标做出了这个决定。我可以告诉你,NLog无法登录到目标。

然而 - 并非完全。它们有一个内部记录器,可用于调试记录器:https://github.com/NLog/NLog/wiki/Internal-Logging 您可以这样配置:

<nlog internalLogFile="c:\log.txt" internalLogLevel="Trace">
   <targets>
      <!-- target configuration here -->
   </targets>
   <rules>
      <!-- log routing rules -->
   </rules>
</nlog>

由于NLog(与Log4Net一起)可能是.NET最大的日志记录库,因此您可能对此感兴趣。

我看到的另一件事是有些图书馆引用https://www.nuget.org/packages/Common.Logging/,我发现它非常优雅。它是一个非常轻量级的包,带有一组可用于记录的接口。然后,调用程序集通过使用适用于NLog或Log4Net的适配器来控制输出发生的情况,并继续使用预先存在的目标。

答案 2 :(得分:1)

正如评论已经说明的那样,这是一个设计决定,因此在技术意义上没有正确答案。

我通常这样做:

  • 如果出现问题,我会在第一次尝试时抛出异常。
  • 我在日志记录框架内捕获此异常,并将其写入系统的事件日志(应始终成功)。
  • 如果由于某种原因也失败了,我会默默地吞下错误。
  • 或者,您可以在调试版本中重新抛出异常或将其写入调试输出。

答案 3 :(得分:0)

我建议在很多情况下应忽略Microsoft引用的声明。作为日志记录类的模式,我建议使用一种方法,该方法返回“日志抑制”令牌,然后抑制日志记录期间发生的异常,但是有一个方法可以取消日志抑制的效果token和,根据传入的参数,返回已被抑制的异常列表,否则,当列表非空时,抛出包含列表的复合异常。我进一步建议日志记录请求要求以后进先出的方式使用令牌,并且在取消先前创建的令牌的影响之前未能取消令牌的效果应被视为例外值得使用错误。

即使这违反了微软的建议,基于参数抛出或不抛出方法也有两个优点:

  1. 在许多情况下,支持try / do模式的操作会调用支持相同模式的方法。如果public try / do方法链接到一个使用参数来区分“try”和“do”的方法,并且要调用的嵌套方法也支持这样的参数,那么就可以进行“try”和“do” “方法共享相同的实现。如果所有公共方法都是“只尝试”或“只做”,那么“do”希望内部方法中的问题显示为它们抛出的异常,并且“try”不希望嵌套“do”方法抛出它必须捕获的异常,然后即使调用者试图在内部使用共享代码,他们也最终不得不:

    if (throwOnException)
    {
      thing.ReadData(whatever);
      success = true;
    }
    else
    {
      success = thing.TryReadData(whatever);
    }
    

    代码片段足够大,应该是它自己的方法,但在语义上比客户端更多地属于thing

  2. 如果tryfinally部分内的所有内容都成功,try / finally块只会在不抛出异常的情况下退出。但是,如果try中发生异常,那么传播该异常通常比让finally中抛出的异常覆盖它更好。通常,无论finally块是否成功,try代码的行为都是相同的,除了try块成功时发生的异常应该成功允许传播,而try阻止失败时发生的那些应该被扼杀。

  3. C#和VB都没有为finally块提供一个很好的方法来知道try是否成功,但是有可能。如果finally块有时会想要抛出异常并且有时会扼杀它们,但是行为相同,那么选择问题报告行为的参数将比使用单独的代码用于pending-exception更清晰 - 异常情况。