检测:.NET中强大的检测选项让你有信心建立易管理的应用程序

[注]:本文发稿时,MSDN Magazine 已在本文中添加新的内容,具体信息参见文中“编辑更新”部分。

原文出处:MSDN Magazine April 2004(Instrumentation)
 

本文讨论的内容包括:
  • 托管领域中的检测
  • 什么样的检测可以帮助你测量
  • 性能计数器,WMI,跟踪和其它一些测量技术
  • 选择最佳检测手段实现自己的意图
  • 安全考虑


  应用系统一旦部署,检测可以使你确定系统的运行状态。这在当今是至关重要的,因为系统支持人员并不一定就是系统的开发人员。就像好的错误处理一样,检测最好在开发当初就完成。一个好的检测手段必须在开发过程的开始阶段就建立起来, 它要决定应该检测什么,如何检测,检测哪里,为什么要检测。
  检测的实现有各种各样的技术可以利用,这些技术提供高质量的支持并且诊断信息来自应用程序自身内部。本文中所专注的技术将混合传统 Windows 核心工具——事件日志、调试跟踪、性能计数器 和 WMI。我还将讨论 Enterprise Instrumentation Framework(EIF) 以及 Microsoft® .NET Framework 提供的特性,比如跟踪和结构化异常处理(SEH)。 .NET Framework 使资源的开销最小化并使所有这一切都易于使用,这很关键,因为商业用户经常认为检测是一个不重要的过程。

前景

  分布式应用程序已变得越来越复杂。从系统的整体上看,其复杂程度令人畏缩。在此,我将专注于企业设计者和建立复杂的分布式应用的开发人员所面对的挑战 。不论你是建立 Web 站点,还是服务,或者传统的客户端/服务器应用,你都将发现,因为其庞大的无形的体系架构,管理这些应用的任务将十分具有挑战性。
   检测有助于你面对上述挑战,通过回答下面这样的一些问题:发生了什么错误?错误发生在哪里以及发生的频率?如何知道应用程序还在运行?每小时完成多少企业事务?能够处理的最大吞吐量是多少?不同活动的最繁忙的时间?应用程序是如何进行伸缩处理的?如何知道谁正在(试图)做什么?
   这些问题最后归结为四个不同的范畴:性能、稳定性、可伸缩性和安全性。性能对于所支持的服务级别协议(SLAs)是至关重要的,稳定性是 Quality of Service(QoS)的契约,尤其是与第三方的契约。可伸缩性对于业务增加是非常重要的,包括销售和市场机会。安全,当然总是不能忽视的。
   你必须认识到的首要事情之一就是断定错误。决定如何处理它们并不是运行时的决策,而是设计时的决策。将它们留到编码阶段常常会导致脆弱以及不连贯的解决方案,它们对于提供良好的检测策略毫无益处。

检测什么及如何计划

  当决定要对正在编写应用程序逻辑进行检测后,第二重要的事情就是明确决定需要检测什么。用户细节,日期/时间和线程/进程信息(对提供先后顺序极其重要)是绝对必要的。你 还会想包括程序名称以及在哪里写事件日志(类/方法名字)。其它常用信息包括状态信息,比如参数值和当前上下文值。你还应该小心避免一些用处不大的调用信息,例如 记录磁盘I/O活动日志,系统的线程总数等等。你还应该包括基本的以及任何代码正在执行的任务特定的信息,比如数据库连接细节(但注意这种方法暴露了敏感数据就创造了安全 风险)。
   你必须决定项目要监测什么以及需要什么支持并形成文档。这个任务涉及到开发团队及测试和支持团队成员。然后根据这三个团队的需要确定在哪里包含检测。你必须 采用整体策略以便检测贯穿在整个设计中。任何错过的地方都会排除在监测之外而给你的项目带来负面影响。

