二 動態配置log4j
1?配置外部配置文件來配置的基本步驟
1.1 一個運用配置文件的實例
Log4j之所以能成功的原因之一是它的靈活性。但如果只是簡單的調用BasicConfigurator.configure()來進行配置工作,那么所有的配置都是在函數中寫死的,以后修改配置就要修改原代碼,這就不能體現出log4j的靈活性了,所以基本上不會通過BasicConfigurator.configure()來進行配置工作的。
為了增加軟件的靈活性,最常用的做法就是使用配置文件,如web.xml之于J2EE,struts-config.xml之于struts一樣,log4j也提供了讓我們把配置信息從程序轉移到配置文件中的方法。Log4j提供了兩種方式的配置文件:XML文件和Java的property配置文件。通過把配置信息轉移到外部文件中,當我們要修改配置信息時,就可以直接修改配置文件而不用去修改代碼了,下面,我們就來完成一個通過配置文件來實現log4j的實例。
例2-a:
package?TestLog4j;
import?org.apache.log4j.Logger;
import?org.apache.log4j.BasicConfigurator;
import?org.apache.log4j.PropertyConfigurator;
import?org.apache.log4j.Priority;?public?class?TestLog4j?
{
static?Logger?logger?=?Logger.getLogger(TestLog4j.class.getName());
public?TestLog4j(){}
public?static?void?main(String[]?args)
{
//通過BasicConfigurator類來初始化
//BasicConfigurator.configure();
//(1)通過配置文件來初始化
PropertyConfigurator.configure("F:\\nepalon\\log4j.properties");
logger.debug("Start?of?the?main()?in?TestLog4j"); //代碼(2)
logger.info("Just?testing?a?log?message?with?priority?set?to?INFO");
logger.warn("Just?testing?a?log?message?with?priority?set?to?WARN");
logger.error("Just?testing?a?log?message?with?priority?set?to?ERROR");
logger.fatal("Just?testing?a?log?message?with?priority?set?to?FATAL");
logger.log(Priority.WARN,?"Testing?a?log?message?use?a?alternate?form");
logger.debug(TestLog4j.class.getName()); //代碼(2)
}
}
在這個例子中,我們用PropertyConfigurator.configure("F:\\nepalon\\log4j.properties")代替BasicConfigurator.configure()進行配置。PropertyConfigurator.configure()函數的參數可以是一個properties文件所在路徑的String對象,可以是一個properties文件所在路徑的URL對象,也可以是一個properties對象。通過PropertyConfigurator.configure()可以通過指定的properties文件來配置信息。如果要用XML文件進行信息配置,可以在代碼中調用DOMConfigurator()函數來進行配置工作。在這里,我們只以properties文件來完成例子。接著,我們來看一下log4j.properties文件中都有些什么東西:
例2-b:
log4j.rootLogger?=?DEBUG,?A1
log4j.appender.A1?=?org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout?=?org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern?=?%-4r?[%t]?%-5p?%c?%x?-?%m%n
運行這個實例,運行結果為
0?[main]?DEBUG?TestLog4j.TestLog4j?-?Start?of?the?main()?in?TestLog4j
20?[main]?INFO?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?INFO
20?[main]?WARN?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?WARN
20?[main]?ERROR?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?ERROR
20?[main]?FATAL?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?FATAL
180?[main]?WARN?TestLog4j.TestLog4j?-?Testing?a?log?message?use?a?alternate?form
180?[main]?DEBUG?TestLog4j.TestLog4j?-?TestLog4j.TestLog4j
下面,我們分析一下這個配置文件。
1)?由于每一個Logger對旬都有一個級別,文件的第一行就是定義了一個Logger及其級別。在這里定義了一個根記錄器(root?logger),這涉及到記錄器的層次問題,在些暫時不深入討論,在后面的章節再進行討論。
2)?第二行定義了一個名為A1的輸出流,這個流就是控制臺,所以通過Logger對象打印的信息會在控制臺輸出。
3)?第三行定義了打印信息的布局。在這里我們用PatternLayout作為此記錄器的布局,PatternLayout允許你以靈活的格式來打印信息。
4)?第四行指定的打印信息的具體格式,從結果可知,這個實例的打印格式為:當前打印語句所使用的時間?[日志所在的線程]?打印的級別?當前日志所在的類的全名?日志信息。
現在我們來修改一下這個記錄器的級別,把第一行的DEBUG改為INFO,再運行程序,結果將變為:
0?[main]?INFO?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?INFO
10?[main]?WARN?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?WARN
10?[main]?ERROR?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?ERROR
10?[main]?FATAL?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?FATAL
10?[main]?WARN?TestLog4j.TestLog4j?-?Testing?a?log?message?use?a?alternate?form
由于這個Logger的級別變為INFO,而代碼(2)是調用debug()函數來輸出日志信息時只能當記錄器級別為DEBUG時才輸出信息,所以代碼(2)將不輸出信息。
1.2 實例原理
1.2.1 初始化配置信息
如果要通過JAVA的properties文件來配置信息,那么在代碼中就要通過PropertyConfigurator.configure()函數從properties文件中加載配置信息,這個函數有三種參數形式:一個properties文件所在路徑的String對象,可以是一個properties文件所在路徑的URL對象,也可以是一個properties對象。如果要用XML文件來配置信息,則可用類型的
DOMConfigurator()函數來從一個XML文件中加載配置信息。
1.2.2 輸出端Appender
在上面的例子中,我們都是簡單的把日志信息輸出到控制臺中。其實在log4j中還可以把日志信息輸出到其它的輸出端,對于同一個日志信息,我們還可以讓它同時輸出到多個輸出端中,如同時在控制臺和文件中進行打印。一個輸出端就是一個appender。要在配置文件中定義一個appender有三步:
1)?在定義一個記錄器的同時定義出該記錄器的輸出端appender。在例2的配置文件的第一句log4j.rootLogger?=?DEBUG,?A1中,我們定義了一個根記錄器,它的級別為DEBUG,它有一個appender名為A1。定義根記錄器的格式為log4j.rootLogger?=?[?level?],?appendName1,?appendName2,?...appendNameN。同一個記錄器可有多個輸出端。
2)?定義appender的輸出目的地。定義一個appender的輸出目的地的格式為log4j.appender.appenderName?=?fully.qualified.name.of.appender.class。log4j提供了以下幾種常用的輸出目的地:
??org.apache.log4j.ConsoleAppender,將日志信息輸出到控制臺
??org.apache.log4j.FileAppender,將日志信息輸出到一個文件
??org.apache.log4j.DailyRollingFileAppender,將日志信息輸出到一個,并且每天輸出到一個新的日志文件
??org.apache.log4j.RollingFileAppender,將日志信息輸出到一個文件,通過指定文件的的尺寸,當文件大小到達指定尺寸的時候會自動把文件改名,如名為example.log的文件會改名為example.log.1,同時產生一個新的example.log文件。如果新的文件再次達到指定尺寸,又會自動把文件改名為example.log.2,同時產生一個example.log文件。依此類推,直到example.log.?MaxBackupIndex,MaxBackupIndex的值可在配置文件中定義。
??org.apache.log4j.WriterAppender,將日志信息以流格式發送到任意指定的地方。
??org.apache.log4j.jdbc.JDBCAppender,通過JDBC把日志信息輸出到數據庫中。
在例2中,log4j.appender.A1?=?org.apache.log4j.ConsoleAppender定義了名為A1的appender的輸出目的地為控制臺,所以日志信息將輸出到控制臺。
3)?定義與所選的輸出目的地相關的參數,定義格式為:
log4j.appender.appenderName.optionName1?=?value1
......
log4j.appender.appenderName.optionNameN?=?valueN
其中一個最常用的參數layout將在下面介紹。
1.2.3 輸出格式(布局)layout
通過appender可以控制輸出的目的地,而如果要控制輸出的格式,就可通過log4j的layout組件來實現。通過配置文件定義一個appender的輸出格式,也通常需要兩個步驟:
1)?定義appender的布局模式。定義一個appender的布局模式的格式為log4j.appender.appenderName.layout?=?fully.qualified.name.of.layout.class。Log4j提供的布局模式有以下幾種:
??org.apache.log4j.HTMLLayout,以HTML表格形式布局
??org.apache.log4j.PatternLayout,可以靈活地指定布局模式
??org.apache.log4j.SimpleLayout,包含日志信息的級別和信息字符串
在例2 中log4j.appender.A1.layout?=?org.apache.log4j.PatternLayout定義了名為A1的appender的布局模式為PatternLayout。
2)?定義與所選的布局模式相關的設置信息,定義格式為:
log4j.appender.appenderName.layout.optionName1?=?value1
......
log4j.appender.appenderName.layout.optionNameN?=?valueN
選擇了不同的布局模式可能會有不同的設置信息。實例2所選的布局模式PatternLayout的一個PatternLayout為ConversionPattern?,通過定義這個PatternLayout的值,我們可以指定輸出信息的輸出格式。在例2的配置文件中的定義如下log4j.appender.A1.layout.ConversionPattern?=?%-4r?[%t]?%-5p?%c?%x?-?%m%n。在下面,我們將介紹布局模式PatternLayout的參數ConversionPattern的各個值代表的含義。
1.2.4 ConversionPattern參數的格式含義
格式名?含義
%c?輸出日志信息所屬的類的全名
%d?輸出日志時間點的日期或時間,默認格式為ISO8601,也可以在其后指定格式,比如:%d{yyy-MM-dd?HH:mm:ss?},輸出類似:2002-10-18-?22:10:28
%f?輸出日志信息所屬的類的類名
%l?輸出日志事件的發生位置,即輸出日志信息的語句處于它所在的類的第幾行
%m?輸出代碼中指定的信息,如log(message)中的message
%n?輸出一個回車換行符,Windows平臺為"\r\n",Unix平臺為"\n"
%p?輸出優先級,即DEBUG,INFO,WARN,ERROR,FATAL。如果是調用debug()輸出的,則為DEBUG,依此類推
%r?輸出自應用啟動到輸出該日志信息所耗費的毫秒數
%t?輸出產生該日志事件的線程名
1.3 定義多個輸出目的地的實例
從上面的實例原理中我們已經知道,同一個日志信息可以同時輸出到多個輸出目的地,在這個例子中,我們將實現一個把日志信息同時輸出到控制器、一個文件中的實例和數據庫中。這個實例的Java代碼我們沿用例2中的代碼,我們只需修改配置文件即可。這也體現了log4j的靈活性。
例3-a:
create?table?log4j(
logID?int?primary?key?identity,
message?varchar(1024),
priority?varchar(10),
milliseconds?int,
category?varchar(256),
thread?varchar(100),
NDC?varchar(256),
createDate?datetime,
location?varchar(256),
caller?varchar(100),
method?varchar(100),
filename?varchar(100),
line?int
)
例3-b:
#1?定義了兩個輸出端
log4j.rootLogger?=?INFO,?A1,?A2,A3
#2?定義A1輸出到控制器
log4j.appender.A1?=?org.apache.log4j.ConsoleAppender
#3?定義A1的布局模式為PatternLayout
log4j.appender.A1.layout?=?org.apache.log4j.PatternLayout
#4?定義A1的輸出格式
log4j.appender.A1.layout.ConversionPattern?=?%-4r?[%t]?%-5p?%c?-?%m%n
#5?定義A2輸出到文件
log4j.appender.A2?=?org.apache.log4j.RollingFileAppender
#6?定義A2要輸出到哪一個文件
log4j.appender.A2.File?=?F:\\nepalon\\classes\\example3.log
#7?定義A2的輸出文件的最大長度
log4j.appender.A2.MaxFileSize?=?1KB
#8?定義A2的備份文件數
log4j.appender.A2.MaxBackupIndex?=?3
#9?定義A2的布局模式為PatternLayout
log4j.appender.A2.layout?=?org.apache.log4j.PatternLayout
#10?定義A2的輸出格式
log4j.appender.A2.layout.ConversionPattern?=?%d{yyyy-MM-dd?hh:mm:ss}:%p?%t?%c?-?%m%n
#11區?定義A3輸出到數據庫
log4j.appender.A3?=?org.apache.log4j.jdbc.JDBCAppender
log4j.appender.A3.BufferSize?=?40
log4j.appender.A3.Driver?=?com.microsoft.jdbc.sqlserver.SQLServerDriver
log4j.appender.A3.URL?=?jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=nepalon
log4j.appender.A3.User?=?sa
log4j.appender.A3.Password?=?
log4j.appender.A3.layout?=?org.apache.log4j.PatternLayout
log4j.appender.A3.layout.ConversionPattern?=?INSERT?INTO?log4j?(createDate,?thread,?priority,?category,?message)?values(getdate(),?'%t',?'%-5p',?'%c',?'%m')
配置文件中的6、7、8行顯示了輸出端為RollingFileAppender的特有參數及其運用的方法。11區顯示了輸出端為JDBCAppender的特有參數及其運用方法。在這著重講解一下6、7、8行的作用。6行指定日志信息輸出到哪個文件,7行指定日志文件的最大長度,最后要詳細介紹8行。第8行的參數是設置備份文件的個數的參數,在這里我們設置為3,表示最多有3個備份文件,具體作用為:
1)?當example3.log文件的大小超過K時,就把文件改名為example3.log.1,同時生成一個新的example3.log文件
2)?當example3.log文件的大小再次超過1K,又把文件改名為example3.log.1。但由于此時example3.log.1已存在,則先把example3.log.1更名為example3.log.2,再把example3.log文件改名為example3.log.1
3)?同理,當example3.log文件的大小再次超過1K,先把example3.log.2文件更名為example3.log.3,把example3.log.1文件更名為example3.log.2,再把example3.log文件改名為example3.log.1
4)?當example3.log文件的大小再次超過1K,先把example3.log.2文件更名為example3.log.3,舊的example3.log.3文件將被覆蓋;把example3.log.1文件更名為example3.log.2,舊的example3.log.2文件被覆蓋;最后把example3.log文件改名為example3.log.1并覆蓋掉舊的example3.log.1文件。
運行結果將分為兩部分
在控制器中:
0?[main]?INFO?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?INFO
11?[main]?WARN?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?WARN
21?[main]?ERROR?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?ERROR?21?[main]?FATAL?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?FATAL
21?[main]?WARN?TestLog4j.TestLog4j?-?Testing?a?log?message?use?a?alternate?form
在文件example3.log中:
2003-12-18?04:23:02:INFO?main?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?INFO
2003-12-18?04:23:02:WARN?main?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?WARN
2003-12-18?04:23:02:ERROR?main?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?ERROR
2003-12-18?04:23:02:FATAL?main?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?FATAL
2003-12-18?04:23:02:WARN?main?TestLog4j.TestLog4j?-?Testing?a?log?message?use?a?alternate?form
1.4 配置log4j的總結
這個教程到這里,關于配置log4j的配置文件的基本原理已經講完了,而且通過例3我們已經可以完成基本的日志工作了?,F在,我們就做一個總結。配置一個配置文件的基本步驟如下:
1)?定義一個Logger。在定義Logger時指定該Logger的級別級其輸出目的地。定義Logger的格式為
log4j.rootLogger?=?[?level?],?appendName1,?appendName2,?...appendNameN。
2)?定義appender的輸出目的地。定義一個appender的輸出目的地的格式為
log4j.appender.appenderName?=?fully.qualified.name.of.appender.class。
log4j提供的輸出端有ConsoleAppender、FileAppender?、DailyRollingFileAppender、RollingFileAppender和WriterAppender。
3)?定義appender的除布局模式外的其它相關參數,如例3中第6、7、8定義了A2的相關參數。定義格式為
log4j.appender.appenderName.optionName1?=?value1
......
log4j.appender.appenderName.optionNameN?=?valueN
如果除了布局模式外不需要定義別的參數,可跳過這一步(如例3中的A1)。
4)?定義appender的布局模式。定義一個appender的布局模式的格式為
log4j.appender.appenderName.layout?=?fully.qualified.name.of.layout.class。
布局模式其實也是步驟3)中的一個部分,只是布局模式參數是每一個appender必須定義的參數。Log4j提供的布局模式有HTMLLayout、PatternLayout和SimpleLayout。
5)?定義與所選的布局模式相關的設置信息,定義格式為
og4j.appender.appenderName.layout.optionName1?=?value1
......
log4j.appender.appenderName.layout.optionNameN?=?valueN
2 記錄器的層次Logger?hierarchy
2.1 何為記錄器的層次hierarchy
首先,我們先看一下何為層次,以我們最熟悉的繼承為例,下面是一張類圖
在這個繼承體系中,類B是類C的父類,類A是類C的祖先類,類D是類C的子類。這些類之間就構成一種層次關系。在這些具有層次關系的類中,子類都可繼承它的父類的特征,如類B的對象能調用類A中的非private實例變量和函數;而類C由于繼承自類B,所以類B的對象可以同時調用類A和類B中的非private實例變量和函數。
在log4j中,處于不同層次中的Logger也具有象類這樣的繼承關系。
2.2 記錄器的層次
如果一個應用中包含了上千個類,那么也幾乎需要上千個Logger實例。如何對這上千個Logger實例進行方便地配置,就是一個很重要的問題。Log4J采用了一種樹狀的繼承層次巧妙地解決了這個問題。在Log4J中Logger是具有層次關系的。它有一個共同的根,位于最上層,其它Logger遵循類似包的層次。下面我們將進行介紹。
2.2.1 根記錄器root?logger
就象一個Java中的Object類一樣,log4j中的logger層次中有一個稱之為根記錄器的記錄器,其它所有的記錄器都繼承自這個根記錄器。根記錄器有兩個特征:
1)?根記錄器總是存在。就像Java中的Object類一樣,因為用log4j輸出日志信息是通過記錄器來實現的,所以只要你應用了log4j,根記錄器就肯定存在的。
2)?根記錄器沒有名稱,所以不能通過名稱來取得根記錄器。但在Logger類中提供了getRootLogger()的方法來取得根記錄器。
2.2.2 記錄器的層次
Logger遵循類似包的層次。如
static?Logger?rootLog?=?Logger.getRootLogger();
static?Logger?log1?=?Logger.getLogger("test4j");
static?Logger?log2?=?Logger.getLogger("test4j.test4j2");
static?Logger?log3?=?Logger.getLogger("test4j.test4j2.test4j2");
那么rootLog是log2的祖先子記錄器,log1是log2的父子記錄器,log3是log2的子記錄器。記錄器象Java中的類繼承一樣,子記錄器可以繼承父記錄器的設置信息,也可以可以覆寫相應的信息。
首先先看一下記錄器層次中的繼承有什么用處。假設程序中的每個包都具有一些基本的日志信息,而包中的不同包可能會有些額外的日志信息要輸出,這種情況就可以象處理Java中的類一樣,運用Logger中的層次關系來達到目的。假設有個名為A的包,我包下的所有類都要把日志信息輸出到控制臺中;A.B包除了輸出到控制臺外還要輸出到文件中;A.C包除了輸出到控制臺中還要輸出到HTML文檔中。這樣我們就可以通過定義一個父記錄器A,它負責把日志信息輸出到控制臺中;定義一個A的子記錄器A.B,它負責把日志信息輸出到文件中;定義一個A的子記錄器A.C,它負責把日志信息輸出到HTML文檔中。
記錄器遵循的是類似包的層次,這樣做為我們帶來了大大的方便。Logger類中的getLogger()方法可以取得Logger對象,這個方法有三種參數形式String、Class和URL,其實不論是用哪一種,最終都是通過記錄器的名字來取得記錄器對象的。如果要取得一個名為A.B的記錄器對象,我們可以Logger.getLogger("A.B")。但從上面的例子中,我們都是通過Logger.getLogger(TestLog4j.class.getName())這種方法來取得記錄器對象。這是為什么呢?現在我們假設A.B的包下有一個類BClass,那么我們調用BClass.class.getName()得到的是這個類的全名A.B.BClass。所以當調用Logger.getLogger(BClass.class.getName())時,最理想的情況是返回名為A.B.BClass的記錄器對象。但是如果不存在名為A.B.BClass的記錄器時它會怎樣呢?其實通過Logger類的getLogger方法取得記錄器時存在下面兩種情況:
1)?如果存在與所要找的名字完全相同的記錄器,則返回相應的記錄器對象。
當調用Logger.getLogger(BClass.class.getName())時,如果定義了名為A.B.BClass的記錄器,它就返回該記錄器的對象。
2)?但如果找不到,它會嘗試返回在記錄器層次中與所要找的記錄器最接近的記錄器對象。
當調用Logger.getLogger(BClass.class.getName())時,如果沒有定義了名為A.B.BClass的記錄器,那會嘗試返回名為A.B的記錄器的對象;如果又沒有定義名為A.B的記錄器,它會嘗試返回名為A的記錄器的對象;如果也沒定義名為A的記錄器,它就會返回根記錄器的對象,而根記錄器是必須存在的,所以你總能得到一個記錄器對象。
好了,現在我們回到前面的問題,我們為什么總要通過Logger.getLogger(BClass.class.getName())這種以類全名作為參數來取得記錄器對象呢?其實這是為了管理方便。因為我們在定義設計Logger時也遵循類似包的規則,使設計器的名稱與程序中的類包對應。如上面的假設中我們的程序中有A包,A包下有B包和C包,B包下又有類BClass,那么我們就可使設計器的名為A、A.B、A.C、A.B.BClass,以此類推。那么當我們通過類命名來取得設計器對象時,總能取到與所要的設計器最接近的設計器對象。
2.3 如何應用記錄器的層次
2.3.1 如果定義及獲取不同層次的記錄器
任何一個記錄器的使用都有兩個步驟:
1)?在配置文件中定義相應的記錄器。
在配置文件中定義記錄器的格式有兩種
??定義根記錄器的格式為
log4j.rootLogger?=?[?level?],?appendName1,?appendName2,?...appendNameN
??定義一個非根記錄器的格式為
log4j.logger.loggerName1?=?[?level?],?appendName1,...appendNameN
......
log4j.logger.loggerNameM?=?[?level?],?appendName1,?...appendNameN
我們可以定義任意個非根記錄器。
2)?在代碼中調用Logger類的取得記錄器方法取得相應的記錄器對象。
要取得根記錄器對象可通過Logger.getRootLogger()函數,要取得非根記錄器可通過Logger.getLogger()函數。
理論知道就講到這里,紙上得來終覺淺,下面,我們來小小演練一下。
例4-a:
package?TestLog4j;
import?org.apache.log4j.Logger;
import?org.apache.log4j.PropertyConfigurator;
import?org.apache.log4j.Priority;
import?TestLog4j.TestLog4j2.TestLog4j2;
public?class?TestLog4j?
{
static?Logger?logger?=?Logger.getLogger(TestLog4j.class.getName()); //(2)
public?TestLog4j(){}
public?static?void?main(String[]?args)
{
//同時輸出到控制臺和一個文件的實例并實現了Logger的繼承
PropertyConfigurator.configure("F:\\nepalon\\log4j2.properties");
logger.debug("Start?of?the?main()?in?TestLog4j");
logger.info("Just?testing?a?log?message?with?priority?set?to?INFO");
logger.warn("Just?testing?a?log?message?with?priority?set?to?WARN");
logger.error("Just?testing?a?log?message?with?priority?set?to?ERROR");
logger.fatal("Just?testing?a?log?message?with?priority?set?to?FATAL");
logger.log(Priority.WARN,?"Testing?a?log?message?use?a?alternate?form");
logger.debug(TestLog4j.class.getName());
TestLog4j2?testLog4j2?=?new?TestLog4j2(); //(1)
testLog4j2.testLog();
}
}
在類TestLog4j中我們調用了另一個類TestLog4j2,下面看一下類TestLog4j2的代碼。
例4-b:
package?TestLog4j.TestLog4j2;
import?org.apache.log4j.Logger;
import?org.apache.log4j.PropertyConfigurator;
import?org.apache.log4j.Priority;
public?class?TestLog4j2?
{
static?Logger?logger?=?Logger.getLogger(TestLog4j2.class.getName()); //(1)
public?TestLog4j2(){}
public?void?testLog()
{
//同時輸出到控制臺和一個文件的實例
PropertyConfigurator.configure("F:\\nepalon\\log4j2.properties");
logger.debug("2Start?of?the?main()");
logger.info("2Just?testing?a?log?message?with?priority?set?to?INFO");
logger.warn("2Just?testing?a?log?message?with?priority?set?to?WARN");
logger.error("2Just?testing?a?log?message?with?priority?set?to?ERROR");
logger.fatal("2Just?testing?a?log?message?with?priority?set?to?FATAL");
logger.log(Priority.DEBUG,?"Testing?a?log?message?use?a?alternate?form");
logger.debug("2End?of?the?main()");
}
}
最后我們來看一下配置文件。
例4-c:
log4j2.properties文件內容
#1區
####?Use?two?appenders,?one?to?log?to?console,?another?to?log?to?a?file
log4j.rootLogger?=?debug,?stdout
#2區
#Print?only?messages?of?priority?WARN?or?higher?for?your?category
log4j.logger.TestLog4j=?,?R
log4j.logger.TestLog4j.TestLog4j2=WARN
#3區
####?First?appender?writes?to?console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#?Pattern?to?output?the?caller's?file?name?and?line?number.
log4j.appender.stdout.layout.ConversionPattern=%5p?[%t]?(%F:%L)?-?%m%n
#4區
####?Second?appender?writes?to?a?file
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=F:\\nepalon\\classes\\TestLog4j\\example.log
#?Control?the?maximum?log?file?size
log4j.appender.R.MaxFileSize=100KB
#?Archive?log?files?(one?backup?file?here)
log4j.appender.R.MaxBackupIndex=1
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd?hh:mm:ss}:%p?%t?%c?-?%m%n
先看一下運行結果。
在控制臺中的結果為:
DEBUG?[main]?(?:?)?-?Start?of?the?main()?in?TestLog4j
INFO?[main]?(?:?)?-?Just?testing?a?log?message?with?priority?set?to?INFO
WARN?[main]?(?:?)?-?Just?testing?a?log?message?with?priority?set?to?WARN
ERROR?[main]?(?:?)?-?Just?testing?a?log?message?with?priority?set?to?ERROR
FATAL?[main]?(?:?)?-?Just?testing?a?log?message?with?priority?set?to?FATAL
WARN?[main]?(?:?)?-?Testing?a?log?message?use?a?alternate?form
DEBUG?[main]?(?:?)?-?TestLog4j.TestLog4j
WARN?[main]?(?:?)?-?2Just?testing?a?log?message?with?priority?set?to?WARN
ERROR?[main]?(?:?)?-?2Just?testing?a?log?message?with?priority?set?to?ERROR
FATAL?[main]?(?:?)?-?2Just?testing?a?log?message?with?priority?set?to?FATAL
輸出文件的結果為:
2003-12-19?04:19:44:DEBUG?main?TestLog4j.TestLog4j?-?Start?of?the?main()?in?TestLog4j
2003-12-19?04:19:44:INFO?main?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?INFO
2003-12-19?04:19:44:WARN?main?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?WARN
2003-12-19?04:19:44:ERROR?main?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?ERROR
2003-12-19?04:19:44:FATAL?main?TestLog4j.TestLog4j?-?Just?testing?a?log?message?with?priority?set?to?FATAL
2003-12-19?04:19:44:WARN?main?TestLog4j.TestLog4j?-?Testing?a?log?message?use?a?alternate?form
2003-12-19?04:19:44:DEBUG?main?TestLog4j.TestLog4j?-?TestLog4j.TestLog4j
2003-12-19?04:19:44:WARN?main?TestLog4j.TestLog4j2.TestLog4j2?-?2Just?testing?a?log?message?with?priority?set?to?WARN
2003-12-19?04:19:44:ERROR?main?TestLog4j.TestLog4j2.TestLog4j2?-?2Just?testing?a?log?message?with?priority?set?to?ERROR
2003-12-19?04:19:44:FATAL?main?TestLog4j.TestLog4j2.TestLog4j2?-?2Just?testing?a?log?message?with?priority?set?to?FATAL
首先,先來看一下配置文件都有些什么東西。
1)?在1區中定義了一個根記錄器。這個根記錄器具有DEBUG級別并有一個名稱為stdout的輸出端appender。
2)?2區中的內容是這一節的重點,也是應用到記錄器層次的地方,但其實也只有兩句,充分體現了log4j的簡單性。在這里,我們定義了兩個名稱分別為TestLog4j和TestLog4j.TestLog4j2設計器。
??在定義TestLog4j記錄器時沒有指定級別,所以它的級別繼承自它的父記錄器,即要記錄器,所以它的級別也為DEBUG。在定義TestLog4j記錄器時又定義了一個名稱為R的輸出端,所以它的輸出端有兩個,一個從根記錄器繼承而來的名為stdout的輸出端,另一個為在此定義的名為R的輸出端。在此需要注意的是,在定義記錄器時必須先定義記錄器的級別,然后才是記錄器的輸出端。如果只想定義輸出端而不定義級別,則雖然級別可以為空,但逗號分隔符不能省略。如定義TestLog4j記錄器的做法。
??在定義TestLog4j.TestLog4j2記錄器時又指定了它的級別,由于一個記錄器的級別只能有一個,所以新指定的級別將覆寫掉它的父記錄器的級別(這就象Java中的多態)。我們沒有定義TestLog4j.TestLog4j2記錄器的輸出端,所以它的輸出端將從它的父記錄器中繼承而來。它的父記錄器為estLog4j記錄器,所以它和estLog4j記錄器一樣具有兩個名稱分別為stdout和R的輸出端。
3)?剩下的3區和4區分別設置了兩個輸出端的參數值。
接下來,回到我們的代碼,看一下是如何取得記錄器,在取記錄器時又發生了什么。
1)?例4-a中的代碼(2)中,語句Logger.getLogger()中的參數TestLog4j.class.getName()的值為TestLog4j.?TestLog4j,所以此語句的結果是取得一個名為TestLog4j.?TestLog4j的記錄器的對象。但在配置文件中并沒有定義這樣的記錄器,所以最終將返回與所需的名稱TestLog4j.?TestLog4j最接近的記錄器對象,即名為TestLog4j的記錄器的對象。
2)?例4-b中的代碼(1)的原理與例4-a中的代碼(2)相似,期望取得的是名為TestLog4j.TestLog4j2.?TestLog4j2的記錄器對象,但最終返回的是TestLog4j.TestLog4j2記錄器的對象。
三 log4j與J2EE的結合
到目前為止,這篇文章講的都是如何在application中應用log4j,而Java現在的應用主流是J2EE和J2ME。現在,我們來看一下要如何在J2EE開發中應用log4j。其實在Web?application中應用log4j也很簡單,與在application中應用log4j不同之處就是要在所有應用log4j的代碼之前對log4j進行初始化。所以,我們在web?application中就要把log4j的初始化工作獨立出來,把它放在Servlet中。下面,我們看一個例子。
例5-a:
進行初始化的Servlet:
import?org.apache.log4j.PropertyConfigurator;
import?javax.servlet.http.HttpServlet;
import?javax.servlet.http.HttpServletRequest;
import?javax.servlet.http.HttpServletResponse;
/**
*?log4j.jar的初始化類,參考web.xml
*/
public?class?Log4jInit?extends?HttpServlet?
{
public?void?init()?
{
//通過web.xml來動態取得配置文件
String?prefix?=?getServletContext().getRealPath("/");
String?file?=?getInitParameter("log4j-init-file");
//?如果沒有給出相應的配置文件,則不進行初始化
if(file?!=?null)?
{
PropertyConfigurator.configure(prefix+file); //(1)
}
}
public?void?doGet(HttpServletRequest?req,?HttpServletResponse?res)?
{}?
}
下面來看一下這個Servlet在web.xml中的定義。
例5-b:
<servlet>
<servlet-name>log4j-init</servlet-name>
<servlet-class>TestLog4j.Log4jInit</servlet-class>
<init-param>
<param-name>log4j-init-file</param-name>
<param-value>sort.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
因為log4j的初始化要在所有的log4j調用之前完成,所以在web.xml文件中,我們一定要把對應的Servlet定義的load-on-startup應設為1,以便在Web容器啟動時即裝入該Servlet。
完成了這兩個步驟這后,我們就可以象在application開發中一樣在web?application任何地方應用log4j了。下面是在javabean中的應用的一個例子。
例5-c:
import?org.apache.log4j.Logger;
public?class?InfoForm?
{
static?Logger?logger?=?Logger.getLogger(InfoForm.class);
protected?String?title;
protected?String?content;
public?InfoForm()?{}
public?void?setTitle(Object?value)?
{
logger.debug("nepalon:title?=?"?+?title);
title?=?value;
}
public?String?getTitle()?
{
logger.debug("nepalon:title?=?"?+?title);
return?title;
}
public?void?setContent(String?value)?
{
content?=?value;
logger.debug("nepalon:?content()?=?"?+?content);
}
public?String?getContent()?
{
logger.debug("nepalon:?content?=?\n"?+?content);
return?content;
}
}