65.9K
CodeProject 正在变化。 阅读更多。
Home

XQuiSoft 日志提供程序模式(开源)概述

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.26/5 (11投票s)

2006 年 4 月 19 日

BSD

17分钟阅读

viewsIcon

113311

downloadIcon

476

XQuiSoft 日志介绍,并与其他日志组件进行比较

引言

XQuiSoft Logging 是一个开源组件,用于支持 .NET 应用程序中的日志记录。它是 XQuiSoft 应用程序框架的一部分,该框架完全开源。它超越了 .NET Framework 内置的 System.Diagnostics.TraceSystem.Web.TraceContext 的功能。您可以从 http://sourceforge.net/projects/xqs-logging 获取项目的最新版本以及单元测试(演示)代码。

此日志组件以前依赖于 xquisoft 框架中的另外两个开源项目。截至 2010 年 12 月,这些项目已全部分发在一起。

演示代码须知

解压演示项目时,您需要执行以下两项操作之一。

  • 将 Web 项目移动到 inetpub\wwwroot。并将解决方案和资源文件夹复制到 C:\SomeFolder\
  • 更新解决方案文件,使其指向每个项目的正确路径,并创建指向项目的项目名称的虚拟文件夹。然后,更新所有日志包引用,使其指向资源文件夹的新位置。

在我的测试机器上,解决方案位于我的 C:\ 驱动器上的一个工作文件夹中,我将其用于所有项目。然后,当我添加 Web 项目时,它们被添加到 inetpub\wwwroot。这使得打包解决方案有些困难。

基本功能

易于使用:您要做的就是通过调用 Log 类上的单个 static 方法来写入一条日志消息。日志方法有多种重载,可用于指定更多的或更少的信息。在编写消息时,您无需创建日志记录器实例,也无需选择特定的日志记录器进行写入。实际上,您只需键入“Log.Write(...);”,其中 ... 是消息以及您认为需要添加的其他详细信息。

可扩展性:日志组件基于提供程序模式。这意味着 Log 类将实际的写入调用委托给一个或多个已配置的提供程序(监听器),这些提供程序派生自基类 LogProvider。每个实现提供程序定义日志事件的写入位置、是否缓冲,或写入给定数据源所需的任何其他自定义配置选项。如果您有自定义数据存储,您可以创建自己的日志提供程序并为其配置为在您的应用程序中使用。然后,由其他组件作者编写的任何组件都会自动使用您的日志提供程序(假设他们编写日志消息:)。

多种配置方法:您可以通过两种方式配置应用程序以使用日志记录。第一种方法是在应用程序配置文件中创建一个配置节。该节将定义要使用的日志记录器,以及要应用于日志记录器的任何过滤器。第二种方法是在运行时初始化一个新的日志记录器并将其添加到 Log 的提供程序集合中。您还可以在应用程序生命周期的任何点配置日志提供程序,或从主 Log.Providers 集合中删除日志记录器。典型的场景是使用配置文件。您可以在下载的测试应用程序中以及本文后面找到示例。文档还包括示例应用程序配置文件节。有关如何配置每个日志提供程序的详细信息,请参阅每个日志提供程序的帮助。LogConfiguration 的帮助也显示了如何组合多个日志提供程序。

稳定性: Log 类本身通常不会向尝试写入日志事件的组件抛出异常。日志组件中的异常由您选择的备份日志记录器处理。每个日志记录器可以有不同的备份日志记录器,并且备份日志记录器可以链式设置。如果没有备份日志记录器,或者备份日志记录器也失败,那么日志错误将抛给调用者。

灵活的过滤:过滤也基于提供程序模式。有一个基类 FilterManager,带有一个提供程序集合,其中每个提供程序都派生自 FilterProvider。每个过滤器提供程序都通过一个键(过滤器的名称)进行访问。内置了强大的过滤器,但您可以创建自己的过滤器提供程序。过滤器可以与内置的“Or”和“And”过滤器组合成更复杂的过滤器。每个日志提供程序都可以配置为使用相同或不同的过滤器提供程序。

组合:日志可以配置为具有子日志记录器。例如,BufferedLogProvider 将所有消息保存在内存缓冲区中。当需要刷新缓冲区时,它会将这些消息发送给一个或多个组成日志提供程序。其中每个都可以选择记录,或尝试在记录之前进一步过滤每条消息。并非所有日志记录器都支持组合,只有在有意义的情况下才支持。有关详细信息,请参阅每种日志提供程序类型的帮助。您可以将任何其他日志提供程序配置为 BufferedLogProvider 的组成部分,因此,简而言之,所有日志提供程序目标都可以缓冲。

