動態配置
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
實例原理
如果要通過
JAVA
的
properties
文件來配置信息,那么在代碼中就要通過
PropertyConfigurator.configure()
函數從
properties
文件中加載配置信息,這個函數有三種參數形式:一個
properties
文件所在路徑的
String
對象,可以是一個
properties
文件所在路徑的
URL
對象,也可以是一個
properties
對象。如果要用
XML
文件來配置信息,則可用類型的
DOMConfigurator()
函數來從一個
XML
文件中加載配置信息。
1.2.2
輸出端
Appender
在上面的例子中,我們都是簡單的把日志信息輸出到控制臺中。其實在
log4j
中還可以把日志信息輸出到其它的輸出端,對于同一個日志信息,我們還可以讓它同時輸出到多個輸出端中,如同時在控制臺和文件中進行打印。一個輸出端就是一個
appender
。要在配置文件中定義一個
appender
有三步:
)?
在定義一個記錄器的同時定義出該記錄器的輸出端
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
將在下面介紹。
通過
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?}
,輸出類似:
%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
中:
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
遵循類似包的層次。下面我們將進行介紹。
就象一個
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
如何應用記錄器的層次
任何一個記錄器的使用都有兩個步驟:
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
輸出文件的結果為:
首先,先來看一下配置文件都有些什么東西。
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;
}
}