深入了解企业仪器框架






4.26/5 (16投票s)
2003年8月31日
12分钟阅读

130779
本文探讨了 Microsoft Enterprise Instrumentation Framework,并详细介绍了传统仪器技术的不足是如何通过此框架克服的。
引言
毋庸置疑,跟踪和仪器是任何应用程序非常重要的一个方面。仪器这个术语指的是监控或测量产品性能水平以及诊断错误的能力。在编程方面,这可能涉及到代码跟踪、调试技术、性能计数器、事件日志等等(MSDN)。.NET Framework 包含了 Trace
和 Debug
类,它们提供了上述仪器方面的功能,并且在应用程序开发中非常有价值。不幸的是,这些类存在各种不足之处,使其在某些情况或类型的应用程序中不可行。为了解决传统仪器技术的不足,微软推出了一套名为 **企业仪器框架 (Enterprise Instrumentation Framework)**(简称 EIF)的全面框架。在本文中,我将详细介绍 EIF 是什么,以及如何使用 EIF 来克服 Trace
和 Debug
类的不足。
背景
在深入探讨之前,我想推荐两篇关于 Trace 和 Debug 类及其缺点的必读文章。
- “关于使用 Debug 和 Trace 类(包括异常处理)的论述” 作者:**Marc Clifton**:这篇文章深入剖析了 trace 和 debug 类,并讨论了如何增强它们以提供更好的仪器功能。
- “在 .NET 应用程序的发布版本中实现跟踪功能的注意事项” 作者:**Vagif Abilov**:这篇文章讨论了 trace 和 debug 类的一些不足之处,并提到了许多框架提供的类之外的其他替代方案。本文最后提到了企业仪器框架。因此,我的文章基本上是接续 Vagif 的内容。
什么是 EIF?
企业仪器框架为应用程序的仪器提供了一个非常简单但可扩展的框架。最重要的是,EIF 提供了一个“统一 API”,有助于与现有的框架(如 **WMI、事件日志**等)集成。此外,正如其名称所示,该框架适用于企业级应用程序。也就是说,该框架不仅能够对单个机器上的应用程序进行仪器化,还能对分布在多台机器上的应用程序进行仪器化。
让我们首先了解 EIF 的组成部分,然后进行与现有仪器技术的比较分析。下一节将描述 EIF 的基本元素。
事件源
事件源代表事件发生的来源。通常,这些可以由特定的应用程序本身或应用程序的特定部分或组件来表示。EIF 提供三种类型的事件源。
类型 | 描述 |
软件元素 | 此类型用于在精细级别控制仪器行为。开发人员可以做的是,将应用程序划分为多个源,以便独立控制事件的引发。 |
Application | 在未指定事件源的情况下引发的事件将使用应用程序事件源。在这种情况下,企业仪器框架会内部定义一个 SoftwareElement 事件源来表示被仪器化的应用程序。该事件源被赋予保留名称 _Application_。 |
请求 | 在我看来,请求跟踪是 EIF 提供的最佳功能。使用请求跟踪,我们可以对应用程序进行仪器化,即使它跨越了进程或机器边界。为了实现这一点,开发人员会创建一个请求事件源,并在应用程序代码中指定请求的开始和结束点,这使得企业仪器框架能够自动管理该请求的上下文。我将在后续章节中更详细地讨论这一点。 |
事件接收器
事件接收器基本上控制事件数据的目标存储。企业仪器框架提供三个标准的事件接收器。您可以使用 **标准** 事件接收器或 **自定义** 事件接收器。提供的标准事件接收器是 **WMIEventSink**、**TraceEventSink** 和 **LogEventSink**。
事件接收器 | 描述 |
TraceEventSink |
此事件接收器将事件写入 Windows 事件跟踪日志文件(如果跟踪会话处于活动状态)。这种跟踪机制适用于高频率事件,它可以生成每秒数百甚至数千个事件的正在运行的应用程序。 |
LogEventSink |
此事件接收器将事件输出到 Windows 事件日志,适用于低频率事件,如错误、警告或高级审计。 |
WMIEventSink |
此事件接收器将事件输出到 Windows Management Instrumentation (WMI)。这是 Windows 2000 系统上最慢的标准事件机制,因此应主要用于不频繁或高可见度的事件。 |
自定义事件接收器 |
开发人员可以利用现有的日志格式和消息路由机制,例如 Microsoft Message Queue (MSMQ),来创建自定义接收器。开发人员可以使用以下两种方法之一来实现自定义事件接收器:
|
事件模式
事件模式定义了应用程序可以引发的一组事件。在实现方面,事件模式代表了 EIF 提供的一组类。同样,有两种事件模式:**标准** 和 **内部**。标准事件是开发人员显式引发的事件,包括错误、审计、管理事件和诊断跟踪事件。内部事件是由 EIF 本身自动触发的(主要与请求跟踪相关)。
我们还可以通过创建自己的事件来扩展标准事件模式。标准事件类提供了一些可以继承以创建自定义事件类的基类。
下面是一些使用 Event
类引发事件的简单示例。事件模式提供了一套详尽的事件和方法供您使用。在此详细介绍所有这些没有意义,EIF 文档已经足够详尽。
TraceMessageEvent.Raise("A simple trace event")
AuditMessageEvent.Raise("A simple audit event")
配置
在前一节中,我们了解了事件源、事件接收器和事件模式的含义。在本节中,我们将通过配置在应用程序中关联它们。每个应用程序都应维护一个配置文件来存储 EIF 设置,默认情况下,该文件名为 _EnterpriseInstrumentation.config_。可以通过 Configuration API 以编程方式创建此文件,或手动编辑它。以下是典型的配置部分:
事件类别
在本节中,我们记录了哪些类型的事件类被分组到命名的类别中。这有助于在应用程序中逻辑地分组某些事件类。例如,创建一个名为 _All Events_ 的类别来包含 System.Object
类型(因为所有类都隐式派生自 System.Object
)的所有事件,该类别将包含所有事件。这是一个例子:
<eventCategories>
<eventCategory name="All Events"
description="A category that contains all events.">
<event type="System.Object" />
</eventCategory>
</eventCategories>
事件接收器
本节记录了应用程序使用的所有事件接收器及其对应的参数。下面显示的示例声明了一个名为 _traceSink_ 的 TraceEventSink
,并且会话名称已配置为 TraceSession
。
<eventSinks>
<eventSink name="traceSink"
description="Outputs events to the Windows Event Trace."
type="Microsoft.EnterpriseInstrumentation.EventSinks.TraceEventSink">
<parameter name="sessionName" value="TraceSession" />
</eventSink>
</eventSinks>
事件源
本节列出了应用程序中的所有事件源。下面给出的示例记录了一个名为 MyRequest
的请求事件源。
<eventSources>
<eventSource name="MyRequest" type="request"
internalExceptionHandler="ignore"
description="A request event source." />
</eventSources>
Filter
本节将一个 Event
类别与一个或多个 Event
接收器绑定。下面显示的配置将 _All Events_ 事件类别分别与 TraceEventSink
和 LogEventSinks
关联。也就是说,任何引发的事件都将自动记录到 Windows 事件跟踪日志和事件日志。
<filters>
<filter name="defaultSoftwareElementFilter"
description="A default filter for the Software Element event sources.">
<eventCategoryRef name="All Events">
<eventSinkRef name="traceSink"/>
<eventSinkRef name="logSink"/>
</eventCategoryRef>
</filter>
</filters>
FilterBindings
此部分将事件源绑定到过滤器。在下面的示例中,_Application_ 事件源被绑定到 defaultSoftwareElementFilter
过滤器。
<filterBindings>
<eventSourceRef name="Application">
<filterRef name="defaultSoftwareElementFilter" />
</eventSourceRef>
</filterBindings>
这是整体图景:应用程序创建 Event
源,将事件类映射到类别,并使用一个或多个 Event
接收器。通过过滤器,应用程序被配置为将特定类别的事件路由到特定的事件接收器,并且事件源通过过滤器绑定映射到这些过滤器。下图大致说明了这些关系。
应用程序使用事件模式的类来引发特定事件,并且根据配置,事件数据将找到一个或多个事件接收器。最终可以查询事件接收器以获取数据,用于分析应用程序的性能或行为。
EIF 来帮忙
本节应是本文中最有趣的部分。在这里,我将讨论传统仪器技术(Trace 和 Debug 类)的各种不足之处,以及 EIF 如何帮助克服这些不足。我还将阐述 EIF 提供的一些我认为非常有趣且有用的功能。
首先,Trace 和 Debug 类是不可继承的。因此,要实现自定义跟踪功能,我们需要采用组合技术。在这里,组合是指将 Trace 类包装在您的类实现中,并在其之上添加您的功能。这种实现可以在 Marc 的文章中看到。另一方面,EIF 事件模式是完全可扩展的。该框架提供了许多基类,可以轻松地进行扩展。例如,一些提供的基事件类是 BaseEvent
、CommonEvent
、DiagnosticEvent
、TraceEvent
等(BaseEvent
位于层次结构的顶部)。
Trace 类的第二个问题是,您无法将不同的跟踪消息与不同的跟踪侦听器关联。例如(如 Vagil 的文章所述),您无法为跟踪侦听器指定不同的严重级别阈值。因此,所有侦听器的跟踪输出都是相同的。在 EIF 中,您可以将不同的事件类分类到类别中,并定义过滤器来确定消息路由到哪个事件接收器。例如,让我们考虑将 InternalErrorMessageEvent
和 ExternalErrorMessageEvent
类分类到一个名为 Errors 的类别中,并将这些事件路由到事件日志。另一方面,我们可以将 AdministrativeEvent
和 AuditEvent
类分类到一个名为 Audit 的类别中,该类别可以配置为将事件路由到跟踪日志。因此,通过配置适当的过滤器,我们可以将不同的事件数据分流到不同的接收器,这是 trace 类和 trace 侦听器明显缺乏的功能。
Trace
及相关类的第三个也是主要缺点是进行分布式请求跟踪的困难/无能。分布式请求跟踪是指能够跟踪跨越多个进程或机器边界的应用程序。如果您还记得,添加到某个进程/应用程序域的 TraceListener
集合(通过 Trace.Listeners.Add 方法)无法直接用于另一个进程/应用程序域。因此,默认情况下,跟踪上下文不会在进程之间流动。为了在不同进程中编写的跟踪消息之间实现一定程度的相关性,我们可能需要编写自定义的跟踪侦听器,将跟踪输出写入公共存储(如 SQL Server 或 MSMQ),并使用跟踪类别来关联跟踪消息。即使这种方法本身也不是完全万无一失的。可能我们需要为此使用复杂的远程处理基础设施。底线是,我们没有“开箱即用”的分布式请求跟踪。有了 EIF,这项功能就是内置的,它使用的是一个远程处理基础设施,并且请求信息存储在 LogicalCallContext
类中,该类会沿着执行路径,跨进程和机器边界流动。
到目前为止,我们看到了 EIF 中一些解决了 Trace 类缺点的功能。现在,我将向您介绍一些我认为非常酷且有用的功能。其中最重要的是,事件类填充的某些信息字段。通常,当我们编写代码中的跟踪语句时,我们不可避免地会编写代码来捕获一些信息,如线程标识、操作执行时间、进程 ID 等。EIF 事件类在我们这边没有任何编码工作的情况下,提供了大量此类信息。每个事件类都提供了一些有用的字段,这些字段被分组到命名的属性组中。某些属性是默认填充的。例如,名为 PopulateRequestInfo
的组默认由所有事件填充。此组包含 RequestSequenceNumber
(用于跟踪请求顺序)、RootRequestInstance
(用于跟踪根请求)等信息。另一方面,某些属性组不是默认填充的。一个例子是 PopulateComPlusInfo
,它包含 COM+ 相关信息,如 ActivityID
、TransactionID
等。EIF 通过提供一个选项,让我们能够决定为特定事件源填充哪些属性组,哪些不填充,从而为我们提供了额外的控制级别。在下面的配置片段中,为 _SecureRequestTrace_ 事件源启用了 PopulateWindowsSecurityInfo
。
<eventSource name="SecureRequestTrace" type="request"
internalExceptionHandler="ignore" >
<eventSourceParameter name="PopulateWindowsSecurityInfo" value="true" />
</eventSource>
另一个好功能是项目安装程序。在导入指令之后,立即添加以下新的空安装程序类,该类继承自 ProjectInstaller
。使用设置为 true 的 RunInstaller
属性对其进行定义。
<RunInstaller(true)> _
public class MyProjectInstaller
Inherits ProjectInstaller
编译应用程序后,在应用程序可执行文件上运行 _installutil.exe_。瞧!您会看到 EnterpriseInstrumentation.config 已自动创建,并记录了所有配置信息。
最后,我真的对 **Trace Session Manager** 印象深刻。Trace Session Manager 作为一个 Windows 服务运行,主要负责维护跟踪会话。每个跟踪会话包含一个跟踪日志文件,所有事件接收器配置为跟踪日志接收器的事件类最终都会将跟踪数据写入此日志文件。(为了方便起见,我们只需确保 EnterpriseInstrumentation.Config 中跟踪日志事件接收器的 _sessionName_ 参数与 _TraceSessions.config_ 中配置的跟踪会话名称匹配)。我已经提到了跟踪日志的一个优点:它们适用于高频事件。另一个是个人偏好。我真的很喜欢使用 TraceLogReader
API 分析跟踪日志文件中的跟踪数据,而不是从事件日志中分析。我认为后者需要更多的“管道”工作)。
结论
EIF 绝对是一个很棒的仪器框架,特别是对于企业级应用程序,并且是 Trace 和 Debug 类的明确改进。EIF 提供易于使用的 API,并且与 VS.NET 无缝集成。使用 EIF 进行仪器化的应用程序易于配置,并且由于仪器化代码而不会产生太多性能下降。因此,我想得出结论,认为开发团队有不止一个令人信服的理由在 .NET 应用程序中采用 EIF 作为其主要的仪器策略。
不幸的是,在我看来,EIF 虽然是一个功能丰富的框架,但并未被微软充分宣传。EIF 只能通过 MSDN Universal 订阅者下载获得。EIF 框架附带的 EIF 文档是我遇到的为数不多的优秀资源之一。另一个资源是 MSDN TV 关于 EIF 的节目,可以在这里找到。
即使到现在,我觉得 EIF 仍然是微软技术栈中一颗未被发掘的宝藏!