事件构成:单个日志事件由级别、来源、类别、消息、详细信息和时间戳组成。级别是表示重要性的整数尺度。内置级别包括错误 (256)、警告 (512)、信息 (1024) 和详细 (2048)。您可以使用这些级别,或创建介于这些级别之间的自定义级别。源是调用消息的实体。类别顾名思义,是源对一条消息相对于另一条消息进行分类的方式。消息和详细信息是长度不同的信息字段。请注意,过滤器可以基于这些字段中的任何一个。

高性能:框架本身不会对应用程序造成任何明显的影响。开销在于写入目标数据源所需的操作。例如,写入文件需要打开和关闭文件的开销。但是,通过结合使用 BufferedLogProviderFileLogProvider,此开销仅在配置的时间间隔内发生。请参阅性能测试结果,或自行运行测试。测试代码可作为本文的下载项,该下载项比较了 XQuisoft Logging、log4net、Enterprise Library (logging) 和 nspring。

内置 LogProviders

  1. DebugLogProvider - 写入 System.Diagnostics.Trace System.Web.TraceContext (如果适用)
  2. ConsoleLogProvider - 写入 System.Console
  3. EmailLogProvider - 将日志消息写入电子邮件。仅推荐用于需要立即关注的错误事件。
  4. FileLogProvider - 将日志消息写入文件。文件名可以根据正在记录的事件进行格式化。这就是您可以实现每小时(或其他间隔)生成一个新日志文件的方法。
  5. RollingFileLogProvider - 将日志消息写入滚动文件列表。您可以配置文件的数量以及每个文件的大小。当您希望将日志记录到磁盘但将磁盘空间使用量保持在给定最大值内,并且只保留最近的日志条目以帮助诊断错误时,此方法最有效。
  6. BufferedLogProvider - 作为共享缓冲区,供一个或多个其他日志提供程序使用。此提供程序可以拥有子日志。当缓冲区达到配置的缓冲区大小时,所有缓冲的消息将一次性发送到子日志。
  7. SessionLogProvider - (仅限 Web 项目)将所有消息存储在当前用户的会话中。如果记录了错误级别的事件,则将缓冲区刷新到选择的其他提供程序。可以配置最大大小(默认为 32)以防止 sessionState 变得过大。
  8. DbLogProvider - 将消息记录到任何支持存储过程的数据库。它附带 SQL Server 的安装脚本。此提供程序与内置的其他日志提供程序位于不同的程序集中,并且它依赖于 XQuiSoft 框架中的 XQuiSoft.Data 组件。您也可以在 sourceforce 上找到它,网址为 http://sourceforge.net/projects/xqs-data/。此组件在本 篇文章中进行了介绍。
  9. NullLogProvider - 日志消息将被丢弃。如果您想忽略日志记录期间的所有异常,以便日志记录失败不会导致您的应用程序崩溃,请将其设置为您日志记录器的 ExceptionProviderName。如果您使用此提供程序并且日志记录配置错误,您将看不到任何日志被写入。

如果 FileLogProviderBufferedLogProviderSessionLogProvider 的子日志,那么对于整个事件缓冲区,输出文件将只打开一次,假设每个事件都进入一个文件。输出循环只会在事件之间或当下一个事件具有不同的目标文件时关闭文件,并打开一个新文件。当目标文件因事件的 timestamp 或其他属性而异时,就会出现这种情况。有关示例,请参阅单元测试,有关实现细节,请参阅源代码。

如果 EmailLogProviderBufferedLogProviderSessionLogProvider 的子日志,那么对于整个事件缓冲区,将只生成一封电子邮件。电子邮件中的每个事件都将出现在新的一行上。每个事件的格式都可以自定义。

内置过滤器

  1. LevelFilter - 仅接受给定级别范围内的事件。MaxLevel MinLevel 定义范围。
  2. ExpressionFilter - 可以过滤任何单个日志事件字段。它与正则表达式匹配。此过滤器的单元测试代码包含示例。
  3. AndFilter - 允许对其他过滤器进行分组,以便所有子过滤器都必须接受,AndFilter 才能接受。可以通过 IsInclusive 设置来否定该过滤器。
  4. OrFilter - 允许对其他过滤器进行分组,以便只有一个子过滤器被接受,OrFilter 才能被接受。评估在第一个接受的子过滤器处终止。当 IsInclusive 配置为 false 时,仅接受所有子过滤器都失败的事件。

