Log4Net使用指南

聲明:本文內容主要譯自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接口。
 
 
3           在程序中使用log4net
在開始對你的程序進行日誌記錄前,需要先啓動log4net引擎。這意味着你需要先配置前面提到的三種組件。你可以用兩種方法來設定配置:在單獨的文件中設定配置或在代碼中定義配置。
因爲下面幾種原因,推薦在一個單獨的文件中定義配置:
l         你不需要重新編譯源代碼就能改變配置;
l         你可以在程序正運行的時候就改變配置。這一點在一些WEB程序和遠程過程調用的程序中有時很重要;
考慮到第一種方法的重要性,我們先看看怎樣在文件中設定配置信息。
3.1          定義配置文件
配置信息可以放在如下幾種形式文件的一種中。
在程序的配置文件裏,如AssemblyName.config 或web.config.
在你自己的文件裏。文件名可以是任何你想要的名字,如AppName.exe.xyz等.
log4net框架會在相對於AppDomain.CurrentDomain.BaseDirectory 屬性定義的目錄路徑下查找配置文件。框架在配置文件裏要查找的唯一標識是<log4net>標籤。一個完整的配置文件的例子如下:
 
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <configSections>
    <section name="log4net"
      type="log4net.Config.Log4NetConfigurationSectionHandler,
            log4net-net-1.0"
    />
 </configSections>
 
 <log4net>
 
    <root>
      <level value="WARN" />
      <appender-ref ref="LogFileAppender" />
      <appender-ref ref="ConsoleAppender" />
    </root>
 
    <logger name="testApp.Logging">
      <level value="DEBUG"/>
    </logger>
   
    <appender name="LogFileAppender"
             type="log4net.Appender.FileAppender" >
      <param name="File" value="log-file.txt" />
      <param name="AppendToFile" value="true" />
 
      <layout type="log4net.Layout.PatternLayout">
        <param name="Header" value="[Header]/r/n"/>
        <param name="Footer" value="[Footer]/r/n"/>
        <param name="ConversionPattern"
           value="%d [%t] %-5p %c [%x] - %m%n"
         />
      </layout>
 
      <filter type="log4net.Filter.LevelRangeFilter">
        <param name="LevelMin" value="DEBUG" />
        <param name="LevelMax" value="WARN" />
      </filter>
    </appender>
   
    <appender name="ConsoleAppender"
              type="log4net.Appender.ConsoleAppender" >
      <layout type="log4net.Layout.PatternLayout">
        <param name="ConversionPattern"
           value="%d [%t] %-5p %c [%x] - %m%n"
        />
      </layout>
    </appender>
   
 </log4net>
</configuration>
你可以直接將上面的文本拷貝到任何程序中使用,但是最好還是能夠理解配置文件是怎樣構成的。 只有當你需要在應用程序配置文件中使用log4net配置時,才需要在<configSection>標籤中加入<section>配置節點入口。對於其他的單獨文件,只有<log4net>標籤內的文本纔是必需的,這些標籤的順序並不是固定的。下面我們依次講解各個標籤內文本的含義:
3.1.1     <root>
<root>
 <level value="WARN" />
 <appender-ref ref="LogFileAppender" />
 <appender-ref ref="ConsoleAppender" />
</root>
 
在框架的體系裏,所有的日誌對象都是根日誌(root logger)的後代。 因此如果一個日誌對象沒有在配置文件裏顯式定義,則框架使用根日誌中定義的屬性。在<root>標籤裏,可以定義level級別值和Appender的列表。如果沒有定義LEVEL的值,則缺省爲DEBUG。可以通過<appender-ref>標籤定義日誌對象使用的Appender對象。<appender-ref>聲明瞭在其他地方定義的Appender對象的一個引用。在一個logger對象中的設置會覆蓋根日誌的設置。而對Appender屬性來說,子日誌對象則會繼承父日誌對象的Appender列表。這種缺省的行爲方式也可以通過顯式地設定<logger>標籤的additivity屬性爲false而改變。
<logger name="testApp.Logging" additivity="false">
</logger>
Additivity的值缺省是true.
 
 
3.1.2     <Logger>
<logger name="testApp.Logging">
 <level value="DEBUG"/>
</logger>
 
