OMG,到底在尋找什么..................
          (構(gòu)造一個(gè)完美的J2EE系統(tǒng)所需要的完整知識(shí)體系)
          posts - 198,  comments - 37,  trackbacks - 0

          Log4J學(xué)習(xí)筆記

          一、簡(jiǎn)介
            在程序中輸出信息的目的有三:一是監(jiān)視程序運(yùn)行情況;一是將程序的運(yùn)行情況記錄到日志文件中,以備將來(lái)查看;一是做為調(diào)試器。但信息輸出的手段不僅限于System.out.println()或System.out.print(),還有日志記錄工具可以選擇。與System.out.pringln()和System.out.print()相比,日志記錄工具可以控制輸出級(jí)別,并且可以在配置文件中對(duì)輸出級(jí)別進(jìn)行設(shè)置,這樣開發(fā)階段的信息在程序發(fā)布后就可以通過(guò)設(shè)置輸出級(jí)別來(lái)消除掉,而無(wú)須對(duì)代碼進(jìn)行修正了。現(xiàn)在流行的日志記錄工具很多,Log4J就是其中的佼佼者。
            Log4J是由著名開源組織Apache推出的一款日志記錄工具,供Java編碼人員做日志輸出之用,可以從網(wǎng)站http://logging.apache.org/log4j上免費(fèi)獲得,最新版本1.2.11。獲得logging-log4j-1.2.11.zip文件后,解壓縮,需要的是其中的log4j-1.2.11.jar文件,將該文件放到特定的文件夾中備用,我放到了我機(jī)器的G:\YPJCCK\Log4J\lib文件夾中。
            這里選擇的IDE是Eclipse和JBuilder。Eclipse用的是3.0.1加語(yǔ)言包,可以到www.eclipse.org網(wǎng)站上下載;JBuilder用的是JBuilder 2005。
          二、配置類庫(kù)
            下面打開Eclipse或JBuilder。
            如果使用的是Eclipse,那么在Eclipse打開后,點(diǎn)擊菜單"文件"->"新建"->"項(xiàng)目",打開"新建項(xiàng)目"對(duì)話框:

          請(qǐng)選中"Java項(xiàng)目",點(diǎn)擊"下一步",進(jìn)入"新建Java項(xiàng)目"對(duì)話框:

          在這個(gè)對(duì)話框中需要設(shè)置項(xiàng)目的名稱以及項(xiàng)目所在目錄,我為自己的項(xiàng)目起名為L(zhǎng)og4JTest,目錄為G:\YPJCCK\Log4J\Eclipse\Log4JTest。設(shè)置好后點(diǎn)擊"下一步",進(jìn)入下一個(gè)窗口。在這個(gè)窗口中選擇名為"庫(kù)"的選項(xiàng)卡,然后點(diǎn)擊"添加外部JAR"按鈕,將保存于特定文件夾中的log4j-1.2.11.jar文件引用進(jìn)來(lái)。

          設(shè)置好后,點(diǎn)擊"完成",至此,已經(jīng)具備了在Eclipse下使用Log4J的環(huán)境。
            如果使用的是JBuilder,那么在JBuilder打開后,點(diǎn)擊菜單"Tools"->"Configure" ->"Libraries",打開"Configure Libraries"對(duì)話框:

          點(diǎn)擊"New"按鈕,打開"New Library Wizard"對(duì)話框:

          使用"Add"按鈕將保存于特定文件夾中的log4j-1.2.11.jar文件引用進(jìn)來(lái),并設(shè)置Name,即該類庫(kù)的名字,我將Name設(shè)置為L(zhǎng)og4J。設(shè)置好后點(diǎn)擊"OK"按鈕,回到"Configure Libraries"對(duì)話框,再點(diǎn)擊"OK"按鈕,則JUnit類庫(kù)已經(jīng)被添加到JBuilder當(dāng)中。
            下面繼續(xù),在JBuilder中創(chuàng)建項(xiàng)目。點(diǎn)擊菜單"File"->"New Project",打開"Project Wizard"對(duì)話框:

          在這個(gè)窗口中設(shè)置項(xiàng)目名稱及存放目錄,我的項(xiàng)目名稱仍為L(zhǎng)og4JTest,路徑為G:/YPJCCK/log4J/JBuilder/Log4JTest。點(diǎn)擊"Next"進(jìn)入下一個(gè)窗口:

          在這個(gè)窗口中選擇"Required Libraries"選項(xiàng)卡,點(diǎn)擊"Add"按鈕,將剛才設(shè)置的JUnit庫(kù)引用進(jìn)來(lái)。然后點(diǎn)擊"Next"按鈕,進(jìn)入下一個(gè)窗口:

          在這個(gè)窗口中用鼠標(biāo)點(diǎn)擊Encoding下拉列表框,然后按一下"G"鍵,選中相應(yīng)選項(xiàng),此時(shí)該項(xiàng)目的字符集就被設(shè)置成GBK了。如果做的是國(guó)內(nèi)項(xiàng)目,這絕對(duì)是個(gè)好習(xí)慣。最后點(diǎn)擊"Finish",項(xiàng)目創(chuàng)建完成。
          三、編寫一個(gè)簡(jiǎn)單的示例
            在了解Log4J的使用方法之前,先編寫一個(gè)簡(jiǎn)單的示例,以對(duì)Log4J有個(gè)感性認(rèn)識(shí)。
          如果使用的是Eclipse,請(qǐng)點(diǎn)擊"文件"->"新建"->"類",打開"新建Java類"對(duì)話框,設(shè)置包為piv.zheng.log4j.test,名稱為Test,并確保"public static void main(String[] args)"選項(xiàng)選中;如果使用的是JBuilder,請(qǐng)點(diǎn)擊"File"->"New Class",打開"Class Wizard"對(duì)話框,設(shè)置Package為piv.zheng.log4j.test,Class name為Test,并確保"Generate main method"選項(xiàng)選中。設(shè)置完成后,點(diǎn)擊"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");
              }
            }
          至此,示例編寫完成。請(qǐng)點(diǎn)擊運(yùn)行按鈕旁邊的倒三角,選擇"運(yùn)行為"->"2 Java應(yīng)用程序"(Eclipse),或者在Test類的選項(xiàng)卡上點(diǎn)擊鼠標(biāo)右鍵,在調(diào)出的快捷菜單中點(diǎn)擊"Run using defaults"(JBuilder),運(yùn)行程序,觀察從控制臺(tái)輸出的信息。
          四、Log4J入門
            看過(guò)程序的運(yùn)行效果后可能會(huì)奇怪,為何控制臺(tái)只輸出了"FATAL - Here is FATAL"這樣一條信息,而程序代碼中的log.debug()、log.info()等方法也都設(shè)置了類似的內(nèi)容,卻沒(méi)有被輸出?其實(shí)答案很簡(jiǎn)單,但在公布之前,先來(lái)了解一下Log4J的使用。
            請(qǐng)先看前邊的示例代碼,會(huì)發(fā)現(xiàn),示例中用到了Logger、Level、ConsoleAppender、SimpleLayout等四個(gè)類。其中Logger類使用最多,甚至輸出的信息也是在其對(duì)象log的fatal方法中設(shè)置的,那么Logger究竟是做什么的呢?其實(shí)Logger就是傳說(shuō)中的日志記錄器(在Log4J中稱為Category),創(chuàng)建方法有三:
            1.根Category,默認(rèn)創(chuàng)建,獲取方法:

          Logger log = Logger.getRootLogger();

            2.用戶創(chuàng)建的Category,方法:

          Logger log = Logger.getLogger("test");

          其中字符串test是為Category設(shè)定的名稱。Category的名稱允許使用任何字符,但區(qū)分大小寫,例如:

          Logger l1 = Logger.getLogger("x");
          Logger l2 = Logger.getLogger("X");

          l1和l2就是兩個(gè)Category;而如果名稱完全相同,例如:

          Logger l1 = Logger.getLogger("x");
          Logger l2 = Logger.getLogger("x");

          l1和l2就是同一個(gè)Category。此外,符號(hào)"."在Category的名稱中有特殊作用,這一點(diǎn)將在后邊介紹。
            3.與方法2類似,只是參數(shù)由字符串換成了類對(duì)象,其目的是通過(guò)類對(duì)象獲取類的全名。這個(gè)方法比較常用,示例中使用的就是這個(gè)方法。
            那么Category是如何輸出信息的呢?其實(shí)示例中用到的debug、info、warn、error、fatal等五個(gè)方法都是用來(lái)輸出信息的。什么,怎么這么多?原因很簡(jiǎn)單,Log4J支持分級(jí)輸出。Log4J的輸出級(jí)別有五個(gè),由低到高依次是DEBUG(調(diào)試)、INFO(信息)、WARN(警告)、ERROR(錯(cuò)誤)和FATAL(致命),分別與以上方法對(duì)應(yīng)。當(dāng)輸出級(jí)別設(shè)置為DEBUG時(shí),以上方法都能夠輸出信息,當(dāng)輸出級(jí)別設(shè)置為INFO時(shí),則只有debug方法將不能再輸出信息,依此類推,當(dāng)輸出級(jí)別設(shè)置為FATAL時(shí),就只有fatal方法可以輸出信息了。現(xiàn)在再回頭看前邊的問(wèn)題,為何只有設(shè)置給fatal方法的信息被輸出就不難理解了,示例中有這樣一行代碼:

          log.setLevel(Level.FATAL);

          正是這行代碼將log對(duì)象的輸出級(jí)別設(shè)成了FATAL。在為log對(duì)象設(shè)置輸出級(jí)別時(shí)用到了Level類,該類中定義了DEBUG、INFO、WARN、ERROR、FATAL等五個(gè)靜態(tài)對(duì)象,與五個(gè)輸出級(jí)別相對(duì)應(yīng)。此外,Level還有兩個(gè)特殊的靜態(tài)對(duì)象ALL和OFF,前者允許所有的方法輸出信息,其級(jí)別其實(shí)比DEBUG還低;后者則會(huì)禁止所有的方法輸出信息,其級(jí)別比FATAL要高。除前邊示例中用到的五個(gè)方法,Logger還提供了這五個(gè)方法的重載,以在輸出信息的同時(shí)拋出異常,以fatal方法為例:

          log.fatal("Here is FATAL", new Exception("Exception"));

          執(zhí)行后輸出信息:
            FATAL - Here is FATAL
            java.lang.Exception: Exception
              at piv.zheng.log4j.test.Test.main(Test.java:24)
          其他方法類似。此外,Logger還提供了log方法,該方法不針對(duì)任何輸出級(jí)別,需要在調(diào)用時(shí)設(shè)置,例如:

          log.log(Level.FATAL, "Here is FATAL");
          log.log(Level.FATAL, "Here is FATAL", new Exception("Exception"));

          雖然一般情況下log方法不如其它方法方便,但由于允許設(shè)置級(jí)別,因此log方法在很多時(shí)候反而比其它方法更靈活,甚至可以在輸出級(jí)別為OFF時(shí)輸出信息。不過(guò)log方法主要是給用戶自定義的輸出級(jí)別用的,而且設(shè)立OFF輸出級(jí)別的目的也為了不輸出任何信息,因此請(qǐng)不要在log方法中使用OFF來(lái)輸出信息。
            此外,Category的輸出級(jí)別并非必須,若未設(shè)置,子Category會(huì)默認(rèn)使用其父Category的輸出級(jí)別,若父Category也沒(méi)設(shè)置,就使用再上一級(jí)Category的設(shè)置,直到根Category為止。根Category默認(rèn)輸出級(jí)別為DEBUG,因此在示例中,若將"log.setLevel(Level.FATAL);"一行注釋掉,則所有方法都會(huì)輸出信息。
            下面簡(jiǎn)單介紹一下Log4J中Category的繼承關(guān)系。其實(shí)在Log4J中Category之間是存在繼承關(guān)系的,根Category默認(rèn)創(chuàng)建,是級(jí)別最高的Category,用戶創(chuàng)建的Category均繼承自它。而用戶創(chuàng)建的Category之間也存在繼承關(guān)系,例如:

          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。也許有點(diǎn)亂,下面來(lái)一個(gè)一個(gè)看。首先看與lx_y、lx_z對(duì)應(yīng)的Category的名稱"x.y"和"x.z","."前邊的是什么,"x",這說(shuō)明與名稱為"x"的Category對(duì)應(yīng)lx就是它們的父Category;而與lx_y_z對(duì)應(yīng)的Category的名稱"x.y.z",最后一個(gè)"."前邊的是什么,"x.y",這說(shuō)明lx_y是lx_y_z的父Category;至于lxy,由于與之對(duì)應(yīng)的Category名稱"xy"之間沒(méi)有".",因此它是一個(gè)與lx同級(jí)的Category,其父Category就是根Category器。此外還有一種情況,例如有一個(gè)名稱為"a.b"的Category,如果沒(méi)有名稱為"a"的Category,那么它的父Category也是根Category。前邊說(shuō)過(guò),"."在Category名稱中有特殊作用,其實(shí)它的作用就是繼承。至此,為何使用類對(duì)象來(lái)創(chuàng)建Category也就不難理解了。
            可是,僅有Category是無(wú)法完成信息輸出的,還需要為Category添加Appender,即Category的輸出源。前邊的例子使用的是ConsoleAppender,即指定Category將信息輸出到控制臺(tái)。其實(shí)Log4J提供的Appender有很多,這里選擇幾常用的進(jìn)行介紹。
            1.org.apache.log4j.WriterAppender,可以根據(jù)用戶選擇將信息輸出到Writer或OutputStream。
            示例代碼:
              SimpleLayout layout = new SimpleLayout ();
              
              //向文件中輸出信息,OutputStream示例
              WriterAppender appender1 = null;
              try {
                appender1 = new WriterAppender(layout, new FileOutputStream("test.txt"));
              }
              catch(Exception ex) {}
              
              //向控制臺(tái)輸出信息,Writer示例
              WriterAppender appender2 = null;
              try {
                appender2 = new WriterAppender(layout, new OutputStreamWriter(System.out));
              }
              catch(Exception ex) {}
              
              //Category支持同時(shí)向多個(gè)目標(biāo)輸出信息
              Logger log = Logger.getLogger(Test.class);
              log.addAppender(appender1);
              log.addAppender(appender2);
              
              log.debug("output");
          這個(gè)示例由第一個(gè)示例修改而來(lái),沒(méi)有設(shè)置輸出級(jí)別,而且向Category中添加了兩個(gè)輸出源,運(yùn)行后會(huì)在控制臺(tái)中輸出"DEBUG - output",并在工程目錄下生成test.txt文件,該文件中也記錄著"DEBUG - output"。若要將test.txt文件放到其它路徑下,例如f:,則將"test.txt"改為"f:/test.txt",又如e:下的temp文件夾,就改為"e:/temp/test.txt"。后邊FileAppender、RollingFileAppender以及DailyRollingFileAppender設(shè)置目標(biāo)文件時(shí)也都可以這樣來(lái)寫。
            2.org.apache.log4j.ConsoleAppender,向控制臺(tái)輸出信息,繼承了WriterAppender,前邊的示例使用的就是它。
            3.org.apache.log4j.FileAppender,向文件輸出信息,也繼承了WriterAppender。
            示例代碼:
              SimpleLayout layout = new SimpleLayout();
              
              //若文件不存在則創(chuàng)建文件,若文件已存在則向文件中追加信息
              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");
          這個(gè)示例也由第一個(gè)示例修改而來(lái),運(yùn)行后會(huì)在工程目錄下生成test.txt文件,該文件中記錄著"DEBUG - output"。再次運(yùn)行程序,查看文件,則"DEBUG - output"有兩行。
            另外,F(xiàn)ileAppender還有一個(gè)構(gòu)造:

          FileAppender(Layout layout, String filename, boolean append)

          與示例的類似,只是多了一個(gè)boolean型的參數(shù)append。append參數(shù)是個(gè)開關(guān),用來(lái)設(shè)置當(dāng)程序重啟,而目標(biāo)文件已存在時(shí),是向目標(biāo)文件追加信息還是覆蓋原來(lái)的信息,當(dāng)值為true時(shí)就追加,這是FileAppender默認(rèn)的,當(dāng)值為false時(shí)則覆蓋。此外,F(xiàn)ileAppender還提供了setAppend方法來(lái)設(shè)置append開關(guān)。
            4.org.apache.log4j.RollingFileAppender,繼承了FileAppender,也是向文件輸出信息,但文件大小可以限制。當(dāng)文件大小超過(guò)限制時(shí),該文件會(huì)被轉(zhuǎn)為備份文件或刪除,然后重新生成。文件的轉(zhuǎn)換或刪除與設(shè)置的備份文件最大數(shù)量有關(guān),當(dāng)數(shù)量大于0時(shí)就轉(zhuǎn)為備份文件,否則(小于等于0)刪除,默認(rèn)的備份文件數(shù)量是1。轉(zhuǎn)換備份文件非常簡(jiǎn)單,就是修改文件名,在原文件名之后加上".1",例如文件test.txt,轉(zhuǎn)為備份文件后文件名為"test.txt.1"。但若同名的備份文件已存在,則會(huì)先將該備份文件刪除或更名,這也與設(shè)置的備份文件最大數(shù)量有關(guān),若達(dá)到最大數(shù)量就刪除,否則更名。若備份文件更名時(shí)也遇到同樣情況,則使用同樣的處理方法,依此類推,直到達(dá)到設(shè)置的備份文件最大數(shù)量。備份文件更名也很簡(jiǎn)單,就是將擴(kuò)展名加1,例如test.txt.1文件更名后變?yōu)閠est.txt.2,test.txt.2文件更名后變?yōu)閠est.txt.3。
            示例代碼:
              SimpleLayout layout = new SimpleLayout();
              
              //若文件不存在則創(chuàng)建文件,若文件已存在則向文件中追加內(nèi)容
              RollingFileAppender appender = null;
              try {
                appender = new RollingFileAppender(layout, "test.txt");
              } catch(Exception e) {}
              //限制備份文件的數(shù)量,本例為2個(gè)
              appender.setMaxBackupIndex(2);
              //限制目標(biāo)文件的大小,單位字節(jié),本例為10字節(jié)
              appender.setMaximumFileSize(10);
              
              Logger log = Logger.getLogger(Test.class);
              log.addAppender(appender);
              
              log.debug("output0");
              log.debug("output1");
              log.debug("output2");
          程序運(yùn)行后,會(huì)在工程目錄下生成test.txt、test.txt.1和test.txt.2三個(gè)文件,其中test.txt內(nèi)容為空,而后兩個(gè)文件則分別記錄著"DEBUG - output2"和"DEBUG - output1",這是怎么回事?原來(lái)由于目標(biāo)文件大小被限制為10字節(jié),而三次使用log.debug方法輸出的信息都超過(guò)了10字節(jié),這樣就導(dǎo)致了三次備份文件轉(zhuǎn)換,所以test.txt內(nèi)容為空。而備份文件最大數(shù)量被設(shè)為2,因此第一次轉(zhuǎn)換的備份文件就被刪掉了,而后兩次的則保存下來(lái)。此外,由于test.txt轉(zhuǎn)換備份文件時(shí)是先轉(zhuǎn)為test.txt.1,再轉(zhuǎn)為test.txt.2,因此最后test.txt.2的內(nèi)容是"DEBUG - output1",而test.txt.1是"DEBUG - output2",這點(diǎn)千萬(wàn)別弄混了。
            另外,RollingFileAppender還提供了兩個(gè)方法:
            (1)setMaxFileSize,功能與setMaximumFileSize一樣,但參數(shù)是字符串,有兩種情況:一是僅由數(shù)字組成,默認(rèn)單位為字節(jié),例如"100",即表示限制文件大小為100字節(jié);一是由數(shù)字及存儲(chǔ)單位組成,例如"1KB"、"1MB"、"1GB",其中單位不區(qū)分大小寫,分別表示限制文件大小為1K、1M、1G。
            (2)rollOver,手動(dòng)將目標(biāo)文件轉(zhuǎn)換為備份文件,使用起來(lái)較靈活,適用于復(fù)雜情況。
            示例代碼:
              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();
          這里沒(méi)限制目標(biāo)文件大小,但程序運(yùn)行后,效果與上例相同。
            5.org.apache.log4j.DailyRollingFileAppender,也繼承了FileAppender,并且也是向文件輸出信息,但會(huì)根據(jù)設(shè)定的時(shí)間頻率生成備份文件。
            時(shí)間頻率格式簡(jiǎn)介:
            '.'yyyy-MM,按月生成,生成時(shí)間為每月最后一天午夜過(guò)后,例如test.txt在2005年7月31日午夜過(guò)后會(huì)被更名為test.txt.2005-07,然后重新生成。
            '.'yyyy-ww,按周生成,生成時(shí)間為每周六午夜過(guò)后,例如test.txt在2005年8月13日午夜過(guò)后會(huì)被更名為test.txt.2005-33,33表示當(dāng)年第33周。
            '.'yyyy-MM-dd,按天生成,生成時(shí)間為每天午夜過(guò)后,例如2005年8月16日午夜過(guò)后,test.txt會(huì)被更名為test.txt.2005-08-16。
            '.'yyyy-MM-dd-a,也是按天生成,但每天會(huì)生成兩次,中午12:00過(guò)后一次,午夜過(guò)后一次,例如test.txt在2005年8月16日12:00過(guò)后會(huì)被更名為test.txt.2005-8-16-上午,午夜過(guò)后會(huì)被更名為test.txt.2005-8-16-下午。
            '.'yyyy-MM-dd-HH,按小時(shí)生成,例如test.txt在2005年8月16日12:00過(guò)后會(huì)被更名為test.txt.2005-8-16-11。
            '.'yyyy-MM-dd-HH-mm,按分鐘生成,例如test.txt在2005年8月16日12:00過(guò)后會(huì)被更名為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");
          編碼完成后運(yùn)行程序,等一分鐘后再次運(yùn)行,由于我是在2005年8月17日15:42分第一次運(yùn)行程序的,因此工程目錄下最終有兩個(gè)文件test.txt和test.txt.2005-08-17-15-42。
            6.org.apache.log4j.AsyncAppender,用于管理不同類型的Appender,也能實(shí)現(xiàn)同時(shí)向多個(gè)源輸出信息,但其執(zhí)行是異步的。
            示例代碼:
              SimpleLayout layout = new SimpleLayout();
              
              //向控制臺(tái)輸出
              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實(shí)現(xiàn)同時(shí)向多個(gè)目標(biāo)輸出信息
              AsyncAppender appender = new AsyncAppender();
              appender.addAppender(appender1);
              appender.addAppender(appender2);
              
              Logger log = Logger.getLogger(Test.class);
              log.addAppender(appender);
              log.debug("output");
          此外,AsyncAppender和Logger都提供了更多的方法來(lái)管理Appender,例如getAppender、getAllAppenders、removeAppender和removeAllAppenders,分別用來(lái)獲取指定的Appender、獲取全部Appender、移除指定的Appender以及移除全部Appender。
            7.org.apache.log4j.jdbc.JDBCAppender,將信息輸出到數(shù)據(jù)庫(kù)。
            示例代碼:
              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");
          這里使用的數(shù)據(jù)庫(kù)是MySQL 5.0.4beta,用戶名root,密碼11111111,我在其中建了一個(gè)庫(kù)zheng,包含表log4j,該表只有一個(gè)字段msg,類型為varchar(300)。此外,本例用到的JDBC驅(qū)動(dòng)可以從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文件。下面再來(lái)看代碼。由于JDBCAppender內(nèi)部默認(rèn)使用PatternLayout格式化輸出信息,因此這里沒(méi)用到SimpleLayout,而appender.setSql所設(shè)置的SQL語(yǔ)句就是PatternLayout所需的格式化字符串,故此其中才有"%m"這樣的字符,有關(guān)PatternLayout的具體內(nèi)容后邊介紹。執(zhí)行后,表log4j增加一條記錄,內(nèi)容為"output"。
            8.org.apache.log4j.nt.NTEventLogAppender,向Windows NT系統(tǒng)日志輸出信息。
            示例代碼:
              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文件夾(我的操作系統(tǒng)裝在了C:\)中復(fù)制一個(gè)名為NTEventLogAppender.dll的文件。如果跟我一樣用的是Log4J 1.2.11,實(shí)在對(duì)不住,Log4J 1.2.11并未提供該文件。雖然logging-log4j-1.2.11.zip文件解壓縮后,其下的src\java\org\apache\log4j\nt文件夾中有一個(gè)make.bat文件執(zhí)行后可以編譯出該文件,但還需要配置,很麻煩。還好,條條大道通羅馬,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,復(fù)制過(guò)去就可以了。程序執(zhí)行后,打開"事件查看器",選擇"應(yīng)用程序日志",其中有一條來(lái)源為Java的記錄,這條記錄就是剛才輸出的信息了。
            9.org.apache.log4j.lf5.LF5Appender,執(zhí)行時(shí)會(huì)彈出一個(gè)窗口,信息在該窗口中以表格的形式顯示。
            示例代碼:
              LF5Appender appender = new LF5Appender();
              Logger log = Logger.getLogger(Test.class);
              log.addAppender(appender);
              log.debug("output");
          由于LF5Appender不需要Layout格式化輸出信息,因此這里沒(méi)有設(shè)置。此外LF5Appender還提供了一個(gè)setMaxNumberOfRecords方法,用來(lái)限制信息在表格中顯示的行數(shù)。
            10.org.apache.log4j.net.SocketAppender,以套接字方式向服務(wù)器發(fā)送日志,然后由服務(wù)器將信息輸出。
            示例代碼:
            //指定要連接的服務(wù)器地址及端口,這里使用的是本機(jī)9090端口
            SocketAppender appender = new SocketAppender("localhost", 9090);
            Logger log = Logger.getLogger(Test.class);
            log.addAppender(appender);
            log.debug("output");
          SocketAppender不需要設(shè)置Layout,因?yàn)镾ocketAppender不負(fù)責(zé)輸出信息。那么如何看到信息輸出的效果呢?這就需要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只有一個(gè)靜態(tài)方法main,該方法意味著SocketServer不僅可以在代碼中被調(diào)用,也可以用java命令執(zhí)行。main方法只有一個(gè)參數(shù),是個(gè)字符串?dāng)?shù)組,但要求必須有三個(gè)元素:元素一用來(lái)指定端口,本例為9090;元素二用來(lái)指定輸出信息時(shí)需要的配置文件,該文件放在工程目錄下,本例使用的test.properties內(nèi)容如下:
            log4j.rootLogger=, console
            log4j.appender.console =org.apache.log4j.ConsoleAppender
            log4j.appender.console.layout=org.apache.log4j.SimpleLayout
          該配置指定SocketServer使用ConsoleAppender以SimpleLayout格式輸出信息;元素三用來(lái)指定一個(gè)路徑,以存放.lcf文件,我指定的是本機(jī)的G:/YPJCCK/Log4J文件夾。.lcf文件也是輸出信息時(shí)使用的配置文件,格式與元素二所指定的配置文件一樣,但test.properties是默認(rèn)配置文件,即當(dāng).lcf文件找不到時(shí)才使用。那么.lcf文件如何命名呢?其實(shí).lcf文件的名稱并不是隨意起的,當(dāng)SocketAppender與SocketServer建立連接時(shí),SocketServer就會(huì)獲得SocketAppender所在計(jì)算機(jī)的IP地址與網(wǎng)絡(luò)ID,并將其格式化成"網(wǎng)絡(luò)ID/IP地址"這樣的字符串,然后獲取其中的網(wǎng)絡(luò)ID作為.lcf文件的主名,例如"zhengyp/127.0.0.1",其中的"zhengyp"就是主文件名,而后再根據(jù)這個(gè)文件名來(lái)調(diào)用相應(yīng)的.lcf文件。這意味著對(duì)不同的計(jì)算機(jī)可以提供不同的配置文件,使信息輸出時(shí)有不同的效果。此外,SocketServer還默認(rèn)了一個(gè)名為generic.lcf的文件,用于處理網(wǎng)絡(luò)ID獲取不到或其他情況,本例是用的就是這個(gè)文件,內(nèi)容如下:
            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格式輸出信息。運(yùn)行程序時(shí)請(qǐng)先運(yùn)行SocketServer,再運(yùn)行SocketAppender。SocketAppender運(yùn)行結(jié)束后,就可以從SocketServer的控制臺(tái)看到輸出的信息了。
            示例代碼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相比,只允許指定一個(gè)默認(rèn)的配置文件,而無(wú)法對(duì)不同計(jì)算機(jī)使用不同的配置文件。
            11.org.apache.log4j.net.SocketHubAppender,也是以套接字方式發(fā)送日志,但與SocketAppender相反,SocketHubAppender是服務(wù)器端,而不是客戶端。
            示例代碼:
              //指定服務(wù)器端口,這里使用的是本機(jī)9090端口
              SocketHubAppender appender = new SocketHubAppender(9090);
              
              Logger log = Logger.getLogger(Test.class);
              log.addAppender(appender);
              while (true) {
                Thread.sleep(1000);
                log.debug("output"); //輸出信息
              }
          由于SocketHubAppender一旦運(yùn)行就開始發(fā)送消息,而無(wú)論有無(wú)接收者,因此這里使用了while語(yǔ)句并將條件設(shè)為true以保證程序持續(xù)運(yùn)行。不過(guò)為了保證性能,這里還使用了Thread.sleep(1000),這樣程序每循環(huán)一次都休眠1秒,如果機(jī)器性能不好,還可以將值設(shè)的再大些。此外,由于SocketHubAppender也不負(fù)責(zé)輸出信息,因此同樣不需要設(shè)置Layout。那么如何看到信息輸出的效果呢?這里我自己寫了個(gè)客戶端程序,代碼如下:
            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 {
                //創(chuàng)建客戶端套接字對(duì)象
                Socket s = new Socket("localhost", 9090);
                //調(diào)用配置文件
                PropertyConfigurator.configure("test.properties");
                //從套接字中恢復(fù)Logger,并輸出信息
                new Thread(new SocketNode(s, LogManager.getLoggerRepository())).start();
              }
            }
          由于SocketHubAppender與SocketAppender一樣,發(fā)送的也是SocketNode對(duì)象,因此編寫該程序時(shí)參考了SocketServer的源碼。此外,這里的配置文件直接使用了上例的test.properties文件。運(yùn)行程序時(shí)請(qǐng)先運(yùn)行SocketHubAppender,再運(yùn)行客戶端程序,然后從客戶端的控制臺(tái)就可以看到效果了。
            13.org.apache.log4j.net.TelnetAppender,與SocketHubAppender有些類似,也是作為服務(wù)器發(fā)送信息,但TelnetAppender發(fā)送的不是SocketNode對(duì)象,而是Category輸出的結(jié)果。
            示例代碼:
              SimpleLayout layout = new SimpleLayout();
              
              TelnetAppender appender = new TelnetAppender();
              appender.setLayout(layout); //設(shè)置Layout
              appender.setPort(9090); //設(shè)置端口號(hào)
              appender.activateOptions(); //應(yīng)用設(shè)置
              
              Logger log = Logger.getLogger(Test.class);
              log.addAppender(appender);
              
              while (true) {
                java.lang.Thread.sleep(1000);
                log.debug("output"); //輸出信息
              }
              //appender.close();
          注意最后一行被注釋掉的代碼,若該行代碼執(zhí)行,則TelnetAppender的資源會(huì)被清理,從而導(dǎo)致TelnetAppender無(wú)法繼續(xù)運(yùn)行。那么如何看到信息輸出的效果呢?這里提供兩種方法:方法一,使用Telnet工具,我使用的就是Windows自帶的Telnet。運(yùn)行TelnetAppender程序后,點(diǎn)擊[開始]菜單->[運(yùn)行],在"運(yùn)行"框中輸入"telnet",回車,telnet客戶端彈出,這是一個(gè)命令行程序,輸入命令"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 {
                //創(chuàng)建客戶端套接字對(duì)象
                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,向指定的電子郵件發(fā)送信息,但只能發(fā)送ERROR和FATAL級(jí)別的信息,而且還沒(méi)提供身份驗(yàn)證功能。
            示例代碼:
            SimpleLayout loyout = new SimpleLayout();
            
            SMTPAppender appender = new SMTPAppender();
            appender.setLayout(loyout); //設(shè)置Layout
            appender.setFrom("zhengyp@126.com"); //設(shè)置發(fā)件人
            appender.setSMTPHost("smtp.126.com"); //設(shè)置發(fā)送郵件服務(wù)器
            appender.setTo("zhengyp@126.com"); //設(shè)置收件人
            appender.setSubject("Log4J Test"); //設(shè)置郵件標(biāo)題
            appender.activateOptions(); //應(yīng)用設(shè)置
            
            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");
          要運(yùn)行此示例,還需要JavaMail 和JAF,前者是Sun推出的電子郵件類庫(kù),可以從http://java.sun.com/products/javamail/downloads/index.html下載,最新版本1.3.3,下載javamail-1_3_3-ea.zip壓縮包后需要其中的mail.jar文件;后者全稱是JavaBeans Activation Framework,提供了對(duì)輸入任意數(shù)據(jù)塊的支持,并能相應(yīng)地對(duì)其進(jìn)行處理,可以從http://www.sun.com/download中找到,最新版本1.1,下載jaf-1_1-ea.zip壓縮包后需要其中的activation.jar文件。不過(guò),程序運(yùn)行后會(huì)拋出兩次異常,分別是log.error和log.fatal方法導(dǎo)致的,失敗的原因很簡(jiǎn)單,我用的郵件服務(wù)器需要身份驗(yàn)證。
            14.piv.zheng.log4j.test.SMTPAppender,自定義的,依照Log4J提供的SMTPAppender修改而來(lái),增加了身份驗(yàn)證功能,并去掉了對(duì)級(jí)別的限制。由于代碼太長(zhǎng),所以放到了另一篇文章《SMTPAppender源碼》中,有興趣的請(qǐng)自行去查看。
            示例代碼:
              SimpleLayout layout = new SimpleLayout();
              
              SMTPAppender appender = new SMTPAppender(layout);
              appender.setFrom("zhengyp@126.com"); //發(fā)件人
              appender.setSMTPHost("smtp.126.com"); //發(fā)送郵件服務(wù)器
              appender.setTo("zhengyp@126.com"); //收件人
              appender.setSubject("Log4J Test"); //郵件標(biāo)題
              appender.setAuth("true"); //身份驗(yàn)證標(biāo)識(shí)
              appender.setUsername("zhengyp"); //用戶名
              appender.setPassword("1111111"); //密碼
              appender.activateOptions(); //應(yīng)用設(shè)置
              
              Logger log = Logger.getLogger(Test.class);
              log.addAppender(appender);
              log.debug("output");
          同樣需要JavaMail 和JAF。程序運(yùn)行后會(huì)發(fā)送一封郵件,快去查看一下自己的郵箱吧^_^
            此外,Log4J還提供了SyslogAppender、JMSAppender(均在org.apache.log4j.net包下)以及更多的Appender,或者用來(lái)向Unix操作系統(tǒng)的syslogd服務(wù)發(fā)送信息,或者通過(guò)JMS方式發(fā)送信息,或者以其他方式發(fā)送信息。由于條件有現(xiàn),就不再介紹了。
            不過(guò),在前邊的示例中還使用了SimpleLayout和PatternLayout來(lái)格式化輸出的信息,這里也簡(jiǎn)單介紹一下。
            1.org.apache.log4j.SimpleLayout,一直用的就是它,輸出的格式比較簡(jiǎn)單,就是"級(jí)別 - 信息"。
            2.org.apache.log4j.HTMLLayout,以HTML格式輸出信息。
            示例代碼:
              HTMLLayout layout = new HTMLLayout();
              layout.setTitle("Log4J Test"); //HTML頁(yè)標(biāo)題
              
              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");
          程序運(yùn)行后會(huì)在工程目錄下生成一個(gè)HTML頁(yè),可以用瀏覽器來(lái)查看。
            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");
          程序運(yùn)行后會(huì)在工程目錄下生成一個(gè)test.xml文件。
            4.org.apache.log4j.TTCCLayout,輸出信息的同時(shí)輸出日志產(chǎn)生時(shí)間、相關(guān)線程及Category等信息。
            示例代碼:
              TTCCLayout layout = new TTCCLayout();
              //是否打印與TTCCLayout關(guān)聯(lián)的Category的名稱,默認(rèn)為true,表示打印
              layout.setCategoryPrefixing(true);
              //是否打印當(dāng)前線程,默認(rèn)為true,表示打印
              layout.setThreadPrinting(true);
              //是否打印輸出和當(dāng)前線程相關(guān)的NDC信息,默認(rèn)為true,表示打印
              layout.setContextPrinting(true);
              //設(shè)置日期時(shí)間格式
              layout.setDateFormat("iso8601");
              //設(shè)置時(shí)區(qū)
              layout.setTimeZone("GMT+8:00");
              //設(shè)置時(shí)區(qū)后需要調(diào)用此方法應(yīng)用設(shè)置
              layout.activateOptions();
              
              ConsoleAppender appender = new ConsoleAppender(layout);
              
              Logger log = Logger.getLogger(Test.class);
              log.addAppender(appender);
              log.debug("output");
          注意,TTCCLayout輸出的時(shí)間格式及時(shí)區(qū)是可以設(shè)置的:
            (1)setDateFormat,設(shè)置日期時(shí)間格式,有五個(gè)常用值:"NULL",表示不輸出;"RELATIVE",輸出信息所用的時(shí)間,以毫秒為單位,默認(rèn)使用該值;"ABSOLUTE",僅輸出時(shí)間部分;"DATE",按當(dāng)前所在地區(qū)顯示日期和時(shí)間;"ISO8601",按ISO8601標(biāo)準(zhǔn)顯示日期和時(shí)間。這些字符串不區(qū)分大小寫。此外,還可以使用時(shí)間模式字符來(lái)格式化日期時(shí)間,詳細(xì)內(nèi)容請(qǐng)參考J2SE文檔中的java.text.SimpleDateFormat類。
            (2)setTimeZone,設(shè)置時(shí)區(qū),詳細(xì)內(nèi)容請(qǐng)參考J2SE文檔中的java.util.TimeZone類和java.util.SimpleTimeZone類。但請(qǐng)注意,當(dāng)日期格式為"RELATIVE"時(shí),設(shè)置時(shí)區(qū)會(huì)造成沖突。
            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");
          模式字符串簡(jiǎn)介:
            %c:Category名稱。還可以使用%c{n}的格式輸出Category的部分名稱,其中n為正整數(shù),輸出時(shí)會(huì)從Category名稱的右側(cè)起查n個(gè)".",然后截取第n個(gè)"."右側(cè)的部分輸出,例如Category的名稱為"x.y.z",指定格式為"%c{2}",則輸出"y.z"。
            %C:輸出信息時(shí)Category所在類的名稱,也可以使用%C{n}的格式輸出。
            %d:輸出信息的時(shí)間,也可以用%d{FormatString}的格式輸出,其中FormatString的值請(qǐng)參考TTCCLayout的setDateFormat方法,但NULL和RELATIVE在%d中無(wú)法使用。
            %F:輸出信息時(shí)Category所在類文件的名稱。
            %l:輸出信息時(shí)Category所在的位置,使用"%C.%M(%F:%L)"可以產(chǎn)生同樣的效果。
            %L:輸出信息時(shí)Category在類文件中的行號(hào)。
            %m:信息本身。
            %M:輸出信息時(shí)Category所在的方法。
            %n:換行符,可以理解成回車。
            %p:日志級(jí)別。
            %r:輸出信息所用的時(shí)間,以毫秒為單位。
            %t:當(dāng)前線程。
            %x:輸出和當(dāng)前線程相關(guān)的NDC信息。
            %X:輸出與當(dāng)前現(xiàn)成相關(guān)的MDC信息。
            %%:輸出%。
          此外,還可以在%與模式字符之間加上修飾符來(lái)設(shè)置輸出時(shí)的最小寬度、最大寬度及文本對(duì)齊方式,例如:
            %30d{DATE}:按當(dāng)前所在地區(qū)顯示日期和時(shí)間,并指定最小寬度為30,當(dāng)輸出信息少于30個(gè)字符時(shí)會(huì)補(bǔ)以空格并右對(duì)齊。
            %-30d{DATE}:也是按當(dāng)前所在地區(qū)顯示日期和時(shí)間,指定最小寬度為30,并在字符少于30時(shí)補(bǔ)以空格,但由于使用了"-",因此對(duì)齊方式為左對(duì)齊,與默認(rèn)情況一樣。
            %.40d{DATE}:也是按當(dāng)前所在地區(qū)顯示日期和時(shí)間,但指定最大寬度為40,當(dāng)輸出信息多于40個(gè)字符時(shí)會(huì)將左邊多出的字符截掉。此外,最大寬度只支持默認(rèn)的左對(duì)齊方式,而不支持右對(duì)齊。
            %30.40d{DATE}:如果輸出信息少于30個(gè)字符就補(bǔ)空格并右對(duì)齊,如果多于40個(gè)字符,就將左邊多出的字符截掉。
            %-30.40d{DATE}:如果輸出信息少于30個(gè)字符就補(bǔ)空格并左對(duì)齊,如果多于40個(gè)字符,就將左邊多出的字符截掉。
          五、Log4J進(jìn)階
            了解以上內(nèi)容后,就已經(jīng)初步掌握Log4J了,但要想靈活使用Log4J,則還需要了解其配置功能。這里簡(jiǎn)單介紹一下。
            1.org.apache.log4j.BasicConfigurator,默認(rèn)使用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其實(shí)都只對(duì)根Category進(jìn)行配置,但由于用戶創(chuàng)建的Category會(huì)繼承根Category的特性(聲明,許多資料介紹Category繼承關(guān)系時(shí)都主要在討論輸出級(jí)別,而事實(shí)上,Category間繼承的不僅是輸出級(jí)別,所有特性都可以繼承),因此輸出時(shí)仍會(huì)顯示BasicConfigurator配置的效果。此外,還可以使用configure方法指定Appender,以自定義輸出。BasicConfigurator允許同時(shí)指定多個(gè)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指定了兩個(gè)Appender,即ConsoleAppender和FileAppender,程序運(yùn)行后信息會(huì)在以SimpleLayout輸出到控制臺(tái)的同時(shí)以PatternLayout輸出到test.log文件。若要清除這些Appender,可以調(diào)用BasicConfigurator的resetConfiguration方法。
            2.org.apache.log4j.PropertyConfigurator,調(diào)用文本配置文件輸出信息,通常使用.properties文件。配置文件以"鍵=值"的形式保存數(shù)據(jù),注釋以"#"開頭。PropertyConfigurator和配置文件在介紹SocketAppender和SocketHubAppender時(shí)曾提到過(guò)。使用PropertyConfigurator可以避免硬編碼。
            示例代碼:
              PropertyConfigurator.configure("test.properties");
              Logger log = Logger.getLogger(Test.class);
              log.debug("output");
          要完成該示例,還需要在工程目錄下創(chuàng)建一個(gè)test.properties文件,內(nèi)容如下:
            ##設(shè)置根Category,其值由輸出級(jí)別和指定的Appender兩部分組成
            #這里設(shè)置輸出級(jí)別為DEBUG
            log4j.rootLogger=DEBUG,appender
            ##輸出信息到控制臺(tái)
            #創(chuàng)建一個(gè)名為appender的Appender,類型為ConsoleAppender
            log4j.appender.appender=org.apache.log4j.ConsoleAppender
            #設(shè)置appender以SimpleLayout輸出
            log4j.appender.appender.layout=org.apache.log4j.SimpleLayout
          此外,PropertyConfigurator也允許同時(shí)指定多個(gè)Appender,例如:
            #這里沒(méi)有設(shè)置輸出級(jí)別,但指定了兩個(gè)Appender
            log4j.rootLogger=,appender1,appender2
            #輸出信息到控制臺(tái)
            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
          關(guān)于更多配置,網(wǎng)上示例很多,這里不再贅述。但要說(shuō)明一件事,就是配置文件中的鍵是怎么來(lái)的。參照后一個(gè)示例,查看PropertyConfigurator源碼,會(huì)發(fā)現(xiàn)"log4j.rootLogger"是定義好的,只能照寫;而"log4j.appender"字樣也可以找到,與指定的Appender名稱appender1、appender2聯(lián)系起來(lái),log4j.appender.appender1和log4j.appender.appender2也就不難理解了;再看下去,還能找到"prefix + ".layout"",這樣log4j.appender.appender1.layout也有了;可是log4j.appender.appender2.File 和log4j.appender.appender2.Append呢?還記得前邊介紹FileAppender時(shí)曾提到的setAppend方法嗎?其實(shí)FileAppender還有個(gè)getAppend方法,這說(shuō)明FileAppender具有Append屬性。那么File呢?當(dāng)然也是FileAppender的屬性了。至于log4j.appender.appender2.layout.ConversionPattern也一樣,只不過(guò)FileAppender換成了PatternLayout。其實(shí)別的Appender和Layout的屬性也都是這樣定義成鍵來(lái)進(jìn)行設(shè)置的。此外,定義鍵時(shí),屬性的首字母不區(qū)分大小寫,例如"File",也可以寫成"file"。
            3.org.apache.log4j.xml.DOMConfigurator,調(diào)用XML配置文件輸出信息。其定義文檔是log4j-1.2.11.jar中org\apache\log4j\xml包下的log4j.dtd文件。與PropertyConfigurator相比,DOMConfigurator似乎是趨勢(shì)。
            示例代碼:
              DOMConfigurator.configure("test.xml");
              Logger log = Logger.getLogger(Test.class);
              log.debug("output");
          要完成該示例,也需要在工程目錄下創(chuàng)建一個(gè)test.xml文件,內(nèi)容如下:
            <?xml version="1.0" encoding="UTF-8" ?>
            <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
            <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
              <!-- 輸出信息到控制臺(tái)
              創(chuàng)建一個(gè)名為appender的Appender,類型為ConsoleAppender -->
              <appender name="appender" class="org.apache.log4j.ConsoleAppender">
                <!-- 設(shè)置appender以SimpleLayout輸出 -->
                <layout class="org.apache.log4j.SimpleLayout"/>
              </appender>
              <!-- 設(shè)置根Category,其值由輸出級(jí)別和指定的Appender兩部分組成
              這里設(shè)置輸出級(jí)別為DEBUG -->
              <root>
                <priority value ="debug" />
                <appender-ref ref="appender"/>
              </root>
            </log4j:configuration>
          此外,DOMConfigurator也允許同時(shí)指定多個(gè)Appender,例如:
            <?xml version="1.0" encoding="UTF-8" ?>
            <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
            <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
              <!-- 輸出信息到控制臺(tái) -->
              <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>
              <!-- 這里沒(méi)有設(shè)置輸出級(jí)別,但指定了兩個(gè)Appender -->
              <root>
                <appender-ref ref="appender1"/>
                <appender-ref ref="appender2"/>
              </root>
            </log4j:configuration>
          由于以上兩個(gè)示例是在PropertyConfigurator的兩個(gè)示例基礎(chǔ)上改的,而且也寫了注釋,因此這里只簡(jiǎn)單介紹一下<param>標(biāo)記。<param>標(biāo)記有兩個(gè)屬性,name和value,前者的值也是Appender或Layout的屬性名,作用與log4j.appender.appender2.File這樣的鍵一樣。設(shè)置時(shí),首字母同樣不區(qū)分大小寫,例如"File"也可以寫成"file"。此外還請(qǐng)注意,使用這兩段XML代碼時(shí)應(yīng)將中文注釋去掉,或者把<?xml version="1.0" encoding="UTF-8" ?>中的UTF-8改成GBK或GB2312,否則會(huì)導(dǎo)致錯(cuò)誤。這里使用的UTF-8是XML默認(rèn)的字符集。
            4.org.apache.log4j.lf5.DefaultLF5Configurator,默認(rèn)使用LF5Appender來(lái)輸出信息,需要調(diào)用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");
            下面討論另外一個(gè)話題:Diagnostic Context。Diagnostic Context意為診斷環(huán)境,針對(duì)于多用戶并發(fā)環(huán)境,在這種環(huán)境下,通常需要對(duì)每個(gè)客戶端提供獨(dú)立的線程以處理其請(qǐng)求,此時(shí)若要在日志信息中對(duì)客戶端加以區(qū)分,為每個(gè)線程分別創(chuàng)建Category是個(gè)辦法。但這樣做并不高效,反而會(huì)導(dǎo)致大量資源被占用。Diagnostic Context所要解決的就是這個(gè)問(wèn)題。Diagnostic Context會(huì)為當(dāng)前線程提供一定空間,然后將信息保存到該空間供Category調(diào)用。與創(chuàng)建一個(gè)Category相比,這點(diǎn)信息所占的資源自然要少得多。
            1.org.apache.log4j.NDC。NDC是Nested Diagnostic Context的簡(jiǎn)寫,意為嵌套診斷環(huán)境,使用時(shí)提供一個(gè)堆棧對(duì)象來(lái)保存信息。堆棧的特點(diǎn)是數(shù)據(jù)后進(jìn)先出、先進(jìn)后出,即清理堆棧時(shí),后保存的數(shù)據(jù)會(huì)被先清掉,而先保存的數(shù)據(jù)則被后清掉。
            示例代碼:
              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(); //將當(dāng)前線程移除,退出NDC環(huán)境
              log.debug("End");
          這里使用了PatternLayout來(lái)格式化信息,其模式字符%x就是用來(lái)輸出NDC信息的。程序運(yùn)行后會(huì)輸出如下內(nèi)容:
            Start
            Before zhengyp
            After
            End
          可以看到,第二行輸出時(shí)由于已向堆棧中添加了信息,因此"zhengyp"也會(huì)同時(shí)輸出;而第三行輸出時(shí)由于信息已被移除,因此就沒(méi)再輸出"zhengyp"。不過(guò)這個(gè)示例僅簡(jiǎn)單演示了NDC的用法,而沒(méi)有顯示出NDC的堆棧特性,所以下面再提供一個(gè)示例,代碼如下:
            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方法嗎?程序運(yùn)行后,從輸出的信息就可以看到效果了。此外,NDC還提供了其他方法:
            (1)get,獲取堆棧中的全部信息。以上例為例,當(dāng)輸出Test2時(shí),使用該方法會(huì)獲得"zhengyp 192.168.0.1"。
            (2)peek,獲取堆棧中最后的信息。仍以上例為例,當(dāng)輸出Test1時(shí)會(huì)獲得"zhengyp",Test2時(shí)為"192.168.0.1",而當(dāng)輸出Test3時(shí)由于"192.168.0.1"已被移除,"zhengyp"又成了最后的信息,因此獲得的仍是"zhengyp"。
            (3)clear,清空堆棧中的全部信息。
            (4)setMaxDepth,設(shè)置堆棧的最大深度,即當(dāng)前的信息可以保留多少,對(duì)之后追加的信息沒(méi)有影響。當(dāng)需要一次清掉多條信息時(shí),使用setMaxDepth會(huì)比多次調(diào)用pop方便。
            2.org.apache.log4j.MDC。MDC是Mapped Diagnostic Context的簡(jiǎn)寫,意為映射診斷環(huán)境,提供了一個(gè)Map對(duì)象來(lái)保存信息。Map對(duì)象使用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重復(fù),則覆蓋之前的值
              MDC.put("name", "zhengyp2");
              MDC.put("ip", "192.168.1.2");
              log.debug("Test2");
              
              //將信息從Map中移除,此時(shí)信息不再輸出
              MDC.remove("name");
              MDC.remove("ip");
              log.debug("End");
          這個(gè)示例演示了MDC的基本用法,格式化信息用的也是PatternLayout,模式字符為"%X",其格式必須為"%X{Key}"。其中Key就是向Map對(duì)象添加信息時(shí)put方法所用的Key,這里為name和ip。由于可以使用"%X{Key}"輸出信息,因此MDC使用起來(lái)會(huì)比NDC更靈活。此外,MDC還提供了get方法來(lái)獲取指定Key的信息。
          六、小結(jié)
            用了近半個(gè)月,終于大概掌握了Log4J。由于本文是邊學(xué)邊寫的,目的是將Log4J的用法記錄下來(lái),而非提供一份中文參考,因此內(nèi)容并不細(xì)致,但盡量提供了示例。不過(guò)到最后才發(fā)現(xiàn),示例存在問(wèn)題,其實(shí)Logger做為類的static成員比較恰當(dāng),而我為了圖方便,竟直接寫到了main方法中,這一點(diǎn)還請(qǐng)注意。
            此外,這里再推薦一下《The Complete log4j Manual》,是對(duì)Log4J較詳細(xì)的介紹,在網(wǎng)上可以找到,只不過(guò)是英文的。

          posted on 2006-10-11 17:56 OMG 閱讀(1058) 評(píng)論(0)  編輯  收藏 所屬分類: Log4j

          <2006年10月>
          24252627282930
          1234567
          891011121314
          15161718192021
          22232425262728
          2930311234

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          IT風(fēng)云人物

          文檔

          朋友

          相冊(cè)

          經(jīng)典網(wǎng)站

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 团风县| 安义县| 丁青县| 鞍山市| 玉环县| 张家口市| 盐边县| 吕梁市| 宕昌县| 兰州市| 镇坪县| 凤山市| 海阳市| 壶关县| 昂仁县| 武夷山市| SHOW| 梁平县| 临沂市| 藁城市| 淮南市| 福贡县| 高碑店市| 五原县| 舟山市| 鄄城县| 清水县| 精河县| 苏州市| 开封县| 济宁市| 建水县| 宾阳县| 册亨县| 东兴市| 孙吴县| 舟山市| 顺平县| 蕉岭县| 公安县| 沧源|