所有过滤器都可以配置为 Inclusive 等于 true false。这就像在过滤器前面有一个 !(非)运算符。您可以任意嵌套任意数量的 OrFilterAndFilter 来获得所需的结果。所有过滤器都添加到同一个 FilterManager 中,每个过滤器都必须有一个唯一的名称,否则当重复命名的过滤器添加到管理器时会抛出异常。有关每种过滤器的用法示例,请参阅单元测试。

配置

首先,在 configSections 中定义自定义配置节的结构,如下所示。您只能有一个 configSections 节点,因此请将此处的内容与您当前拥有的内容合并。

  <configSections>
    <sectionGroup name="XQuiSoft.Logging">
      <section name= "Log" type="XQuiSoft.Logging.LogConfiguration, XQuiSoft.Logging" />
      <section name= "FilterManager" type="XQuiSoft.Logging.FilterConfiguration, 
		XQuiSoft.Logging" />
    </sectionGroup>
  </configSections>

然后,您需要定义您的自定义设置。下面是一个示例,将所有日志消息写入文件,并将错误消息写入电子邮件。所有日志消息首先发送到缓冲区,这允许您的应用程序代码继续运行。然后每 10 秒,一个后台线程会将所有缓冲的消息发送到电子邮件日志记录器和文件日志记录器。电子邮件日志记录器筛选消息,如果其中有任何错误或警告,则会向开发人员发送电子邮件。如果它们都是调试或信息消息(正常运行),则该缓冲区不发送电子邮件。文件日志记录器将从缓冲区发送的所有消息保存到当天的文件中。每天都有不同的日志文件。如果在电子邮件日志记录器或文件日志记录器中发生任何错误,则会将一条日志消息保存到另一个文件中。如果故障转移失败,则会发生应用程序异常。如果这是一个 Web 应用程序,则会在 global.ascx 中触发 Application_Error

从 2010 年 12 月发布开始,BufferedLogProvider 现在将在调用 Log.Fail 时发送所有缓冲的消息。因此,在发生错误时,您将立即看到磁盘上的文件已更新,并且电子邮件已立即发送。

  <XQuiSoft.Logging>
    <Log>
      <providers>
        <add name="BufferLogger" type="XQuiSoft.Logging.BufferedLogProvider, 
		XQuiSoft.Logging"
             TargetProviderNames="FileLogger,EmailLogger" OutputInterval="10" />
        <add name="EmailLogger" type="XQuiSoft.Logging.EmailLogProvider, XQuiSoft.Logging"
             FromAddress=your-application@your-domain.com 
		ToAddresses="developers@your-domain.com"
             ExceptionProviderName="Failover"
             Subject="ERROR IN your application on server XYZ" 
		FilterName="Verbose" ActiveRoot="false"
             ItemFormat="{Timestamp:yyyyMMdd:HHmmss}: {Level}: {Source}: 
		{Category}: {Message}: {Details}"
             SmtpServerName="corp.dir.your-domain.com" SmtpPort="25" />
        <add name="FileLogger" type="XQuiSoft.Logging.FileLogProvider, XQuiSoft.Logging"
             FilePath="logs\application-{Timestamp:yyyyMMdd}-log.txt" ActiveRoot="false"
             ExceptionProviderName="Failover"
             ItemFormat="{Timestamp:yyyyMMdd:HHmmss}: {Level}: 
		{Source}: {Category}: {Message}: {Details}" FilterName="Verbose"/>
        <add name="Failover" type="XQuiSoft.Logging.FileLogProvider, XQuiSoft.Logging"
             FilePath="failover-{Timestamp:yyyyMM}-log.txt" ActiveRoot="false" />
      </providers>
    </Log>
    <FilterManager>
      <providers>
        <add name="Verbose" type="XQuiSoft.Logging.LevelFilter, 
		XQuiSoft.Logging" MaxLevel="Verbose" />
        <add name="ErrorsAndWarnings" type="XQuiSoft.Logging.LevelFilter, 
		XQuiSoft.Logging" MaxLevel="Warning" />
      </providers>
    </FilterManager>
  </XQuiSoft.Logging> 

为什么使用 XQuiSoft Logging 而不是 log4net、Nspring 和 Enterprise Library?

其他日志框架是令人满意的。如果您在应用程序中使用其中一个框架,您可能不想切换。但是,我认为使用 xquisoft 日志记录组件可以编写更少的代码。因此,在您的下一个项目中,请考虑以下几点。以下是我注意到的几个差异点。

如果您发现其中任何内容不正确,请告诉我,我将纠正本文。

