【问题标题】:Catching C# errors at the class library level在类库级别捕获 C# 错误
【发布时间】:2011-08-19 17:01:59
【问题描述】:

我正在尝试为企业级类库制定错误日志记录解决方案。一些要求:

  • 它将被多个端点应用程序使用(因此应该在类库中处理它)
  • 它应该将其数据放入 SQL Server 数据库以便于存储。这也涉及到第三个:
  • 它应该能够支持自定义 Web 应用程序读取其数据。

还有一个要求,那就是“非常高兴拥有”。也就是说,这并不重要,但如果不可行,我最好有一个很好的理由。

  • 在其端点应用程序中不应需要专门的 Logger 对象。他们应该能够简单地抛出异常,并且日志软件应该从那里处理事情。不过,有一个特定的 USING 命令是合乎情理的。

综合考虑,前三点并不难。一个快速的日志类和一些存储过程,中午就收工了吧?最后一点是我不知道如何处理的事情。我以前在朋友的代码中看到过它,所以我知道这是可能的,但不幸的是,朋友已经通过了,他的代码丢失了。有没有人可以为我指出正确的方向,让类库对象以某种方式捕获来自端点应用程序的所有错误以处理它们?

【问题讨论】:

  • Re: #2: 那么你将如何记录无法连接到 SQL Server 的错误呢?
  • 那么您想知道如何让您的类库捕获它可能抛出的所有错误吗?
  • 您是说您希望应用程序中所有未处理的异常都由您的库处理吗?
  • 方面编程可能对日志记录有用。
  • @Seva:可能会向值班的系统管理员发送一封恐慌电子邮件。不过,这超出了问题的范围。

标签: c# logging exception-handling class-library


【解决方案1】:

如果您想捕获并非源自您自己的调用堆栈的所有未处理异常,您可以考虑使用 AppDomain 对象:

AppDomain.CurrentDomain.UnhandledException += (s, e) => {
    // Your logging logic
};

AppDomain.CurrentDomain.FirstChanceException += (s, e) => {
    // Your logging logic
};

【讨论】:

    【解决方案2】:

    听起来微软企业库可能在这里有用...

    http://msdn.microsoft.com/en-us/library/ff649552.aspx

    【讨论】:

    • 除非我错过了什么,否则绝对不会。最后一个要求的主旨是日志记录应该是简单的东西,你不应该重构基本的抛出逻辑来使用它。企业库需要适度的重构,因此不合适。我们放弃这个想法还有其他原因,但这些 cmets 的短暂性不允许我在这里深入探讨。
    • 企业库有一些很好的日志记录和异常处理,尽管我承认在某些情况下它有点过头了。这真的取决于你想要记录什么,这里有一个很好的例子来记录未处理的异常和 asp.net 项目......davidhayden.com/blog/dave/archive/2006/02/15/2802.aspx如果不了解更多关于你的设置,不确定这对你是否有太大用处。另一个很好的异常记录系统是我以前使用过的 elmah,它也记录到 xml 或 sql...code.google.com/p/elmah 但同样是 asp.net。您能给我们提供更多信息吗?
    • ELMAH 不合适,因为它在应用程序级别,我需要在类库级别,以统一的方式收集和处理所有内容 - 如果这有意义的话。老实说,在这种情况下,我认为放弃几乎任何专门用于“asp.net 项目”的东西是安全的,因为这将不可避免地处理最终应用程序,而不是在更高级别上进行日志记录从类库中包含和访问。
    【解决方案3】:

    当我阅读企业级类库时,我的脑海中有这些要求:

    • 您的组件不得抛出异常并扰乱系统。
    • 它应该优雅地处理错误并能够从错误中恢复。

    对于作为开发人员的您来说,这些要求是您睡觉前的好读物。所有的错误场景,即使是你最疯狂的梦想,迟早都会成为现实,你会在凌晨 3 点尖叫着醒来。

    要求听起来不错,但其影响深远。如果您尝试捕获组件中的所有错误,则除了隐藏错误之外别无他法。这样的系统往往很慢,有时它们只是停止工作,没有人知道为什么,因为一些后续错误确实使系统进入了一个实际上没有任何工作的状态。可悲的是,如果您将调试器附加到那台陈旧的机器上,您将不会看到导致问题的原始错误,而是一个后续问题,您必须从那里返回到原始问题。如果您想登录到 SQL 服务器,我可以向您保证,您最终会经常没有或丢失日志。

    您应该考虑更可靠的日志目标(自定义事件日志、日志文件……或两者兼有)。并且不惜一切代价避免异步日志记录,因为未处理的异常会比您在日志记录线程中记录它的速度快得多。

    实际上,当发生致命错误时,您需要尽早停止系统。您可以在不破坏状态的情况下安全恢复的错误通常很少,但值得付出努力,因为您需要将类库设计为健壮(处理预期的错误)和安全(抛出异常,记录它们,...... )。

    在设计阶段,更容易想到一个可以处理所有错误的完美系统。软件架构师喜欢这样的系统。他们可以侥幸逃脱,因为在纸上看起来不错。或者换句话说:泡沫不会崩溃。

    但是在执行时你的类库正在使用

    • 实际物理内存 (OutOfMemoryException)
    • 真实硬盘空间(至少是日志文件)(硬盘已满)
    • 实际 CPU 周期(100% CPU)
    • 实际堆栈空间 (StackOverFlowException)

    如果你试图隐藏致命错误,系统只会变得更难调试,但它仍然会失败,因为你的库将消耗不完全属于你的组件的共享资源。在这种情况下,最好立即停止工作,而不是继续让其他人也失败。

    隐藏组件内所有错误的想法是有缺陷的,因为您无法防止组件内的致命错误影响系统中的其他组件。

    我说我希望你没有问如何在类库级别捕获所有错误,而是在你向用户显示错误并记录它们的端点应用程序中(希望只在每一层中一次,而不是在每一层中再次,因为你不要相信其他层)。正如其他人指出的那样,您可以处理未处理的异常,这些异常确实终止了一个线程,而当您的错误处理程序离开时,该线程又将终止您的进程。除此之外,最好在 Main 方法周围放置一个 try/catch 块来捕获主线程中的错误。如果您的应用程序有一个 UI(Windows 窗体),您还应该注册 Application.ThreadException 事件,当异常从 Window 消息处理程序传播出来时将调用该事件。

    你的, 阿洛伊斯克劳斯

    【讨论】:

    • 我不确定您从哪里得到我试图隐藏错误的想法。恰恰相反,我试图给他们更多的可见性和持久性。我不打算吞下或隐藏致命错误;如果程序要崩溃,那么它就会崩溃。我只会知道它是如何走到那一步的。不过,您关于更可靠目的地的观点是正确的。在我们得到更简单的 SQL 接收器工作情况后,我们实际上打算添加一个 MSMQ 事件接收器。
    • 对不起,如果我误解了您的问题。 MSMQ 是可靠的,但速度很慢。如果您想知道您是如何达到这一点的,您还需要将跟踪添加到您的应用程序中。日志记录主要用于错误报告,但不遵循您的应用程序流程,因为您需要记录的数据量要高几个数量级。 geekswithblogs.net/akraus1/archive/2009/06/21/132968.aspx。错误应该尽可能可靠地传输,而跟踪应该尽可能快。
    猜你喜欢
    • 2010-11-23
    • 1970-01-01
    • 1970-01-01
    • 2023-01-16
    • 2011-04-08
    • 1970-01-01
    • 1970-01-01
    • 2017-11-10
    • 1970-01-01
    相关资源
    最近更新 更多