<logger> 元素預定義了一個具體日誌對象的設置。然後通過調用LogManager.GetLogger(“testAPP.Logging”)函數,你可以檢索具有該名字的日誌。如果LogManager.GetLogger(…)打開的不是預定義的日誌對象,則該日誌對象會繼承根日誌對象的屬性。知道了這一點,我們可以說,其實<logger>標籤並不是必須的。
 
 
3.1.3     <appender>
 
<appender name="LogFileAppender"
          type="log4net.Appender.FileAppender" >
 <param name="File" value="log-file.txt" />
 <param name="AppendToFile" value="true" />
 <layout type="log4net.Layout.PatternLayout">
    <param name="Header" value="[Header]/r/n" />
    <param name="Footer" value="[Footer]/r/n"/>
    <param name="ConversionPattern"
      value="%d [%t] %-5p %c - %m%n"
    />
 </layout>
 <filter type="log4net.Filter.LevelRangeFilter">
    <param name="LevelMin" value="DEBUG" />
    <param name="LevelMax" value="WARN" />
 </filter>
</appender>
 
在<root>標籤或單個的<logger>標籤裏的Appender對象可以用<appender>標籤定義。<appender>標籤的基本形式如上面所示。它定義了appender的名字和類型。 另外比較重要的是<appender>標籤內部的其他標籤。不同的appender有不同的<param>標籤。在這裏,爲了使用FileAppender,你需要一個文件名作爲參數。另外還需要一個在<appender>標籤內部定義一個Layout對象。Layout對象定義在它自己的<layout>標籤內。<layout>標籤的type屬性定義了Layout的類型(在本例裏是PatternLayout),同時也確定了需要提供的參數值。Header和Footer標籤提供了一個日誌會話(logging session)開始和結束時輸出的文字。有關每種appender的具體配置的例子,可以在log4net/doc/manual/example-config-appender.html中得到。
 
3.1.4     log4net.Layout.PatternLayout中的轉換模式(ConversionPattern)
%m(message):輸出的日誌消息,如ILog.Debug(…)輸出的一條消息
%n(new line):換行
%d(datetime):輸出當前語句運行的時刻
%r(run time):輸出程序從運行到執行到當前語句時消耗的毫秒數
%t(thread id):當前語句所在的線程ID
%p(priority): 日誌的當前優先級別,即DEBUG、INFO、WARN…等
%c(class):當前日誌對象的名稱,例如:
       模式字符串爲:%-10c -%m%n
       代碼爲:
ILog log=LogManager.GetLogger(“Exam.Log”);
log.Debug(“Hello”);
    則輸出爲下面的形式:
Exam.Log       - Hello
%L:輸出語句所在的行號
%F:輸出語句所在的文件名
%-數字:表示該項的最小長度,如果不夠,則用空格填充
例如,轉換模式爲%r [%t]%-5p %c - %m%n 的 PatternLayout 將生成類似於以下內容的輸出:
176 [main] INFO org.foo.Bar - Located nearest gas station.
 
3.1.5     <filter>
最後,讓我們看看在Appender元素裏的<filter>標籤。它定義了應用到Appender對象的過濾器。本例中,我們使用了LevelRangeFilter過濾器,它可以只記錄LevelMin和LevelMax參數指定的日誌級別之間的日誌事件。可以在一個Appender上定義多個過濾器(Filter),這些過濾器將會按照它們定義的順序對日誌事件進行過濾。其他過濾器的有關信息可以在log4net的SDK文檔中找到。
 
3.2          使用配置文件
3.2.1     關聯配置文件
當我們創建了上面的配置文件後,我們接下來需要把它和我們的應用聯繫起來。缺省的,每個獨立的可執行程序集都會定義它自己的配置。log4net框架使用 log4net.Config.DOMConfiguratorAttribute在程序集的級別上定義配置文件。
例如:可以在項目的AssemblyInfo.cs文件裏添加以下的語句
[assembly:log4net.Config.DOMConfigurator(ConfigFile="filename",
 ConfigFileExtension="ext",Watch=true/false)]