其他框架很难使用吗?不。但它们并非都那么容易使用。以 NSpring 为例。我没有在 NSpring 中看到任何地方可以创建日志配置。您还必须在应用程序中实例化一个日志记录器,打开它,写入它,然后关闭它。为什么要做这么多工作?如果您为他人编写一个组件,并且想要记录一些内容,您怎么知道您的客户想记录到哪里?您是否让他们将日志记录器传递给您?

NSpring 代码

    Logger log = Logger.CreateConsoleLogger();
    log.Open();
    log.Log("my message", "my category");
    log.Close();

在 log4Net 中,您显然有两种选择。您要么必须知道目标日志记录器(或 appender)的名称,要么必须通过反射来获取它。

Log4net (选项 1)

    ILog Log = LogManager.GetLogger("BufferingForwardingAppender");
    Log.Debug("Test log message");

Log4net (选项 2)

    private static readonly ILog Log = LogManager.GetLogger
				( MethodBase.GetCurrentMethod().DeclaringType);
    Log.Debug("Test log message");

在 XQuiSoft 中,您只需要一行代码。Log 类拥有所有 static 方法,并且每个方法都会委托给已配置的提供程序。Enterprise Library 的用法与您的代码非常相似。

XQuiSoft Logging

    Log.Write(Level.Info, "my category", "my message");
    // 'Log.Info' is a constant integer value.

Enterprise library

    Logger.Write("Message To Log", "Category Name", 0);
    // 0 is the message level, you can also use an enum 

所有这些日志包都是可扩展的。它们都提供一个 abstract 基类供您派生。基类提供调用另一个必须在自定义提供程序中实现的 abstract 方法重载的重载。XQuiSoft 将它们称为“providers”(提供程序),nspring 将它们称为“loggers”(日志记录器),log4net 将它们称为“appenders”(附加程序),而 Enterprise Library 将它们称为“sinks”(接收器)。

最后,我测试了每个日志包的相对性能。我创建了一个单一的解决方案。每个测试都包含在自己的 Web 项目中,这样测试结果就不会受到一个日志包的配置和/或内存开销的影响。每个项目包含一个不显示任何内容的 Web 页面,但在 Page_Load 方法中会将某些内容写入日志。还包含另一个不执行任何操作的 Web 应用程序作为最大性能基线。然后,我使用了 Microsoft Application Center Test 进行负载测试。我模拟了 30 个并发浏览器连接,5 秒的预热时间,以及 5 分钟的持续时间。以下是每个测试的结果,性能最好的排在最前面

Application Name 迭代 平均 RPS 丢失
基线应用程序 331,709 1,104.24 0%
XQuiSoft 文件日志记录器 324,862 1,081.50 2.059%
XQuiSoft 控制台日志记录器 309,499 1,030.51 6.677%
log4net 控制台日志记录器 330,333 996.92 12.436%
nspring 文件日志记录器 258,899 861.49 21.983%
log4net 文件日志记录器 248,153 824.85 25.302%
nspring 控制台日志记录器 199,719 664.74 38.801%

奇怪的是,xquisoft 文件日志记录器是可能的最高效的日志记录方式。它比 xquisoft 控制台日志记录器快的原因是,控制台日志记录器没有通过缓冲区发送。但我认为缓冲控制台写入不会提高速度,因为每次 Console.Write 都不会打开一个资源,即使打开了,也没有办法请求它在调用之间保持打开状态。我使用了 BufferedLogProvider,间隔为 20 秒,在刷新时写入 FileLogProvider。文件日志记录器的文件名根据 timestamp (精确到分钟)而变化(例如,FilePath="~\log\{Timestamp:yyyyMMdd-HHmm}-log.txt")。因此,一些写入(刷新)操作包含发送到两个不同日志文件的缓冲区中的项目(例如,20060419-1146-log.txt 和 20060419-1147-log.txt)。当您查看 XQuiSoft 文件日志记录器测试的图表时,可以看到每 20 秒写入操作发生时性能会下降。

log4net 文件日志记录器实际上是缓冲的,尽管看起来它没有缓冲。我使用了大小为 100BufferingForwardingAppender,它写入 RollingFileAppender。所以它应该试图为每 100 个事件保持文件打开。然而,当我查看 MACT 测试时,图表并没有显示规律的低点来表明缓冲区运行得那么好。

