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

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

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

    posts - 495,comments - 227,trackbacks - 0
    ?大家在編程時經(jīng)常不可避免地要使用到一些日志操作,比如開發(fā)階段的調(diào)試信息、運行時的日志記錄及審計。調(diào)查顯示,日志代碼占代碼總量的4%。通常大家可以簡單地使用System.out.println()語句輸出日志信息,但是往往會有一些判斷,比如:

    if (someCondition) {
     System.out.println("some information.");
    }


    這些判斷造成正常的程序邏輯中混雜了大量的輸出語句。而在開發(fā)階段寫下的這些判斷僅為了調(diào)試的語句,在開發(fā)完成時需要查找并移除。部署運行后,尤其是在一些企業(yè)應(yīng)用系統(tǒng)中,還經(jīng)常需要進一步調(diào)試,這時就遇到了更大的麻煩。所以,我們需要一套完備的、靈活的、可配置的日志工具。Log4J就是優(yōu)秀的選擇。

    Log4J是Apache軟件基金會Jakarta項目下的一個子項目,是用Java編寫的優(yōu)秀日志工具包。通過Log4J可以在不修改代碼的情況下,方便、靈活地控制任意粒度的日志信息的開啟或關(guān)閉,然后使用定制的格式,把日志信息輸出到一個或多個需要的地方。并且,Log4J還有一條平滑的學(xué)習(xí)曲線,在三分鐘內(nèi)就可學(xué)會它的簡單使用。隨著使用深入,你會發(fā)現(xiàn)Log4J功能的強大,幾乎可以滿足日志方面的所有需要。

    快速入門


    先看一段代碼,看看Log4J是多么易于上手,代碼如下:

    package org.javaresearch.log4j;
    import org.apache.log4j.*;
    public class TestLog4J {
     static Logger log = Logger.getLogger(TestLog4J.class.getName());
     public static void main(String args[]) {
      BasicConfigurator.configure();
      // logging的各種方法
      cat.debug("Start of main()");
      cat.info("Just testing a log message with priority set to INFO");
      cat.warn("Just testing a log message with priority set to WARN");
      cat.error("Just testing a log message with priority set to ERROR");
      cat.fatal("Just testing a log message with priority set to FATAL");
      // 另一種不方便的格式
      cat.log(Priority.DEBUG, "Testing a log message use a alternate form");
      log.debug("End of main().");
     }
    }


    把這段代碼保存在一個目錄下,編譯運行(注意要把log4j-1.2.7.jar包含入類路徑中),程序輸出如下:

    0 [main] DEBUG TestLog4J  - Start of main()
    10 [main] INFO TestLog4J  - Just testing a log message with priority set to INFO
    20 [main] WARN TestLog4J  - Just testing a log message with priority set to WARN
    30 [main] ERROR TestLog4J  - Just testing a log message with priority set to ERROR
    30 [main] FATAL TestLog4J  - Just testing a log message with priority set to FATAL
    40 [main] DEBUG TestLog4J  - Testing a log message use a alternate form
    50 [main] DEBUG TestLog4J  - End of main().


    首先解釋一下上面輸出結(jié)果的意義。第一個數(shù)字是指程序開始運行到運行該日志語句所經(jīng)歷的毫秒數(shù)(用來做一點運行效率分析也不錯), “[main]”是日志事件發(fā)生的線程,隨后的“DEBUG”、“INFO”等信息是相應(yīng)日志信息的優(yōu)先級別,“TestLog4”是當(dāng)前Logger的實例名,最后是日志信息。

    在這段程序中,使用了Log4J提供的一個基本配置類BasicConfigurator對Log4J進行初始化。但在實際使用時通常不這么做,因為這多少有點“硬”編碼。今后如果要修改Log4J的配置,就需要修改、重新編譯代碼,這通常不是大家所希望的。通常,我們都提供一個名為 log4j.properties的文件,在第一次調(diào)用到Log4J時,Log4J會在類路徑中定位這個文件,并讀入這個文件完成的配置。這個配置文件告訴Log4J以什么樣的格式、把什么樣的信息、輸出到什么地方。我們來看一個簡單的log4j.properties配置文件的示例,代碼如下:

    log4j.rootLogger=DEBUG, A1
    log4j.appender.A1=org.apache.log4j.ConsoleAppender
    log4j.appender.A1.layout=org.apache.log4j.PatternLayout
    log4j.appender.A1.layout.ConversionPattern= "%-4r [%t] %-5p %c %x - %m%n


    把上面的內(nèi)容存儲為log4j.properties,并放到和TestLog4J.class同一目錄下(當(dāng)然也可以放到其它任何目錄,只要該目錄被包含到類路徑中即可)。具體這些配置文件中每行的意義,在以后章節(jié)會有詳細的說明,現(xiàn)在可以先跳過?,F(xiàn)在你可以注釋掉上面程序中的 “BasicConfigurator. configure();”語句,然后使用log4j.properties屬性文件完成Log4J的配置,重新編譯、運行,就得到和上面一樣的結(jié)果。

    這樣做有什么好處呢?現(xiàn)在就初步領(lǐng)略一些Log4J的靈活、強大功能。比如系統(tǒng)要上線了,希望輸出一些警告和錯誤信息,這時僅需要修改 log4j.properties文件中的“l(fā)og4j.rootCategory=DEBUG, A1”即可,然后設(shè)置日志輸出的最低級別是WARN,設(shè)置為“l(fā)og4j.root Category=WARN, A1”。此時不需要修改任何代碼,重新運行系統(tǒng),輸出結(jié)果就變成了:

    20 [main] WARN TestLog4J  - Just testing a log message with priority set to WARN
    30 [main] ERROR TestLog4J  - Just testing a log message with priority set to ERROR
    30 [main] FATAL TestLog4J  - Just testing a log message with priority set to FATAL


    原理分析


    Log4J有三個主要部件,它們是記錄器(Loggers)、輸出源(Appenders)和布局(Logouts)。記錄器按照布局中指定的格式把日志信息寫入一個或多個輸出源。輸出源可以是控制臺、文本文件、XML文件或Socket,甚至還可以把信息寫入到Windows事件日志或通過電子郵件發(fā)送,這都需要相應(yīng)的類來處理,這些相關(guān)的類是ConsoleAppender、 FileAppender、SocketAppender、NtEventLogAppender和JMSAppender。

    記錄器(Logger)

    首先讓我們看Logger類,代碼如下:

    package org.apache.log4j;
    public class Logger {
     //創(chuàng)建和恢復(fù)方法
     public static Logger getRootLogger();
     public static Logger getLogger(String name);
     public static Logger getLogger(Class clazz);
     // 打印方法
     public void debug(Object message);
     public void info(Object message);
     public void warn(Object message);
     public void error(Object message);
     public void fatal(Object message);
     // 常用打印方法
     public void log(Level l, Object message);
    }


    從這段代碼中可以看出Logger的基本使用。首先需要獲取一個Logger對象,獲取Logger對象的語句為:

    Logger logger = Logger.getLogger(JavaLoggingExample.class.getName());


    有了這個Logger對象,就可以在需要的地方方便地輸出日志信息。對于這些信息是否輸出、輸出的格式等,都可以通過配置文件方便地配置,而不需要修改代碼,這就是Log4J帶來的方便之處。

    記錄器的層次結(jié)構(gòu)

    使用Log4J的Logger.getLogger()方法時會得到一個Logger的實例。如果一個應(yīng)用中包含了上千個類,那么也幾乎需要上千個Logger實例。如何對這上千個Logger實例進行方便地配置,就是一個很重要的問題。Log4J采用了一種樹狀的繼承層次巧妙地解決了這個問題。在Log4J中Logger是具有層次關(guān)系的。它有一個共同的根,位于最上層,其它Logger遵循類似包的層次,比如:

    static Logger root = Logger.getRootLogger();
    static Logger log1 = Logger.getLogger("org");
    static Logger log2 = Logger.getLogger("org.javaresearch");
    static Logger log3 = Logger.getLogger("org.javaresearch.log4j.TestLog4J");


    上面代碼中,log1是log2的父親,是log3的祖先,而root是所有l(wèi)og1、log2、log3的祖先,它們都從root中繼承。所以,一般情況下,僅需要配置好rootLogger,其它子記錄器都會從中繼承rootLogger的配置。如果修改了rootLogger的配置,其它所有的子記錄器也會繼承這種變化。這樣就大大地方便了配置?,F(xiàn)在回頭看看在“快速入門”中的配置文件,我們僅配置了rootLogger,就可以控制所有的Logger的行為。

    級別(Level)

    Log4J中的一個核心概念是日志級別是有序的。Log4J內(nèi)置了5種日志級別為:

    DEBUG  <  INFO  <  WARN  <  ERROR  <  FATAL


    右邊的級別比左邊的高。每一個Logger實例都有一個日志級別,上面的5種輸出方法就是對應(yīng)于5種不同級別的日志請求。比如,如果c是一個Logger實例,c.info("some information")就是一個INFO級別的日志請求。一個日志請求會不會輸出,取決于該Logger實例的日志級別和該日志請求級別的比較。規(guī)則如下:

    假如在一個級別為p的Logger實例中發(fā)生一個級別為q的日志請求,則當(dāng)q >= p時請求才會啟用。

    我們先來看實例2代碼如下:

    // 得到一個logger 實例 "com.foo"
    Logger  logger = Logger.getLogger("com.foo")
    // 現(xiàn)在設(shè)置logger的級別,但正常情況是不需要刻意設(shè)置lgger級別的,因為它已經(jīng)在配置文件中完成了
    logger.setLevel(Level.INFO);
    Logger barlogger = Logger.getLogger("com.foo.Bar");
    //因為 WARN >= INFO,這個請求是可以實現(xiàn)的
    logger.warn("Low fuel level.");
    // 因為DEBUG < INFO,所以這個請求是無法實現(xiàn)的
    logger.debug("Starting search for nearest gas station.");
    // logger實例"com.foo.Bar"將從"com.foo"繼承級別,這樣,因為INFO >= 
    INFO,所以可以實現(xiàn)下面的請求
    barlogger.info("Located nearest gas station.");
    //因為DEBUG < INFO,這個請求是不能實現(xiàn)的 
    barlogger.debug("Exiting gas station search");


    布局(Layout)

    Log4J采用類似C語言中的printf函數(shù)的打印格式格式化日志信息,打印參數(shù)見表1如下:

    %m輸出代碼中指定的消息
    %p輸出優(yōu)先級,即DEBUG,INFO,WARN,ERROR,F(xiàn)ATAL
    %r輸出自應(yīng)用啟動到輸出該log信息耗費的毫秒數(shù)
    %c輸出所屬的類目,通常就是所在類的全名
    %t輸出產(chǎn)生該日志事件的線程名
    %n輸出一個回車換行符,Windows平臺為“\r\n”,Unix平臺為“\n”
    %d輸出日志時間點的日期或時間,默認格式為ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},輸出類似:2002年10月18日 22:10:28,921
    %l輸出日志事件的發(fā)生位置,包括類目名、發(fā)生的線程,以及在代碼中的行數(shù)。舉例:Testlog4.main(TestLog4.java:10)


    ?Log4J的配置

    現(xiàn)在來看log4j.properties配置文件的意義。第一行指定了根Logger的級別是DEBUG,并將此指定輸出到A1。A1 就是第二行定義的org.apache.log4j.ConsoleAppender,此行表示將A1輸出到控制臺。第三行規(guī)定了輸出到A1的格式為 org.apache.log4j.PatternLayout。第四行規(guī)定了輸出到A1格式的轉(zhuǎn)換模式為 org.javaresearch.log4j.TestLog4J。

    很多成熟的服務(wù)器類的軟件日志信息會輸出到控制臺,同時輸出到日志文件備查。使用Log4J可以在不改變?nèi)魏未a的情況下,僅通過修改配置文件就可以輕松地完成這項功能。相關(guān)配置文件如下:

    #### Use two appenders, one to log to console, another to log to a file
    log4j.rootCategory=debug, stdout, R
    
    # Print only messages of priority WARN or higher for your category
    log4j.category.your.category.name=WARN
    
    #### First appender writes to console
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    
    # Pattern to output the caller's file name and line number.
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
    
    #### Second appender writes to a file
    log4j.appender.R=org.apache.log4j.RollingFileAppender
    log4j.appender.R.File=example.log
    
    # Control the maximum log file size
    log4j.appender.R.MaxFileSize=100KB
    # Archive log files (one backup file here)
    log4j.appender.R.MaxBackupIndex=1
    
    log4j.appender.R.layout=org.apache.log4j.PatternLayout
    log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n


    這個配置文件指定了兩個輸出源stdout和R。前者把日志信息輸出到控制臺,后者是一個輪轉(zhuǎn)日志文件。最大的文件是100KB,當(dāng)一個日志文件達到最大尺寸時,Log4J會自動把example.log重命名為example.log.1,然后重建一個新的example.log文件,依次輪轉(zhuǎn)。

    在Web應(yīng)用中使用

    在Web應(yīng)用中,應(yīng)該在哪兒對Log4J進行配置呢?首先要明確,Log4J必須在應(yīng)用的其它代碼執(zhí)行前完成初始化。因為Servlet 是在Web服務(wù)器啟動時立即裝入的,所以,在Web應(yīng)用中一般使用一個專門的Servlet來完成Log4J的配置,并保證在web.xml的配置中,這個Servlet位于其它Servlet之前。下面是一個例子,代碼如下:

    package org.javaresearch.log4j;
    import java.io.*;
    import javax.servlet.*;
    import org.apache.log4j.*;
    public class Log4JInit extends HttpServlet {
     public void init() throws ServletException {
      String prefix = getServletContext().getRealPath("/");
      String file = getServletConfig().getInitParameter("log4j-config-file");
      // 從Servlet參數(shù)讀取log4j的配置文件 
      if (file != null) {
       PropertyConfigurator.configure(prefix + file);
      }
     }
     public void doGet(HttpServletRequest request,HttpServletResponse response)throws 
    IOException, ServletException {}
     public void doPost(HttpServletRequest request,HttpServletResponse response)throws 
    IOException, ServletException {}
    }


    <servlet>
      <servlet-name>log4jinit</servlet-name> 
       <servlet-class>org.javaresearch. log4j.Log4JInit</servlet-class>??????  ??????
       <init-param>
       <param-name> log4j-config-file </param-name> 
    ??????<param-value>/properties/log4j.properties</param-value> 
       </init-param>
       <load-on-startup>1</load-on-startup>
      </servlet>


    注意:上面的load-on-startup應(yīng)設(shè)為1,以便在Web容器啟動時即裝入該Servlet。log4j.properties文件放在根的properties子目錄中,也可以把它放在其它目錄中。應(yīng)該把.properties文件集中存放,這樣方便管理。

    高級話題



    性能

    在記錄一些日志信息時,會一定程度地影響系統(tǒng)的運行效率,這時日志工具是否高效就是一個關(guān)鍵。Log4J的首要設(shè)計目標(biāo)就是高效,一些關(guān)鍵組件都重寫過很多次以不斷提高性能。根據(jù)Log4J項目小組的報告,在AMD Duron 800MHz + JDK1.3.1的環(huán)境下,Log4J判斷一條日志語句是否需要輸出僅需要5納秒。實際的日志語句執(zhí)行的也非??焖?,從使用SimpleLayout的 21微秒(幾乎與System.out.println一樣快),到使用TTCCLayout的37微秒不等。

    嵌套診斷環(huán)境NDC

    在多用戶并發(fā)的環(huán)境下,通常是由不同的線程分別處理不同的客戶端請求。此時要在日志信息中區(qū)分出不同的客戶端,你可以為每一個線程生成一個Logger,從而從一堆日志信息中區(qū)分出哪些信息是屬于哪個線程的,但這種方式并不高效。Log4J巧妙地使用了Neil Harrison提出的“NDC(嵌套診斷環(huán)境)”機制來解決這個問題。Log4J為同一類別的線程生成一個Logger,多個線程共享使用,而它僅在日志信息中添加能夠區(qū)分不同線程的信息。NDC是什么?舉例來說,如果一個Servlet接到并發(fā)請求時,為每一個客戶端創(chuàng)建一個新的線程,然后分配一個用于保存該請求上下文的NDC堆棧。該上下文可能是發(fā)出請求的主機名、IP地址或其它任何可以用于標(biāo)識該請求的信息。這樣,由于不同的客戶端處理線程具有不同的NDC堆棧,即使這個Servlet同時生成多個線程處理不同的請求,這些日志信息仍然可以區(qū)分出來,就好像Log4J為每一個線程都單獨生成了一個 Logger實例一樣。在Log4J中是通過org.apache.log4j.NDC實現(xiàn)這種機制的。使用NDC的方法也很簡單,步驟如下:

    1. 在進入一個環(huán)境時調(diào)用NDC.push(String),然后創(chuàng)建一個NDC;

    2. 所做的日志操作輸出中包括了NDC的信息;

    3. 離開該環(huán)境時調(diào)用NDC.pop方法;

    4. 當(dāng)從一個線程中退出時調(diào)用NDC.remove方法,以便釋放資源。

    下面是一個模擬記錄來自不同客戶端請求事件的例子,代碼如下:

    import org.apache.log4j.Logger;
    import org.apache.log4j.NDC;
    public class TestNDC {
     static Logger log = Logger.getLogger(TestNDC.class.getName());
     public static void main(String[] args) {
      log.info("Make sure %x is in your layout pattern!");
      // 從客戶端獲得IP地址的例子
      String[] ips = {"192.168.0.10","192.168.0.27"};
      for (int i = 0; i<ips.length ; i++)  // 模擬一個運行方法
      {
       // 將IP放進 NDC中
       NDC.push(ips[i]);
       log.info("A NEW client connected, who's ip should appear in this log message.");
       NDC.pop();
      }
      NDC.remove();
      log.info("Finished.");
     }
    }


    注意配置文件中的布局格式中一定要加上%x。系統(tǒng)輸出如下:

    INFO   - Make sure %x is in your layout pattern!
    INFO  192.168.0.10 - A NEW client connected, who's ip should appear in this log 
    message.
    INFO  192.168.0.27 - A NEW client connected, who's ip should appear in this log 
    message.
    INFO   - Finished.


    使用Log4J還是JDK logging API

    從JDK 1.4.0開始,引入了java.util.logging包。雖然Log4J小組曾竭力游說JCP(Java Community Process)采用Log4J作為JDK 1.4的“標(biāo)準(zhǔn)”日志API,雖然最終因Sun的日志API規(guī)范的負責(zé)人Graham Hamilton的一句“Merlin的開發(fā)已經(jīng)到了最后階段,這時不允許再對主要API做出改變”而沒有被采納,但Log4J還是對新的日志API產(chǎn)生了重要影響。那么,我們到底應(yīng)該采用Log4J還是java.util.logging包呢?下面僅對兩者做一簡單的比較。

    1. Log4J更加成熟,從1999年10月開始至今已經(jīng)有3年的時間,并且已經(jīng)在許多項目中有著成熟的應(yīng)用。而JDK中的logging包是在1.4之后才引入的,并且不能運行于JDK 1.3之前的版本。Log4J則可以良好地運行于JDK 1.1之后的所有版本。

    2. Log4J已經(jīng)被移植到多種環(huán)境下,包括log4c(C)、log4cpp(C++)、log4perl(Perl)、log4net(.net)等。在這些環(huán)境下,可以感受到幾乎一致的配置和使用方式。這是JDK中的logging API所不能比擬的。

    3. Log4J還具有更加強力的格式化系統(tǒng),可以使記錄輸出時實現(xiàn)簡單的模式。但是,它不會增加類而導(dǎo)致格式化工具的擴展。眾多的附加程序和處理器使得Log4J數(shù)據(jù)包成為一個絕佳的選擇,所有你所需要的都可能加以實現(xiàn)。

    4. Log4J在性能上做了最大的優(yōu)化。

    Logging API對于簡單的使用是足夠的,但它缺少了許多Log4J所具有的功能。所以,如果你需要一個強力的logging機制,就應(yīng)堅持使用Log4J;而如果只是需要一些簡單的控制或文件記錄,那么可以使用已經(jīng)內(nèi)建在JDK之中的logging API。

    雖然Log4J和JDK logging API是一種競爭關(guān)系,但在logging API還在JCP中討論(JSR47)時,兩者之間就已經(jīng)開始相互影響了。
    posted on 2006-10-17 09:33 SIMONE 閱讀(298) 評論(0)  編輯  收藏

    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 亚洲宅男永久在线| 国产成在线观看免费视频| 亚洲午夜无码久久久久小说| 亚洲乱码国产一区三区| 日本a级片免费看| 美女视频黄a视频全免费| 99久久国产精品免费一区二区| 国产亚洲精品精品精品| ww亚洲ww在线观看国产| 亚洲视频在线一区二区三区| 亚洲精品无码成人片久久 | 久久精品a亚洲国产v高清不卡| 亚洲一区二区三区乱码A| 免费观看一级毛片| 国产免费看JIZZ视频| 18禁男女爽爽爽午夜网站免费| 免费黄网站在线看| 黄视频在线观看免费| 免费国产黄网站在线观看动图| 亚洲AV日韩AV永久无码色欲| 亚洲AV成人无码天堂| 亚洲美女aⅴ久久久91| 亚洲情a成黄在线观看动漫尤物| 亚洲精品一品区二品区三品区| 久久精品亚洲福利| 吃奶摸下高潮60分钟免费视频| 日本高清免费不卡在线| 好爽…又高潮了毛片免费看| 在线观看免费人成视频色| 67194熟妇在线永久免费观看| 999国内精品永久免费视频| 日本免费xxxx| 免费观看黄网站在线播放| 野花高清在线电影观看免费视频| 在线观看免费人成视频色9| 免费无码精品黄AV电影| 成年人网站在线免费观看| 噜噜嘿在线视频免费观看| 日本免费网站观看| 免费大黄网站在线观看| 亚洲精品专区在线观看|