聲明:本文内容主要譯自Nauman Leghari的Using log4net,亦加入了個人的一點心得(節3.1.4)。
1 簡介
1.1 Log4net的優點:
幾乎所有的大型應用都會有自己的用于跟蹤調試的API。因為一旦程式被部署以後,就不太可能再利用專門的調試工具了。然而一個管理者可能需要有一套強大的日志系統來診斷和修複配置上的問題。
經驗表明,日志記錄往往是軟體開發周期中的重要組成部分。它具有以下幾個優點:它可以提供應用程式運作時的精确環境,可供開發人員盡快找到應用程式中的Bug;一旦在程式中加入了Log 輸出代碼,程式運作過程中就能生成并輸出日志資訊而無需人工幹預。另外,日志資訊可以輸出到不同的地方(控制台,檔案等)以備以後研究之用。
Log4net就是為這樣一個目的設計的,用于.NET開發環境的日志記錄包。
1.2 Log4net的安裝:
2 Log4net的結構
log4net 有四種主要的元件,分别是Logger(記錄器), Repository(庫), Appender(附着器)以及 Layout(布局).
2.1 Logger
2.1.1 Logger接口
Logger是應用程式需要互動的主要元件,它用來産生日志消息。産生的日志消息并不直接顯示,還要預先經過Layout的格式化處理後才會輸出。
Logger提供了多種方式來記錄一個日志消息,你可以在你的應用程式裡建立多個Logger,每個執行個體化的Logger對象都被log4net架構作為命名實體(named entity)來維護。這意味着為了重用Logger對象,你不必将它在不同的類或對象間傳遞,隻需要用它的名字為參數調用就可以了。log4net架構使用繼承體系,繼承體系類似于.NET中的名字空間。也就是說,如果有兩個logger,分别被定義為a.b.c和a.b,那麼我們說a.b是a.b.c的祖先。每一個logger都繼承了祖先的屬性
Log4net架構定義了一個ILog接口,所有的logger類都必須實作這個接口。如果你想實作一個自定義的logger,你必須首先實作這個接口。你可以參考在/extension目錄下的幾個例子。
ILog接口的定義如下:
public interface ILog
{
void Debug(object message);
void Info(object message);
void Warn(object message);
void Error(object message);
void Fatal(object message);
//以上的每一個方法都有一個重載的方法,用來支援異常處理。
//每一個重載方法都如下所示,有一個異常類型的附加參數。
void Debug(object message, Exception ex);
// ...
//Boolean 屬性用來檢查Logger的日志級别
//(我們馬上會在後面看到日志級别)
bool isDebugEnabled;
bool isInfoEnabled;
//… 其他方法對應的Boolean屬性
}
Log4net架構定義了一個叫做LogManager的類,用來管理所有的logger對象。它有一個GetLogger()靜态方法,用我們提供的名字參數來檢索已經存在的Logger對象。如果架構裡不存在該Logger對象,它也會為我們建立一個Logger對象。代碼如下所示:
log4net.ILog log = log4net.LogManager.GetLogger("logger-name");
通常來說,我們會以類(class)的類型(type)為參數來調用GetLogger(),以便跟蹤我們正在進行日志記錄的類。傳遞的類(class)的類型(type)可以用typeof(Classname)方法來獲得,或者可以用如下的反射方法來獲得:
System.Reflection.MethodBase.GetCurrentMethod().DeclaringType
盡管符号長了一些,但是後者可以用于一些場合,比如擷取調用方法的類(class)的類型(type)。
2.1.2 日志的級别
正如你在ILog的接口中看到的一樣,有五種不同的方法可以跟蹤一個應用程式。事實上,這五種方法是運作在Logger對象設定的不同日志優先級别上。這幾種不同的級别是作為常量定義在log4net.spi.Level類中。你可以在程式中使用任何一種方法。但是在最後的釋出中你也許不想讓所有的代碼來浪費你的CPU周期,是以,架構提供了7種級别和相應的Boolean屬性來控制日志記錄的類型。
Level有以下幾種取值
級别
允許的方法
Boolean屬性
優先級别
OFF
Highest
FATAL
void Fatal(...);
bool IsFatalEnabled;
RROR
void Error(...);
bool IsErrorEnabled;
WARN
void Warn(...);
bool IsWarnEnabled;
INFO
void Info(...);
bool IsInfoEnabled;
DEBUG
void Debug(...);
bool IsDebugEnabled;
ALL
Lowest
表1 Logger的日志級别
在log4net架構裡,通過設定配置檔案,每個日志對象都被配置設定了一個日志優先級别。如果沒有給一個日志對象顯式地配置設定一個級别,那麼該對象會試圖從他的祖先繼承一個級别值。
ILog接口的每個方法都有一個預先定義好了的級别值。正如你在表1看到的,ILog的Inof()方法具有INFO級别。同樣的,以此類推,Error()方法具有ERROR級别。當我們使用以上的任何一種方法時,log4net架構會檢查日志對象logger的級别和方法的級别。隻有當方法的級别高于日志級别時,日志請求才會被接受并執行。
舉例說明,當你建立了一個日志對象,并且把他的級别設定為INFO。于是架構會設定日志的每個Boolean屬性。當你調用相應的日志方法時,架構會檢查相應的Boolean屬性,以決定該方法能不能執行。如下的代碼:
Logger.Info("message");
Logger.Debug("message");
Logger.Warn("message");
對于第一種方法,Info()的級别等與日志的級别(INFO),是以日志請求會被傳遞,我們可以得到輸出結果”message”。
對于第二種方法,Debug()的級别低于日志對象logger的日志級别(INFO),是以,日志請求被拒絕了,我們得不到任何輸出。同樣的,針對第三行語句,我們可以很容易得出結論。
在表1中有兩個特殊的級别:ALL和OFF。ALL表示允許所有的日志請求。OFF是拒絕所有的請求。
你也可以顯式地檢查Logger對象的Boolean屬性,如下所示:
if (logger.IsDebugEnabled)
Logger.Debug("message");
2.2 Repository
Repository主要用于負責日志對象組織結構的維護。在log4net的以前版本中,架構僅支援分等級的組織結構(hierarchical organization)。這種等級結構本質上是庫的一個實作,并且定義在log4net.Repository.Hierarchy 名字空間中。要實作一個Repository,需要實作log4net.Repository.ILoggerRepository 接口。但是通常并不是直接實作該接口,而是以log4net.Repository.LoggerRepositorySkeleton為基類繼承。體系庫(hierarchical repository )則由log4net.Repository.Hierarchy.Hierarchy類實作。
如果你是個log4net架構的使用者,而非擴充者,那麼你幾乎不會在你的代碼裡用到Repository的類。相反的,你需要用到LogManager類來自動管理庫和日志對象。
2.3 Appender
一個好的日志架構應該能夠産生多目的地的輸出。比如說輸出到控制台或儲存到一個日志檔案。log4net 能夠很好的滿足這些要求。它使用一個叫做Appender的元件來定義輸出媒體。正如名字所示,這些元件把它們附加到Logger日志元件上并将輸出傳遞到輸出流中。你可以把多個Appender元件附加到一個日志對象上。Log4net架構提供了幾個Appender元件。關于log4net提供的Appender元件的完整清單可以在log4net架構的幫助手冊中找到。有了這些現成的Appender元件,一般來說你沒有必要再自己編寫了。但是如果你願意,可以從log4net.Appender.AppenderSkeleton類繼承。
2.4 Appender Filters
一個Appender 對象預設地将所有的日志事件傳遞到輸出流。Appender的過濾器(Appender Filters) 可以按照不同的标準過濾日志事件。在log4net.Filter的名字空間下已經有幾個預定義的過濾器。使用這些過濾器,你可以按照日志級别範圍過濾日志事件,或者按照某個特殊的字元串進行過濾。你可以在API的幫助檔案中發現更多關于過濾器的資訊。
2.5 Layout
Layout 元件用于向使用者顯示最後經過格式化的輸出資訊。輸出資訊可以以多種格式顯示,主要依賴于我們采用的Layout元件類型。可以是線性的或一個XML檔案。Layout元件和一個Appender元件一起工作。API幫助手冊中有關于不同Layout元件的清單。一個Appender對象,隻能對應一個Layout對象。要實作你自己的Layout類,你需要從log4net.Layout.LayoutSkeleton類繼承,它實作了ILayout接口。