.Net 新的可能性

  如果你有 Visual Basic® 6.0 的背景,.NET Framework 就像沙漠中的一片绿洲。过去的挑战是把所有的核心功能需求都编到 某一个应用程序中。没有大量的时间和努力检测是不可能做好的。解决方案要么过于复杂(棘手的 C++ 动态链接库)要么太单纯(App.LogEvent)。很容易 引起让人难以接受的性能开销,它经常导致不完善的检测。
   具有讽刺意味的是如果不借助检测,你无法确切知道应用程序的局部运行得有多快。容量计划(预期负载分析与实际容量对比)是一个关键活动,当遇到基于 Web 的分布式 应用时,相对来说,这一点仍然是被忽视的。往往是体现在一个应用程序的表面,典型的实际客户如:Mercury Interactive的 LoadRunner 或微软的 ACT 工具。如果应用程序开始变慢了, 它慢在哪里?所有的环节都慢了么?那太不可能了,但你需要去证明之。好的检测可以回答这类问题并去掉许多猜测工作。
   软件开发正经历着一场基本变化,开发者正试图尽力赶上这种变化。典型地基于 Web 的应用程序 的开发方法是将任务分割给单个开发者,构建新系统的某一个小部件并在单独的机器上测试。然后随同团队其余的工作一起将它部署到测试环境。从根本上说,这个过程是有缺陷的。基于 Web 的应用程序都是在单一的工作站上开发和测试,在那里它们运行良好。通常没有考虑一旦部署会有成千上万的用户点击,而不只是一个用户这一事实。通过早期的集成 来构建确认测试能大大有助于解决这样的问题。
   综上所述,没有良好的检测,复杂的分布式应用程序是不可管理的。所以让我们看一下使用检测你可以完成什么。首先我将回顾一下传统的选项,然后考察在 .NET Framework 中可利用的新可能性。

事件日志

  事件日志(eventvwr.exe)一直是 Windows NT® 的一部分。它提供了集中记录日志信息的场所,其日志信息主要有三类:应用程序,系统和安全性。你可以向每个 分类中写信息,但通常关系最密切的是应用程序组。在 Visual Basic .NET 中执行下面的代码可以很容易实现:
Imports System.Diagnostics



EventLog.WriteEntry("MyAppSource", "Details", System.Diagnostics.EventLogEntryType.Information)

  有几件事情这里需要指出。如果你使用过 Visual Basic 6.0 的 App.Logevent,你会注意到你可以随意的定义自己的事件源(在事件查看器里面不再有 VBRuntime 长列表)。而且,过去创建事件描述实在是痛苦 ,因为它们牵涉构建一个消息编译器资源文件并且要将它链接到应用程序 DLLs 中。现在由 .NET Framework 为你执行这些任务。在幕后, 该机制是一样的。查看注册表中下面的键值:
HKLM/System/CurrentControlSet/Services/EventLog/Application/MyAppSource

  如果你把先前的代码片段放到一个 Windows 应用程序(Windows Application)项目里,就会有该键值。当事件描述被显示时它被 eventvr.exe(或任何其它的)读取 以定位你所引发的事件的描述。它指向一个通用的事件描述处理器,该处理器由.NET Framework 提供,位于:
%WINDOWS%/Microsoft.NET/Framework/%version%/EventLogMessages.dll

  这只是一个纯资源动态链接库,它必须带一个可替代参数 %l,它关联你在代码中指定给事件查看器的描述。当该语句被实际执行时,该注册表条目在运行时被添加,而不是在编译时。 虽然你可能很快会发现在注册表的 Application 键值下创建了一堆垃圾,那是因为每次你改变源文件参数时,你都会加入一个新的子键。这些子键将会永远的保存在注册表中除非你故意的 删除它们(这也是在开发初期要考虑检测的另一个原因)。

性能计数器

  性能计数器在 Visual Basic 6.0 时期很难实现。在 Visual Basic 6.0 中不可能直接使用性能计数器 API 的原因是因为进入点需要在你的链接库中定义——在 Visual Basic 中的一切都被 COM CoClass 的四个标准 Dll 进入点所所控制,没有办法加入你自己的入口点。为了得到应用程序的统计数据,典型的做法是利用性能计数器(perfmon.exe)加载的辅助 DLL并与之沟通。只有在合适的点调用更新计数器,你的应用程序才能得到检测。这并不容易,因为在对象实体间需要一些共享内存来维护计数器的值。这是因为 Perfmon 运行在一个单独的进程,在应用程序里调用核心集合的函数(驻留在Perfmon进程)是独立于你的单独对象的。.NET Framework 解决了这些令人头疼的问题。Figure 1显示了当前的计数器 的体系架构。.NET Framework 组件 netfxperf.dll 运行在 Microsoft Management Console (MMC)进程中(mmc.exe),mmc.exe 进程 操纵者控制性能计数器管理单元。该进程(B)加载 CRL 并与进程 A 中的你的应用程序沟通,以提获取统计数据。
 