我无法解释 nspring 文件日志记录器为何比 nspring 控制台日志记录器和 log4net 文件日志记录器都快。总体而言,根据 MACT 测试结果图,nspring 日志记录器的性能水平非常不稳定。这可能是因为 nspring 日志记录器不支持配置文件,我必须在每次 page_load 时重新创建一个日志记录器。大部分开销可以归因于无法缓存日志记录器实例之间的页面调用。如果有人能用改进的 nspring 用法更新我的测试代码,请告诉我。

我没有运行 Enterprise Library 的性能测试,原因有几个。首先,我无法运行它们。显然,您不能仅仅通过 xcopy ent lib DLL 来使用它们。您需要安装性能计数器。Web 页面上的一个错误与性能计数器有关,并且无法打开注册表。为了禁用它们,您必须编辑并重新编译库!为什么他们不能提供一个配置设置来不尝试写入性能计数器?我不想处理这些。第二,我没有测试它的原因是,在 log4net 和 ent lib 之间已经有性能比较,显示 ent lib 比 log4net 慢得多。XQuiSoft Logging 比 log4net 快,所以 XQuiSoft Logging 比 ent lib 快。

Microsoft Enterprise Library Logging Block 与 Log4net 的比较后续(Loren Halvorson)
http://weblogs.asp.net/lorenh/archive/2005/03/20/395289.aspx

异常处理

2006 年 6 月 12 日,日志组件的新版本更新了异常的处理方式。早期版本最初采用了不同的异常处理方法。首先,我通常有两个主要的异常处理规则。

  1. 不要吞噬和忽略异常!始终将其报告给日志,和/或应用程序用户。此规则的唯一“例外”(双关语)是如果您可以成功地缓解问题。例如,如果第一种方法不起作用,您可能有备用逻辑。
  2. 不要捕获并传播您没有明确逻辑来处理的异常。不要使用 catch (Exception ex){}。此规则适用于应用程序的数据层和业务层,也适用于独立组件。您应该捕获 Exception 的唯一时间是在 Application_Exception 或等效项中。您在最高级别捕获它以防止全面崩溃。此处理程序通常会记录问题并向用户显示警告。

在日志组件(1.5 版本之前)中,如果日志记录失败,则会吞噬异常。我的推理是,日志记录中的失败不应该导致您的应用程序崩溃。您将显示给用户的实际上是业务层异常。

为了处理日志记录器中的异常,该组件始终在基类 LogProvider 上提供一个名为 ExceptionProviderName 的属性。它确定要将来自主日志记录器的异常发送到的备份日志记录器的名称。您可以“链接”故障转移日志记录器,最终使用一个永远不会失败的日志记录器,可能是系统日志。然而,如果最终的日志记录器失败了,它就会被忽略。

我已经重新考虑了这一立场,以符合我的异常处理规则 #1。现在,链中的最后一个日志记录器将向日志组件的调用者抛出 LogException。然而,如果尝试记录 LogException,它将被忽略,因为如果可以忽略它,它将已经被记录。这可以防止无限异常循环。

如果您仍然想忽略日志记录异常,您可以这样做。配置一个 NullLogProvider 并将其设置为您异常链中的最后一个日志记录器。不要在您的应用程序代码中吞噬 LogExceptions。这样,您可以将最后一个日志异常故障转移配置为 DebugLogProvider ,并让页面输出致命的日志错误,而无需删除该异常吞噬代码。

结论

我已经涵盖了 XQuiSoft Logging 组件的基本功能和用法。我还将 XQuiSoft Logging 与市场上其他主要的日志记录组件/框架进行了比较。从我所看到的来看,XQuiSoft Logging 是一个更好的组件。但话又说回来,我有点偏见,因为我写了 XQuiSoft Logging。我想看到其他人进行类似的比较并看看结果是什么?

如果您已经广泛使用另一个日志记录组件并且对此非常满意,那么请继续使用它。但是,如果您开始一个新项目,请考虑 XQuiSoft Logging 相对于其他组件的优势。评估它,看看它是否适合您的工具箱。

历史

  • 2006 年 4 月 19 日:文章首次提交
  • 2006 年 4 月 20 日:添加了 log4net 和 entlib 性能比较的链接。还添加了此日志组件所需的开源项目的链接。
  • 2006 年 6 月 12 日:添加了新的异常处理策略。更新到组件版本 1.5.5000.0,可在 Sourceforge 上获取。由于没有进行 API 更改,因此未更新示例代码。
  • 2009 年 6 月 18 日:链接已更新。添加了配置示例。更新到组件版本 2.2.50727.0,可在 sourceforge 上获取。由于没有进行 API 更改,因此未更新示例代码。
  • 2010 年 12 月 6 日:更新了一些新功能和新的日志提供程序
© . All rights reserved.