4.1.6 <filter>
過濾器,只能作為<appender>的子元素。
支持的屬性:
type |
必須的,Filter的類型 |
支持的子元素:
param |
0個或多個, 設置一些參數。 |
4.1.7 <param>
<param>元素可以是任何元素的子元素。
支持的屬性:
name |
必須的,取值是父對象的參數名。 |
value |
可選的,value和type中,必須有一個屬性被指定。value是一個能被轉化為參數值的字符串。 |
type |
可選的,value和type中,必須有一個屬性被指定。type是一個類型名,如果type不是在log4net程序集中定義的,就需要使用全名。 |
支持的子元素:
param |
0個或多個, 設置一些參數。 |
4.2 <appender>配置
<appender>在配置文件中至少有一個,也可以有多個,有些<appender>類型還可以引用其他<appender>類型,具體參數可參見上表。
下面只對寫入回滾文件與輸出到數據庫(這里使用SQL數據庫)配置體會說一下,其他配置可參考官方網站:http://logging.apache.org/log4net/release/config-examples.html
4.2.1寫入回滾文件
<appender name="ReflectionLayout" type="log4net.Appender.RollingFileAppender,log4net">
<!--日志文件路徑,“/”與“/”作用相同,到達的目錄相同,文件夾不存在則新建 -->
<!--按文件大小方式輸出時在這里指定文件名,並且當天的日志在下一天時在文件名后自動追加當天日期形成新文件。-->
<!—按照日期形式輸出時,直接連接元素DatePattern的value形成文件路徑。此處使用這種方式 -->
<!--param的名稱,可以直接查對應的appender類的屬性名即可,這里要查的就是RollingFileAppender類的屬性 -->
<param name="File" value="D:/Log/" />
<!--是否追加到文件-->
<param name="AppendToFile" value="true" />
<!--記錄日志寫入文件時,不鎖定文本文件,防止多線程時不能寫Log,官方說線程非安全-->
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<!—使用Unicode編碼-->
<Encoding value="UTF-8" />
<!--最多產生的日志文件數,超過則只保留最新的n個。設定值value="-1"為不限文件數-->
<param name="MaxSizeRollBackups" value="10" />
<!--是否只寫到一個文件中-->
<param name="StaticLogFileName" value="false" />
<!--按照何種方式產生多個日志文件(日期[Date],文件大小[Size],混合[Composite])-->
<param name="RollingStyle" value="Composite" />
<!--按日期產生文件夾和文件名[在日期方式與混合方式下使用]-->
<!—此處按日期產生文件夾,文件名固定。注意" 的位置-->
<param name="DatePattern" value="yyyy-MM-dd/"ReflectionLayout.log"" />
<!—這是按日期產生文件夾,並在文件名前也加上日期-->
<param name="DatePattern" value="yyyyMMdd/yyyyMMdd"-TimerServer.log"" />
<!—這是先按日期產生文件夾,再形成下一級固定的文件夾—>
<param name="DatePattern" value="yyyyMMdd/"TimerServer/TimerServer.log"" />
<!--每個文件的大小。只在混合方式與文件大小方式下使用。
超出大小后在所有文件名后自動增加正整數重新命名,數字最大的最早寫入。
可用的單位:KB|MB|GB。不要使用小數,否則會一直寫入當前日志-->
<param name="maximumFileSize" value="500KB" />
<!--計數類型為1,2,3…-->
<param name="CountDirection" value="1"/>
<!—過濾設置,LevelRangeFilter為使用的過濾器。 -->
<filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="DEBUG" />
<param name="LevelMax" value="WARN" />
</filter>
<!--記錄的格式。一般用log4net.Layout.PatternLayout布局-->
<!—此處用繼承了log4net.Layout.PatternLayout的自定義布局,TGLog.ExpandLayout2
為命名空間。%property{Operator}、%property{Action}是自定義的輸出-->
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<param name="ConversionPattern"
value="記錄時間:%date 線程ID:[%thread] 日志級別:%-5level 記錄類:%logger 操作者ID:%property{Operator} 操作類型:%property{Action}%n 當前機器名:%property%n當前機器名及登錄用戶:%username %n 記錄位置:%location%n 消息描述:%property{Message}%n 異常:%exception%n 消息:%message%newline%n%n" />
</layout>
</appender>
注意這些配置屬性有些是可選的,如果需要,一定要寫正確,否則要么輸出的不是自己想要的結果,要么干脆不輸出任何信息。
4.2.1寫入SQL數據庫
需要在相應的數據庫中准備好一張表,創建語句如下:
CREATE TABLE [Log] (
[ID] [int] IDENTITY (1, 1) NOT NULL ,
[Date] [datetime] NOT NULL ,
[Thread] [varchar] (100) COLLATE Chinese_PRC_CI_AS NULL ,
[Level] [varchar] (100) COLLATE Chinese_PRC_CI_AS NULL ,
[Logger] [varchar] (200) COLLATE Chinese_PRC_CI_AS NULL ,
[Operator] [int] NULL ,
[Message] [text] COLLATE Chinese_PRC_CI_AS NULL ,
[ActionType] [int] NULL ,
[Operand] [varchar] (300) COLLATE Chinese_PRC_CI_AS NULL ,
[IP] [varchar] (20) COLLATE Chinese_PRC_CI_AS NULL ,
[MachineName] [varchar] (100) COLLATE Chinese_PRC_CI_AS NULL ,
[Browser] [varchar] (50) COLLATE Chinese_PRC_CI_AS NULL ,
[Location] [text] COLLATE Chinese_PRC_CI_AS NULL ,
[Exception] [text] COLLATE Chinese_PRC_CI_AS NULL
)
<appender name="ADONetAppender" type="log4net.Appender.ADONetAppender,log4net">
<!--BufferSize為緩沖區大小,只有日志記錄超設定值才會一塊寫入到數據庫-->
<bufferSize value="10" /><!—或寫為<param name="BufferSize" value="10" />-->
<!--引用-->
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<!--連接數據庫字符串-->
<connectionString value="data source=.;initial catalog=Test;integrated security=false;persist security info=True;User ID=sa;Password=;" />
<!--插入到表Log-->
<commandText value="INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Operator],[Message],[ActionType],[Operand],[IP],[MachineName],[Browser],[Location],[Exception]) VALUES (@log_date, @thread, @log_level, @logger,@operator, @message,@action_type,@operand,@ip,@machineName,@browser,@location,@exception)" />
<!—日志記錄時間,RawTimeStampLayout為默認的時間輸出格式 -->
<parameter>
<parameterName value="@log_date" />
<dbType value="DateTime" />
<layout type="log4net.Layout.RawTimeStampLayout" />
</parameter>
<!--線程號-->
<parameter>
<parameterName value="@thread" />
<dbType value="String" />
<!—長度不可以省略,否則不會輸出-->
<size value="100" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%thread" />
</layout>
</parameter>
<!--日志等級-->
<parameter>
<parameterName value="@log_level" />
<dbType value="String" />
<size value="100" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%level" />
</layout>
</parameter>
<!--日志記錄類名稱-->
<parameter>
<parameterName value="@logger" />
<dbType value="String" />
<size value="200" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%logger" />
</layout>
</parameter>
<!--操作者。這個是自定義的輸出字段,使用重新實現的布局器ReflectionLayout -->
<parameter>
<parameterName value="@operator" />
<!—設置為Int32時只有bufferSize的 value<="1"才正確輸出,沒有找出原因。-->
<dbType value="Int16" />
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<conversionPattern value="%property{Operator}" />
</layout>
</parameter>
<!--操作對象-->
<parameter>
<parameterName value="@operand" />
<dbType value="String" />
<size value="300" />
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<conversionPattern value="%property{Operand}" />
</layout>
</parameter>
<!—IP地址-->
<parameter>
<parameterName value="@ip" />
<dbType value="String" />
<size value="20" />
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<conversionPattern value="%property{IP}" />
</layout>
</parameter>
<!--機器名-->
<parameter>
<parameterName value="@machineName" />
<dbType value="String" />
<size value="100" />
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<conversionPattern value="%property{MachineName}" />
</layout>
</parameter>
<!--瀏覽器-->
<parameter>
<parameterName value="@browser" />
<dbType value="String" />
<size value="50" />
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<conversionPattern value="%property{Browser}" />
</layout>
</parameter>
<!—日志消息-->
<parameter>
<parameterName value="@message" />
<dbType value="String" />
<size value="3000" />
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<conversionPattern value="%property{Message}" />
</layout>
</parameter>
<!--動作類型-->
<parameter>
<parameterName value="@action_type" />
<dbType value="Int16" />
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<conversionPattern value="%property{ActionType}" />
</layout>
</parameter>
<!—記錄日志的位置-->
<parameter>
<parameterName value="@location" />
<dbType value="String" />
<size value="2000" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%location" />
</layout>
</parameter>
<!—異常信息。ExceptionLayout 為異常輸出的默認格式-->
<parameter>
<parameterName value="@exception" />
<dbType value="String" />
<size value="4000" />
<layout type="log4net.Layout.ExceptionLayout" />
</parameter>
</appender>
注意:
向表中輸出的字段不能多於數據表本身字段,而反之則可以,但這些多余字段一定使其可以為空,否則便寫不到數據庫;
輸出字段的類型一定是對應數據表字段數據類型可以隱式轉換的,而且長度也不能超過,否則也不能寫入;
數據表字段設置盡量可以為空,這樣可以避免一條日志記錄存在空數據導致后面的日志都記錄不了。
4.3<logger>的配置
在配置文件<appender>中的配置好了輸出的介質,格式,過濾方式,還要定義日志對象<logger>。
在框架的體系里,所有的日志對象都是根日志(root logger)的后代。 因此如果一個日志對象沒有在配置文件里顯式定義,則框架使用根日志中定義的屬性。在<root>標簽里,可以定義level級別值和Appender的列表。如果沒有定義LEVEL的值,則缺省為DEBUG。可以通過<appender-ref>標簽定義日志對象使用的Appender對象。<appender-ref>聲明了在其他地方定義的Appender對象的一個引用。在一個logger對象中的設置會覆蓋根日志的設置。而對Appender屬性來說,子日志對象則會繼承父日志對象的Appender列表。這種缺省的行為方式也可以通過顯式地設定<logger>標簽的additivity屬性為false而改變。
<root>不顯式申明時使用默認的配置。我覺得在使用時不定義<root>,自定義多個<logger>,在程序中記錄日志時直接使用<logger>的name來查找相應的<logger>,這樣更靈活一些。例如:
<!--同時寫兩個文件和數據庫-->
<logger name="ReflectionLayout">
<level value="DEBUG"/>
<appender-ref ref="HashtableLayout"/>
<appender-ref ref="ReflectionLayout"/>
<appender-ref ref="ADONetAppender"/>
</logger>
4.4關聯配置文件
log4net默認關聯的是應用程序的配置文件App.config(BS程序是Web.config),可以使用程序集自定義屬性來進行設置。下面來介紹一下這個自定義屬性:
log4net.Config.XmlConifguratorAttribute。
XmlConfiguratorAttribute有3個屬性:
ConfigFile: 配置文件的名字,文件路徑相對於應用程序目錄
(AppDomain.CurrentDomain.BaseDirectory)。ConfigFile屬性不能和ConfigFileExtension屬性一起使用。
ConfigFileExtension: 配置文件的擴展名,文件路徑相對於應用程序的目錄。ConfigFileExtension屬性不能和ConfigFile屬性一起使用。
Watch: 如果將Watch屬性設置為true,就會監視配置文件。當配置文件發生變化的時候,就會重新加載。
如果ConfigFile和ConfigFileExtension都沒有設置,則使用應用程序的配置文件App.config(Web.config)。
可以在項目的AssemblyInfo.cs文件里添加以下的語句:
//監視默認的配置文件,App.exe.config
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
//監視配置文件,App.exe.log4net。
[assembly: log4net. Config.XmlConfigurator(ConfigFileExtension = "log4net", Watch = true)]
//使用配置文件log4net.config,不監視改變。注意log4net.config文件的目錄,BS程序在站點目錄//下,CS則在應用程序啟動目錄下,如調試時在/bin/Debug下,一般將文件屬性的文件輸出目錄調為//始終復制即可
[assembly: log4net. Config.XmlConfigurator(ConfigFile = "log4net.config")]
//使用配置文件log4net.config,不監視改變
[assembly: log4net. Config.XmlConfigurator()]
也可以在Global.asax的Application_Start里或者是Program.cs中的Main方法中添加,注意這里一定是絕對路徑,如下所示:
//這是在BS程序下,使用自定義的配置文件log4net.xml,使用Server.MapPath("~") + //@"/log4net.xml”來取得路徑。/log4net.xml為相對於站點的路徑
// ConfigureAndWatch()相當於Configure(Watch = true)
log4net.Config.XmlConfigurator.ConfigureAndWatch(
new System.IO.FileInfo(Server.MapPath("~") + @"/log4net.xml"));
//這是在CS程序下,可以用以下方法獲得:
string assemblyFilePath = Assembly.GetExecutingAssembly().Location;
string assemblyDirPath = Path.GetDirectoryName(assemblyFilePath);
string configFilePath = assemblyDirPath + " //log4net.xml";
log4net.Config.XmlConfigurator.ConfigureAndWatch(
new FileInfo(configFilePath));
或直接使用絕對路徑:
//使用自定義的配置文件,直接絕對路徑為:c:/log4net.config
log4net.Config.XmlConfigurator.Configure(new System.IO.FileInfo(@"c:/log4net.config"));
5、如何記錄日志
Log4net使用很方便,先申明一個封裝類ILog 的對象,如下:
log4net.ILog log = log4net.LogManager.GetLogger("ReflectionLayout");
其中"ReflectionLayout"便是我們自定義的日志對象<logger>的name的值。
對應5個日志輸出級別,log有5 個方法,每個方法都有兩個重載,使用如下:
try
{
log.Debug("這是一個測試!");
}
catch(Exception ec)
{
log.Error("出現錯誤!", ec);
}
如果我們需要輸出的消息是要區別開來,不按一個字符串全部輸出,就需要進行一些擴展了。
6、Log4net的簡單擴展
6.1通過重寫布局Layout輸出傳入的 message對象的屬性
6.1.1重寫Layout類
通過繼承log4net.Layout.PatternLayout類,使用log4net.Core.LoggingEvent類的方法得到了要輸出的message類的名稱,然后通過反射得到各個屬性的值,使用PatternLayout類AddConverter方法傳入得到的值。這里注意要引用用到的類的命名空間。
代碼見附注8.2。
6.1.2配置相應的配置文件
配置文件其他地方不用改動,只是需要改動<appender>中的<layout>。例如:
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<param name="ConversionPattern"
value="記錄時間:%date 操作者ID:%property{Operator}
操作類型:%property{Action}%n 消息描述:%property{Message}%n 異常:%exception%n " />
</layout>
其中<layout>的type由原來的log4net.Layout.PatternLayout換為自定義的TGLog.ExpandLayout2.ReflectionLayout(TGLog.ExpandLayout2為命名空間)。%property{Operator}輸出的即為message類對象的屬性Operator的值。數據庫配置同樣,相應的字段如果是自定義的,則輸出選用自定義的<layout>。例:
<!--動作類型-->
<parameter>
<parameterName value="@action_type" />
<dbType value="Int16" />
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<conversionPattern value="%property{ActionType}" />
</layout>
</parameter>
6.1.3程序中如何使用
和一般使用方法基本相同,只是傳入的參數是一個自定義的類,類的屬性和配置文件中<layout>所有的%property{屬性}是一致的,即%property{屬性}在輸出的時候就查找傳入message類中有無對應的屬性,如果有就輸出值,沒有則輸出null。例:
log4net.ILog log = log4net.LogManager.GetLogger("ReflectionLayout");
try
{
log.Debug(new LogMessage(
1,
"操作對象:0",
(int)TGLog.ActionType.Other,
"這是四個參數測試")
);
}
catch(Exception ec)
{
log.Error(new LogMessage(
1,
"操作對象:0",
(int)TGLog.ActionType.Other,
"這是全部參數測試",
"192.168.1.1",
"MyComputer",
"Maxthon(MyIE2)Fans"),
ec
);
}
LogMessage的全部屬性的構造方法如下:
public LogMessage(
int operatorID,
string operand,
int ActionType,
string message,
string ip,
string machineName,
string browser
)
{
this.ActionType = ActionType;
this.Operator = operatorID;
this.Message = message;
this.Operand = operand;
this.IP = ip;
this.Browser = browser;
this.MachineName = machineName;
}
6.2通過重新實現ILog接口來增加輸入的參數
6.2.1重寫LogImpl,LogManager類及實現ILog接口
這種方式是通過構造一個名為IMyLog接口,是繼承Ilog接口而來,然后分別在MyLogImpl,MyLogManager重新實現IMyLog接口,增加了每種方法的參數。MyLogImpl,MyLogManager分別繼承LogImpl,LogManager而來。
代碼分別見8.3、8.4、8.5:
6.2.2配置相應的配置文件
配置文件其他地方不用改動,只是需要改動<appender>中的<layout>元素name為ConversionPattern的value中輸出格式。例如:
<layout type=" log4net.Layout.PatternLayout ">
<param name="ConversionPattern"
value="記錄時間:%date 操作者ID:%property{Operator}
操作類型:%property{Action}%n 消息描述:%property{Message}%n 異常:%exception%n " />
</layout>
%property{參數}中的參數在MyLogImpl類中定義,如語句:
loggingEvent.Properties["Operator"] = operatorID;
就定義了Operator輸出參數,即%property{Operator}輸出的即為IMyLog中的參數operatorID的值。
數據庫配置同樣。例:
<!--動作類型-->
<parameter>
<parameterName value="@action_type" />
<dbType value="Int16" />
<layout type=" log4net.Layout.PatternLayout ">
<conversionPattern value="%property{ActionType}" />
</layout>
</parameter>
6.2.3程序中如何使用
先引用IMyLog ,MyLogManager所在的命名空間,創建一個IMyLog對象,myLog的5 個方法,每個方法都有四個重載,增加了多參數的重載。例:
IMyLog myLog = MyLogManager.GetLogger("ExpandILog");
try
{
myLog.Debug("這是一個參數重載測試!");
}
catch(Exception ec)
{
log.Error(
1,
"操作對象:0",
(int)TGLog.ActionType.Other,
"這是全部參數測試",
"192.168.1.1",
"MyComputer",
"Maxthon(MyIE2)Fans",
ec
);
}
7、總結
Log4net 功能很多,這里只是對已經嘗試用過的功能總結一下,普通寫日志已經足夠。需要注意的是:
1. Log4net本身也有一些缺陷,比如一個記錄引起了log4net本身的異常,就會使后面的日志無法記錄下來,尤其是在寫入數據庫時。例如使用6.1擴展后,int型的屬性在<appender >的元素<bufferSize>設置不為1時,<dbType value="Int32" />時,就不能輸出到數據庫,而<dbType value="Int16" />則沒任何問題。
2. Log4net本身出現了異常,比如配置文件出現錯誤,有些日志輸出方式會記錄下這些異常,例如應用程序控制台;有些則不會輸出這些錯誤,如數據庫與文件。
3. 擴展時也會留下一些問題。例如在使用6.1擴展輸出字段時就會出現,在log.debug(object message)中,如果message是一個自定義的類,屬性與配置文件中輸出設置也一致,構造函數時也只構造一個參數的實例,寫文件與寫數據庫都成功,而將message按沒有擴展的方式直接傳入一個字符串,即log.debug(“信息內容”)使用則只能寫入文件,而數據庫則沒寫入。自定義的Layout 就是繼承默認的PatternLayout,本來不應該出錯,但出現了問題。原因分析是自定義的message類有類型為int的屬性,作為一個對象傳入時在默認值0,而直接使用字符串則int型的字段得不到默認值,引發異常。所以建議在有擴展存在時,最好多設幾個<logger>,區分清楚,按照統一的形式記錄日志,不要混合使用。
4. 配置文件的設置一定要准確,在一點不正確就會導致日志不能正常輸出,所以在配置時先從最簡單的開始,同時輸出方式選擇一種能輸出log4net本身異常的方式,成功后一點一點加在新配置,這樣出錯了也容易找到那個地方配置有問題。
5. log4net擴展性很強,幾乎所有的組件都可以重寫,在配置文件中配置好就可以使用。
8、附注:
8.1PatterLayout格式化字符表
轉換字符 |
效果 |
a |
等價於appdomain |
appdomain |
引發日志事件的應用程序域的友好名稱。(使用中一般是可執行文件的名字。) |
c |
等價於 logger |
C |
等價於 type |
class |
等價於 type |
d |
等價於 date |
date |
發生日志事件的本地時間。 使用 DE>%utcdate 輸出UTC時間。date后面還可以跟一個日期格式,用大括號括起來。DE>例如:%date{HH:mm:ss,fff}或者%date{dd MMM yyyy HH:mm:ss,fff}。如果date后面什么也不跟,將使用ISO8601 格式 。 日期格式和.Net中DateTime類的ToString方法中使用的格式是一樣。 另外log4net還有3個自己的格式Formatter。 它們是 "ABSOLUTE", "DATE"和"ISO8601"分別代表 AbsoluteTimeDateFormatter, DateTimeDateFormatter和Iso8601DateFormatter。例如:%date{ISO8601}或%date{ABSOLUTE}。 它們的性能要好於ToString。 |
exception |
異常信息 日志事件中必須存了一個異常對象,如果日志事件不包含沒有異常對象,將什么也不輸出。異常輸出完畢后會跟一個換行。一般會在輸出異常前加一個換行,並將異常放在最后。 |
F |
等價於 file |
file |
發生日志請求的源代碼文件的名字。 警告:只在調試的時候有效。調用本地信息會影響性能。 |
identity |
當前活動用戶的名字(Principal.Identity.Name). 警告:會影響性能。(我測試的時候%identity返回都是空的。) |
l |
等價於 location |
L |
等價於 line |
location |
引發日志事件的方法(包括命名空間和類名),以及所在的源文件和行號。 警告:會影響性能。沒有pdb文件的話,只有方法名,沒有源文件名和行號。 |
level |
日志事件等級 |
line |
引發日志事件的行號 警告:會影響性能。 |
logger |
記錄日志事件的Logger對象的名字。 可以使用精度說明符控制Logger的名字的輸出層級,默認輸出全名。 注意,精度符的控制是從右開始的。例如:logger 名為 "a.b.c", 輸出模型為%logger{2} ,將輸出"b.c"。 |
m |
等價於 message |
M |
等價於 method |
message |
由應用程序提供給日志事件的消息。 |
mdc |
MDC (舊為:ThreadContext.Properties) 現在是事件屬性的一部分。 保留它是為了兼容性,它等價於 property。 |
method |
發生日志請求的方法名(只有方法名而已)。 警告:會影響性能。 |
n |
等價於 newline |
newline |
換行符 |
ndc |
NDC (nested diagnostic context) |
p |
等價於 level |
P |
等價於 property |
properties |
等價於 property |
property |
輸出事件的特殊屬性。例如: %property{user} 輸出user屬性。屬性是由loggers或appenders添加到時間中的。 有一個默認的屬性"DE>log4net:HostName"總是會有。DE> %property將輸出所有的屬性 。 (擴展后可以使用)
|
r |
等價於 timestamp |
t |
等價於 thread |
timestamp |
從程序啟動到事件發生所經過的毫秒數。 |
thread |
引發日志事件的線程,如果沒有線程名就使用線程號。 |
type |
引發日志請求的類的全名。. 可以使用精度控制符。例如: 類名是 "log4net.Layout.PatternLayout", 格式模型是%type{1} 將輸出"PatternLayout"。(也是從右開始的。) 警告:會影響性能。 |
u |
等價於 identity |
username |
當前用戶的WindowsIdentity。(類似:HostName/Username) 警告:會影響性能。 |
utcdate |
發生日志事件的UTC時間。DE>后面還可以跟一個日期格式,用大括號括起來。DE>例如:%utcdate{HH:mm:ss,fff}或者%utcdate{dd MMM yyyy HH:mm:ss,fff}。如果utcdate后面什么也不跟,將使用ISO8601 格式 。 日期格式和.Net中DateTime類的ToString方法中使用的格式是一樣。 另外log4net還有3個自己的格式Formatter。 它們是 "ABSOLUTE", "DATE"和"ISO8601"分別代表 AbsoluteTimeDateFormatter, DateTimeDateFormatter和Iso8601DateFormatter。例如:%date{ISO8601}或%date{ABSOLUTE}。 它們的性能要好於ToString。 |
w |
等價於 username |
x |
等價於 ndc |
X |
等價於 mdc |
% |
%%輸出一個百分號 |
關於調用本地信息(caller location information)的說明:
%type %file %line %method %location %class %C %F %L %l %M 都會調用本地信息。這樣做會影響性能。本地信息使用System.Diagnostics.StackTrace得到。.Net 1.0 不支持System.Diagnostics.StackTrace 類。
本地信息在調試模式下可以正常獲取,在非調試模式下可能獲取不到,或只能獲取一部分。(根據我的測試,其實是需要有一個程序數據庫(.pdb)文件。)
%property屬性要用代碼來設置才能使用(也就是擴展一下),
默認屬性log4net:HostName不用設置。
轉義字符的修飾符:
Format modifier |
left justify |
minimum width |
maximum width |
comment |
%20logger |
false |
20 |
none |
如果logger名不足20個字符,就在左邊補空格。 |
%-20logger |
true |
20 |
none |
如果logger名不足20個字符,就在右邊補空格。 |
%.30logger |
NA |
none |
30 |
超過30個字符將截斷。 |
%20.30logger |
false |
20 |
30 |
logger名要在20到30之間,少了在左邊補空格,多了截斷。 |
%-20.30logger |
true |
20 |
30 |
logger名要在20到30之間,少了在右邊補空格,多了截斷。 |
8.2Layout類代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net.Layout;
using log4net.Layout.Pattern;
using System.Reflection;
using System.Collections;
using FastReflectionLib;
namespace TGLog.ExpandLayout2
{
public class ReflectionLayout : PatternLayout
{
public ReflectionLayout()
{
this.AddConverter("property", typeof(ReflectionPatternConverter));
}
}
public class ReflectionPatternConverter : PatternLayoutConverter
{
protected override void Convert(
System.IO.TextWriter writer,
log4net.Core.LoggingEvent loggingEvent
)
{
if (Option != null)
{
// 寫入指定鍵的值
WriteObject(
writer,
loggingEvent.Repository,
LookupProperty(Option,
loggingEvent)
);
}
else
{
// 寫入所有關鍵值對
WriteDictionary(
writer,
loggingEvent.Repository,
loggingEvent.GetProperties()
);
}
}
/// <summary>
/// 通過反射獲取傳入的日志對象的某個屬性的值
/// </summary>
/// <param name="property"></param>
/// <returns></returns>
private object LookupProperty(
string property,
log4net.Core.LoggingEvent loggingEvent)
{
object propertyValue = string.Empty;
PropertyInfo propertyInfo =
loggingEvent.MessageObject.GetType().GetProperty(property);
if (propertyInfo != null)
{
propertyValue =
propertyInfo.GetValue(loggingEvent.MessageObject, null);
}
return propertyValue;
}
}
}
8.3 MyLogImpl類代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net.Core;
namespace TGLog.ExpandILog
{
public class MyLogImpl : LogImpl, IMyLog
{
/// <summary>
/// The fully qualified name of this declaring type not the type of any subclass.
/// </summary>
private readonly static Type ThisDeclaringType = typeof(MyLogImpl);
public MyLogImpl(ILogger logger)
: base(logger)
{
}
#region Implementation of IMyLog
public void Debug(int operatorID, string operand, int actionType,object message,
string ip, string browser, string machineName)
{
Debug(operatorID, operand, actionType, message,
ip, browser, machineName, null);
}
public void Debug(int operatorID, string operand, int actionType,object message,
string ip, string browser, string machineName, System.Exception t)
{
if (this.IsDebugEnabled)
{
LoggingEvent loggingEvent =
new LoggingEvent(ThisDeclaringType, Logger.Repository,
Logger.Name, Level.Info, message, t);
loggingEvent.Properties["Operator"] = operatorID;
loggingEvent.Properties["Operand"] = operand;
loggingEvent.Properties["ActionType"] = actionType;
loggingEvent.Properties["IP"] = ip;
loggingEvent.Properties["Browser"] = browser;
loggingEvent.Properties["MachineName"] = machineName;
Logger.Log(loggingEvent);
}
}
public void Info(int operatorID, string operand, int actionType, object message,
string ip, string browser, string machineName)
{
Info(operatorID, operand, actionType, message, ip, browser, machineName, null);
}
public void Info(int operatorID, string operand, int actionType, object message,
string ip, string browser, string machineName, System.Exception t)
{
if (this.IsInfoEnabled)
{
LoggingEvent loggingEvent =
new LoggingEvent(ThisDeclaringType, Logger.Repository,
Logger.Name, Level.Info, message, t);
loggingEvent.Properties["Operator"] = operatorID;
loggingEvent.Properties["Operand"] = operand;
loggingEvent.Properties["ActionType"] = actionType;
loggingEvent.Properties["IP"] = ip;
loggingEvent.Properties["Browser"] = browser;
loggingEvent.Properties["MachineName"] = machineName;
Logger.Log(loggingEvent);
}
}
public void Warn(int operatorID, string operand, int actionType, object message,
string ip, string browser, string machineName)
{
Warn(operatorID, operand, actionType, message, ip, browser, machineName, null);
}
public void Warn(int operatorID, string operand, int actionType, object message,
string ip, string browser, string machineName, System.Exception t)
{
if (this.IsWarnEnabled)
{
LoggingEvent loggingEvent =
new LoggingEvent(ThisDeclaringType, Logger.Repository,
Logger.Name, Level.Info, message, t);
loggingEvent.Properties["Operator"] = operatorID;
loggingEvent.Properties["Operand"] = operand;
loggingEvent.Properties["ActionType"] = actionType;
loggingEvent.Properties["IP"] = ip;
loggingEvent.Properties["Browser"] = browser;
loggingEvent.Properties["MachineName"] = machineName;
Logger.Log(loggingEvent);
}
}
public void Error(int operatorID, string operand, int actionType, object message,
string ip, string browser, string machineName)
{
Error(operatorID, operand, actionType, message, ip, browser, machineName, null);
}
public void Error(int operatorID, string operand, int actionType, object message,
string ip, string browser, string machineName, System.Exception t)
{
if (this.IsErrorEnabled)
{
LoggingEvent loggingEvent =
new LoggingEvent(ThisDeclaringType, Logger.Repository,
Logger.Name, Level.Info, message, t);
loggingEvent.Properties["Operator"] = operatorID;
loggingEvent.Properties["Operand"] = operand;
loggingEvent.Properties["ActionType"] = actionType;
loggingEvent.Properties["IP"] = ip;
loggingEvent.Properties["Browser"] = browser;
loggingEvent.Properties["MachineName"] = machineName;
Logger.Log(loggingEvent);
}
}
public void Fatal(int operatorID, string operand, int actionType, object message,
string ip, string browser, string machineName)
{
Fatal(operatorID, operand, actionType, message, ip, browser, machineName, null);
}
public void Fatal(int operatorID, string operand, int actionType, object message,
string ip, string browser, string machineName, System.Exception t)
{
if (this.IsFatalEnabled)
{
LoggingEvent loggingEvent =
new LoggingEvent(ThisDeclaringType, Logger.Repository,
Logger.Name, Level.Info, message, t);
loggingEvent.Properties["Operator"] = operatorID;
loggingEvent.Properties["Operand"] = operand;
loggingEvent.Properties["ActionType"] = actionType;
loggingEvent.Properties["IP"] = ip;
loggingEvent.Properties["Browser"] = browser;
loggingEvent.Properties["MachineName"] = machineName;
Logger.Log(loggingEvent);
}
}
#endregion Implementation of IMyLog
}
}
8.4 MyLogManager類代碼
#region Copyright & License
//
// Copyright 2001-2005 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion
using System;
using System.Reflection;
using System.Collections;
using log4net;
using log4net.Core;
using log4net.Repository;
using log4net.Repository.Hierarchy;
namespace TGLog.ExpandILog
{
public class MyLogManager
{
#region Static Member Variables
/// <summary>
/// The wrapper map to use to hold the <see cref="EventIDLogImpl"/> objects
/// </summary>
private static readonly WrapperMap s_wrapperMap = new WrapperMap(newWrapperCreationHandler(WrapperCreationHandler));
#endregion
#region Constructor
/// <summary>
/// Private constructor to prevent object creation
/// </summary>
private MyLogManager() { }
#endregion
#region Type Specific Manager Methods
/// <summary>
/// Returns the named logger if it exists
/// </summary>
/// <remarks>
/// <para>If the named logger exists (in the default hierarchy) then it
/// returns a reference to the logger, otherwise it returns
/// <c>null</c>.</para>
/// </remarks>
/// <param name="name">The fully qualified logger name to look for</param>
/// <returns>The logger found, or null</returns>
public static IMyLog Exists(string name)
{
return Exists(Assembly.GetCallingAssembly(), name);
}
/// <summary>
/// Returns the named logger if it exists
/// </summary>
/// <remarks>
/// <para>If the named logger exists (in the specified domain) then it
/// returns a reference to the logger, otherwise it returns
/// <c>null</c>.</para>
/// </remarks>
/// <param name="domain">the domain to lookup in</param>
/// <param name="name">The fully qualified logger name to look for</param>
/// <returns>The logger found, or null</returns>
public static IMyLog Exists(string domain, string name)
{
return WrapLogger(LoggerManager.Exists(domain, name));
}
/// <summary>
/// Returns the named logger if it exists
/// </summary>
/// <remarks>
/// <para>If the named logger exists (in the specified assembly's domain) then it
/// returns a reference to the logger, otherwise it returns
/// <c>null</c>.</para>
/// </remarks>
/// <param name="assembly">the assembly to use to lookup the domain</param>
/// <param name="name">The fully qualified logger name to look for</param>
/// <returns>The logger found, or null</returns>
public static IMyLog Exists(Assembly assembly, string name)
{
return WrapLogger(LoggerManager.Exists(assembly, name));
}
/// <summary>
/// Returns all the currently defined loggers in the default domain.
/// </summary>
/// <remarks>
/// <para>The root logger is <b>not</b> included in the returned array.</para>
/// </remarks>
/// <returns>All the defined loggers</returns>
public static IMyLog[] GetCurrentLoggers()
{
return GetCurrentLoggers(Assembly.GetCallingAssembly());
}
/// <summary>
/// Returns all the currently defined loggers in the specified domain.
/// </summary>
/// <param name="domain">the domain to lookup in</param>
/// <remarks>
/// The root logger is <b>not</b> included in the returned array.
/// </remarks>
/// <returns>All the defined loggers</returns>
public static IMyLog[] GetCurrentLoggers(string domain)
{
return WrapLoggers(LoggerManager.GetCurrentLoggers(domain));
}
/// <summary>
/// Returns all the currently defined loggers in the specified assembly's domain.
/// </summary>
/// <param name="assembly">the assembly to use to lookup the domain</param>
/// <remarks>
/// The root logger is <b>not</b> included in the returned array.
/// </remarks>
/// <returns>All the defined loggers</returns>
public static IMyLog[] GetCurrentLoggers(Assembly assembly)
{
return WrapLoggers(LoggerManager.GetCurrentLoggers(assembly));
}
/// <summary>
/// Retrieve or create a named logger.
/// </summary>
/// <remarks>
/// <para>Retrieve a logger named as the <paramref name="name"/>
/// parameter. If the named logger already exists, then the
/// existing instance will be returned. Otherwise, a new instance is
/// created.</para>
///
/// <para>By default, loggers do not have a set level but inherit
/// it from the hierarchy. This is one of the central features of
/// log4net.</para>
/// </remarks>
/// <param name="name">The name of the logger to retrieve.</param>
/// <returns>the logger with the name specified</returns>
public static IMyLog GetLogger(string name)
{
return GetLogger(Assembly.GetCallingAssembly(), name);
}
/// <summary>
/// Retrieve or create a named logger.
/// </summary>
/// <remarks>
/// <para>Retrieve a logger named as the <paramref name="name"/>
/// parameter. If the named logger already exists, then the
/// existing instance will be returned. Otherwise, a new instance is
/// created.</para>
///
/// <para>By default, loggers do not have a set level but inherit
/// it from the hierarchy. This is one of the central features of
/// log4net.</para>
/// </remarks>
/// <param name="domain">the domain to lookup in</param>
/// <param name="name">The name of the logger to retrieve.</param>
/// <returns>the logger with the name specified</returns>
public static IMyLog GetLogger(string domain, string name)
{
return WrapLogger(LoggerManager.GetLogger(domain, name));
}
/// <summary>
/// Retrieve or create a named logger.
/// </summary>
/// <remarks>
/// <para>Retrieve a logger named as the <paramref name="name"/>
/// parameter. If the named logger already exists, then the
/// existing instance will be returned. Otherwise, a new instance is
/// created.</para>
///
/// <para>By default, loggers do not have a set level but inherit
/// it from the hierarchy. This is one of the central features of
/// log4net.</para>
/// </remarks>
/// <param name="assembly">the assembly to use to lookup the domain</param>
/// <param name="name">The name of the logger to retrieve.</param>
/// <returns>the logger with the name specified</returns>
public static IMyLog GetLogger(Assembly assembly, string name)
{
return WrapLogger(LoggerManager.GetLogger(assembly, name));
}
/// <summary>
/// Shorthand for <see cref="LogManager.GetLogger(string)"/>.
/// </summary>
/// <remarks>
/// Get the logger for the fully qualified name of the type specified.
/// </remarks>
/// <param name="type">The full name of <paramref name="type"/> will
/// be used as the name of the logger to retrieve.</param>
/// <returns>the logger with the name specified</returns>
public static IMyLog GetLogger(Type type)
{
return GetLogger(Assembly.GetCallingAssembly(), type.FullName);
}
/// <summary>
/// Shorthand for <see cref="LogManager.GetLogger(string)"/>.
/// </summary>
/// <remarks>
/// Get the logger for the fully qualified name of the type specified.
/// </remarks>
/// <param name="domain">the domain to lookup in</param>
/// <param name="type">The full name of <paramref name="type"/> will
/// be used as the name of the logger to retrieve.</param>
/// <returns>the logger with the name specified</returns>
public static IMyLog GetLogger(string domain, Type type)
{
return WrapLogger(LoggerManager.GetLogger(domain, type));
}
/// <summary>
/// Shorthand for <see cref="LogManager.GetLogger(string)"/>.
/// </summary>
/// <remarks>
/// Get the logger for the fully qualified name of the type specified.
/// </remarks>
/// <param name="assembly">the assembly to use to lookup the domain</param>
/// <param name="type">The full name of <paramref name="type"/> will
/// be used as the name of the logger to retrieve.</param>
/// <returns>the logger with the name specified</returns>
public static IMyLog GetLogger(Assembly assembly, Type type)
{
return WrapLogger(LoggerManager.GetLogger(assembly, type));
}
#endregion
#region Extension Handlers
/// <summary>
/// Lookup the wrapper object for the logger specified
/// </summary>
/// <param name="logger">the logger to get the wrapper for</param>
/// <returns>the wrapper for the logger specified</returns>
private static IMyLog WrapLogger(ILogger logger)
{
return (IMyLog)s_wrapperMap.GetWrapper(logger);
}
/// <summary>
/// Lookup the wrapper objects for the loggers specified
/// </summary>
/// <param name="loggers">the loggers to get the wrappers for</param>
/// <returns>Lookup the wrapper objects for the loggers specified</returns>
private static IMyLog[] WrapLoggers(ILogger[] loggers)
{
IMyLog[] results = new IMyLog[loggers.Length];
for (int i = 0; i < loggers.Length; i++)
{
results[i] = WrapLogger(loggers[i]);
}
return results;
}
/// <summary>
/// Method to create the <see cref="ILoggerWrapper"/> objects used by
/// this manager.
/// </summary>
/// <param name="logger">The logger to wrap</param>
/// <returns>The wrapper for the logger specified</returns>
private static ILoggerWrapper WrapperCreationHandler(ILogger logger)
{
return new MyLogImpl(logger);
}
#endregion
}
}
8.5 IMyLog類代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net;
namespace TGLog.ExpandILog
{
public interface IMyLog : ILog
{
void Debug(int operatorID, string operand, int actionType, object message,
string ip, string browser, string machineName);
void Debug(int operatorID, string operand, int actionType,object message,
string ip, string browser, string machineName, Exception t);
void Info(int operatorID, string operand, int actionType, object message,
string ip, string browser, string machineName);
void Info(int operatorID, string operand, int actionType, object message,
string ip, string browser, string machineName, Exception t);
void Warn(int operatorID, string operand, int actionType, object message,
string ip, string browser, string machineName);
void Warn(int operatorID, string operand, int actionType, object message,
string ip, string browser, string machineName, Exception t);
void Error(int operatorID, string operand, int actionType, object message,
string ip, string browser, string machineName);
void Error(int operatorID, string operand, int actionType, object message,
string ip, string browser, string machineName, Exception t);
void Fatal(int operatorID, string operand, int actionType, object message,
string ip, string browser, string machineName);
void Fatal(int operatorID, string operand, int actionType, object message,
string ip, string browser, string machineName, Exception t);
}
}
8.6附件
8.7參考
1、http://peibing211.blog.163.com/blog/static/37116360200992811595469/
2、http://www.cnblogs.com/qiangzi/archive/2009/09/10/1541023.html
3、http://blog.chinaunix.net/u/23701/showart_1414206.html
4、http://itrust.cnblogs.com/archive/2005/01/25/97225.html
5、http://www.cnitblog.com/seeyeah/archive/2009/09/20/61491.aspx
6、http://www.cnblogs.com/zhmore/archive/2009/03/19/1416707.html
7、http://blog.shinylife.net/blog/article.asp?id=948
8、http://www.cnblogs.com/manhoo/archive/2009/06/25/1511066.html