Log4Net指南
英文好的直接看這里:http://www.codeproject.com/Articles/140911/log4net-Tutorial
介紹
log4net.是.NET下面最偉大的日志工具之一。簡(jiǎn)單、強(qiáng)大、可擴(kuò)展,簡(jiǎn)直是日志工具的黃金標(biāo)準(zhǔn). 在我看來唯一欠缺是一個(gè)比較直接的使用指南。 這個(gè)文檔,在深度主要講如何使用,但它還是有點(diǎn)模糊。基本上,如果你已經(jīng)知道log4net能做什么,如果你只是想知道語(yǔ)法,那么這個(gè)文檔就適合你了.外面的文檔通常是針對(duì)一類系統(tǒng). 我希望我的這份指南能有所突破,我會(huì)提供一份完整的指南,包含了一些為曾經(jīng)遇到的問題。下面的例子和信息是基于log4net組提供的文檔的基礎(chǔ)上編寫的.
基礎(chǔ)
log4net有三個(gè)部分組成:配置、安裝、和調(diào)用. 配置通常在app.config 或 web.config 文件中. 為,我們下面會(huì)深入的講解這一塊. 如果你想通過獨(dú)立的配置文件來提升可擴(kuò)展性,請(qǐng)看 "Getting Away from app.config"這一節(jié). 無論你選擇哪一種配置方式,setup相關(guān)的代碼是必須的,通過這些代碼建立與日志模塊的通道.最后,最簡(jiǎn)單部分就是調(diào)用相關(guān)寫日志的方法。
日志等級(jí)
一共有7個(gè)日志等級(jí),其中有5種等級(jí)你可以通過代碼調(diào)用。他們是下面幾種 (等級(jí)從高到低):
- OFF - 不會(huì)產(chǎn)生日志 (不能被調(diào)用)
- FATAL
- ERROR
- WARN
- INFO
- DEBUG
- ALL - 所有的操作都會(huì)產(chǎn)生日志 (不能被調(diào)用)
配置
通常建立一個(gè)log4net 日志器的標(biāo)準(zhǔn)方法,在桌面程序中在app.config文件中配置,web程序則在web.config文件中配置. 為了能讓log4net正常工作,需要在配置文件增加幾項(xiàng)配置,下面章節(jié)將詳細(xì)說明相關(guān)配置,修改配置文件后,無需重新編譯.
<Root>
<root> <level value="INFO"/> <appender-ref ref ="FileAppender" /> <appender-ref ref="ConsoleAppender" /> </root> <!--Additivity的值缺省是true--> <logger name="testApp.Logging" additivity="false"> </logger>
<Loggers>
<logger name="Log4NetTest.OtherClass"> <level value="DEBUG"/> <appender-ref ref="ConsoleAppender"/> </logger>
注意這里的 logger名稱是帶命名空間的完整的類名稱。如果你想監(jiān)測(cè)整個(gè)命名空間,這里只要改成命名空間名稱即可. 我不建議在多個(gè)logger中復(fù)用appenders ,這可能會(huì)帶來不可預(yù)知的后果。
ConfigSections
因?yàn)樵谝粋€(gè)配置文件中,除了log4net相關(guān)的配置信息外,往往還有其他的配置信息。所以你需要指定一個(gè)配置段用來標(biāo)識(shí)log4net配置所在位置。下面是一個(gè)例子用來表示log4net的相關(guān)配置信息位于"log4net"這個(gè)XML標(biāo)簽下:
<configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/> </configSections>
Appender (General)
appender用來配置日志的相關(guān)信息.用來指定日志信息寫在哪里,如何寫,以及在什么情況下寫日志. 不同的appender 可能會(huì)有不同的parameters ,但有一些是共性的元素,首先是名字(name)和類型( type)。 每個(gè)appender 必須被命名,且必須指定一種類型,下面是一個(gè)appender實(shí)例:
<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>
Layout
在每個(gè)appender中必須要有一個(gè)layout配置. 使用不同類型的Layout可能會(huì)有一點(diǎn)不同,但是基本的東西都是一樣的. 你需要指定一種類型來指明如何寫日志。有多種選擇,但是為我建議你使用PatternLayout. This will allow you to specify how you want your data written to the data repository. 如果你使用PatternLayout,你需要定義一個(gè)子項(xiàng),來指定轉(zhuǎn)換格式.下面我和會(huì)針對(duì)轉(zhuǎn)換格式進(jìn)行詳細(xì)講解, 這里是一個(gè)關(guān)于 pattern layout 的簡(jiǎn)單例子:
<layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline"/>
</layout>
轉(zhuǎn)換格式(Conversion Patterns)
就如為上面所提到的,轉(zhuǎn)換格式是用于針對(duì)PatternLayout類型的appender ,指定如何存儲(chǔ)信息. 轉(zhuǎn)換格式中有很多關(guān)鍵字,就像字符串轉(zhuǎn)義符一樣。這里我將針對(duì)幾個(gè)我認(rèn)為比較有用和重要的進(jìn)行介紹. 完整的轉(zhuǎn)換格式列表可以看log4net 文檔.
%date - 將時(shí)間以本地時(shí)區(qū)格式輸出. 例如可以用帶“{}”的格式描述符%date{MMMM dd, yyyy HH:mm:ss, fff} 輸出時(shí)間字符串: "January 01, 2011 14:15:43, 767". 但是一般建議使用log4net 自動(dòng)的時(shí)間格式 (ABSOLUTE, DATE, or ISO8601) ,因?yàn)樾阅軙?huì)更好.
%utcdate -這個(gè)與 %date 基本相同,但是它輸出的是通用時(shí)間(格林威治時(shí)),其他的都一樣.
%exception - If an exception is passed in, it will be entered and a new line will be placed after the exception. If no exception is passed in, this entry will be ignored and no new line will be put in. This is usually placed at the end of a log entry, and usually a new line is placed before the exception as well.
%level - 用于指定時(shí)間的等級(jí) (DEBUG, INFO, WARN, etc.).
%message - 輸出的日志消息,如ILog.Debug(…)輸出的一條消息.
%newline - 這是新增一行,基于應(yīng)用程序運(yùn)行的平臺(tái),他將轉(zhuǎn)換為指定指定平臺(tái)的換行符。使用這個(gè)轉(zhuǎn)換符和使用特定平臺(tái)的的換行符相比,沒有性能差異。
%timestamp - 從應(yīng)用程序啟動(dòng)起的毫秒數(shù).
%thread - 發(fā)起寫日志的線程名稱(線程如果沒有命名,則顯示線程ID)
除了以上這些,還有一些比較有用但是要慎用的轉(zhuǎn)換符。這些轉(zhuǎn)換符的使用可能會(huì)給性能帶來負(fù)面影響,如下:
%identity - 當(dāng)前使用 Principal.Identity.Name 方法的用戶標(biāo)識(shí).
%location - 在Debug 模式下特別有用,用來顯示在哪里調(diào)用寫日志的的方法(行數(shù),方法名等). 但是在Release模式下,信息會(huì)變少.
%line - 調(diào)用的代碼行號(hào).
%method -調(diào)用的方法名.
%username - 輸出 WindowsIdentity 屬性.
你可能會(huì)發(fā)現(xiàn)有些是采用字母而不是名稱(如:%m 對(duì)應(yīng)%message),這是一種簡(jiǎn)寫,我們?cè)谶@里不做展開了. 另外需要注意每一個(gè)轉(zhuǎn)換格式都可以指定輸出長(zhǎng)度,為了長(zhǎng)度規(guī)整,會(huì)自動(dòng)增加空格或?qū)?nèi)容進(jìn)行截取.基本的語(yǔ)法是在%符號(hào)和名字中加填寫一個(gè)數(shù)字:
X- 指定最小字符數(shù),不足指定最小字符數(shù)的將自動(dòng)在字符串左邊補(bǔ)足空字符串. For example,%10messagewill give you "hi".-X-與上面一樣,只不過是補(bǔ)在字符串右邊,%-10messagewill give you "hi"..X- 指定最大字符數(shù) ,注意如果超過最大字符串,則從字符串的頭部截取而不是尾部. 例如,如果輸入"Error entry" ,%.10message返回"rror entry" 。
你也可以將兩種組合起來使用,像這樣: "%10.20message",如果輸入不足10,則在左邊補(bǔ)空格,如果超過20個(gè)字節(jié),則從起始位置截取.
過濾器(Filters)
filter是appender中的一個(gè)重要部分,通過Filters,你可以指定日志等級(jí),甚至可以查找消息中的關(guān)鍵字。Filters 可以混合使用,但是使用時(shí)需要小心. 當(dāng)一個(gè)消息符合過濾器條件的時(shí)候,它將被寫入日志,并且當(dāng)前這個(gè)過濾器的處理就結(jié)束了。因此當(dāng)你做一個(gè)復(fù)雜過濾的時(shí)候,過濾器的順序就變得尤為重要.
字符串匹配過濾器(StringMatchFilter)
<filter type="log4net.Filter.StringMatchFilter"> <stringToMatch value="test" /> </filter> <filter type="log4net.Filter.DenyAllFilter" />
級(jí)別范圍過濾器(LevelRangeFilter)
一個(gè)級(jí)別范圍過濾器用來告訴系統(tǒng),只有在指定范圍內(nèi)的級(jí)別日志才會(huì)記錄,因此,在下面的例子中 INFO, WARN, ERROR, 或 FATAL級(jí)別的日志將被記錄。但是DEBUG 級(jí)別的日志將被忽略。這里不需要在最后添加DenyAllFilter 過濾器,因?yàn)樗[含表示 不在該范圍內(nèi)被拒絕記錄。
<filter type="log4net.Filter.LevelRangeFilter"> <levelMin value="INFO" /> <levelMax value="FATAL" /> </filter>
級(jí)別匹配過濾器(LevelMatchFilter)
級(jí)別匹配過濾器和級(jí)別范圍過濾器一樣,只不過它僅針對(duì)單個(gè)級(jí)別進(jìn)行匹配。但是它并不隱含不匹配就拒絕記錄日志的規(guī)則。所以我們需要在最后加上DenyAllFilter 過濾器
<filter type="log4net.Filter.LevelMatchFilter"> <levelToMatch value="ERROR"/> </filter> <filter type="log4net.Filter.DenyAllFilter"/>
拒絕所有過濾器(DenyAllFilter)
如果忘記了這個(gè)過濾器,你的appender 將不能按預(yù)想的邏輯工作. 這個(gè)過濾器的目的就是指定所有內(nèi)容都不做日志記錄.如果只有這一個(gè)過濾器,那么什么內(nèi)容都不會(huì)記錄日志.
<filter type="log4net.Filter.DenyAllFilter" />
Appenders
每一類appender 因?yàn)槠浯鎯?chǔ)數(shù)據(jù)的位置不同,都有其自己的語(yǔ)法集合。其中記錄到數(shù)據(jù)庫(kù)的方式是最不同尋常的。我將列出一些我認(rèn)為最常用的類型進(jìn)行介紹。然而,通過上面講的這些信息,你可能已經(jīng)能使用網(wǎng)上給出的例子. log4net官方網(wǎng)站有很多針對(duì)不同類型appenders的例子.就像我之前所說的,通過閱讀log4net 的文檔,一般都不會(huì)有什么問題.我一般都拷貝他們的例子,然后根據(jù)自己的需要進(jìn)行修改.
Console Appender
我通常在測(cè)試中使用這類appender ,但是它在實(shí)際產(chǎn)品中也是很有用的。它將日志寫到輸出窗口,如果是控制臺(tái)程序,則寫到命令窗口。下面的例子會(huì)輸出這樣的日志: "2010-12-26 15:41:03,581 [10] WARN Log4NetTest.frmMain - This is a WARN test." 并在最后有一個(gè)換行.
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender"> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date{ABSOLUTE} [%thread] %level %logger - %message%newline"/> </layout> <filter type="log4net.Filter.StringMatchFilter"> <stringToMatch value="test" /> </filter> <filter type="log4net.Filter.DenyAllFilter" />
</appender>
File Appender
這類appender 將把日志寫到text文件中。這里我們需要注意的是,我們必須指定text文件的名字(在下面這個(gè)例子中,日志文件的名字是mylogfile.txt 這個(gè)文件將保存在應(yīng)用程序同一目錄下) ,我們已經(jīng)指定了追加的模式(而非覆蓋模式),我們還指定了Minimal Lock ,以確保這個(gè)文件不會(huì)被多個(gè) appenders影響。
<appender name="FileAppender" type="log4net.Appender.FileAppender"> <file value="mylogfile.txt" /> <appendToFile value="true" /> <lockingModel type="log4net.Appender.FileAppender+MinimalLock" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread] %level %logger - %message%newline" /> </layout> <filter type="log4net.Filter.LevelRangeFilter"> <levelMin value="INFO" /> <levelMax value="FATAL" /> </filter> </appender>
Rolling File Appender
這個(gè)appender 可以用在所有使用File appender 的地方。其功能和File appender 基本一樣,只不過額外增加了設(shè)置文件大小的功能,超過文件大小則新寫一個(gè)文件。 這樣你就不需要當(dāng)心長(zhǎng)時(shí)間運(yùn)行的日志文件過載問題。如果沒有采用這種appender ,只要寫一個(gè)文件的時(shí)間足夠長(zhǎng),甚至一個(gè)小應(yīng)用程序都可能壓垮操作系統(tǒng)的文件系統(tǒng)。下面的例子中,我將記錄與上面file appender類似式樣的日志。但是我指定了日志文件的大小為10MB,并且在我刪除之前,指定保留存儲(chǔ)5個(gè)歸檔文件。這歸檔文件的名字和日志文件名相同,只不過后面跟了“.”和數(shù)字(例如: mylogfile.txt.2 表示第二個(gè)歸檔文件).staticLogFileName 屬性確保當(dāng)前日志文件以 file 標(biāo)簽里定義的命名。(在我的例子中是: mylogfile.txt).
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender"> <file value="mylogfile.txt" /> <appendToFile value="true" /> <rollingStyle value="Size" /> <maxSizeRollBackups value="5" /> <maximumFileSize value="10MB" /> <staticLogFileName value="true" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread] %level %logger - %message%newline" /> </layout> </appender>
示例代碼
public sealed class Logger { #region [ 單例模式 ] private static readonly Logger _logger = new Logger(); private static readonly log4net.ILog _Logger4net = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); /// <summary> /// 無參私有構(gòu)造函數(shù) /// </summary> private Logger() { } /// <summary> /// 得到單例 /// </summary> public static Logger Singleton { get { return _logger; } } #endregion #region [ 參數(shù) ] public bool IsDebugEnabled { get { return _Logger4net.IsDebugEnabled; } } public bool IsInfoEnabled { get { return _Logger4net.IsInfoEnabled; } } public bool IsWarnEnabled { get { return _Logger4net.IsWarnEnabled; } } public bool IsErrorEnabled { get { return _Logger4net.IsErrorEnabled; } } public bool IsFatalEnabled { get { return _Logger4net.IsFatalEnabled; } } #endregion #region [ 接口方法 ] #region [ Debug ] public void Debug(string message) { if (this.IsDebugEnabled) { this.Log(LogLevel.Debug, message); } } public void Debug(string message, Exception exception) { if (this.IsDebugEnabled) { this.Log(LogLevel.Debug, message, exception); } } public void DebugFormat(string format, params object[] args) { if (this.IsDebugEnabled) { this.Log(LogLevel.Debug, format, args); } } public void DebugFormat(string format, Exception exception, params object[] args) { if (this.IsDebugEnabled) { this.Log(LogLevel.Debug, string.Format(format, args), exception); } } #endregion #region [ Info ] public void Info(string message) { if (this.IsInfoEnabled) { this.Log(LogLevel.Info, message); } } public void Info(string message, Exception exception) { if (this.IsInfoEnabled) { this.Log(LogLevel.Info, message, exception); } } public void InfoFormat(string format, params object[] args) { if (this.IsInfoEnabled) { this.Log(LogLevel.Info, format, args); } } public void InfoFormat(string format, Exception exception, params object[] args) { if (this.IsInfoEnabled) { this.Log(LogLevel.Info, string.Format(format, args), exception); } } #endregion #region [ Warn ] public void Warn(string message) { if (this.IsWarnEnabled) { this.Log(LogLevel.Warn, message); } } public void Warn(string message, Exception exception) { if (this.IsWarnEnabled) { this.Log(LogLevel.Warn, message, exception); } } public void WarnFormat(string format, params object[] args) { if (this.IsWarnEnabled) { this.Log(LogLevel.Warn, format, args); } } public void WarnFormat(string format, Exception exception, params object[] args) { if (this.IsWarnEnabled) { this.Log(LogLevel.Warn, string.Format(format, args), exception); } } #endregion #region [ Error ] public void Error(string message) { if (this.IsErrorEnabled) { this.Log(LogLevel.Error, message); } } public void Error(string message, Exception exception) { if (this.IsErrorEnabled) { this.Log(LogLevel.Error, message, exception); } } public void ErrorFormat(string format, params object[] args) { if (this.IsErrorEnabled) { this.Log(LogLevel.Error, format, args); } } public void ErrorFormat(string format, Exception exception, params object[] args) { if (this.IsErrorEnabled) { this.Log(LogLevel.Error, string.Format(format, args), exception); } } #endregion #region [ Fatal ] public void Fatal(string message) { if (this.IsFatalEnabled) { this.Log(LogLevel.Fatal, message); } } public void Fatal(string message, Exception exception) { if (this.IsFatalEnabled) { this.Log(LogLevel.Fatal, message, exception); } } public void FatalFormat(string format, params object[] args) { if (this.IsFatalEnabled) { this.Log(LogLevel.Fatal, format, args); } } public void FatalFormat(string format, Exception exception, params object[] args) { if (this.IsFatalEnabled) { this.Log(LogLevel.Fatal, string.Format(format, args), exception); } } #endregion #endregion #region [ 內(nèi)部方法 ] /// <summary> /// 輸出普通日志 /// </summary> /// <param name="level"></param> /// <param name="format"></param> /// <param name="args"></param> private void Log(LogLevel level, string format, params object[] args) { switch (level) { case LogLevel.Debug: _Logger4net.DebugFormat(format, args); break; case LogLevel.Info: _Logger4net.InfoFormat(format, args); break; case LogLevel.Warn: _Logger4net.WarnFormat(format, args); break; case LogLevel.Error: _Logger4net.ErrorFormat(format, args); break; case LogLevel.Fatal: _Logger4net.FatalFormat(format, args); break; } } /// <summary> /// 格式化輸出異常信息 /// </summary> /// <param name="level"></param> /// <param name="message"></param> /// <param name="exception"></param> private void Log(LogLevel level, string message, Exception exception) { switch (level) { case LogLevel.Debug: _Logger4net.Debug(message, exception); break; case LogLevel.Info: _Logger4net.Info(message, exception); break; case LogLevel.Warn: _Logger4net.Warn(message, exception); break; case LogLevel.Error: _Logger4net.Error(message, exception); break; case LogLevel.Fatal: _Logger4net.Fatal(message, exception); break; } } #endregion }//end of class #region [ enum: LogLevel ] /// <summary> /// 日志級(jí)別 /// </summary> public enum LogLevel { Debug, Info, Warn, Error, Fatal } #endregion
別忘了,需要在類定義前加上一句:
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
否則什么都不會(huì)寫哦。
在app.config/web.config以外進(jìn)行配置
你可能想在獨(dú)立的配置文件中進(jìn)行l(wèi)og4net 的配置.事實(shí)上, 你可能會(huì)發(fā)現(xiàn)這是一種最佳的配置方式,因?yàn)槟憧梢栽谀愕牟煌捻?xiàng)目中形成不同的標(biāo)準(zhǔn)配置文件。這有助于減少開發(fā)時(shí)間,及是配置文件標(biāo)準(zhǔn)化。要實(shí)現(xiàn)這一目的,你只要在你的程序的兩個(gè)地方進(jìn)行修改即可。首先,你需要將你的配置文件保存成另一個(gè)文件。 除了不在app.config 或web.config 文件中外,其他的格式都一樣。第二點(diǎn)需要改變的是在setup調(diào)用的地方,如下:
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "MyStandardLog4Net.config", Watch = true)]
在上面這行代碼中,你也可以用"ConfigFileExtension"后綴代替 "ConfigFile"。這樣,你需要把你的配置文件名稱改成你的assembly 名稱。如下所示:
[assembly: log4net.Config.XmlConfigurator(ConfigFileExtension = "mylogger", Watch = true)]
在上面這個(gè)例子中,如果我們的應(yīng)用程序名字叫 test.exe,那么log4net 配置文件的名字為 :text.exe.mylogger.
配置文件模版
下面是一個(gè)空白的配置文件模版
<!--This is the root of your config file--> <configuration> <!-- Level 0 --> <!--This specifies what the section name is--> <configSections> <!-- Level 1 --> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/> <!-- Level 2 --> </configSections> <log4net> <!-- Level 1 --> <appender> <!-- Level 2 --> <layout> <!-- Level 3 --> <conversionPattern /> <!-- Level 4 --> </layout> <filter> <!-- Level 3 --> </filter> </appender> <root> <!-- Level 2 --> <level /> <!-- Level 3 --> <appender-ref /> <!-- Level 3 --> </root> <logger> <!-- Level 2 --> <level /> <!-- Level 3 --> <appender-ref /> <!-- Level 3 --> </logger> </log4net> </configuration>

浙公網(wǎng)安備 33010602011771號(hào)