英文好的直接看這里:http://www.codeproject.com/Articles/140911/log4net-Tutorial
介紹
log4net.是.NET下面最偉大的日志工具之一。簡單、強大、可擴展,簡直是日志工具的黃金標准. 在我看來唯一欠缺是一個比較直接的使用指南。 這個文檔,在深度主要講如何使用,但它還是有點模糊。基本上,如果你已經知道log4net能做什么,如果你只是想知道語法,那么這個文檔就適合你了.外面的文檔通常是針對一類系統. 我希望我的這份指南能有所突破,我會提供一份完整的指南,包含了一些為曾經遇到的問題。下面的例子和信息是基於log4net組提供的文檔的基礎上編寫的.
基礎
log4net有三個部分組成:配置、安裝、和調用. 配置通常在app.config 或 web.config 文件中. 為,我們下面會深入的講解這一塊. 如果你想通過獨立的配置文件來提升可擴展性,請看 "Getting Away from app.config"這一節. 無論你選擇哪一種配置方式,setup相關的代碼是必須的,通過這些代碼建立與日志模塊的通道.最后,最簡單部分就是調用相關寫日志的方法。
日志等級
一共有7個日志等級,其中有5種等級你可以通過代碼調用。他們是下面幾種 (等級從高到低):
- OFF - 不會產生日志 (不能被調用)
- FATAL
- ERROR
- WARN
- INFO
- DEBUG
- ALL - 所有的操作都會產生日志 (不能被調用)
配置
通常建立一個log4net 日志器的標准方法,在桌面程序中在app.config文件中配置,web程序則在web.config文件中配置. 為了能讓log4net正常工作,需要在配置文件增加幾項配置,下面章節將詳細說明相關配置,修改配置文件后,無需重新編譯.
<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名稱是帶命名空間的完整的類名稱。如果你想監測整個命名空間,這里只要改成命名空間名稱即可. 我不建議在多個logger中復用appenders ,這可能會帶來不可預知的后果。
ConfigSections
因為在一個配置文件中,除了log4net相關的配置信息外,往往還有其他的配置信息。所以你需要指定一個配置段用來標識log4net配置所在位置。下面是一個例子用來表示log4net的相關配置信息位於"log4net
"這個XML標簽下:
<configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/> </configSections>
Appender (General)
appender用來配置日志的相關信息.用來指定日志信息寫在哪里,如何寫,以及在什么情況下寫日志. 不同的appender 可能會有不同的parameters ,但有一些是共性的元素,首先是名字(name)和類型( type)。 每個appender 必須被命名,且必須指定一種類型,下面是一個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>
Layout
在每個appender中必須要有一個layout配置. 使用不同類型的Layout可能會有一點不同,但是基本的東西都是一樣的. 你需要指定一種類型來指明如何寫日志。有多種選擇,但是為我建議你使用PatternLayout. This will allow you to specify how you want your data written to the data repository. 如果你使用PatternLayout,你需要定義一個子項,來指定轉換格式.下面我和會針對轉換格式進行詳細講解, 這里是一個關於 pattern layout 的簡單例子:
<layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline"/>
</layout>
轉換格式(Conversion Patterns)
就如為上面所提到的,轉換格式是用於針對PatternLayout類型的appender ,指定如何存儲信息. 轉換格式中有很多關鍵字,就像字符串轉義符一樣。這里我將針對幾個我認為比較有用和重要的進行介紹. 完整的轉換格式列表可以看log4net 文檔.
%date
- 將時間以本地時區格式輸出. 例如可以用帶“{}”的格式描述符%date{MMMM dd, yyyy HH:mm:ss, fff} 輸出時間字符串: "January 01, 2011 14:15:43, 767". 但是一般建議使用log4net 自動的時間格式 (ABSOLUTE, DATE, or ISO8601) ,因為性能會更好.
%utcdate
-這個與 %date
基本相同,但是它輸出的是通用時間(格林威治時),其他的都一樣.
%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
- 用於指定時間的等級 (DEBUG, INFO, WARN, etc.).
%message
- 輸出的日志消息,如ILog.Debug(…)輸出的一條消息.
%newline
- 這是新增一行,基於應用程序運行的平台,他將轉換為指定指定平台的換行符。使用這個轉換符和使用特定平台的的換行符相比,沒有性能差異。
%timestamp
- 從應用程序啟動起的毫秒數.
%thread
- 發起寫日志的線程名稱(線程如果沒有命名,則顯示線程ID)
除了以上這些,還有一些比較有用但是要慎用的轉換符。這些轉換符的使用可能會給性能帶來負面影響,如下:
%identity
- 當前使用 Principal.Identity.Name
方法的用戶標識.
%location
- 在Debug 模式下特別有用,用來顯示在哪里調用寫日志的的方法(行數,方法名等). 但是在Release模式下,信息會變少.
%line
- 調用的代碼行號.
%method
-調用的方法名.
%username
- 輸出 WindowsIdentity
屬性.
你可能會發現有些是采用字母而不是名稱(如:%m 對應%message),這是一種簡寫,我們在這里不做展開了. 另外需要注意每一個轉換格式都可以指定輸出長度,為了長度規整,會自動增加空格或對內容進行截取.基本的語法是在%符號和名字中加填寫一個數字:
X
- 指定最小字符數,不足指定最小字符數的將自動在字符串左邊補足空字符串. For example,%10message
will give you "hi
".-X
-與上面一樣,只不過是補在字符串右邊,%-10message
will give you "hi
"..X
- 指定最大字符數 ,注意如果超過最大字符串,則從字符串的頭部截取而不是尾部. 例如,如果輸入"Error entry
" ,%.10message返回
"rror entry
" 。
你也可以將兩種組合起來使用,像這樣: "%10.20message
",如果輸入不足10,則在左邊補空格,如果超過20個字節,則從起始位置截取.
過濾器(Filters)
filter是appender中的一個重要部分,通過Filters,你可以指定日志等級,甚至可以查找消息中的關鍵字。Filters 可以混合使用,但是使用時需要小心. 當一個消息符合過濾器條件的時候,它將被寫入日志,並且當前這個過濾器的處理就結束了。因此當你做一個復雜過濾的時候,過濾器的順序就變得尤為重要.
字符串匹配過濾器(StringMatchFilter)
<filter type="log4net.Filter.StringMatchFilter"> <stringToMatch value="test" /> </filter> <filter type="log4net.Filter.DenyAllFilter" />
級別范圍過濾器(LevelRangeFilter)
一個級別范圍過濾器用來告訴系統,只有在指定范圍內的級別日志才會記錄,因此,在下面的例子中 INFO, WARN, ERROR, 或 FATAL級別的日志將被記錄。但是DEBUG 級別的日志將被忽略。這里不需要在最后添加DenyAllFilter 過濾器,因為它隱含表示 不在該范圍內被拒絕記錄。
<filter type="log4net.Filter.LevelRangeFilter"> <levelMin value="INFO" /> <levelMax value="FATAL" /> </filter>
級別匹配過濾器(LevelMatchFilter)
級別匹配過濾器和級別范圍過濾器一樣,只不過它僅針對單個級別進行匹配。但是它並不隱含不匹配就拒絕記錄日志的規則。所以我們需要在最后加上DenyAllFilter 過濾器
<filter type="log4net.Filter.LevelMatchFilter"> <levelToMatch value="ERROR"/> </filter> <filter type="log4net.Filter.DenyAllFilter"/>
拒絕所有過濾器(DenyAllFilter)
如果忘記了這個過濾器,你的appender 將不能按預想的邏輯工作. 這個過濾器的目的就是指定所有內容都不做日志記錄.如果只有這一個過濾器,那么什么內容都不會記錄日志.
<filter type="log4net.Filter.DenyAllFilter" />
Appenders
每一類appender 因為其存儲數據的位置不同,都有其自己的語法集合。其中記錄到數據庫的方式是最不同尋常的。我將列出一些我認為最常用的類型進行介紹。然而,通過上面講的這些信息,你可能已經能使用網上給出的例子. log4net官方網站有很多針對不同類型appenders的例子.就像我之前所說的,通過閱讀log4net 的文檔,一般都不會有什么問題.我一般都拷貝他們的例子,然后根據自己的需要進行修改.
Console Appender
我通常在測試中使用這類appender ,但是它在實際產品中也是很有用的。它將日志寫到輸出窗口,如果是控制台程序,則寫到命令窗口。下面的例子會輸出這樣的日志: "2010-12-26 15:41:03,581 [10] WARN Log4NetTest.frmMain - This is a WARN test." 並在最后有一個換行.
<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文件的名字(在下面這個例子中,日志文件的名字是mylogfile.txt 這個文件將保存在應用程序同一目錄下) ,我們已經指定了追加的模式(而非覆蓋模式),我們還指定了Minimal Lock ,以確保這個文件不會被多個 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
這個appender 可以用在所有使用File appender 的地方。其功能和File appender 基本一樣,只不過額外增加了設置文件大小的功能,超過文件大小則新寫一個文件。 這樣你就不需要當心長時間運行的日志文件過載問題。如果沒有采用這種appender ,只要寫一個文件的時間足夠長,甚至一個小應用程序都可能壓垮操作系統的文件系統。下面的例子中,我將記錄與上面file appender類似式樣的日志。但是我指定了日志文件的大小為10MB,並且在我刪除之前,指定保留存儲5個歸檔文件。這歸檔文件的名字和日志文件名相同,只不過后面跟了“.”和數字(例如: mylogfile.txt.2 表示第二個歸檔文件).staticLogFileName
屬性確保當前日志文件以 file 標簽里定義的命名。(在我的例子中是: 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> /// 無參私有構造函數 /// </summary> private Logger() { } /// <summary> /// 得到單例 /// </summary> public static Logger Singleton { get { return _logger; } } #endregion #region [ 參數 ] 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 [ 內部方法 ] /// <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> /// 日志級別 /// </summary> public enum LogLevel { Debug, Info, Warn, Error, Fatal } #endregion
別忘了,需要在類定義前加上一句:
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
否則什么都不會寫哦。
在app.config/web.config以外進行配置
你可能想在獨立的配置文件中進行log4net 的配置.事實上, 你可能會發現這是一種最佳的配置方式,因為你可以在你的不同的項目中形成不同的標准配置文件。這有助於減少開發時間,及是配置文件標准化。要實現這一目的,你只要在你的程序的兩個地方進行修改即可。首先,你需要將你的配置文件保存成另一個文件。 除了不在app.config 或web.config 文件中外,其他的格式都一樣。第二點需要改變的是在setup調用的地方,如下:
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "MyStandardLog4Net.config", Watch = true)]
在上面這行代碼中,你也可以用"ConfigFileExtension
"后綴代替 "ConfigFile
"。這樣,你需要把你的配置文件名稱改成你的assembly 名稱。如下所示:
[assembly: log4net.Config.XmlConfigurator(ConfigFileExtension = "mylogger", Watch = true)]
在上面這個例子中,如果我們的應用程序名字叫 test.exe,那么log4net 配置文件的名字為 :text.exe.mylogger.
配置文件模版
下面是一個空白的配置文件模版
<!--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>