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

开始使用日志应用程序块

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.68/5 (15投票s)

2004年12月16日

17分钟阅读

viewsIcon

239366

downloadIcon

1673

如何让日志记录应用程序块的基础功能与您的应用程序协同工作。

注意: 在项目能够成功生成之前,您需要按照 设置您的环境 中的步骤进行操作。

我已经使用企业库重写了这篇文章,而不是使用 EIF 和独立的日志记录应用程序块。可以在 使用企业库进行日志记录 中找到。

目录

引言

您是否曾遇到过一个日志记录系统混乱不堪的系统?一个似乎将跟踪消息保存到五个不同文件中的系统?或者需要七个不同的注册表标志来控制其输出的系统?

日志记录系统被添加到系统中的方式往往是临时的、按需的。当这种方法应用于具有许多子系统和层的庞大系统时,它可能导致实现许多“自制”日志记录机制;每种实现都有其自身的配置特殊性,并且可能以不同的方式进行日志记录(例如,一个子系统记录到 Windows 事件日志,一个记录到根目录下的文件,一个记录到数据库等)。本文将向您介绍 Microsoft 的 日志记录应用程序块,并展示它如何为应用程序的日志记录带来一致性。

本文档适用于那些从未接触过日志记录应用程序块的人,那些正在评估它的人,以及那些看过它并认为它太麻烦的人。我将概述日志记录应用程序块提供的功能,然后描述如何在您的环境中实现基本功能。

本文档不会深入探讨日志记录应用程序块比 Microsoft 企业仪器框架 (EIF) 提供的附加功能。

背景

许多应用程序,尤其是大型系统,都可以从一致的日志记录方法中受益。.NET 开发人员可用的日志记录库有很多,例如 Log4NetNSpring。截至 2003 年 4 月,又多了一个选择:Microsoft 企业仪器框架 (EIF)。

EIF 提供了一种简单的方式,让您的代码以一致的方式在托管应用程序中引发“事件”。强大的配置文件允许在运行时(而不是编译时)确定这些事件的路由。路由到“事件接收器”可以基于事件类型和事件源的组合。配置文件还允许您指定只有应用程序的某些元素或其中的特定进程产生日志记录。

作为 Microsoft 模式与实践 (Patterns & Practices) 系列的一部分,日志记录应用程序块已经发布。这个应用程序块通过三个新的事件接收器扩展了 EIF

  • Microsoft Message Queuing (MSMQ)
  • SQL Server 基本版
  • SQL Server 灵活版

它还提供了 Windows 事件日志和 Windows 管理规范 (WMI) 事件接收器的新版本,增加了日志级别等功能。另一项新功能是事件转换,它允许您在存储事件之前更改事件的内容。例如,转换可以删除敏感信息或添加额外数据。通过与 Web 服务增强功能 (WSE) 集成,日志记录应用程序块允许日志跟踪跨越 Web 服务边界。

当我第一次查看日志记录应用程序块时,我几乎因为工作量太大而放弃了它。我很高兴我坚持了下来。

基础知识

EIF,以及因此的日志记录应用程序块,使用了三个基本概念来提供日志记录框架:事件、事件源和事件接收器。

事件

事件是当您的应用程序需要记录某些信息时您“引发”的对象。例如,在应用程序中添加以下代码将引发一个 `TraceMessageEvent` 类型的事件

TraceMessageEvent.Raise(“Hello World”);

就是这么简单。好吧,您确实需要添加一些 using 语句,并且您的项目必须引用 EIF……但代码可以这么简单!

EIF 定义了许多事件,例如

  • TraceMessageEvent
  • ErrorEvent
  • AdminMessageEvent
  • AuditMessageEvent

这些都包含许多“字段”,这些字段会在事件引发时自动填充系统详细信息(例如,计算机名称、时间戳、应用程序域名称等)。通过配置文件,可以请求填充其他字段。例如,COM+ 属性(包括当前事务 ID 等字段)或安全信息可以添加到事件的内容中。

每种事件类型还添加了自己的特定字段。例如,`ErrorEvent` 允许设置严重级别字段。

日志记录应用程序块包含 EIF 事件的某些新版本。它们提供了新字段和一些新功能(例如,日志级别)。它还添加了一些全新的事件,例如用于 Web 服务的 `MeteringEvent`。

事件源

事件源允许您指示事件是从何处引发的。所有事件都从事件源引发。如果开发人员没有显式指定事件源,则会使用默认源。上面显示的 the code 没有显式设置源,因此使用了默认的“Application”源。下面的代码显式定义了事件的源

