檢測:.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 軟件詞彙表,如有不當之處請大家批評指正。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章