Figure 1 .NET Framework 中性能计数器架构

  为了让 Perfmon 管理单元(或任何其它程序)找到你的计数器,必须添加大量注册表设置。就像事件日志一样,当执行实际的代码行时,.NET Framework 自动的遵循这些设置。其中有一个主键要被创建 

HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet /Services/category/

这个键通常只包含一个名为 Performance 的子键。
   为此可以使用一个通用的 DLL,唯一要解决的问题是你的程序集被管理单元加载时如何与该键关联,以便 netfxperf.dll 知道从哪里得到你的计数器(和它们的值)。解决方法是使用该键下的 Wbem 值。这些值被 WMI 用来获得 你的计数器。当它执行到你的代码时,.NET Framework 使用底层的 WMI 策略注册你的计数器。以后,当执行 Perfmon 时,它从注册表中提取你的计数器名字并使用 WMI, 建立一个与之连接的请求。如果你程序正在运行,WMI会提供到实际值的连接。顺便说一下,这是使用 WMI 自动发现和清除的特点完成的,这也是为什么某些注册表值用 WbemAdap 预设的原因(WMI是 一个基于 Web 的企业管理的实现,也就是 WBEM)。另外一些注册表计数器值,First Counter/Help 和 Last Counter/Help, 必须与系统中所有的计数器串接在一起。在过去这可以通过调用 lodctr/unlodctr API函数完成。如果出错,就会有大问题,因为那将会影响 Perfmon 枚举机器中所有计数器的能力。.NET Framework 在这个问题上再一次为你考虑了所有的一切。
   .NET Framework 支持所有可获得的 Windows 计数器类型。最常用的是绝对值,平均值和时段分组值。这样便可以表达诸如应用程序所执行的 SQL 命令数 量,编译某个值所花费的平均时间,或者每秒钟 Web 服务请求数。在 .NET Framework 中创建性能计数器并增加它的值易入反掌,代码如下:

Imports System.Diagnostics

Dim objCat as PerfomanceCounterCategory

Dim objCtr as PerformanceCounter

objCat = PerformanceCounterCategory.Create("MyApp", _

					"MyApp help",_

					 "counter 1", _

					"counter 1 help")

objCtr = new PerformaceCounter("MyApp", "counter 1", false);

objCtr.Increment()
  此外,在开发期间,你必须注意不要把注册表弄得太乱。当分类(category)的 Create 方法被执行,MyApp 键值就会出现在刚才所示的注册表路径下。当 new PerformanceCounter 被执行时, 该 Wbem 值在 WMI ADAP 处理期间被创建。

