聲明:本文内容主要譯自Nauman Leghari的Using log4net,亦加入了個人的一點心得(節3.1.4)。
<b>1 </b><b>簡介</b>
<b>1.1 </b><b>Log4net</b><b>的優點:</b>
幾乎所有的大型應用都會有自己的用于跟蹤調試的API。因為一旦程式被部署以後,就不太可能再利用專門的調試工具了。然而一個管理者可能需要有一套強大的日志系統來診斷和修複配置上的問題。
經驗表明,日志記錄往往是軟體開發周期中的重要組成部分。它具有以下幾個優點:它可以提供應用程式運作時的精确環境,可供開發人員盡快找到應用程式中的Bug;一旦在程式中加入了Log 輸出代碼,程式運作過程中就能生成并輸出日志資訊而無需人工幹預。另外,日志資訊可以輸出到不同的地方(控制台,檔案等)以備以後研究之用。
Log4net就是為這樣一個目的設計的,用于.NET開發環境的日志記錄包。
<b>1.2 </b><b>Log4net</b><b>的安裝:</b>
<b>2 </b><b>Log4net</b><b>的結構</b>
log4net 有四種主要的元件,分别是Logger(記錄器), Repository(庫), Appender(附着器)以及 Layout(布局).
<b>2.1 </b><b>Logger</b>
<b>2.1.1 </b><b>Logger</b><b>接口</b>
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)。
<b>2.1.2 </b><b>日志的級别</b>
正如你在ILog的接口中看到的一樣,有五種不同的方法可以跟蹤一個應用程式。事實上,這五種方法是運作在Logger對象設定的不同日志優先級别上。這幾種不同的級别是作為常量定義在log4net.spi.Level類中。你可以在程式中使用任何一種方法。但是在最後的釋出中你也許不想讓所有的代碼來浪費你的CPU周期,是以,架構提供了7種級别和相應的Boolean屬性來控制日志記錄的類型。
Level有以下幾種取值
<b>級别</b>
<b>允許的方法</b>
<b>Boolean</b><b>屬性</b>
<b>優先級别</b>
<b>OFF</b>
<b> </b>
<b>Highest</b>
<b>FATAL</b>
<b>void Fatal(...);</b>
<b>bool IsFatalEnabled;</b>
<b>ERROR</b>
<b>void Error(...);</b>
<b>bool IsErrorEnabled;</b>
<b>WARN</b>
<b>void Warn(...);</b>
<b>bool IsWarnEnabled;</b>
<b>INFO</b>
<b>void Info(...);</b>
<b>bool IsInfoEnabled;</b>
<b>DEBUG</b>
<b>void Debug(...);</b>
<b>bool IsDebugEnabled;</b>
<b>ALL</b>
<b>Lowest</b>
表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");
<b>2.2 </b><b>Repository</b>
Repository主要用于負責日志對象組織結構的維護。在log4net的以前版本中,架構僅支援分等級的組織結構(hierarchical organization)。這種等級結構本質上是庫的一個實作,并且定義在log4net.Repository.Hierarchy 名字空間中。要實作一個Repository,需要實作log4net.Repository.ILoggerRepository 接口。但是通常并不是直接實作該接口,而是以log4net.Repository.LoggerRepositorySkeleton為基類繼承。體系庫 (hierarchical repository )則由log4net.Repository.Hierarchy.Hierarchy類實作。
如果你是個log4net架構的使用者,而非擴充者,那麼你幾乎不會在你的代碼裡用到Repository的類。相反的,你需要用到LogManager類來自動管理庫和日志對象。
<b>2.3 </b><b>Appender</b>
一個好的日志架構應該能夠産生多目的地的輸出。比如說輸出到控制台或儲存到一個日志檔案。log4net 能夠很好的滿足這些要求。它使用一個叫做Appender的元件來定義輸出媒體。正如名字所示,這些元件把它們附加到Logger日志元件上并将輸出傳遞到輸出流中。你可以把多個Appender元件附加到一個日志對象上。 Log4net架構提供了幾個Appender元件。關于log4net提供的Appender元件的完整清單可以在log4net架構的幫助手冊中找到。有了這些現成的Appender元件,一般來說你沒有必要再自己編寫了。但是如果你願意,可以從log4net.Appender.AppenderSkeleton類繼承。
<b>2.4 </b><b>Appender Filters</b>
一個Appender 對象預設地将所有的日志事件傳遞到輸出流。Appender的過濾器(Appender Filters) 可以按照不同的标準過濾日志事件。在log4net.Filter的名字空間下已經有幾個預定義的過濾器。使用這些過濾器,你可以按照日志級别範圍過濾日志事件,或者按照某個特殊的字元串進行過濾。你可以在API的幫助檔案中發現更多關于過濾器的資訊。
<b>2.5 </b><b>Layout</b>
Layout 元件用于向使用者顯示最後經過格式化的輸出資訊。輸出資訊可以以多種格式顯示,主要依賴于我們采用的Layout元件類型。可以是線性的或一個XML檔案。Layout元件和一個Appender元件一起工作。API幫助手冊中有關于不同Layout元件的清單。一個Appender對象,隻能對應一個Layout對象。要實作你自己的Layout類,你需要從log4net.Layout.LayoutSkeleton類繼承,它實作了ILayout接口。
<b>3 </b><b>在程式中使用log4net</b>
在開始對你的程式進行日志記錄前,需要先啟動log4net引擎。這意味着你需要先配置前面提到的三種元件。你可以用兩種方法來設定配置:在單獨的檔案中設定配置或在代碼中定義配置。
因為下面幾種原因,推薦在一個單獨的檔案中定義配置:
l 你不需要重新編譯源代碼就能改變配置;
l 你可以在程式正運作的時候就改變配置。這一點在一些WEB程式和遠端過程調用的程式中有時很重要;
考慮到第一種方法的重要性,我們先看看怎樣在檔案中設定配置資訊。
<b>3.1 </b><b>定義配置檔案</b>
配置資訊可以放在如下幾種形式檔案的一種中。
在程式的配置檔案裡,如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>
<b> <root></b>
<level value="WARN" />
<appender-ref ref="LogFileAppender" />
<appender-ref ref="ConsoleAppender" />
<b> </root></b>
<b> <logger name="testApp.Logging"></b>
<level value="DEBUG"/>
<b> </logger></b>
<b> <appender name="LogFileAppender"</b>
type="log4net.Appender.FileAppender" >
<param name="File" value="log-file.txt" />
<param name="AppendToFile" value="true" />
<b> <layout type="log4net.Layout.PatternLayout"></b>
<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"
/>
<b> </layout></b>
<b> <filter type="log4net.Filter.LevelRangeFilter"></b>
<param name="LevelMin" value="DEBUG" />
<param name="LevelMax" value="WARN" />
<b> </filter></b>
<b> </appender></b>
<b> <appender name="ConsoleAppender"</b>
type="log4net.Appender.ConsoleAppender" >
value="%d [%t] %-5p %c [%x] - %m%n"
/>
</log4net>
</configuration>
你可以直接将上面的文本拷貝到任何程式中使用,但是最好還是能夠了解配置檔案是怎樣構成的。 隻有當你需要在應用程式配置檔案中使用log4net配置時,才需要在<configSection>标簽中加入<section>配置節點入口。對于其他的單獨檔案,隻有<log4net>标簽内的文本才是必需的,這些标簽的順序并不是固定的。下面我們依次講解各個标簽内文本的含義:
<b>3.1.1 </b><b><root></b>
<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.
<b>3.1.2 </b><b><Logger></b>
<logger name="testApp.Logging">
<level value="DEBUG"/>
<logger> 元素預定義了一個具體日志對象的設定。然後通過調用LogManager.GetLogger(“testAPP.Logging”)函數,你可以檢索具有該名字的日志。如果LogManager.GetLogger(…)打開的不是預定義的日志對象,則該日志對象會繼承根日志對象的屬性。知道了這一點,我們可以說,其實<logger>标簽并不是必須的。
<b>3.1.3 </b><b><appender></b>
<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中得到。
<b>3.1.4 </b><b>log4net.Layout.PatternLayout</b><b>中的轉換模式(ConversionPattern)</b>
%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.
<b>3.1.5 </b><b><filter></b>
最後,讓我們看看在Appender元素裡的<filter>标簽。它定義了應用到Appender對象的過濾器。本例中,我們使用了LevelRangeFilter過濾器,它可以隻記錄LevelMin和LevelMax參數指定的日志級别之間的日志事件。可以在一個Appender上定義多個過濾器(Filter),這些過濾器将會按照它們定義的順序對日志事件進行過濾。其他過濾器的有關資訊可以在log4net的SDK文檔中找到。
<b>3.2 </b><b>使用配置檔案</b>
<b>3.2.1 </b><b>關聯配置檔案</b>
當我們建立了上面的配置檔案後,我們接下來需要把它和我們的應用聯系起來。預設的,每個獨立的可執行程式集都會定義它自己的配置。log4net架構使用log4net.Config.DOMConfiguratorAttribute在程式集的級别上定義配置檔案。
例如:可以在項目的AssemblyInfo.cs檔案裡添加以下的語句
[assembly:log4net.Config.DOMConfigurator(ConfigFile="filename",
ConfigFileExtension="ext",Watch=true/false)]
l <b>ConfigFile:</b>指出了我們的配置檔案的路徑及檔案名,包括擴充名。
l <b>ConfigFileExtension:</b>如果我們對被編譯程式的程式集使用了不同的檔案擴充名,那麼我們需要定義這個屬性,預設的,程式集的配置檔案擴充名為”config”。
l <b>Watch (Boolean</b><b>屬性):</b> 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對象。
<b>3.2.2 </b><b>建立或擷取日志對象</b>
日志對象會使用在配置檔案裡定義的屬性。如果某個日志對象沒有事先在配置檔案裡定義,那麼架構會根據繼承結構擷取祖先節點的屬性,最終的,會從根日志擷取屬性。如下所示:
Log4net.ILog log = Log4net.LogManager.GetLogger("MyLogger");
<b>3.2.3 </b><b>輸出日志資訊</b>
可以使用ILog的幾種方法輸出日志資訊。你也可以在調用某方法前先檢查IsXXXEnabled布爾變量,再決定是否調用輸出日志資訊的函數,這樣可以提高程式的性能。因為架構在調用如ILog.Debug(…)這樣的函數時,也會先判斷是否滿足Level日志級别條件。
if (log.IsDebugEnabled) log.Debug("message");
if (log.IsInfoEnabled) log.Info("message);
<b>3.3 </b><b>在程式中配置log4net</b>
除了前面講的用一個配置檔案來配置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
new log4net.Layout.XMLLayout(),"testfile.xml"));
// using a ConsoleAppender with a PatternLayout
new log4net.Appender.ConsoleAppender(
new log4net.Layout.PatternLayout("%d
[%t] %-5p %c - %m%n")));
// using a ConsoleAppender with a SimpleLayout
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架構被配置好以後,就可以如前所述使用日志功能了。
<b>4 </b><b>總結</b>
使用log4net可以很友善地為應用添加日志功能。應用Log4net,使用者可以很精确地控制日志資訊的輸出,減少了多餘資訊,提高了日志記錄性能。同時,通過外部配置檔案,使用者可以不用重新編譯程式就能改變應用的日志行為,使得使用者可以根據情況靈活地選擇要記錄的資訊。
本文轉自齊師傅部落格園部落格,原文連結:http://www.cnblogs.com/youring2/archive/2011/04/26/2029245.html,如需轉載請自行聯系原作者