log4net.是.NET下面最偉大的日誌工具之一。簡單、強大、可擴展,簡直是日誌工具的黃金標準. 在我看來惟一欠缺是一個比較直接的使用指南。 這個文檔,在深度主要講如何使用,但它仍是有點模糊。基本上,若是你已經知道log4net能作什麼,若是你只是想知道語法,那麼這個文檔就適合你了.外面的文檔一般是針對一類系統. 我但願個人這份指南能有所突破,我會提供一份完整的指南,包含了一些爲曾經遇到的問題。下面的例子和信息是基於log4net組提供的文檔的基礎上編寫的. web
log4net有三個部分組成:配置、安裝、和調用. 配置一般在app.config 或 web.config 文件中. 爲,咱們下面會深刻的講解這一塊. 若是你想經過獨立的配置文件來提高可擴展性,請看 "Getting Away from app.config"這一節. 不管你選擇哪種配置方式,setup相關的代碼是必須的,經過這些代碼創建與日誌模塊的通道.最後,最簡單部分就是調用相關寫日誌的方法。數據庫
一共有7個日誌等級,其中有5種等級你能夠經過代碼調用。他們是下面幾種 (等級從高到低):app
一般創建一個log4net 日誌器的標準方法,在桌面程序中在app.config文件中配置,web程序則在web.config文件中配置. 爲了能讓log4net正常工做,須要在配置文件增長几項配置,下面章節將詳細說明相關配置,修改配置文件後,無需從新編譯.框架
<root> <level value="INFO"/> <appender-ref ref ="FileAppender" /> <appender-ref ref="ConsoleAppender" /> </root> <!--Additivity的值缺省是true--> <logger name="testApp.Logging" additivity="false"> </logger>
<logger name="Log4NetTest.OtherClass"> <level value="DEBUG"/> <appender-ref ref="ConsoleAppender"/> </logger>
注意這裏的 logger名稱是帶命名空間的完整的類名稱。若是你想監測整個命名空間,這裏只要改爲命名空間名稱便可. 我不建議在多個logger中複用appenders ,這可能會帶來不可預知的後果。ide
由於在一個配置文件中,除了log4net相關的配置信息外,每每還有其餘的配置信息。因此你須要指定一個配置段用來標識log4net配置所在位置。下面是一個例子用來表示log4net的相關配置信息位於"log4net
"這個XML標籤下:函數
<configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/> </configSections>
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>
在每一個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>
就如爲上面所提到的,轉換格式是用於針對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個字節,則從起始位置截取.
filter是appender中的一個重要部分,經過Filters,你能夠指定日誌等級,甚至能夠查找消息中的關鍵字。Filters 能夠混合使用,可是使用時須要當心. 當一個消息符合過濾器條件的時候,它將被寫入日誌,而且當前這個過濾器的處理就結束了。所以當你作一個複雜過濾的時候,過濾器的順序就變得尤其重要.
<filter type="log4net.Filter.StringMatchFilter"> <stringToMatch value="test" /> </filter> <filter type="log4net.Filter.DenyAllFilter" />
一個級別範圍過濾器用來告訴系統,只有在指定範圍內的級別日誌纔會記錄,所以,在下面的例子中 INFO, WARN, ERROR, 或 FATAL級別的日誌將被記錄。可是DEBUG 級別的日誌將被忽略。這裏不須要在最後添加DenyAllFilter 過濾器,由於它隱含表示 不在該範圍內被拒絕記錄。
<filter type="log4net.Filter.LevelRangeFilter"> <levelMin value="INFO" /> <levelMax value="FATAL" /> </filter>
級別匹配過濾器和級別範圍過濾器同樣,只不過它僅針對單個級別進行匹配。可是它並不隱含不匹配就拒絕記錄日誌的規則。因此咱們須要在最後加上DenyAllFilter 過濾器
<filter type="log4net.Filter.LevelMatchFilter"> <levelToMatch value="ERROR"/> </filter> <filter type="log4net.Filter.DenyAllFilter"/>
若是忘記了這個過濾器,你的appender 將不能按預想的邏輯工做. 這個過濾器的目的就是指定全部內容都不作日誌記錄.若是隻有這一個過濾器,那麼什麼內容都不會記錄日誌.
<filter type="log4net.Filter.DenyAllFilter" />
每一類appender 由於其存儲數據的位置不一樣,都有其本身的語法集合。其中記錄到數據庫的方式是最不一樣尋常的。我將列出一些我認爲最經常使用的類型進行介紹。然而,經過上面講的這些信息,你可能已經能使用網上給出的例子. log4net官方網站有不少針對不一樣類型appenders的例子.就像我以前所說的,經過閱讀log4net 的文檔,通常都不會有什麼問題.我通常都拷貝他們的例子,而後根據本身的須要進行修改.
我一般在測試中使用這類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>
這類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>
這個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)]
不然什麼都不會寫哦。
你可能想在獨立的配置文件中進行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>