l         ConfigFile:指出了我們的配置文件的路徑及文件名,包括擴展名。
l         ConfigFileExtension:如果我們對被編譯程序的程序集使用了不同的文件擴展名,那麼我們需要定義這個屬性,缺省的,程序集的配置文件擴展名爲”config”。
l         Watch (Boolean屬性): log4net框架用這個屬性來確定是否需要在運行時監視文件的改變。如果這個屬性爲true,那麼FileSystemWatcher將會被用來監視文件的改變,重命名,刪除等事件。
其中:ConfigFile和ConfigFileExtension屬性不能同時使用,ConfigFile指出了配置文件的名字,例如,ConfigFile=”Config.txt”
ConfigFileExtension則是指明瞭和可執行程序集同名的配置文件的擴展名,例如,應用程序的名稱是”test.exe”,ConfigFileExtension=”txt”,則配置文件就應該是”test.exe.txt” ;
也可以不帶參數應用DOMConfiguratio():
 [assembly: log4net.Config.DOMConfigurator()]
也可以在程序代碼中用DOMConfigurator類打開配置文件。類的構造函數需要一個FileInfo對象作參數,以指出要打開的配置文件名。 這個方法和前面在程序集裏設置屬性打開一個配置文件的效果是一樣的。
log4net.Config.DOMConfigurator.Configure(
 new FileInfo("TestLogger.Exe.Config"));
DOMConfigurator 類還有一個方法ConfigureAndWatch(..), 用來配置框架並檢測文件的變化。
以上的步驟總結了和配置相關的各個方面,下面我們將分兩步來使用logger對象。
3.2.2     創建或獲取日誌對象
日誌對象會使用在配置文件裏定義的屬性。如果某個日誌對象沒有事先在配置文件裏定義,那麼框架會根據繼承結構獲取祖先節點的屬性,最終的,會從根日誌獲取屬性。如下所示:
Log4net.ILog log = Log4net.LogManager.GetLogger("MyLogger");
3.2.3     輸出日誌信息
可以使用ILog的幾種方法輸出日誌信息。你也可以在調用某方法前先檢查IsXXXEnabled布爾變量,再決定是否調用輸出日誌信息的函數,這樣可以提高程序的性能。因爲框架在調用如ILog.Debug(…)這樣的函數時,也會先判斷是否滿足Level日誌級別條件。
if (log.IsDebugEnabled) log.Debug("message");
if (log.IsInfoEnabled) log.Info("message);
 
3.3          在程序中配置log4net
除了前面講的用一個配置文件來配置log4net以外,還可以在程序中用代碼來配置log4net框架。如下面的例子:
 
// 和PatternLayout一起使用FileAppender
log4net.Config.BasicConfigurator.Configure(
 new log4net.Appender.FileAppender(
     new log4net.Layout.PatternLayout("%d
       [%t]%-5p %c [%x] - %m%n"),"testfile.log"));
 
// using a FileAppender with an XMLLayout
log4net.Config.BasicConfigurator.Configure(
 new log4net.Appender.FileAppender(
    new log4net.Layout.XMLLayout(),"testfile.xml"));
 
// using a ConsoleAppender with a PatternLayout
log4net.Config.BasicConfigurator.Configure(
 new log4net.Appender.ConsoleAppender(
    new log4net.Layout.PatternLayout("%d
      [%t] %-5p %c - %m%n")));
 
// using a ConsoleAppender with a SimpleLayout
log4net.Config.BasicConfigurator.Configure(
 new log4net.Appender.ConsoleAppender(new
    log4net.Layout.SimpleLayout()));
儘管這裏用代碼配置log4net也很方便,但是你卻不能分別配置每個日誌對象。所有的這些配置都是被應用到根日誌上的。
log4net.Config.BasicConfigurator 類使用靜態方法Configure 設置一個Appender 對象。而Appender的構造函數又會相應的要求Layout對象。你也可以不帶參數直接調用BasicConfigurator.Configure(),它會使用一個缺省的PatternLayout對象,在一個ConsoleAppender中輸出信息。如下所示:
 
log4net.Config.BasicConfigurator.Configure();
在輸出時會顯示如下格式的信息:
 
0 [1688] DEBUG log1 A B C - Test
20 [1688] INFO log1 A B C - Test
當log4net框架被配置好以後,就可以如前所述使用日誌功能了。
 
 
4           總結
使用log4net可以很方便地爲應用添加日誌功能。應用Log4net,使用者可以很精確地控制日誌信息的輸出,減少了多餘信息,提高了日誌記錄性能。同時,通過外部配置文件,用戶可以不用重新編譯程序就能改變應用的日誌行爲,使得用戶可以根據情況靈活地選擇要記錄的信息。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章