EventSource theBusinessLayerSource = new EventSource(“Business Layer”);
TraceMessageEvent.Raise(theBusinessLayerSource, “Hello World Again”);

`EventSource` 类被称为“软件元素事件源”。这些事件源指示软件的特定部分正在引发事件。它们可用于按照您认为合适的方式对应用程序进行分区。您可能为配置类使用一个,为数据访问层使用一个,甚至为系统中的每个类使用一个单独的。

还有第二种事件源,“请求事件源”。此类型用于指示事件是作为特定进程(或应用程序中的执行路径)的一部分而引发的。例如,您可能使用它来指示事件是作为“创建新客户”进程的一部分而引发的。

RequestEventSource createCustomerSource = 
   new RequestEventSource(“Create New Customer”);
using (RequestTrace request = new RequestTrace(createCustomerSource)
{
   TraceMessageEvent.Raise(“Hello World Once More”);
}

`RequestEventSource` 被包装在 `RequestTrace` 中。`using` 语句确保 `RequestTrace` 被正确终止。在 `using` 语句的打开和关闭括号之间引发的所有事件都将被标记为来自“创建新客户”请求事件源。这包括在其他方法、类甚至程序集中引发的事件(只要它们使用 EIF)。请求事件源可以嵌套,事件将被标记为具有最内层的事件源和根事件源。

软件元素事件源和请求事件源可以一起使用,即,上面的代码可以更改为

using (RequestTrace request = new RequestTrace(createCustomerSource)
{
   TraceMessageEvent.Raise(theBusinessLayerSource, “Hello World One Last Time”);
}

这将导致一个事件同时被标记为“Business Layer”和“Create New Customer”事件源。

关于以上示例的一个警告,创建事件源可能很慢。因此,建议您仅创建一次每个事件源,并将其保留在静态引用中以供重用。

日志记录应用程序块改进了请求事件源的功能。它允许 `RequestTrace` 跨 Web 服务调用工作!

事件接收器

虽然事件源自事件源,但它们最终会到达事件接收器。事件接收器接收事件并负责持久化它们。EIF 提供了三个事件接收器

  • `TraceEventSink` - 写入 Windows 事件跟踪日志文件。
  • `LogEventSink` - 写入 Windows 事件日志。
  • `WMIEventSink` - 输出到 WMI。

如本文的 背景 部分所述,日志记录应用程序块修改了后两者,并增加了三个事件接收器。您也可以编写自己的自定义事件接收器。

查看已记录的事件需要针对每个特定数据存储的读取器。事件查看器可用于 Windows 事件日志。EIF 随附了一个 C# 示例项目 TraceViewer,用于打开 Windows 事件跟踪日志文件。WMI 事件可以通过 WMI 事件查看器进行查看。

连接基础知识

希望上面的部分能让您对 EIF 和日志记录应用程序块提供的功能有所了解。该部分没有描述事件、事件源和事件接收器对象是如何相互作用的。这种“连接”由 EIF 根据配置文件中的设置提供。

还有三个概念需要理解:事件类别、过滤器和过滤器绑定。这些不是在编译时定义的,而是在运行时定义的,当 EIF 读取配置文件时。因此,如果您需要一个新的事件类别,您只需更改配置文件。正是这种灵活性使得 EIF 如此强大。

事件类别

事件可以分组到事件类别中。通过创建类别,您可以独立地为特定事件组打开或关闭日志记录。

通常,您创建的一个类别是“所有事件”类别。您可能希望另一个类别仅包含审计事件,另一个包含跟踪消息等。请注意,一个事件可以属于多个类别。

例如:

<eventCategory name="All Events" 
          description="A category that contains all events.">
   <event type="System.Object" />
</eventCategory>

此片段定义了一个名为“All Events”的事件类别。它指示类型为 `System.Object` 的所有事件,或派生自 `System.Object` 的事件,都应属于此类别。由于每个类都派生自 `System.Object`,因此此类别自动包含所有事件。

过滤器。

过滤器确定哪些事件类别被路由到哪个事件接收器。例如,您可能希望将所有跟踪消息发送到 `TraceEventSink`,并将所有审计消息发送到 `TraceEventSink` 和 `LogEventSink`。

要实现这一点,您首先需要创建两个事件类别,一个包含跟踪事件(参见下文的“跟踪事件”),另一个包含审计事件(参见下文的“审计事件”)。其次,您需要创建一个过滤器,指定来自“跟踪事件”类别的事件应定向到 `TraceEventSink` (`traceSink`),而来自“审计事件”类别的事件应发送到 `TraceEventSink` 和 `LogEventSink` (`logSink`)。如下所示

<eventCategories>

 <eventCategory name="Trace Events">
   <event type="Microsoft.ApplicationBlocks.Logging.Schema.TraceMessageEvent, 
                                                                        ... " />
 </eventCategory>
   
 <eventCategory name="Audit Events">
   <event type="Microsoft.ApplicationBlocks.Logging.Schema.AuditMessageEvent, 
                                                                        ... " />
 </eventCategory> 
      
</eventCategories>

<filters>
   <filter name="filterTraceAndAuditDifferently">
   
      <eventCategoryRef name="Trace Events">
         <eventSinkRef name="traceSink"/>
      </eventCategoryRef>
      
      <eventCategoryRef name="Audit Events">
         <eventSinkRef name="traceSink"/>
         <eventSinkRef name="logSink"/>
      </eventCategoryRef>
      
   </filter>
</filters>

过滤器绑定

过滤器绑定将事件源链接到一个或多个过滤器。这允许您将系统不同部分引发的事件路由到不同的过滤器。

例如,如果您在系统的单个进程中遇到问题,您可以创建连接到包装该进程的请求事件源和将“所有事件”类别中的事件定向到 `TraceEventSink` 的过滤器的单个过滤器绑定。所有其他事件将被忽略。例如

<eventSources>
   <eventSource name="Create New Customer" type="request"/>
</eventSources>

<filters>
   <filter name="Trace All">
      <eventCategoryRef name="All Events">
         <eventSinkRef name="traceSink"/>
      </eventCategoryRef>
   </filter>
</filters>

<filterBindings>
   <eventSourceRef name="Create New Customer">
      <filterRef name="Trace All" />
   </eventSourceRef>
</filterBindings>

此片段将确保作为“创建新客户”进程一部分引发的所有事件都发送到 `traceSink`。

设置您的环境

首先,您的系统至少必须满足以下要求

  • Windows 2000 Service Pack 2(最好是 SP3)、Windows Server 2003 或 Windows XP。
  • .NET Framework 1.1。
  • Visual Studio .NET 2003 Enterprise Architect、Enterprise Developer 或 .NET Professional 版本。

以下是我为创建开发环境所遵循的步骤

  • 安装 EIF。现在可以从 此处 获取通用版本;
  • 通过编辑文件 *[EIF]\Bin\Trace Service\TraceSessions.config*(其中 [EIF] 是 EIF 的安装目录)来启用跟踪会话,并将 `enabled` 属性的值更改为 `“true”`。注意,此配置文件允许您指定 Windows 事件跟踪日志写入哪个文件;
  • 安装 Web 服务增强功能 2.0,可从 此处 获取。我选择了 Visual Studio Developer 选项;
  • 运行包含日志记录应用程序块的自解压 ZIP 文件,可从 此处 获取;
  • 通过运行提取的 *Logging.msi* 文件安装日志记录应用程序块;
  • 按照提取的 *Logging.pdf* 文件中“构建日志记录块”部分中的说明进行操作。这些说明将指导您构建和安装该块。

关于此过程的一些说明

  • 这个过程看起来很繁琐,但很简单,值得去做;
  • “Visual Studio 命令提示符”应该可以在您的开始菜单中找到;
  • 我确实发现 MeteringOutputFilter 和 RequestTracingFilter 项目中对 `Microsoft.Web.Services` 的引用必须更改为 `Microsoft.Web.Services2`(并相应地更改某些源文件中的 `using` 指令);
  • 如果您不打算构建示例,则更改示例的配置文件可能不是必需的,但使用 Visual Studio 的“在文件中替换”功能很简单。

检测应用程序

也许掌握日志记录应用程序块的最佳方法是尝试一下。本文的下载包含一个示例 Windows Forms 应用程序(*LoggingBlockInvestigator.exe*)的代码,该程序应允许您进行实验。您需要编辑其 *EnterpriseInstrumentation.config* 文件,将 `PublicKeyToken` 属性替换为您在构建日志记录应用程序块时确定的值(执行一次字符串 25ffac55882d4eb6 的查找和替换)。

引发事件

*LoggingBlockInvestigator.exe* 应用程序在 `Main` 方法中引发两个审计事件,一个在启动时,一个在关机时

AuditMessageEvent auditEvent = new AuditMessageEvent();
auditEvent.Message = "Starting the application";
EventSource.Application.Raise(auditEvent);

Application.Run(new MainForm());

AuditMessageEvent.Raise("Shutting down the application");

这段代码展示了从默认“Application”事件源引发事件的两种技术。第一种技术显式创建一个事件对象并用消息初始化它。第二种更紧凑的技术隐式创建事件。

如果您构建并运行 *LoggingBlockInvestigator.exe* 应用程序,然后关闭它,您将在 Windows 事件日志的“Application”部分找到两个新条目。如果您查看详细信息,您将看到一个包含相应消息的 XML 文档。

使用 EIF,Microsoft 提供了一个简单的跟踪日志查看器。如果您运行 *[EIF]\Samples\Trace Viewer\TraceViewer.exe* 并打开 *TraceLog.Log* 文件,您应该也会在那里看到列出的两个事件。

创建事件源

默认情况下,所有消息都标记为由“Application”事件源引发。示例应用程序中“Log From My Source”按钮处理程序中的代码使用显式源代替

private static EventSource eventSource = new EventSource("My Source");
private void LogFromMySource_Click(object sender, System.EventArgs e)
{
   TraceMessageEvent.Raise(eventSource, "Traced from my source");
}

运行示例应用程序并单击“Log From My Source”按钮,然后使用 *TraceViewer.exe* 打开 *TraceLog.Log* 文件。最后一个条目应该是一个跟踪消息。查看详细信息将显示 `EventSourceName` 设置为“My Source”。

上面的代码正在使用软件元素事件源。按钮“Log With Request Trace”后面的代码使用两个嵌套的请求事件源

private RequestEventSource requestEventSource1 = 
           new RequestEventSource("My Request Source For Tracing");
private RequestEventSource requestEventSource2 = 
           new RequestEventSource("My Nested Request Source For Tracing");
private void LogWithRequestTrace_Click(object sender, System.EventArgs e)
{
   using (RequestTrace request = new RequestTrace(requestEventSource1))
   {
      TraceMessageEvent.Raise(eventSource, "My first request");

      using (RequestTrace requestNested = new RequestTrace(requestEventSource2))
      {
         TraceMessageEvent.Raise("My second request");
      }
   }
}

如果您单击“Log With Request Trace”按钮,以下事件将被保存到 *TraceLog.Log* 文件中

  • `TraceRequestStartEvent` - 表明“My Request Source For Tracing”已启动;
  • `TraceMessageEvent` - “My first request”;
  • `TraceMessageEvent` - “My first request”的重复,因为我们将此事件同时分配了一个软件元素和一个请求事件源,两者都被配置为将事件路由到 `TraceLogSink`;
  • `TraceNestedRequestStartEvent` - 表明“My Nested Request Source For Tracing”已启动。详细信息表明 RootRequestName 是“My Request Source For Tracing”;
  • `TraceMessageEvent` - “My second request”事件,该事件仅保存一次,因为它没有分配软件元素事件源;
  • `TraceNestedRequestEndEvent` - 表明嵌套请求已完成。详细信息显示了执行时间;
  • `TraceRequestEndEvent` - 表明外部请求已完成。

创建自定义事件接收器

除了使用提供的事件接收器外,您还可以创建自己的事件接收器。示例应用程序中的 *CustomEventSink.cs* 展示了它的简单性。该类派生自 `Microsoft.EnterpriseInstrumentation.EventSinks.EventSink` 并重写了 `Write` 方法。在 `Write` 方法中设置一个断点,然后单击“Log To My Sink”按钮。您的断点应该会被命中,使您能够探索 `eventToRaise` 对象。

日志级别

日志记录应用程序块引入的一项新功能是日志级别。`ApplicationLogLevel` 指示应发布哪些事件。在示例应用程序中,这在 *App.config* 文件中设置为 `“debug”`。如果将此值更改为 `“error”`,则只有“Audit At Error Level”按钮才会导致任何新消息被记录到 Windows 事件日志中。在 `Main` 方法中引发的两个审计事件将被忽略。

如果未显式设置事件的日志级别,则默认为 `“debug”`。以下代码(来自“Audit At Error Level”按钮)显示了如何提高日志级别

AuditMessageEvent auditErrorEvent = new AuditMessageEvent();
auditErrorEvent.Message = "Logged at error level";
auditErrorEvent.EventPublishLogLevel = (int)LogLevel.Error;
EventSource.Application.Raise(auditErrorEvent);

应注意,日志记录应用程序块并未更改 `TraceEventSink` 的功能,因此它仍然会记录路由到它的所有事件,无论日志级别如何。

配置

最后几节介绍了您可以添加到应用程序以开始通过日志记录应用程序块进行日志记录的代码。还有一项非常重要的任务……配置。如果不配置 EIF,您的事件将永远无法到达事件接收器。

EnterpriseInstrumentation.config 文件详细说明了事件、事件源和事件接收器是如何链接的。可以为您生成此文件的骨架。您必须确保您的项目引用 `System.Configuration.Install` 程序集,并且它包含一个类定义,如下所示

[RunInstaller(true)]
public class LoggingBlockInvestigatorInstaller : ProjectInstaller {};

然后,从命令行运行

installutil MyApplication.exe

这将生成一个功能齐全的 *EnterpriseInstrumentation.config* 文件,其中预填充了您应用程序及其引用的程序集中可以找到的所有事件、事件源和事件接收器。它还将包含一些默认的类别、过滤器和过滤器绑定。然后,您可以试验创建新的类别、过滤器和过滤器绑定。

如果您查看示例应用程序的 *EnterpriseInstrumentation.config* 文件,您会发现它在其中一个事件源上使用了 `EventSourceParameter`

<eventSource name="My Source With Debug Info" ... >
   <eventSourceParameter name="PopulateDebugInfo" value="true" />
</eventSource>

`PopulateDebugInfo` 参数用于指示从“My Source With Debug Info”事件源引发的任何事件都应填充其调试信息字段。如果您运行示例应用程序并单击“Log With Debug Information”按钮,*TraceLog.Log* 文件中的最后一个条目将填充其 `StackTrace` 字段。其他预定义的 `EventSourceParameter` 包括

  • `PopulateComPlusInfo` - 填充 COM+ 上下文信息;
  • `PopulateManagedSecurityInfo` - 填充 .NET 安全上下文信息;
  • `PopulateWindowsSecurityInfo` - 填充 Windows 安全上下文信息。

此外,在文件中,您将看到自定义事件接收器声明如下

<eventSink name="customEventSink" ... >
   <parameter name="MyParam" value="My own parameter" />
</eventSink>

“`MyParam`”参数被传递给事件接收器的构造函数。如果您删除它,您会发现 `CustomEventSink` 会抛出异常,因为它被编码为期望该参数。此异常会被 EIF 捕获并记录到 Windows 事件日志中。注意:异常是否被记录取决于配置文件中事件源声明上的 `internalExceptionHandler` 属性。

事件接收器参数可用于任何您喜欢的内容。EIF 和日志记录应用程序块提供的事件接收器将它们用于

  • 指定应写入哪个计算机的 Windows 事件日志;
  • 在写入 `TraceEventSink` 时要使用的跟踪会话的名称(这是在 *TraceSessions.config* 文件中配置的名称)。

未来

未来呢?Microsoft 与 Avanade 合作,一直在开发 Patterns & Practices 应用程序块的下一代产品。Enterprise Library 预计将于 2005 年初正式发布。该库将整合多个现有的应用程序块,包括日志记录。

您可以在 GotDotNet 找到有关新库的更多信息。查看目前可用的文档,您从当前日志记录应用程序块获得的任何经验都将使理解新库变得更容易。但是,不会有简单的升级路径。一些新功能包括

  • 新的接收器,例如 `EmailSink`;
  • 通过 Enterprise Library Configuration Console 进行配置;
  • 不再依赖 EIF。

更新: Enterprise Library 现已推出。

结论

希望本文档能为您提供有关日志记录应用程序块的优点和功能的良好介绍。如果在整个系统中一致地实施,它应该为调试和监控提供一个强大而有力的工具。本文提供的信息应能让您开始将该块集成到您的代码中。

至少,我希望本文强调了在项目早期考虑日志记录的好处,并选择一种一致的方法(无论是使用 Log4Net、NSpring、日志记录应用程序块、`System.Diagnostics.Trace` 还是自制框架)。

最后,您可能想等待新的 Enterprise Library。在这种情况下,本文介绍的概念应该有助于您快速掌握它。

修订历史

2005年1月5日

  • 希望我已经清楚地说明,本文提供的示例(*LoggingBlockInvestigator.exe*)与 Microsoft 提供的 EIF 示例(*TraceViewer.exe*)是不同的。

2005年2月10日

© . All rights reserved.