2013年12月14日 星期六

我的 log4net 設定的 Best Practice


        一個系統是否容易追蹤與維護?出了問題容不容易快速查閱、釐清問題癥結?系統日誌(System logging)絕對佔著舉足輕重的角色。.NET 寫系統日誌的方案,最出名的大致上只有兩種:log4netNLog。兩者之間沒什麼優劣比較,這種東西,精通其中之一就很夠用。



        而我身為 log4net 多年來的長期支持者,對於這個強而有力的工具如何妥善地被運用有些心得在此分享,希望可以協助初階的開發人員快速掌握系統日誌的便利性。網路上關於 log4net 的介紹與使用已經很多,大家可以自己找來看,如果你還不熟悉這工具的用法,除了官方網站的英文文件外,以下幾篇還蠻值得參考、快速學習:


        假設你已經知道如何使用 log4net 了,但是它的組態設定千變萬化,怎麼樣才是個比較高階通用、方便容易使用的設定方式呢?我這邊要分享的就是這個經驗。

        首先,log4net 的設定檔有兩種主流載入法(我曾在某公司還有看過非常不入流的第三種):

  1. 嵌入 app.config 或 web.config,跟 .NET 應用程式組態檔共存。上面簡體的指南就是介紹這種方式。
  2. 讀取外部檔案。上面的第二篇簡易教學採用的就是這種方式。
  3. 在程式中用 StringBuilder 組好 xml 設定後,用 XmlReader 直接餵進去。小孩子千萬不要學壞!
        無論如何,我『非常強烈推薦』採用第二種做法!原因如下:
  1. 可以將公司內的 log4net 設定統一管理,採用相同的 Text pattern、filename、location convention。我會在公司內透過 CI Server ,用統一規範過的 log4net.config 將所有生成的 application package 內的 log4net 設定覆蓋,達到一致性、去複雜化的效果。如果 log4net 的設定藏在應用程式的組態檔內,這件事就非常困難
    試想:若公司內有大大小小數十個系統,每個系統寫出的 log 格式、檔案命名、路徑各自發揮都不同,可是會讓維護人員真的動手查問題前先玩抓迷藏,花時間去找不同系統藏在不同地方的 log 檔,而且格式各不相同,解讀與理解困難。
  2. 常會有必要需要臨時動態調整 log 的設定,例如平時沒事的時候把過濾層級關到 WARN,只有出事的時候才想打開 DEBUG 層級。如果把設定擺在應用程式組態內,一旦修改,會導致必須重新啟動應用程式,網站會將在線使用者登出,而服務必須重新下上,影響系統營運!而且有些錯誤只要一重開系統就會消失,等於束手無策!
  3. 廣告:可利用我寫的 GUI Editor 來視覺化編輯這個獨立的組態檔案。
        以上兩個理由,我想已經足夠說服大家將設定獨立於外部的重要性。至於從外部讀取組態的方式,基本上,我都是在程式的進入點就立刻做掉,我制定的文件規範範例如下:


        ConfigureAndWatch 的用途就是為了動態偵測組態檔案的變化,自動重新讀取而不用重新啟動系統。

        另外,最好在啟動的時候多塞入一個自定的 Global property(原因在後續介紹組態時會用到)如下:



        再來是我多年經驗而推薦的設定內容(點此直接下載):

圖有點小,請點擊上圖另開分頁看原圖

        解說:


  1. 依不同系統,動態產生該系統專屬的 log 檔與資料夾路徑:如果一個通用的 log4net 組態可以不用為了錯開不同系統的 log 而個別客製化修改檔案路徑,就減少了許多管理與維護的麻煩。這裡運用了 log4net 可以生成自定屬性的方式,從 appname 的 property 中讀取目前的系統名稱組合出對應路徑與檔名。
  2. 為了強化 log 內文字的讀取與解析,利用 tab 當作資料的分隔字符,保留日後可以透過 ETL 解析統計的可行性。但是自從 1.2.9 版本之後,log4net 不再支援組態檔內直接吃 tab 字元,而是必須透過編碼代表 [TAB]
  3. 避免獨占檔案 IO:基本上,log4net 在寫當下的 log file 是會獨占該檔案的所有權,其他人只能 read-only。這導致當有多個 processes “因故”會寫入同一個 log 檔案時(我不鼓勵共用 log 檔),只有一個會成功,其他 processes 都會失敗,導致其他程式的 log 遺失。或者希望在系統運行中想清除既有的 log 檔案內容或刪除檔案,也無法做到。採用 MinimalLock 策略可以避免上述問題,但請注意,雖然這種方式會影響效能,不過我還是偏愛採用。
  4. 將不同 LEVEL 的 log 分開在不同檔案:當系統發生錯誤,可以根據對應 level 的 log 檔快速找到問題點。平時對系統健康做檢查時,亦可直接檢視 Error.log 該檔案的大小就知道系統現在是否穩定,不用實際打開 log 檔,讓系統管理更輕鬆。
  5. 針對特定 class 或 namespace 底下的程式關閉 log,避免干擾:過多的資料等於沒有資料!當一個系統每秒會產生1MB的 log,一天下來吃掉幾十 GB 的 log 檔案,問題的追蹤與維護就會變成大海撈針。將程式中平時不必要寫的 log 移除相關程式碼是不錯的方法,但是卻不可逆改用 logger level override 的方式,可以關閉特定程式碼輸出的 log,但是保留想關程式碼,在未來無法預期的哪一天需要用到時再打開
        其實 log4net 的學問很廣,深入研究能夠精細調整的還有很多,尤其種類繁多的 Appender,是非常值得所有 .NET 程式設計師投資時間研究的工具。如果這篇文章能當作進階應用經驗傳承的引子,除了介紹與教學外,希望有更多人能分享這類的實戰經驗

沒有留言:

張貼留言