跟踪

  Win32 调试 API 到哪都是一个便利的工具。OutputDebugString 函数可被添加到程序中提供动态跟踪,这样你就可以使用 DbgView.exe ((http://www.sysinternals.com)或者 DBMON(随 Platform SDK 一起提供)之类的工具看到程序的调试信息。你甚至可以从远程机器连接到它。.NET Framework 在跟踪实现上仍然利用了这些 API,这是我下面要讨论的内容。
   跟踪是一个很好的主意。你想看看在什么条件下用到了哪些方法:ASP.NET在 System.Web.TraceContext 类中提供了 Trace 方法机制,而基础类库(BCL)在 System.Diagnostics 名字空间中提供了 Trace 和 Debug 类。为了在程序中使用 System.Diagnostics 进行跟踪,必须在你的代码中加入跟踪语句并确认跟踪在编译时是被允许的。跟踪语句有好几种类似的形式。最常用的形式就像下面的:
System.Diagnostic.Trace.WriteLine("something useful")

  System.Diagnostics.Trace 和 System.Diagnostics.Debug 都是封装好的类。 它们之间的主要差别在于条件编译字符串常常在生成过程中包含这些类的调用。Debug 类方法只有在 DEBUG 编译常量被设置时才会被编译到生成过程中去。而只 有设置了 TRACE 常量,Trace 方法才会被编译到任何生成过程中(具体细节参见 SDK 文档)。在程序的 .config 文件中还可以设置跟踪级别 ,这样你就有几种手段控制实际产生的信息的详细程度。
   使用 System.Diagnostics 名字空间你还可以编写自己的跟踪监听器类并把它们加入到 Trace 或 Debug Listeners 集合。当 CLR 执行 某个 Trace 或 Debug 语句时,集合中的所有监听器会被依次调用。调试跟踪提供的 DefaultTraceListener 在该集合中总是缺省的。而框架也会使用 EventLogTraceListener 将这些消息转储到当前日志,并使用 TextWriteTraceListener 将它们写到流中。

结构化异常

  结构化异常处理(SEH)是.NET托管环境最好的特性之一。从 Visual Basic 6.0 的观点看,这是一个在 On Error 语句上所做的奇特的改进。你可以明确的捕捉一个异常因为你知道如何处理它们,或者你什么也不作而让别人在这个异常链的更高层上捕捉到它。


Figure 2 日志异常

  但如果你仅仅是为此使用 SEH,那么你就会遗漏许多东西。你可以实现你自己的异常基类,而所有其它定制异常都从它派生。在其内部你可以捕捉到相关的环境数据(比如当前用户名和线程ID) ,这样你就有了一个完整的日志记录所发生的一切,这样就使你能做出更佳的异常情况评估并按需要产生管理报告。Figure 2 举例说明了某个异常被传播到调用链 的过程,调用链再将它送给能记录它们的通用处理例程。你可以用任何方法记录这些日志,但数据库通常是最有效的(不幸的是,你还需要做许多工作)。如果异常日志 机制是时间敏感的,它也可以异步完成,以便在将异常信息存储到日志时应用程序能够继续执行。

WMI

  WMI 是目前 Windows 中最强大,最复杂的检测机制。在 .NET Framework 之前,人们对它的评价就是难以使用。这主要是因为 Visual Basic 6.0 之类的 RAD(快速应用程序开发)工具很难使用 WMI API 的缘故。WMI 是很大的——只要看一下SDK文档就明白了。幸运的是为了使用其最佳特性,你可以不必完全理解它。例如,加入一个 对 System.Management.dll 的引用,你就可以写出下面的代码:

Imports System.Management.Instrumentation _

<System.ComponentModel.RunInstaller(True), _

InstrumentationClass(InstrumentationType.Event)>_

Public Class MyEvent

	'' event "property"

	Public EventProperty as String

end Class

  更好的是下面的代码,它比刚才所示的更有效。

<System.ComponentModel.RunInstaller(True)>_



Public Class MyEvent _

	     Inherits BaseEvent

	''Event "property"

	Public EventProp as String

End Class

  你可以很容易引发事件,

Dim objEvent as MyEvent



objEvent = new MyEvent

objEvent.EventProp = "Hello world!"

Instrumentation.Fire(objEvent)

  该代码段给 WMI 发布了一个事件,它支持松耦合发布/订阅模型。WMI 让你引发事件,该事件将被传递到它们的订阅者,如果还没有订阅者运行,则在以后一旦有订阅者时 ,该事件便能被拾起。这意味着即使你知道没有监听,也不会产生实际发布事件时的开销。
   写自己的监听器的一个好理由就是可以很好的利用公司已经有的日志工具,比如现 有的向中央数据库写日志的 COM 组件,这个被系统使用的数据库也支持个人编写所有应用程序。
   WMI 是基于标准的;它是 Distributed Management Task Force (DMTF)、WBEM 发起者、和 DMTF Common Information Model(CIM)的实现。更多细节请访问 http://www.dmtf.org。 只要钻研一下你就会找到这些缩写词。WMI 能够同 SNMP程序进行互操作,使得真正想要实现集中化监视的企业成为可能。在异类的 Web 服务世界里, 异类的检测能力是不可缺少的。
   为了开始使用这些事件,使用合适的 WMI 范围和查询创建System.Management.ManagementEventWatcher,并为 EventArrived 事件注册 一个事件处理器。Figure 3 代码示范了一个简单的事件处理器代码。
   你甚至可以使用 WMI 服务器资源管理器扩展使其变得更简单(参见微软下载中心的 “Managed WMI Extensions for Visual Studio .NET 2003 Server Explorer”)。你可以仅建立一个事件的查询(用 WQL,SQL 的子集),并将它们拖拽到窗体上。我所展示的代码都是当时产生的。

好的,坏的和糟糕的

  现在让我们看一下通过考察各个选项的优缺点,从而确定何时应该使用或不应该使用每个选项。
   事件日志有很多好处。它容易使用,持久耐用,还可以从远程机器上查看或者将事件日志记录到远程机器。你甚至可以定期的存储日志为以后的研究和报表使用。不幸的是在大部分现实情况下其缺点掩盖了其优点。事件日志是有 局限的。在创建事件日志时你可以设定其大小,然后其大小就是固定的了。从这点来看,要么日志尝试抛出异常,要么是用配置对日志进行包装,首先改写早期编写的条目。由于这个限制,事件日志 只是在记录少量信息时很有用,比如像程序启动和停止消息或者更高级别的通知消息等关键事件。另外,只有 Windows NT 上才有这个工具,如果你 仍然面向 Windows 98 和 Windows Me,这两个操作系统是不提供该工具的。
   性能计数器非常有用。它们提供了对程序健康状况 极好的综合统计。对于研究与负载有关的问题,已连接客户端数,数据库连接数,平均换页次数都是理想的度量。同样,由于有了事件日志机制,性能计数器并不适合 大量专门细节信息。性能计数器使用平均运行值或绝对值工作。计数器之所以有用是因为 Perfmon 提供了远程机器访问和文件日志。它甚至提供了报警机制,举个例子,运行一个程序发送一个电子邮件或者短信息(SMS), 是否平均响应时间超过了某个 SLA 。
   跟踪是 CLR 领域的真正福音。内建支持提供的功能不用做任何修改便能使用(out-of-the-box),并且 .NET Framework 允许你使用它提供的跟踪处理器 ,或者也可以实现自己的处理器。
   需要注意的是 ASP.NET 提供了自己的跟踪工具,不同于框架中 System.Diagnostics 名字空间所提供的。在 ASP.NET中,如果你的代码运行在 Web Form 中 ,你可以通过 HttpContext.Current.Trace 或者 Page.Trace 实现跟踪。由某些属性返回的 TraceContext 类 公开了两个方法可以写输出消息:Write 和 Warn。所有输出在程序虚拟目录的 trace.axd 页中可以看到。
   两个方法之间唯一的差别在于使用 Warn 方法输出的消息在 trace.axd 日志中以红色显示。在当前输出已满时你可以转储 ASP.NET 跟踪输出到 相应流中,使得消息被追加到每页的最后。这只对调试有用,不能用在常规的产品检测中。每个 ASP.NET 请求也会对一些额外的有用信息进行日志。比如当前会话 ID、时间 、HTTP状态代码和标题以及被显示的 cookies。
   缺省情况下,System.Diagnostics 跟踪系统和 ASP.NET 跟踪系统没有任何联系。写到一个系统中的消息决不会转发到另一个系统中。然而可以相当容易的写一个自定义的监听器 来驱动所有跟踪和调试消息都到 ASP.NET 跟踪系统中。如果你这样做了,一定要检查当前 HttpContext 是否存在。
   在写自定义的监听器时,有几个问题需要明白。比如,如果你要使用性能计数器和事件日志的话,需要创建计数器和事件源,你需要编写访问注册表的功能(像前面提到的 那样)。在 ASP.NET 缺省帐户下,出于安全考虑这是不可能实现的,所以在使用之前你必须安装程序和进行正确的设置。如果有可能,与其编程,还不如用 web.config 配置你的跟踪监听器 ,以便在部署后可以通过修改 web.config 或者 <appname>.config 来添加或删除监听器, 就象下面这样: 

<system.diagnostics>

    <trace autoflush="true" indentsize="0">

        <listeners>

            <add name="CustomListener",                          

                       type="MyLibrary.Class1,MyLibrary"/>

        </listeners>

    </trace>

</system.diagnostics>

  add 元素中的 type 属性必须被设为监听器的完全限定类型名字。当使用 ASP.NET 跟踪时,所有的跟踪调用日志都在内部记录,可以通过内置的 IHttpHandler 显示,IHttpHandler 暴露了一个http://yourservername/yourapp/trace.axd 跟踪数据。
  这里 记录日志的最多请求个数缺省值是10,但是在 web.config 中可以改为其它值,像下面这样:

<trace

        enabled="true"

        requestLimit="15"

        pageOutput="false"

        traceMode="SortByTime"

        localOnly="true"

/>

  不幸的是,使用 ASP.NET 跟踪后,你不能再使用自己的跟踪处理器来处理输出消息。另外,可能也是更加重要的问题,那就是在你开发的类库中不能假定已经得到了可使用的 HttpContext 对象。如果你开发了一个能够被服务器应用程序配置采用的一般类库,在试图得到当前的 TraceContext 时你总需要检查 HttpContenxt.Current 是否是一个有效的引用。
   一旦请求个数超过了 RequestLimit 的限制,对于有价值的跟踪信息你也不能再进行日志了。这种放弃跟踪对于现存的问题诊断是没有用的 ,因为在你想查看的请求之前很有可能还有上千个请求,而除了给定的页去跟踪请求之外你又没有其它的工具。因此在代码中检查正确的控制流对于调试情况是一种更加有用的技术。
   使用 ASP.NET 跟踪的一个更一般的问题是你必须在配置文件中明确地打开它。然而,当你这样做完后便会导致程序重启。对于配置应用程序检测来说 ,这样做比较笨。而如果要调查瞬间情况,而它又必须获取当前应用程序的状态信息,那么这样做真是个问题。
  信息太多也会成为问题——当使用诊断调试时,并没有提供过滤机制,除非你在代码中使用了小粒度跟踪级别和跟踪开关。你要产生的信息量是无法估量的。尤其 在没有使用持久性日志存储的情况下,比如文件,没有工具能够有效的处理它们。
   我已经讨论过了有效地使用 SEH。然而,那并不意味着它是一种检测工具。你不再会为了信息类型日志而委屈代码抛出异常,这减少了异常是为基于 CLR 的代码而设计的这一透明度。另外它也伤害了性能。
   WMI 强大的特点就是能够为你的程序提供检测规则,即计划(schema)。这个计划定义了你所提供的检测。它用事件和数据类进行定义。.NET通过这些计划内容为程序提供简单的控制。WMI 是已讨论过的日志方案的补充。比如,事件日志信息和性能计数器能够被 WMI 访问。这意味着如果你已经使用了这些 机制,你现在就可以使用 WMI 了。WMI 开发包(SDK)提供了不同的工具,如基于浏览器的CIM studio,这样你就能够检查程序的检测结果(和查询计划)。许多 工业级的工具都是基于 WMI 的,像 Microsoft Operations Manager(MOM),HP 的 OpenView 和 IBM 的 Tivoli 产品。在.NET的 System.Management 名字空间中提供的 WMI 类可以让你引发自己的事件及提供管理 控制手段。
   System.Managment 提供到 WMI 的自然映射体系。WMI 类变成了 .NET Framework 类。性质 和属性变成了域和限定符。.NET Framework 数据类型清晰地映射到 WMI 数据类型,包括字符串和数组。

托管世界

  由于有了 EIF,.NET为这些工具提供了更加强大的能力。它建立在 WMI 之上,更加容易使用。虽然 EIF 戏剧性地减少了 WMI 的开销,但它没有包括在 .NET Framework 中或 者 Visual Studio® 中。直到现在也无法得到,除非你是 msdn 订阅者,所以它还没有进入主流。但这很快就会改变,特别是 由于有了 Microsoft Patterns and Practices 组发布的新的 Logging Application Block。[MSDN 编辑更新(3/26/2004):EIF 现在已经可以从 Microsoft Enterprise Instrumentation Framework 下载] WMI 是所有这些的基础,因为它被设计成具备可伸缩性,不需要停止应用程序便可重新配置,相对于基于 web.config/app.config 的方案或者 #define TRACE 和 DEBUG 来说是一个很好的选择。
   EIF 提供了 Microsoft.EnterpriseInstrumentation 名字空间。有两个重要的构造函数:事件源和接收器(sinks)。事件源是一个事件的创造者或发布者。通过 关联的事件接收器配置来区分。接收器使用和处理事件。例如,通过将这些记录到 Windows 事件日志或数据库来完成。EIF 中的事件可以是显式的或隐式的。 它以类的形式提供了四个隐式事件:TraceMessageEvent、ErrorMessageEvent、AuditMessageEvent 和 AdminMessageEvent。
   为了使用这些事件,所有事件接收器必须派生自 EventSink 抽象类,它有一个静态方法Raise。EIF 提供了三个事件接收器:Windows Trace、Event Log 和 WMI。Windows 跟踪作为一种服务实现,运行在核心模态来优化性能。Event Log 接收器直接 将事件中转到 Windows Application Event Log,WMI 接收器为记录事件日志 而使用 WMI。EIF 的强大在于你编写访问事件的代码的样式总是一样的。这使得代码的检测 尽可能容易。你所要做的只是调用事件的静态方法 Raise,就像下面这样:

Imports Microsoft.EnterpriseInstrumentation

TraceMessageEvent.Rasie("Hello World");

  你也可以定义你自己的接收器来扩展或加入一些功能。EIF是安装感知的,会创建一个EnterpriseInstrumention.config 文件,你可以手动或者用 EIF API 配置类来编辑。
   显式事件是你可以自己定义的,像下面这样:

<System.ComponentModel.RunInstaller(true)>

Public Class EIFEvent

Inherits Microsoft.EnterpriseInstrumentation.Schema.BaseEvent

	Public MyProperty as String ''field

End Class

  正如你所看到的,它和前面的 WMI 例子几乎一样。这是因为该类派生自 BaseEvent,有效的替代了 BaseEvent,当开发你自己的事件源时,你就可以在 WMI 中使用它了,就如我所讨论的那样。定义的属性确保在运行 InstallUtil.exe 时,你的事件被安装在 WMI 中,而不是 .exe 或 .dll中。
   需要两个配置文件将所有东西联系起来。在安装时产生的 EnterpriseInstrumentation.config 文件允许你定义源和接收器的关系,也可以用 范围来过滤基于对象类型的事件。当你使用 Windows 跟踪接收器时,要用 TraceSessions.config 来定义跟踪的细节。稍后我将详细讲解 TraceSessions.config,但首先看一下其它比较突出的特点,见 Figure 4
   Figure 4 中的代码有四部分。第一,有一个<eventCategory>调用所有事件。而<event>元素把 System.Object 定义为类型,它包括了由 System.Object 或派生类型引发的所有标签事件——这意味着一切。第二个元素是<filter>。它定义了为所有事件目录包装的事件接收器。一个引发的事件将会被所提供的所有的三个接收器所处理。<eventSource>元素定义了要被检测的程序的名字。这允许你通过 <filterBinding> 把程序的事件连接到一个或多个事件接收器。它定义了源和接收器的关系。这个例子使用了隐式的 Application 事件源,它是 EIF 为整个应用程序提供的。它使用了先前代码例子中同样源。
   缺省文件 TraceSessions.config 驻留在 [EI_DIR]/Bin/Trace Service 目录,EI_DIR 是你安装EIF 的位置。你可以使用缺省的会话名字 TraceSession,或者加入你自己的会话名字。更多细节参考EIF 文档。
   配置 WMI 和 EIF 应用并让它们正常工作并不太容易。开发期间 你还会发现,由于涉及了托管/非托管之间的交互,你会 碰到几个致命异常。
   通过提供开发者需要的公用工具,Logging Application Block 建立在 WMI 和 EIF 之上,比如数据库和基于队列的日志。更多细节参考 Patterns and Practices

策略

   通过对 WMI 及其相关技术处理行为进行抽象提取。EIF 比 WMI更进一步。它不仅将源和接收器分离,而且为公共请求提供了标准的接收器。 没有 EIF,你 就必须自己决定要使用的检测类型以及自己要专门为此编写代码。比如,如果你想要 记录事件日志,就必须像前面所讲的那样用 System.Diagnostics.EventLog。但是如果你的程序大小增加了,并想把检测日志移到数据库时怎么办?你必须 自己做许多手工查找替换操作。有了 EIF,你只需要修改一下 EnterpriseInstrumentation.config 配置文件,用不同的事件接收器即可——如果你使用 Logging Application Block,你甚至可以不必自己编写的 此事件接收器,因为已经提供了一个。
   你需要考虑如何最好地利用每一个可用的工具。在 Windows 中,它们的每一个特性都很明确,为了效率你需要所有的一切。这好像要做许多工作,即使有了.NET Framework,其实不然。明智地使用每一个工具会事半功倍。Figure 5 给出了每一个工具的使用指南。
   第一件事就是顺序。任意写异常代码和跟踪是绝对没有效率的。当你编码时你必须考虑需要加入什么样的检测。错误和异常处理 完全是一样的,你从中完不出什么新花样。写代码时,你应该明确自己的意图是什么——指语义上的。保持清醒的头脑, 实现手头的任务。这是添加异常、错误处理和检测的最佳时机,因为你在考虑每一行代码是否会出错。你也在考虑测量什么最有用。这些在设计和建模阶段做会更好,但 这些细节标准常常在开始编码之前很难做到,所以你必须要务实一点。
   一旦你加入了检测代码,你会发现它只不过是将简单的文本日志 到了一个不同地方 而已。其灵活性给予你可扩展的机会。当添加新的特性而不影响实际的应用程序代码时,跟踪代码的实现是能更改的。这就象编写了一个好的基础一样,在开始编码之前 ,SEH 也非常关键。检测也是如此,其好处是在将来的项目中进行重用。
   比如,你想决定方法的时序。如果在每个函数中你都有跟踪进/出( in/out )和异常路径,你便可以测量它们之间的时间差。注意这是来自面向方面的(aspect-oriented) 编程( AOP )的不同方案,它在截取调用方面是有局限的, 也无法知道那些调用都作了什么。这里你显式加入此信息对以后是有帮助的,或者你想过滤此项检测。你也许只想记录正在接收的大于 100 KB 大小的 SOAP 消息。尽管 不是一个错误,你只是想跟踪一下。所以你可以用这些信息添加一个专门的日志点,你的通用处理器会为你进行跟踪。通过自定义的事件接收器,你可以轻松地实现这 个任务。

安全

  日志信息有着自己的安全危险。如果你开始检测敏感数据,比如数据库连接字符串,你便面临着在网络上以明文方式发送它的风险。对于分布式应用程序来说,这 意味着一个大的安全漏洞。
   正如早先提到的,为了使用性能计数器,在代码中对 Windows 事件日志 、WMI、注册表进行某种程度地存取是必不可少的。一个更一般的问题是检测数据被记录后马上对之进行访问。如果你的生产应用程序运行在一个 有非武装区域(DMZs)、防火墙之类的隔离的网络 上,你便不可能从公司网络中删除这些日志。这使得将所有的日志记录信息存储到数据库中 显得更加重要,以便届时进行问题调查时,能被授权读取数据库。

结论

  当问到为什么一个程序不能被更好的检测时,许多设计者和开发者都把性能时为视为关键因素。从来不会将性能作为提供不了合适检测的借口。当你需要考虑业务逻辑的处理时间时 ,这种争论在大多数情况下是站不住脚的。好的检测可以节省许多痛苦的熬夜时间,并使你的企业程序保持平稳的运行。本文我仅仅描述了检测的一些基础知识。 意在抛砖引玉,相信你一定有办法在我的思路上进行扩展。理解 .NET Framework 的新特点是值得为之努力的。花费一点时间仔细看看,想想如何将我所讲的内容应用到下一个项目中。

作者简介

  Jon Fancey 在英国的一家 IT 顾问公司工作, 专注于企业级软件设计和实现。他的 e-mail 联系地址: [email protected]
 


[译者注]

  本文中的一些缩略语解释:

Instrumentation:检测,在应用程序或系统软件中,向管理系统提供性能信息和其它信息的监视功能,或这种监视功能的使用 Windows Management Instrumentation(WMI):Windows 管理规范 Enterprise Instrumentation Framework(EIF):企业检测框架 Structured Exception Handling(SEH):结构化异常处理 Service-Level Agreements(SLAs):服务级别协议。服务提供者和那些服务的客户之间的协议或合约,它设置有关可用性、性能和其它可评估目标的预计服务级别 Web-Based Enterprise Management(WBEM):基于Web的企业管理 Distributed Management Task Force(DMTF):分布式管理任务强制。计算机供应商联盟,致力于对使用各种不同操作系统的企业(这种情况很常见)定义流线型管理方式 Common Information Model(CIM):公共信息模型。用于描述网络管理信息的一种模式,这种模式不限于实现方法且是面向对象的。分布式管理任务组织(DMTF)负责开发与维护 CIM 规范 Logging Application Block(LAB):日志程序模块 Aspect-Oriented Programming (AOP): 面向方面编程
  •  
以上解释主要参考 IBM 的 Tivoli 软件词汇表,如有不当之处请大家批评指正。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章