<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Vincent

    Vicent's blog
    隨筆 - 74, 文章 - 0, 評論 - 5, 引用 - 0
    數(shù)據(jù)加載中……

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

    一、簡介
      在程序中輸出信息的目的有三:一是監(jiān)視程序運行情況;一是將程序的運行情況記錄到日志文件中,以備將來查看;一是做為調(diào)試器。但信息輸出的手段不僅限于System.out.println()或System.out.print(),還有日志記錄工具可以選擇。與System.out.pringln()和System.out.print()相比,日志記錄工具可以控制輸出級別,并且可以在配置文件中對輸出級別進行設(shè)置,這樣開發(fā)階段的信息在程序發(fā)布后就可以通過設(shè)置輸出級別來消除掉,而無須對代碼進行修正了。現(xiàn)在流行的日志記錄工具很多, Log4J就是其中的佼佼者。
      Log4J是由著名開源組織Apache推出的一款日志記錄工具,供Java編碼人員做日志輸出之用,可以從網(wǎng)站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網(wǎng)站上下載;JBuilder用的是JBuilder 2005。
    二、配置類庫
      下面打開Eclipse或JBuilder。
      如果使用的是Eclipse,那么在Eclipse打開后,點擊菜單"文件"->"新建"->"項目",打開"新建項目"對話框:

    請選中"Java項目",點擊"下一步",進入"新建Java項目"對話框:

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

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

    點擊"New"按鈕,打開"New Library Wizard"對話框:

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

    在這個窗口中設(shè)置項目名稱及存放目錄,我的項目名稱仍為Log4JTest,路徑為G:/YPJCCK/log4J/JBuilder/Log4JTest。點擊"Next"進入下一個窗口:

    在這個窗口中選擇"Required Libraries"選項卡,點擊"Add"按鈕,將剛才設(shè)置的JUnit庫引用進來。然后點擊"Next"按鈕,進入下一個窗口:

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

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

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

    log.setLevel(Level.FATAL);

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

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

    雖然一般情況下log方法不如其它方法方便,但由于允許設(shè)置級別,因此log方法在很多時候反而比其它方法更靈活,甚至可以在輸出級別為OFF時輸出信息。不過log方法主要是給用戶自定義的輸出級別用的,而且設(shè)立OFF輸出級別的目的也為了不輸出任何信息,因此請不要在log方法中使用OFF來輸出信息。
      此外,Category的輸出級別并非必須,若未設(shè)置,子Category會默認(rèn)使用其父Category的輸出級別,若父Category也沒設(shè)置,就使用再上一級Category的設(shè)置,直到根Category為止。根Category默認(rèn)輸出級別為DEBUG,因此在示例中,若將 "log.setLevel(Level.FATAL);"一行注釋掉,則所有方法都會輸出信息。
      下面簡單介紹一下Log4J中 Category的繼承關(guān)系。其實在Log4J中Category之間是存在繼承關(guān)系的,根Category默認(rèn)創(chuàng)建,是級別最高的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。也許有點亂,下面來一個一個看。首先看與lx_y、lx_z對應(yīng)的Category的名稱"x.y"和"x.z","."前邊的是什么,"x",這說明與名稱為 "x"的Category對應(yīng)lx就是它們的父Category;而與lx_y_z對應(yīng)的Category的名稱"x.y.z",最后一個"."前邊的是什么,"x.y",這說明lx_y是lx_y_z的父Category;至于lxy,由于與之對應(yīng)的Category名稱"xy"之間沒有".",因此它是一個與lx同級的Category,其父Category就是根Category器。此外還有一種情況,例如有一個名稱為"a.b"的 Category,如果沒有名稱為"a"的Category,那么它的父Category也是根Category。前邊說過,"."在Category名稱中有特殊作用,其實它的作用就是繼承。至此,為何使用類對象來創(chuàng)建Category也就不難理解了。
      可是,僅有Category是無法完成信息輸出的,還需要為Category添加Appender,即Category的輸出源。前邊的例子使用的是ConsoleAppender,即指定 Category將信息輸出到控制臺。其實Log4J提供的Appender有很多,這里選擇幾常用的進行介紹。
      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) {}
        
        //向控制臺輸出信息,Writer示例
        WriterAppender appender2 = null;
        try {
          appender2 = new WriterAppender(layout, new OutputStreamWriter(System.out));
        }
        catch(Exception ex) {}
        
        //Category支持同時向多個目標(biāo)輸出信息
        Logger log = Logger.getLogger(Test.class);
        log.addAppender(appender1);
        log.addAppender(appender2);
        
        log.debug("output");
    這個示例由第一個示例修改而來,沒有設(shè)置輸出級別,而且向Category中添加了兩個輸出源,運行后會在控制臺中輸出"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)文件時也都可以這樣來寫。
      2.org.apache.log4j.ConsoleAppender,向控制臺輸出信息,繼承了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");
    這個示例也由第一個示例修改而來,運行后會在工程目錄下生成test.txt文件,該文件中記錄著"DEBUG - output"。再次運行程序,查看文件,則"DEBUG - output"有兩行。
      另外,F(xiàn)ileAppender還有一個構(gòu)造:

    FileAppender(Layout layout, String filename, boolean append)

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


    posted on 2006-09-01 13:25 Binary 閱讀(435) 評論(0)  編輯  收藏 所屬分類: Apache jakarta

    主站蜘蛛池模板: 亚洲日本va在线观看| 免费无码午夜福利片 | 日本免费电影一区二区| 亚洲精品高清久久| 全免费a级毛片免费看不卡| 日韩成人毛片高清视频免费看| 国产成人亚洲精品青草天美| 亚洲人成网站免费播放| jizz免费在线观看| 亚洲人配人种jizz| 伊人久久大香线蕉亚洲五月天| 丁香花免费完整高清观看| 一边摸一边桶一边脱免费视频 | 日韩高清在线免费观看| 三年在线观看免费观看完整版中文 | 蜜桃传媒一区二区亚洲AV| 亚洲成A人片在线观看无码不卡 | 欧洲乱码伦视频免费国产| 亚洲男人天堂影院| 伊人久久综在合线亚洲91 | 久久久久亚洲AV片无码| 日韩免费一级毛片| 99视频在线看观免费| 99久久国产免费中文无字幕| 久久精品国产亚洲AV未满十八| 亚洲天天做日日做天天看| 亚洲免费日韩无码系列| 欧美大尺寸SUV免费| 午夜精品射精入后重之免费观看| 看全免费的一级毛片| 亚洲国产精品成人精品软件| 亚洲中文字幕无码一区二区三区| 免费看无码自慰一区二区| 免费黄色福利视频| 久久免费看少妇高潮V片特黄| 免费人成在线观看播放a| 亚洲最大中文字幕无码网站| 亚洲一区二区中文| 2022中文字字幕久亚洲| 国产gav成人免费播放视频| 国产精品久久久久久久久免费|