上個月在調整雙向簡訊專案相關的服務時,遇到一個無法寫出 Log 的偶發性 bug:不僅無法寫出 Log,連 Windows Event Log 都沒有!正所謂人總是失去才懂得珍惜,平日打開事件檢視器就能看到系統幫忙整理好的事件紀錄檔,一旦沒了還真令人一個頭兩個大。在今天的文章中將介紹 Windows 中提供的 Event Log 功能。
所謂的 Windows Event Logs 其實就如同我們在寫程式時習慣以 Log Files紀錄程式運行時的資訊,Windows Event Logs 則紀錄 Windows 作業系統運行時每一個動作的資訊(在其他作業系統中,例如 Linux 也有相同功能的登錄檔)。
微軟貼心地提供了事件檢視器這個元件,方便使用者以系統管理員的身分查看資料。這支應用程式可分為三個部分:監控特定記錄檔及資料夾的 logger、通知檔案儲存的 XML 檔案、提供使用者查看介面的 Event Viewer,即我們常用的事件檢視器。
圖一、事件檢視器介面,點選 Event Logs 可看到更詳細的紀錄內容,也有提供在觸發特定事件時自訂排程工作的功能。
Logs 預設主要可分成三種類:應用程式(Application)、系統(System)、安全性(Security),系統將這些記錄檔以 binary format 保存在系統槽底下的 Windows\System32\winevt\Logs 路徑中。為保持記錄檔的完整性。Windows 的 Event Log Application 以 C# 和 .NET Framework 開發,利用 RSA 和 AES 加密,及 HMAC 雜湊過後儲存檔案。檔案記錄了包含本機系統運作、網路或伺服器等等事件,可包括任何影響系統的事件類型,例如:嘗試登入引發的錯誤、系統設定被修改、應用程式錯誤、系統執行失敗……等等。
系統紀錄檔顧名思義,紀錄電腦作業系統產生的事件檔案。通常這些事件由系統預建,可能包含關於硬體上面的更動、驅動程式、系統更改、以及所有關於機器的動作紀錄。
安全性紀錄檔包括了登入及登出的紀錄,以及其他和系統安全性有關的活動,通常用於監控是否有嘗試或執行成功的未經授權的異常活動,便於用以排除系統內的問題。
應用程式紀錄檔則紀錄與應用程式相關的事件,例如錯誤訊息、警告訊息、或執行動作,通常用於軟體程式除錯。除了以上三種主要種類外,還有一些其他的紀錄,例如:目錄服務事件、檔案複製服務事件、DNS 事件等等。
雖然 Windows Event Log 流程實際上存在著一些漏洞,例如可以手動關閉 Windows上的 Event Log 服務;有些重要資訊例如時間、主機及使用者名稱可被變更、可將 Event Logs 轉移到另一台主機上……等等,但透過適當的手段,這些狀況是可以被避免的,如系統註冊表機制、資料變更時以雜湊演算數位簽章等。
那麼,如何在 .NET C# 中讀寫系統 Event Log 呢?
在 .NET Framework(和 Core 3.0 之後,Core1~2拔掉了此內建類別)提供了 EventLog 類別供使用者操作事件的讀寫。首先若尚無事件來源就用 CreateEventSource() 先建立事件來源,事件來源名稱沒有特別限制,但在電腦上必須是唯一不重複的名稱(若重複則會 Throw Exception);但單一事件紀錄檔可以同時與多個事件來源相關。此事件來源從創建到可以正常使用有一段延遲時間,因此要注意避免立即使用。以底下範例為例,在成功第一次建立事件來源會先結束應用程式,使其可以被系統註冊。第二次再進入這段程式時因事件來源已存在,直接宣告一 EventLog 物件,並將創建的事件來源指定給此物件,使用完 EventLog 物件應該以 Dispose 方法釋放其資源。
1.using System;
2.using System.Diagnostics;
3.using System.Threading;
4.
5.class MySample{
6. public static void Main(){
7. if(!EventLog.SourceExists("MySource"))
8. {
9. EventLog.CreateEventSource("MySource", "MyNewLog");
10. Console.WriteLine("CreatedEventSource");
11. Console.WriteLine("Exiting, execute the application a second time to use the source.");
12. return;
13. }
14. // Create an EventLog instance and assign its source.
15. EventLog myLog = new EventLog();
16. myLog.Source = "MySource";
17.
18. // Write an informational entry to the event log.
19. myLog.WriteEntry("Writing to event log.");
20. }
每個事件紀錄檔大小都有最大上限限制,一般使用者可自微軟提供的事件檢視器為達到上限時的行為進行簡易的設定。或者在 C# 中則可透過 ModifyOverflowPolicy 方法去做進一步的調整。事件紀錄檔可經由 EventLogEntryType 指定項目型別(此欄位也會影響在事件檢視器中顯示的圖示)。
為應用程式自訂EventLog並不困難,在使用上須注意的是,從 Windows Vista / Windows Server 2003 版本開始,系統限制在建立新的 Event Source 時使用者必須擁有管理員權限;會有這項限制的原因是,在判斷 Event Source 是否為本機唯一時,必須搜尋所有 Event Logs 包括安全性紀錄檔,然而從這個 OS 版本開始,一般使用者存取安全性紀錄檔的權限被拿掉了,因此非管理員權限去做操作時會被 throw SecurityException。另外,使用此類別時也要注意不要將相關事件紀錄物件傳遞給不受信任的程式碼,否則可能引發安全性相關的疑慮。
Reference
https://stackoverflow.com/questions/25725151/write-to-windows-application-event-log