一、簡介
在程序中輸出信息的目的有三:一是監視程序運行情況;一是將程序的運行情況記錄到日志文件中,以備將來查看;一是做為調試器。但信息輸出的手段不僅限于System.out.println()或System.out.print(),還有日志記錄工具可以選擇。與System.out.pringln()和System.out.print()相比,日志記錄工具可以控制輸出級別,并且可以在配置文件中對輸出級別進行設置,這樣開發階段的信息在程序發布后就可以通過設置輸出級別來消除掉,而無須對代碼進行修正了。現在流行的日志記錄工具很多,Log4J就是其中的佼佼者。
Log4J是由著名開源組織Apache推出的一款日志記錄工具,供Java編碼人員做日志輸出之用,可以從網站http://logging.apache.org/log4j上免費獲得,最新版本1.2.11。獲得logging-log4j-1.2.11.zip文件后,解壓縮,需要的是其中的log4j-1.2.11.jar文件,將該文件放到特定的文件夾中備用,我放到了我機器的G:\YPJCCK\Log4J\lib文件夾中。
這里選擇的IDE是Eclipse和JBuilder。Eclipse用的是3.0.1加語言包,可以到www.eclipse.org網站上下載;JBuilder用的是JBuilder 2005。
二、配置類庫
下面打開Eclipse或JBuilder。
如果使用的是Eclipse,那么在Eclipse打開后,點擊菜單"文件"->"新建"->"項目",打開"新建項目"對話框:
請選中"Java項目",點擊"下一步",進入"新建Java項目"對話框:
在這個對話框中需要設置項目的名稱以及項目所在目錄,我為自己的項目起名為Log4JTest,目錄為G:\YPJCCK\Log4J\Eclipse\Log4JTest。設置好后點擊"下一步",進入下一個窗口。在這個窗口中選擇名為"庫"的選項卡,然后點擊"添加外部JAR"按鈕,將保存于特定文件夾中的log4j-1.2.11.jar文件引用進來。
設置好后,點擊"完成",至此,已經具備了在Eclipse下使用Log4J的環境。
如果使用的是JBuilder,那么在JBuilder打開后,點擊菜單"Tools"->"Configure" ->"Libraries",打開"Configure Libraries"對話框:
點擊"New"按鈕,打開"New Library Wizard"對話框:
使用"Add"按鈕將保存于特定文件夾中的log4j-1.2.11.jar文件引用進來,并設置Name,即該類庫的名字,我將Name設置為Log4J。設置好后點擊"OK"按鈕,回到"Configure Libraries"對話框,再點擊"OK"按鈕,則JUnit類庫已經被添加到JBuilder當中。
下面繼續,在JBuilder中創建項目。點擊菜單"File"->"New Project",打開"Project Wizard"對話框:
在這個窗口中設置項目名稱及存放目錄,我的項目名稱仍為Log4JTest,路徑為G:/YPJCCK/log4J/JBuilder/Log4JTest。點擊"Next"進入下一個窗口:
在這個窗口中選擇"Required Libraries"選項卡,點擊"Add"按鈕,將剛才設置的JUnit庫引用進來。然后點擊"Next"按鈕,進入下一個窗口:
在這個窗口中用鼠標點擊Encoding下拉列表框,然后按一下"G"鍵,選中相應選項,此時該項目的字符集就被設置成GBK了。如果做的是國內項目,這絕對是個好習慣。最后點擊"Finish",項目創建完成。
三、編寫一個簡單的示例
在了解Log4J的使用方法之前,先編寫一個簡單的示例,以對Log4J有個感性認識。
如果使用的是Eclipse,請點擊"文件"->"新建"->"類",打開"新建Java類"對話框,設置包為piv.zheng.log4j.test,名稱為Test,并確保"public static void main(String[] args)"選項選中;如果使用的是JBuilder,請點擊"File"->"New Class",打開"Class Wizard"對話框,設置Package為piv.zheng.log4j.test,Class name為Test,并確保"Generate main method"選項選中。設置完成后,點擊"OK"。代碼如下:
package piv.zheng.log4j.test;
import org.apache.log4j.Logger;
import org.apache.log4j.Level;
import org.apache.log4j.SimpleLayout;
import org.apache.log4j.ConsoleAppender;
public class Test {
public static void main(String[] args) {
SimpleLayout layout = new SimpleLayout();
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.setLevel(Level.FATAL);
log.debug("Here is DEBUG");
log.info("Here is INFO");
log.warn("Here is WARN");
log.error("Here is ERROR");
log.fatal("Here is FATAL");
}
}
至此,示例編寫完成。請點擊運行按鈕旁邊的倒三角,選擇"運行為"->"2 Java應用程序"(Eclipse),或者在Test類的選項卡上點擊鼠標右鍵,在調出的快捷菜單中點擊"Run using defaults"(JBuilder),運行程序,觀察從控制臺輸出的信息。
四、Log4J入門
看過程序的運行效果后可能會奇怪,為何控制臺只輸出了"FATAL - Here is FATAL"這樣一條信息,而程序代碼中的log.debug()、log.info()等方法也都設置了類似的內容,卻沒有被輸出?其實答案很簡單,但在公布之前,先來了解一下Log4J的使用。
請先看前邊的示例代碼,會發現,示例中用到了Logger、Level、ConsoleAppender、SimpleLayout等四個類。其中Logger類使用最多,甚至輸出的信息也是在其對象log的fatal方法中設置的,那么Logger究竟是做什么的呢?其實Logger就是傳說中的日志記錄器(在Log4J中稱為Category),創建方法有三:
1.根Category,默認創建,獲取方法:
Logger log = Logger.getRootLogger();
2.用戶創建的Category,方法:
Logger log = Logger.getLogger("test");
其中字符串test是為Category設定的名稱。Category的名稱允許使用任何字符,但區分大小寫,例如:
Logger l1 = Logger.getLogger("x");
Logger l2 = Logger.getLogger("X");
l1和l2就是兩個Category;而如果名稱完全相同,例如:
Logger l1 = Logger.getLogger("x");
Logger l2 = Logger.getLogger("x");
l1和l2就是同一個Category。此外,符號"."在Category的名稱中有特殊作用,這一點將在后邊介紹。
3.與方法2類似,只是參數由字符串換成了類對象,其目的是通過類對象獲取類的全名。這個方法比較常用,示例中使用的就是這個方法。
那么Category是如何輸出信息的呢?其實示例中用到的debug、info、warn、error、fatal等五個方法都是用來輸出信息的。什么,怎么這么多?原因很簡單,Log4J支持分級輸出。Log4J的輸出級別有五個,由低到高依次是DEBUG(調試)、INFO(信息)、WARN(警告)、ERROR(錯誤)和FATAL(致命),分別與以上方法對應。當輸出級別設置為DEBUG時,以上方法都能夠輸出信息,當輸出級別設置為INFO時,則只有debug方法將不能再輸出信息,依此類推,當輸出級別設置為FATAL時,就只有fatal方法可以輸出信息了。現在再回頭看前邊的問題,為何只有設置給fatal方法的信息被輸出就不難理解了,示例中有這樣一行代碼:
log.setLevel(Level.FATAL);
正是這行代碼將log對象的輸出級別設成了FATAL。在為log對象設置輸出級別時用到了Level類,該類中定義了DEBUG、INFO、WARN、ERROR、FATAL等五個靜態對象,與五個輸出級別相對應。此外,Level還有兩個特殊的靜態對象ALL和OFF,前者允許所有的方法輸出信息,其級別其實比DEBUG還低;后者則會禁止所有的方法輸出信息,其級別比FATAL要高。除前邊示例中用到的五個方法,Logger還提供了這五個方法的重載,以在輸出信息的同時拋出異常,以fatal方法為例:
log.fatal("Here is FATAL", new Exception("Exception"));
執行后輸出信息:
FATAL - Here is FATAL
java.lang.Exception: Exception
at piv.zheng.log4j.test.Test.main(Test.java:24)
其他方法類似。此外,Logger還提供了log方法,該方法不針對任何輸出級別,需要在調用時設置,例如:
log.log(Level.FATAL, "Here is FATAL");
log.log(Level.FATAL, "Here is FATAL", new Exception("Exception"));
雖然一般情況下log方法不如其它方法方便,但由于允許設置級別,因此log方法在很多時候反而比其它方法更靈活,甚至可以在輸出級別為OFF時輸出信息。不過log方法主要是給用戶自定義的輸出級別用的,而且設立OFF輸出級別的目的也為了不輸出任何信息,因此請不要在log方法中使用OFF來輸出信息。
此外,Category的輸出級別并非必須,若未設置,子Category會默認使用其父Category的輸出級別,若父Category也沒設置,就使用再上一級Category的設置,直到根Category為止。根Category默認輸出級別為DEBUG,因此在示例中,若將"log.setLevel(Level.FATAL);"一行注釋掉,則所有方法都會輸出信息。
下面簡單介紹一下Log4J中Category的繼承關系。其實在Log4J中Category之間是存在繼承關系的,根Category默認創建,是級別最高的Category,用戶創建的Category均繼承自它。而用戶創建的Category之間也存在繼承關系,例如:
Logger lx = Logger.getLogger("x");
Logger lxy = Logger.getLogger("xy");
Logger lx_y = Logger.getLogger("x.y");
Logger lx_z = Logger.getLogger("x.z");
Logger lx_y_z = Logger.getLogger("x.y.z");
其中的lx_y、lx_z就是lx的子Category,而lx_y_z是lx_y的子Category。但lxy并不是lx的子Category。也許有點亂,下面來一個一個看。首先看與lx_y、lx_z對應的Category的名稱"x.y"和"x.z","."前邊的是什么,"x",這說明與名稱為"x"的Category對應lx就是它們的父Category;而與lx_y_z對應的Category的名稱"x.y.z",最后一個"."前邊的是什么,"x.y",這說明lx_y是lx_y_z的父Category;至于lxy,由于與之對應的Category名稱"xy"之間沒有".",因此它是一個與lx同級的Category,其父Category就是根Category器。此外還有一種情況,例如有一個名稱為"a.b"的Category,如果沒有名稱為"a"的Category,那么它的父Category也是根Category。前邊說過,"."在Category名稱中有特殊作用,其實它的作用就是繼承。至此,為何使用類對象來創建Category也就不難理解了。
可是,僅有Category是無法完成信息輸出的,還需要為Category添加Appender,即Category的輸出源。前邊的例子使用的是ConsoleAppender,即指定Category將信息輸出到控制臺。其實Log4J提供的Appender有很多,這里選擇幾常用的進行介紹。
1.org.apache.log4j.WriterAppender,可以根據用戶選擇將信息輸出到Writer或OutputStream。
示例代碼:
SimpleLayout layout = new SimpleLayout ();
//向文件中輸出信息,OutputStream示例
WriterAppender appender1 = null;
try {
appender1 = new WriterAppender(layout, new FileOutputStream("test.txt"));
}
catch(Exception ex) {}
//向控制臺輸出信息,Writer示例
WriterAppender appender2 = null;
try {
appender2 = new WriterAppender(layout, new OutputStreamWriter(System.out));
}
catch(Exception ex) {}
//Category支持同時向多個目標輸出信息
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender1);
log.addAppender(appender2);
log.debug("output");
這個示例由第一個示例修改而來,沒有設置輸出級別,而且向Category中添加了兩個輸出源,運行后會在控制臺中輸出"DEBUG - output",并在工程目錄下生成test.txt文件,該文件中也記錄著"DEBUG - output"。若要將test.txt文件放到其它路徑下,例如f:,則將"test.txt"改為"f:/test.txt",又如e:下的temp文件夾,就改為"e:/temp/test.txt"。后邊FileAppender、RollingFileAppender以及DailyRollingFileAppender設置目標文件時也都可以這樣來寫。
2.org.apache.log4j.ConsoleAppender,向控制臺輸出信息,繼承了WriterAppender,前邊的示例使用的就是它。
3.org.apache.log4j.FileAppender,向文件輸出信息,也繼承了WriterAppender。
示例代碼:
SimpleLayout layout = new SimpleLayout();
//若文件不存在則創建文件,若文件已存在則向文件中追加信息
FileAppender appender = null;
try {
appender = new FileAppender(layout, "test.txt");
} catch(Exception e) {}
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
這個示例也由第一個示例修改而來,運行后會在工程目錄下生成test.txt文件,該文件中記錄著"DEBUG - output"。再次運行程序,查看文件,則"DEBUG - output"有兩行。
另外,FileAppender還有一個構造:
FileAppender(Layout layout, String filename, boolean append)
與示例的類似,只是多了一個boolean型的參數append。append參數是個開關,用來設置當程序重啟,而目標文件已存在時,是向目標文件追加信息還是覆蓋原來的信息,當值為true時就追加,這是FileAppender默認的,當值為false時則覆蓋。此外,FileAppender還提供了setAppend方法來設置append開關。
4.org.apache.log4j.RollingFileAppender,繼承了FileAppender,也是向文件輸出信息,但文件大小可以限制。當文件大小超過限制時,該文件會被轉為備份文件或刪除,然后重新生成。文件的轉換或刪除與設置的備份文件最大數量有關,當數量大于0時就轉為備份文件,否則(小于等于0)刪除,默認的備份文件數量是1。轉換備份文件非常簡單,就是修改文件名,在原文件名之后加上".1",例如文件test.txt,轉為備份文件后文件名為"test.txt.1"。但若同名的備份文件已存在,則會先將該備份文件刪除或更名,這也與設置的備份文件最大數量有關,若達到最大數量就刪除,否則更名。若備份文件更名時也遇到同樣情況,則使用同樣的處理方法,依此類推,直到達到設置的備份文件最大數量。備份文件更名也很簡單,就是將擴展名加1,例如test.txt.1文件更名后變為test.txt.2,test.txt.2文件更名后變為test.txt.3。
示例代碼:
SimpleLayout layout = new SimpleLayout();
//若文件不存在則創建文件,若文件已存在則向文件中追加內容
RollingFileAppender appender = null;
try {
appender = new RollingFileAppender(layout, "test.txt");
} catch(Exception e) {}
//限制備份文件的數量,本例為2個
appender.setMaxBackupIndex(2);
//限制目標文件的大小,單位字節,本例為10字節
appender.setMaximumFileSize(10);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output0");
log.debug("output1");
log.debug("output2");
程序運行后,會在工程目錄下生成test.txt、test.txt.1和test.txt.2三個文件,其中test.txt內容為空,而后兩個文件則分別記錄著"DEBUG - output2"和"DEBUG - output1",這是怎么回事?原來由于目標文件大小被限制為10字節,而三次使用log.debug方法輸出的信息都超過了10字節,這樣就導致了三次備份文件轉換,所以test.txt內容為空。而備份文件最大數量被設為2,因此第一次轉換的備份文件就被刪掉了,而后兩次的則保存下來。此外,由于test.txt轉換備份文件時是先轉為test.txt.1,再轉為test.txt.2,因此最后test.txt.2的內容是"DEBUG - output1",而test.txt.1是"DEBUG - output2",這點千萬別弄混了。
另外,RollingFileAppender還提供了兩個方法:
(1)setMaxFileSize,功能與setMaximumFileSize一樣,但參數是字符串,有兩種情況:一是僅由數字組成,默認單位為字節,例如"100",即表示限制文件大小為100字節;一是由數字及存儲單位組成,例如"1KB"、"1MB"、"1GB",其中單位不區分大小寫,分別表示限制文件大小為1K、1M、1G。
(2)rollOver,手動將目標文件轉換為備份文件,使用起來較靈活,適用于復雜情況。
示例代碼:
SimpleLayout layout = new SimpleLayout();
RollingFileAppender appender = null;
try {
appender = new RollingFileAppender(layout, "test.txt");
} catch(Exception e) {}
appender.setMaxBackupIndex(2);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output0");
appender.rollOver();
log.debug("output1");
appender.rollOver();
log.debug("output2");
appender.rollOver();
這里沒限制目標文件大小,但程序運行后,效果與上例相同。
5.org.apache.log4j.DailyRollingFileAppender,也繼承了FileAppender,并且也是向文件輸出信息,但會根據設定的時間頻率生成備份文件。
時間頻率格式簡介:
'.'yyyy-MM,按月生成,生成時間為每月最后一天午夜過后,例如test.txt在2005年7月31日午夜過后會被更名為test.txt.2005-07,然后重新生成。
'.'yyyy-ww,按周生成,生成時間為每周六午夜過后,例如test.txt在2005年8月13日午夜過后會被更名為test.txt.2005-33,33表示當年第33周。
'.'yyyy-MM-dd,按天生成,生成時間為每天午夜過后,例如2005年8月16日午夜過后,test.txt會被更名為test.txt.2005-08-16。
'.'yyyy-MM-dd-a,也是按天生成,但每天會生成兩次,中午12:00過后一次,午夜過后一次,例如test.txt在2005年8月16日12:00過后會被更名為test.txt.2005-8-16-上午,午夜過后會被更名為test.txt.2005-8-16-下午。
'.'yyyy-MM-dd-HH,按小時生成,例如test.txt在2005年8月16日12:00過后會被更名為test.txt.2005-8-16-11。
'.'yyyy-MM-dd-HH-mm,按分鐘生成,例如test.txt在2005年8月16日12:00過后會被更名為test.txt.2005-8-16-11-59。
示例代碼:
SimpleLayout layout = new SimpleLayout();
DailyRollingFileAppender appender = null;
try {
appender = new DailyRollingFileAppender(layout, "test.txt", "'.'yyyy-MM-dd-HH-mm");
} catch(Exception e) {}
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
編碼完成后運行程序,等一分鐘后再次運行,由于我是在2005年8月17日15:42分第一次運行程序的,因此工程目錄下最終有兩個文件test.txt和test.txt.2005-08-17-15-42。
6.org.apache.log4j.AsyncAppender,用于管理不同類型的Appender,也能實現同時向多個源輸出信息,但其執行是異步的。
示例代碼:
SimpleLayout layout = new SimpleLayout();
//向控制臺輸出
ConsoleAppender appender1 = null;
try {
appender1 = new ConsoleAppender(layout);
} catch(Exception e) {}
//向文件輸出
FileAppender appender2 = null;
try {
appender2 = new FileAppender(layout, "test.txt");
} catch(Exception e) {}
//使用AsyncAppender實現同時向多個目標輸出信息
AsyncAppender appender = new AsyncAppender();
appender.addAppender(appender1);
appender.addAppender(appender2);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
此外,AsyncAppender和Logger都提供了更多的方法來管理Appender,例如getAppender、getAllAppenders、removeAppender和removeAllAppenders,分別用來獲取指定的Appender、獲取全部Appender、移除指定的Appender以及移除全部Appender。
7.org.apache.log4j.jdbc.JDBCAppender,將信息輸出到數據庫。
示例代碼:
JDBCAppender appender = new JDBCAppender();
appender.setDriver("com.mysql.jdbc.Driver");
appender.setURL("jdbc:mysql://localhost:3306/zheng");
appender.setUser("root");
appender.setPassword("11111111");
appender.setSql("insert into log4j (msg) values ('%m')");
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
這里使用的數據庫是MySQL 5.0.4beta,用戶名root,密碼11111111,我在其中建了一個庫zheng,包含表log4j,該表只有一個字段msg,類型為varchar(300)。此外,本例用到的JDBC驅動可以從http://dev.mysql.com/downloads/connector/j/3.1.html下載,版本3.1.8a,下載mysql-connector-java-3.1.8a.zip文件后解壓縮,需要其中的mysql-connector-java-3.1.8-bin.jar文件。下面再來看代碼。由于JDBCAppender內部默認使用PatternLayout格式化輸出信息,因此這里沒用到SimpleLayout,而appender.setSql所設置的SQL語句就是PatternLayout所需的格式化字符串,故此其中才有"%m"這樣的字符,有關PatternLayout的具體內容后邊介紹。執行后,表log4j增加一條記錄,內容為"output"。
8.org.apache.log4j.nt.NTEventLogAppender,向Windows NT系統日志輸出信息。
示例代碼:
SimpleLayout layout = new SimpleLayout();
NTEventLogAppender appender = new NTEventLogAppender("Java", layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
注意,要完成此示例,還需向C:\WINNT\system32文件夾(我的操作系統裝在了C:\)中復制一個名為NTEventLogAppender.dll的文件。如果跟我一樣用的是Log4J 1.2.11,實在對不住,Log4J 1.2.11并未提供該文件。雖然logging-log4j-1.2.11.zip文件解壓縮后,其下的src\java\org\apache\log4j\nt文件夾中有一個make.bat文件執行后可以編譯出該文件,但還需要配置,很麻煩。還好,條條大道通羅馬,1.2.11不行,就換1.2.9,可以從http://apache.justdn.org/logging/log4j/1.2.9下載,下載后解壓縮logging-log4j-1.2.9.zip文件,在其下的src\java\org\apache\log4j\nt文件夾中找到NTEventLogAppender.dll,復制過去就可以了。程序執行后,打開"事件查看器",選擇"應用程序日志",其中有一條來源為Java的記錄,這條記錄就是剛才輸出的信息了。
9.org.apache.log4j.lf5.LF5Appender,執行時會彈出一個窗口,信息在該窗口中以表格的形式顯示。
示例代碼:
LF5Appender appender = new LF5Appender();
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
由于LF5Appender不需要Layout格式化輸出信息,因此這里沒有設置。此外LF5Appender還提供了一個setMaxNumberOfRecords方法,用來限制信息在表格中顯示的行數。
10.org.apache.log4j.net.SocketAppender,以套接字方式向服務器發送日志,然后由服務器將信息輸出。
示例代碼:
//指定要連接的服務器地址及端口,這里使用的是本機9090端口
SocketAppender appender = new SocketAppender("localhost", 9090);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
SocketAppender不需要設置Layout,因為SocketAppender不負責輸出信息。那么如何看到信息輸出的效果呢?這就需要SocketServer和SimpleSocketServer了。
示例代碼1:
package piv.zheng.log4j.test;
import org.apache.log4j.net.SocketServer;
public class TestServer {
public static void main(String[] args) {
SocketServer.main(new String[]{"9090", "test.properties", "G:/YPJCCK/Log4J"});
}
}
這是SocketServer的示例。SocketServer只有一個靜態方法main,該方法意味著SocketServer不僅可以在代碼中被調用,也可以用java命令執行。main方法只有一個參數,是個字符串數組,但要求必須有三個元素:元素一用來指定端口,本例為9090;元素二用來指定輸出信息時需要的配置文件,該文件放在工程目錄下,本例使用的test.properties內容如下:
log4j.rootLogger=, console
log4j.appender.console =org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.SimpleLayout
該配置指定SocketServer使用ConsoleAppender以SimpleLayout格式輸出信息;元素三用來指定一個路徑,以存放.lcf文件,我指定的是本機的G:/YPJCCK/Log4J文件夾。.lcf文件也是輸出信息時使用的配置文件,格式與元素二所指定的配置文件一樣,但test.properties是默認配置文件,即當.lcf文件找不到時才使用。那么.lcf文件如何命名呢?其實.lcf文件的名稱并不是隨意起的,當SocketAppender與SocketServer建立連接時,SocketServer就會獲得SocketAppender所在計算機的IP地址與網絡ID,并將其格式化成"網絡ID/IP地址"這樣的字符串,然后獲取其中的網絡ID作為.lcf文件的主名,例如"zhengyp/127.0.0.1",其中的"zhengyp"就是主文件名,而后再根據這個文件名來調用相應的.lcf文件。這意味著對不同的計算機可以提供不同的配置文件,使信息輸出時有不同的效果。此外,SocketServer還默認了一個名為generic.lcf的文件,用于處理網絡ID獲取不到或其他情況,本例是用的就是這個文件,內容如下:
log4j.rootLogger=, console
log4j.appender.console =org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%m%n
該配置指定SocketServer使用ConsoleAppender以PatternLayout格式輸出信息。運行程序時請先運行SocketServer,再運行SocketAppender。SocketAppender運行結束后,就可以從SocketServer的控制臺看到輸出的信息了。
示例代碼2:
package piv.zheng.log4j.test;
import org.apache.log4j.net.SimpleSocketServer;
public class TestServer {
public static void main(String[] args) {
SimpleSocketServer.main(new String[]{"9090", "test.properties"});
}
}
這是SimpleSocketServer的示例,與SocketServer相比,只允許指定一個默認的配置文件,而無法對不同計算機使用不同的配置文件。
11.org.apache.log4j.net.SocketHubAppender,也是以套接字方式發送日志,但與SocketAppender相反,SocketHubAppender是服務器端,而不是客戶端。
示例代碼:
//指定服務器端口,這里使用的是本機9090端口
SocketHubAppender appender = new SocketHubAppender(9090);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
while (true) {
Thread.sleep(1000);
log.debug("output"); //輸出信息
}
由于SocketHubAppender一旦運行就開始發送消息,而無論有無接收者,因此這里使用了while語句并將條件設為true以保證程序持續運行。不過為了保證性能,這里還使用了Thread.sleep(1000),這樣程序每循環一次都休眠1秒,如果機器性能不好,還可以將值設的再大些。此外,由于SocketHubAppender也不負責輸出信息,因此同樣不需要設置Layout。那么如何看到信息輸出的效果呢?這里我自己寫了個客戶端程序,代碼如下:
package piv.zheng.log4j.test;
import java.net.Socket;
import java.lang.Thread;
import org.apache.log4j.LogManager;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.net.SocketNode;
public class TestClient {
public static void main(String[] args) throws Exception {
//創建客戶端套接字對象
Socket s = new Socket("localhost", 9090);
//調用配置文件
PropertyConfigurator.configure("test.properties");
//從套接字中恢復Logger,并輸出信息
new Thread(new SocketNode(s, LogManager.getLoggerRepository())).start();
}
}
由于SocketHubAppender與SocketAppender一樣,發送的也是SocketNode對象,因此編寫該程序時參考了SocketServer的源碼。此外,這里的配置文件直接使用了上例的test.properties文件。運行程序時請先運行SocketHubAppender,再運行客戶端程序,然后從客戶端的控制臺就可以看到效果了。
13.org.apache.log4j.net.TelnetAppender,與SocketHubAppender有些類似,也是作為服務器發送信息,但TelnetAppender發送的不是SocketNode對象,而是Category輸出的結果。
示例代碼:
SimpleLayout layout = new SimpleLayout();
TelnetAppender appender = new TelnetAppender();
appender.setLayout(layout); //設置Layout
appender.setPort(9090); //設置端口號
appender.activateOptions(); //應用設置
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
while (true) {
java.lang.Thread.sleep(1000);
log.debug("output"); //輸出信息
}
//appender.close();
注意最后一行被注釋掉的代碼,若該行代碼執行,則TelnetAppender的資源會被清理,從而導致TelnetAppender無法繼續運行。那么如何看到信息輸出的效果呢?這里提供兩種方法:方法一,使用Telnet工具,我使用的就是Windows自帶的Telnet。運行TelnetAppender程序后,點擊[開始]菜單->[運行],在"運行"框中輸入"telnet",回車,telnet客戶端彈出,這是一個命令行程序,輸入命令"open localhost 9090",回車,然后就可以看到效果了。方法二,自己寫程序,代碼如下:
package piv.zheng.log4j.test;
import java.net.*;
import java.io.*;
public class TestClient {
public static void main(String[] args) throws Exception {
//創建客戶端套接字對象
Socket s = new Socket("localhost", 9090);
//將BufferedReader與Socket綁定,以輸出Socket獲得的信息
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
//獲得信息并輸出
String line = in.readLine();
while (line != null) {
System.out.println(line);
line = in.readLine();
}
}
}
13.org.apache.log4j.net.SMTPAppender,向指定的電子郵件發送信息,但只能發送ERROR和FATAL級別的信息,而且還沒提供身份驗證功能。
示例代碼:
SimpleLayout loyout = new SimpleLayout();
SMTPAppender appender = new SMTPAppender();
appender.setLayout(loyout); //設置Layout
appender.setFrom("zhengyp@126.com"); //設置發件人
appender.setSMTPHost("smtp.126.com"); //設置發送郵件服務器
appender.setTo("zhengyp@126.com"); //設置收件人
appender.setSubject("Log4J Test"); //設置郵件標題
appender.activateOptions(); //應用設置
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("Here is DEBUG");
log.info("Here is INFO");
log.warn("Here is WARN");
log.error("Here is ERROR");
log.fatal("Here is FATAL");
要運行此示例,還需要JavaMail 和JAF,前者是Sun推出的電子郵件類庫,可以從http://java.sun.com/products/javamail/downloads/index.html下載,最新版本1.3.3,下載javamail-1_3_3-ea.zip壓縮包后需要其中的mail.jar文件;后者全稱是JavaBeans Activation Framework,提供了對輸入任意數據塊的支持,并能相應地對其進行處理,可以從http://www.sun.com/download中找到,最新版本1.1,下載jaf-1_1-ea.zip壓縮包后需要其中的activation.jar文件。不過,程序運行后會拋出兩次異常,分別是log.error和log.fatal方法導致的,失敗的原因很簡單,我用的郵件服務器需要身份驗證。
14.piv.zheng.log4j.test.SMTPAppender,自定義的,依照Log4J提供的SMTPAppender修改而來,增加了身份驗證功能,并去掉了對級別的限制。由于代碼太長,所以放到了另一篇文章《SMTPAppender源碼》中,有興趣的請自行去查看。
示例代碼:
SimpleLayout layout = new SimpleLayout();
SMTPAppender appender = new SMTPAppender(layout);
appender.setFrom("zhengyp@126.com"); //發件人
appender.setSMTPHost("smtp.126.com"); //發送郵件服務器
appender.setTo("zhengyp@126.com"); //收件人
appender.setSubject("Log4J Test"); //郵件標題
appender.setAuth("true"); //身份驗證標識
appender.setUsername("zhengyp"); //用戶名
appender.setPassword("1111111"); //密碼
appender.activateOptions(); //應用設置
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
同樣需要JavaMail 和JAF。程序運行后會發送一封郵件,快去查看一下自己的郵箱吧^_^
此外,Log4J還提供了SyslogAppender、JMSAppender(均在org.apache.log4j.net包下)以及更多的Appender,或者用來向Unix操作系統的syslogd服務發送信息,或者通過JMS方式發送信息,或者以其他方式發送信息。由于條件有現,就不再介紹了。
不過,在前邊的示例中還使用了SimpleLayout和PatternLayout來格式化輸出的信息,這里也簡單介紹一下。
1.org.apache.log4j.SimpleLayout,一直用的就是它,輸出的格式比較簡單,就是"級別 - 信息"。
2.org.apache.log4j.HTMLLayout,以HTML格式輸出信息。
示例代碼:
HTMLLayout layout = new HTMLLayout();
layout.setTitle("Log4J Test"); //HTML頁標題
FileAppender appender = null;
try {
appender = new FileAppender(layout, "test.html");
} catch(Exception e) {}
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
程序運行后會在工程目錄下生成一個HTML頁,可以用瀏覽器來查看。
3.org.apache.log4j.xml.XMLLayout,以XML格式輸出信息。
示例代碼:
XMLLayout layout = new XMLLayout();
FileAppender appender = null;
try {
appender = new FileAppender(layout, "test.xml");
} catch(Exception e) {}
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
程序運行后會在工程目錄下生成一個test.xml文件。
4.org.apache.log4j.TTCCLayout,輸出信息的同時輸出日志產生時間、相關線程及Category等信息。
示例代碼:
TTCCLayout layout = new TTCCLayout();
//是否打印與TTCCLayout關聯的Category的名稱,默認為true,表示打印
layout.setCategoryPrefixing(true);
//是否打印當前線程,默認為true,表示打印
layout.setThreadPrinting(true);
//是否打印輸出和當前線程相關的NDC信息,默認為true,表示打印
layout.setContextPrinting(true);
//設置日期時間格式
layout.setDateFormat("iso8601");
//設置時區
layout.setTimeZone("GMT+8:00");
//設置時區后需要調用此方法應用設置
layout.activateOptions();
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
注意,TTCCLayout輸出的時間格式及時區是可以設置的:
(1)setDateFormat,設置日期時間格式,有五個常用值:"NULL",表示不輸出;"RELATIVE",輸出信息所用的時間,以毫秒為單位,默認使用該值;"ABSOLUTE",僅輸出時間部分;"DATE",按當前所在地區顯示日期和時間;"ISO8601",按ISO8601標準顯示日期和時間。這些字符串不區分大小寫。此外,還可以使用時間模式字符來格式化日期時間,詳細內容請參考J2SE文檔中的java.text.SimpleDateFormat類。
(2)setTimeZone,設置時區,詳細內容請參考J2SE文檔中的java.util.TimeZone類和java.util.SimpleTimeZone類。但請注意,當日期格式為"RELATIVE"時,設置時區會造成沖突。
5.org.apache.log4j.PatternLayout,用模式字符靈活指定信息輸出的格式。
示例代碼:
String pattern = "Logger: %c %n"
+ "Date: %d{DATE} %n"
+ "Message: %m %n";
PatternLayout layout = new PatternLayout(pattern);
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
模式字符串簡介:
%c:Category名稱。還可以使用%c{n}的格式輸出Category的部分名稱,其中n為正整數,輸出時會從Category名稱的右側起查n個".",然后截取第n個"."右側的部分輸出,例如Category的名稱為"x.y.z",指定格式為"%c{2}",則輸出"y.z"。
%C:輸出信息時Category所在類的名稱,也可以使用%C{n}的格式輸出。
%d:輸出信息的時間,也可以用%d{FormatString}的格式輸出,其中FormatString的值請參考TTCCLayout的setDateFormat方法,但NULL和RELATIVE在%d中無法使用。
%F:輸出信息時Category所在類文件的名稱。
%l:輸出信息時Category所在的位置,使用"%C.%M(%F:%L)"可以產生同樣的效果。
%L:輸出信息時Category在類文件中的行號。
%m:信息本身。
%M:輸出信息時Category所在的方法。
%n:換行符,可以理解成回車。
%p:日志級別。
%r:輸出信息所用的時間,以毫秒為單位。
%t:當前線程。
%x:輸出和當前線程相關的NDC信息。
%X:輸出與當前現成相關的MDC信息。
%%:輸出%。
此外,還可以在%與模式字符之間加上修飾符來設置輸出時的最小寬度、最大寬度及文本對齊方式,例如:
%30d{DATE}:按當前所在地區顯示日期和時間,并指定最小寬度為30,當輸出信息少于30個字符時會補以空格并右對齊。
%-30d{DATE}:也是按當前所在地區顯示日期和時間,指定最小寬度為30,并在字符少于30時補以空格,但由于使用了"-",因此對齊方式為左對齊,與默認情況一樣。
%.40d{DATE}:也是按當前所在地區顯示日期和時間,但指定最大寬度為40,當輸出信息多于40個字符時會將左邊多出的字符截掉。此外,最大寬度只支持默認的左對齊方式,而不支持右對齊。
%30.40d{DATE}:如果輸出信息少于30個字符就補空格并右對齊,如果多于40個字符,就將左邊多出的字符截掉。
%-30.40d{DATE}:如果輸出信息少于30個字符就補空格并左對齊,如果多于40個字符,就將左邊多出的字符截掉。
五、Log4J進階
了解以上內容后,就已經初步掌握Log4J了,但要想靈活使用Log4J,則還需要了解其配置功能。這里簡單介紹一下。
1.org.apache.log4j.BasicConfigurator,默認使用ConsoleAppender以PatternLayout(使用PatternLayout.TTCC_CONVERSION_PATTERN,即"%r [%t] %p %c %x - %m%n"格式)輸出信息。
示例代碼:
BasicConfigurator.configure();
Logger log = Logger.getLogger(Test.class);
log.debug("output");
注意,BasicConfigurator及其它Configurator其實都只對根Category進行配置,但由于用戶創建的Category會繼承根Category的特性(聲明,許多資料介紹Category繼承關系時都主要在討論輸出級別,而事實上,Category間繼承的不僅是輸出級別,所有特性都可以繼承),因此輸出時仍會顯示BasicConfigurator配置的效果。此外,還可以使用configure方法指定Appender,以自定義輸出。BasicConfigurator允許同時指定多個Appender。
示例代碼:
SimpleLayout layout1 = new SimpleLayout();
ConsoleAppender appender1 = new ConsoleAppender(layout1);
BasicConfigurator.configure(appender1);
String pattern = "Logger: %c %n"
+ "Date: %d{DATE} %n"
+ "Message: %m %n";
PatternLayout layout2 = new PatternLayout(pattern);
FileAppender appender2 = null;
try {
appender2 = new FileAppender(layout2, "test.log", false);
}
catch(Exception e){}
BasicConfigurator.configure(appender2);
Logger log = Logger.getLogger(Test.class);
log.debug("output");
這里用BasicConfigurator指定了兩個Appender,即ConsoleAppender和FileAppender,程序運行后信息會在以SimpleLayout輸出到控制臺的同時以PatternLayout輸出到test.log文件。若要清除這些Appender,可以調用BasicConfigurator的resetConfiguration方法。
2.org.apache.log4j.PropertyConfigurator,調用文本配置文件輸出信息,通常使用.properties文件。配置文件以"鍵=值"的形式保存數據,注釋以"#"開頭。PropertyConfigurator和配置文件在介紹SocketAppender和SocketHubAppender時曾提到過。使用PropertyConfigurator可以避免硬編碼。
示例代碼:
PropertyConfigurator.configure("test.properties");
Logger log = Logger.getLogger(Test.class);
log.debug("output");
要完成該示例,還需要在工程目錄下創建一個test.properties文件,內容如下:
##設置根Category,其值由輸出級別和指定的Appender兩部分組成
#這里設置輸出級別為DEBUG
log4j.rootLogger=DEBUG,appender
##輸出信息到控制臺
#創建一個名為appender的Appender,類型為ConsoleAppender
log4j.appender.appender=org.apache.log4j.ConsoleAppender
#設置appender以SimpleLayout輸出
log4j.appender.appender.layout=org.apache.log4j.SimpleLayout
此外,PropertyConfigurator也允許同時指定多個Appender,例如:
#這里沒有設置輸出級別,但指定了兩個Appender
log4j.rootLogger=,appender1,appender2
#輸出信息到控制臺
log4j.appender.appender1=org.apache.log4j.ConsoleAppender
log4j.appender.appender1.layout=org.apache.log4j.SimpleLayout
#輸出信息到文件
log4j.appender.appender2=org.apache.log4j.FileAppender
log4j.appender.appender2.File=test.log
log4j.appender.appender2.Append=false
log4j.appender.appender2.layout=org.apache.log4j.PatternLayout
log4j.appender.appender2.layout.ConversionPattern=Logger: %c %nDate: %d{DATE} %nMessage: %m %n
關于更多配置,網上示例很多,這里不再贅述。但要說明一件事,就是配置文件中的鍵是怎么來的。參照后一個示例,查看PropertyConfigurator源碼,會發現"log4j.rootLogger"是定義好的,只能照寫;而"log4j.appender"字樣也可以找到,與指定的Appender名稱appender1、appender2聯系起來,log4j.appender.appender1和log4j.appender.appender2也就不難理解了;再看下去,還能找到"prefix + ".layout"",這樣log4j.appender.appender1.layout也有了;可是log4j.appender.appender2.File 和log4j.appender.appender2.Append呢?還記得前邊介紹FileAppender時曾提到的setAppend方法嗎?其實FileAppender還有個getAppend方法,這說明FileAppender具有Append屬性。那么File呢?當然也是FileAppender的屬性了。至于log4j.appender.appender2.layout.ConversionPattern也一樣,只不過FileAppender換成了PatternLayout。其實別的Appender和Layout的屬性也都是這樣定義成鍵來進行設置的。此外,定義鍵時,屬性的首字母不區分大小寫,例如"File",也可以寫成"file"。
3.org.apache.log4j.xml.DOMConfigurator,調用XML配置文件輸出信息。其定義文檔是log4j-1.2.11.jar中org\apache\log4j\xml包下的log4j.dtd文件。與PropertyConfigurator相比,DOMConfigurator似乎是趨勢。
示例代碼:
DOMConfigurator.configure("test.xml");
Logger log = Logger.getLogger(Test.class);
log.debug("output");
要完成該示例,也需要在工程目錄下創建一個test.xml文件,內容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- 輸出信息到控制臺
創建一個名為appender的Appender,類型為ConsoleAppender -->
<appender name="appender" class="org.apache.log4j.ConsoleAppender">
<!-- 設置appender以SimpleLayout輸出 -->
<layout class="org.apache.log4j.SimpleLayout"/>
</appender>
<!-- 設置根Category,其值由輸出級別和指定的Appender兩部分組成
這里設置輸出級別為DEBUG -->
<root>
<priority value ="debug" />
<appender-ref ref="appender"/>
</root>
</log4j:configuration>
此外,DOMConfigurator也允許同時指定多個Appender,例如:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- 輸出信息到控制臺 -->
<appender name="appender1" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.SimpleLayout"/>
</appender>
<!-- 輸出信息到文件 -->
<appender name="appender2" class="org.apache.log4j.FileAppender">
<param name="File" value="test.log"/>
<param name="Append" value="false"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="Logger: %c %nDate: %d{DATE} %nMessage: %m %n"/>
</layout>
</appender>
<!-- 這里沒有設置輸出級別,但指定了兩個Appender -->
<root>
<appender-ref ref="appender1"/>
<appender-ref ref="appender2"/>
</root>
</log4j:configuration>
由于以上兩個示例是在PropertyConfigurator的兩個示例基礎上改的,而且也寫了注釋,因此這里只簡單介紹一下<param>標記。<param>標記有兩個屬性,name和value,前者的值也是Appender或Layout的屬性名,作用與log4j.appender.appender2.File這樣的鍵一樣。設置時,首字母同樣不區分大小寫,例如"File"也可以寫成"file"。此外還請注意,使用這兩段XML代碼時應將中文注釋去掉,或者把<?xml version="1.0" encoding="UTF-8" ?>中的UTF-8改成GBK或GB2312,否則會導致錯誤。這里使用的UTF-8是XML默認的字符集。
4.org.apache.log4j.lf5.DefaultLF5Configurator,默認使用LF5Appender來輸出信息,需要調用log4j-1.2.11.jar中org\apache\log4j\lf5\config包下的defaultconfig.properties文件。
示例代碼:
try {
DefaultLF5Configurator.configure();
}
catch(Exception e){}
Logger log = Logger.getLogger(Test.class);
log.debug("output");
下面討論另外一個話題:Diagnostic Context。Diagnostic Context意為診斷環境,針對于多用戶并發環境,在這種環境下,通常需要對每個客戶端提供獨立的線程以處理其請求,此時若要在日志信息中對客戶端加以區分,為每個線程分別創建Category是個辦法。但這樣做并不高效,反而會導致大量資源被占用。Diagnostic Context所要解決的就是這個問題。Diagnostic Context會為當前線程提供一定空間,然后將信息保存到該空間供Category調用。與創建一個Category相比,這點信息所占的資源自然要少得多。
1.org.apache.log4j.NDC。NDC是Nested Diagnostic Context的簡寫,意為嵌套診斷環境,使用時提供一個堆棧對象來保存信息。堆棧的特點是數據后進先出、先進后出,即清理堆棧時,后保存的數據會被先清掉,而先保存的數據則被后清掉。
示例代碼:
PatternLayout layout = new PatternLayout("%m %x%n");
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
String tmp = "zhengyp"; //模擬從客戶端獲取的信息
log.debug("Start");
NDC.push(tmp); //添加信息到堆棧中
log.debug("Before");
NDC.pop(); //將信息從堆棧中移除
log.debug("After");
NDC.remove(); //將當前線程移除,退出NDC環境
log.debug("End");
這里使用了PatternLayout來格式化信息,其模式字符%x就是用來輸出NDC信息的。程序運行后會輸出如下內容:
Start
Before zhengyp
After
End
可以看到,第二行輸出時由于已向堆棧中添加了信息,因此"zhengyp"也會同時輸出;而第三行輸出時由于信息已被移除,因此就沒再輸出"zhengyp"。不過這個示例僅簡單演示了NDC的用法,而沒有顯示出NDC的堆棧特性,所以下面再提供一個示例,代碼如下:
TTCCLayout layout = new TTCCLayout();
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("Start");
NDC.push("zhengyp"); //添加信息到堆棧中
log.debug("Test1");
NDC.push("192.168.0.1"); //向堆棧中追加信息
log.debug("Test2");
NDC.pop(); //從堆棧中移除信息,但移除的只是最后的信息
log.debug("Test3");
NDC.pop(); //再次從堆棧中移除信息
log.debug("Test4");???
log.debug("End");
這里格式化輸出信息使用的是TTCCLayout,還記得其setContextPrinting方法嗎?程序運行后,從輸出的信息就可以看到效果了。此外,NDC還提供了其他方法:
(1)get,獲取堆棧中的全部信息。以上例為例,當輸出Test2時,使用該方法會獲得"zhengyp 192.168.0.1"。
(2)peek,獲取堆棧中最后的信息。仍以上例為例,當輸出Test1時會獲得"zhengyp",Test2時為"192.168.0.1",而當輸出Test3時由于"192.168.0.1"已被移除,"zhengyp"又成了最后的信息,因此獲得的仍是"zhengyp"。
(3)clear,清空堆棧中的全部信息。
(4)setMaxDepth,設置堆棧的最大深度,即當前的信息可以保留多少,對之后追加的信息沒有影響。當需要一次清掉多條信息時,使用setMaxDepth會比多次調用pop方便。
2.org.apache.log4j.MDC。MDC是Mapped Diagnostic Context的簡寫,意為映射診斷環境,提供了一個Map對象來保存信息。Map對象使用Key、Value的形式保存值。
示例代碼:
PatternLayout layout = new PatternLayout("%m %X{name} %X{ip}%n");
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("Start");
//添加信息到Map中
MDC.put("name", "zhengyp1");
MDC.put("ip", "192.168.1.1");
log.debug("Test1");
//添加信息到Map中,若Key重復,則覆蓋之前的值
MDC.put("name", "zhengyp2");
MDC.put("ip", "192.168.1.2");
log.debug("Test2");
//將信息從Map中移除,此時信息不再輸出
MDC.remove("name");
MDC.remove("ip");
log.debug("End");
這個示例演示了MDC的基本用法,格式化信息用的也是PatternLayout,模式字符為"%X",其格式必須為"%X{Key}"。其中Key就是向Map對象添加信息時put方法所用的Key,這里為name和ip。由于可以使用"%X{Key}"輸出信息,因此MDC使用起來會比NDC更靈活。此外,MDC還提供了get方法來獲取指定Key的信息。
六、小結
用了近半個月,終于大概掌握了Log4J。由于本文是邊學邊寫的,目的是將Log4J的用法記錄下來,而非提供一份中文參考,因此內容并不細致,但盡量提供了示例。不過到最后才發現,示例存在問題,其實Logger做為類的static成員比較恰當,而我為了圖方便,竟直接寫到了main方法中,這一點還請注意。
此外,這里再推薦一下《The Complete log4j Manual》,是對Log4J較詳細的介紹,在網上可以找到,只不過是英文的。