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

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

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

    隨筆 - 1, 文章 - 14, 評論 - 3, 引用 - 0
    數據加載中……

    Log4j簡明手冊

    Log4j簡明手冊

    1. 概述
    本文主要描述Log4j的API的唯一特性和它的設計原理。Log4j是一個基于許多作者的開放源碼的項目。它允許開發員以任意的間隔來控制日志的輸出。它通過設在外部的配置文件而達到運行時靈活的設置。最重要的是,Log4j有一個平穩的學習曲線。注意:根據來自用戶的反饋判斷,它很容易使人上癮。

    2. 導言

    幾乎所有的大型應用程序都包括它的自己的日志和跟蹤API。順應這個規則,E.U. SEMPER 項目決定寫它自己的跟蹤PAI。這是1996年初。在無數次加強,幾次變形和許多工作后,那個API變成了如今的Log4j,一個流行的java日志包。這個包以Apache Software License協議發布,一個成熟的開放源嗎協議。最新的Log4j版本,包括全部的源碼,class文件和文檔,你可以在http://jakarta.apache.org/Log4j/上找到。順便,Log4j已經給C, C++, C#, Python, Ruby, and Eiffel 語言都提供了接口。

    為了調試而插入日志輸出到代碼里是一個低技術成分的方法,但它可能也是唯一的方法,因為調試器并不是一直可用或者可以適應的,尤其對于多線程的分布使式的大型程序而言。

    經驗指出調試是軟件開發周期中一個重要的組成部分。

    Log4j擁有幾個優點:

    首先,它提供關于運行程序的準確的環境。一旦代碼被插入,不需要人工干預就可以產生調試信息。

    其次,日志輸出可以被有計劃的保存在永久媒體中以便日后研究。

    另外,除了在開發周期中,一個充分詳盡的日志包可以被用來作為以后的統計工具。

    Log4j當然還有它的缺點,它可能減慢程序。如果太詳細,它可能導致屏幕盲目滾動。排除這些情況,Log4j是可靠的,快速的,可以擴展的。因為日志很少是一個應用程序的主要目的, 設計者們正努力使得Log4j API學習和使用簡單化。

    3. 日志類別、輸出源和布局

    Log4j有三個主要的組件:日志類別(Loggers)、輸出源( Appenders)和布局(Layouts)。這三種類型的組件一起工作使得開發員可以根據信息的類型和級別記錄它們,并且在運行時控制這些信息的輸出格式和位置。

    3.1 日志類別的層次結構(Loggers)

    Log4j首要的相對于簡單的使用System.out.println()方法的優點是基于它的在禁止一些特定的信息輸出的同時不妨礙其它信息的輸出的能力。這個能力源自于日志命名空間,也就是說,所有日志聲明的空間,它根據一些開發員選擇的公式而分類。以前的觀察引導我們選擇類別作為包的中心概念。然而,自從Log4j的1.2版本,Logger類被Catalog類所取代,對于那些熟悉Log4j以前版本的人來說,Logger類可以被想象成僅僅是Category 類的別名。

    Loggers 被指定為實體,Logger的名字是大小寫敏感的,它們遵循以下的命名

    規則:

    2 命名繼承

    如果類別的名稱(后面加一個點)是其子類別名稱的前綴,則它就是另一個類別的祖輩。

    如果一個類別(Logger)和它的子類別之間沒有其它的繼承關系,我們就稱之為parent與child的關系。

    例如,類別"com.foo"是類別"com.foo.Bar"的parent。相似的,"java"是"java.util"的parent,是"java.util.Vector"的父輩。.這個命名規則應該被大多數的開發員所熟悉。

    根(root) 類別位于logger繼承結構的最上層。它有兩種例外:

    1.它一直存在

    2.它不能根據名稱而獲得。

    調用類的靜態方法Logger.getRootLogger可以得到它。其它所有的Logger可以通過靜態方法Logger.getLogger而得到它們自己的實例。這個方法取希望的Logger名作為參數。Logger的一些基本的方法示例如下:

    package org.apache.Log4j;

    public Logger class {

    // Creation & retrieval methods:

    public static Logger getRootLogger();

    public static Logger getLogger(String name);

    // printing methods:

    public void debug(Object message);

    public void info(Object message);

    public void warn(Object message);

    public void error(Object message);

    // generic printing method:

    public void log(Level l, Object message);

    }

    Loggers可以被分配的級別。所有級別的集合包括:

    DEBUG

    INFO

    WARN

    ERROR

    FATAL

    它們被定義于org.apache.Log4j.Level 類。雖然我們不鼓勵,但是你們可以通過繼承Level類來定義你們自己的級別。我們隨后將介紹一個比較好的方法。

    如果一個Logger沒有被分配一個級別,那么它將從一個被分配了級別的最接近它的ancestor哪里繼承。

    正規的說:

    2 級別繼承

    對于一個給定的Logger C,它的繼承的級別等于從C開始上溯到的第一個擁有非空級別的Logger的級別。

    為了保證所有的Logger最終能夠繼承到一個級別,根Logger通常有一個已經定義了的級別。

    以下四個表中的數據演示了根據以上規則得到的結果。

    類別名
    分配的級別
    繼承的級別

    root
    Proot
    Proot

    X
    none
    Proot

    X.Y
    none
    Proot

    X.Y.Z
    none
    Proot

    Example 1



    在例子1中,只有根Logger定義了一個級別,它的級別的值--"Proot"被所有其它的Loggers X, X.Y, 和X.Y.Z所繼承。

    類別名
    分配的級別
    繼承的級別

    root
    Proot
    Proot

    X
    Px
    Px

    X.Y
    Pxy
    Pxy

    X.Y.Z
    Pxyz
    Pxyz

    Example 2



    在例子2中,所有的Logger都有一個被分配的級別值,所以它們不需要級別繼承。

    類別名
    分配的級別
    繼承的級別

    root
    Proot
    Proot

    X
    Px
    Px

    X.Y
    none
    Px

    X.Y.Z
    Pxyz
    Pxyz

    Example 3



    在例子3中,根Logger,以及X和X.Y.Z被分別分配了級別Proot,Px和Pxyz。Logger X.Y從它的parent X繼承了級別值Px。

    類別名
    分配的級別
    繼承的級別

    root
    Proot
    Proot

    X
    Px
    Px

    X.Y
    none
    Px

    X.Y.Z
    none
    Px

    Example 4



    在例子4中,根Logger和X被分別分配了級別"Proot"和"Px",Logger X.Y 和 X.Y.Z從被分配了級別的最接近它們的ancestor X那里得到繼承。

    我們需要通過調用Logger的輸出的實例方法之一來實現日志請求。這些輸出的方法是debug, info, warn, error, fatal 和 log.

    通過定義輸出方法來區分日志的請求的級別。例如,如果c是一個Logger的實例,那么聲明 c.info 就是一個INFO級別的日志請求。

    如果一個日志的請求的級別高于或等于日志的級別那么它就能被啟用。反之,將被禁用。一個沒有被安排級別的Logger將從它的父輩中得到繼承。這個規則總結如下。

    2 基本的選擇規則

    假如在一個級別為q的Logger中發生一個級別為p的日志請求,如果p>=q,那么請求將被啟用。

    這是Log4j的核心原則。它假設級別是有序的。對于標準級別,我們定義DEBUG < INFO < WARN < ERROR < FATAL.

    以下是關于這條規則的一個例子。

    // get a logger instance named "com.foo"

    Logger logger = Logger.getLogger("com.foo");

    // Now set its level. Normally you do not need to set the

    // level of a logger progamitcally. This is usually done

    // in configuration files.

    cat.setLevel(Level.INFO);

    Logger barlogger = Logger.getLogger("com.foo.Bar");

    // This request is enabled, because WARN >= INFO.

    logger.warn("Low fuel level.");

    // This request is disabled, because DEBUG < INFO.

    logger.debug("Starting search for nearest gas station.");

    // The logger instance barlogger, named "com.foo.Bar",

    // will inherit its level from the logger named

    // "com.foo" Thus, the following request is enabled

    // because INFO >= INFO.

    barlogger.info("Located nearest gas station.");

    // This request is disabled, because DEBUG < INFO.

    barlogger.debug("Exiting gas station search");

    調用getLogger方法將返回一個同名的Logger對象的實例。

    例如,

    Categoty x = Logger.getLogger("wombat");

    Categoty y = Logger.getLogger("wombat");

    x和y參照的是同一個Logger對象。

    這樣我們就可以先定義一個Logger,然后在代碼的其它地方不需傳參就可以重新得到我們已經定義了的Logger的實例.

    同基本的生物學理論--父先于子相反,Log4j 的loggers可以以任何順序創造和配置。特別是,一個后實例化的"parent"logger能夠找到并且連接它的子logger。

    配置Log4j的環境通常在一個應用程序被初始化的時候進行,最好的方法是通過讀一個配置文件。這個方法我們將簡短介紹。

    Log4j使得通過軟件組件命名logger很容易。我們可以通過Logger的靜態的初始化方法在每一個類里定義一個logger,令logger的名字等于類名的全局名,而實現logger的命名。這是一個實效的簡單的定義一個logger的方法。因為日志輸出帶有產生日志的類的名字,這個命名策略使得我們更容易定位到一個日志信息的來源。雖然普通,但卻是命名logger的常用策略之一。Log4j沒有限制定義logger的可能。開發員可以自由的按照它們的意愿定義logger的名稱。

    然而,以類的所在位置來命名Logger好象是目前已知的最好方法。

    3.2 輸出源(Appenders)和布局(Layouts)

    有選擇的能用或者禁用日志請求僅僅是Log4j的一部分功能。Log4j允許日志請求被輸出到多個輸出源。用Log4j的話說,一個輸出源被稱做一個Appender. 。Appender包括console(控制臺), files(文件), GUI components(圖形的組件), remote socket servers(socket 服務), JMS(java信息服務), NT Event Loggers(NT的事件日志), and remote UNIX Syslog daemons(遠程UNIX的后臺日志服務)。它也可以做到異步記錄。

    一個logger可以設置超過一個的appender。

    用addAppender 方法添加一個appender到一個給定的logger。對于一個給定的logger它每個生效的日志請求都被轉發到該logger所有的appender上和該logger的父輩logger的appender上。換句話說,appender自動從它的父輩獲得繼承。舉例來說,如果一個根logger擁有一個console appender,那么所有生效的日志請求至少會被輸出到console上。如果一個名為C的logger有一個file類型的appender,那么它就會對它自己以及所有它的子logger生效。我們也可以通過設置appender的additivity flag 為false,來重載appender的默認行為,以便繼承的屬性不在生效。

    調節輸出源(appender)添加性的規則如下。

    輸出源的可添加性(Appender Additivity )

    一個名為C的logger的日志定義的輸出將延續到它自身以及它的ancestor logger的appenders。這就是術語"appender additivity"的含義。

    然而,logger C的一個ancestor logger P,它的附加標志被設為false,那么C的輸出將被定位到所有C的appender,以及從它開始上溯到P的所有ancestor logger的appender。

    Loggers的附加標記(additivity flag)默認為true。

    下表是一個例子。

    Logger
    Name
    Added
    Appenders
    Additivity
    Flag
    Output Targets
    Comment

    root
    A1
    not applicable
    A1
    The root logger is anonymous but can be accessed with the Logger.getRootLogger() method. There is no default appender attached to root.

    x
    A-x1, A-x2
    true
    A1, A-x1, A-x2
    Appenders of "x" and root.

    x.y
    none
    true
    A1, A-x1, A-x2
    Appenders of "x" and root.

    x.y.z
    A-xyz1
    true
    A1, A-x1, A-x2, A-xyz1
    Appenders in "x.y.z", "x" and root.

    security
    A-sec
    false
    A-sec
    No appender accumulation since the additivity flag is set to false.

    security.access
    none
    true
    A-sec
    Only appenders of "security" because the additivity flag in "security" is set to false.


    經常,用戶希望自定義不但輸出源,而且定義輸出格式。這個是通過在一個appender上附加一個layout來完成的。layout是負責根據用戶的希望來格式化日志請求。而appender是負責發送格式化的輸出到它的目的地。PatternLayout,作為Log4j標準版中的一部分,讓用戶指以類似C語言的printf方法的格式來指定日志的輸出格式。

    例如,轉化模式為"%r [%t] %-5p %c - %m%n" 的PatternLayout 將輸出類似如下的信息:

    176 [main] INFO org.foo.Bar - Located nearest gas station.

    第一個欄位是自從程序開始后消逝的毫秒數。

    第二個欄位是做出日志的線程。

    第三個欄位是log的級別。

    第四個欄位是日志請求相關的logger的名字。而"-"后的文字是信息的表述。

    Log4j將根據用戶定義的公式來修飾日志信息的內容。例如,如果你經常需要記錄Oranges,一個在你當前的項目被用到的對象類型,那么你可以注冊一個OrangeRenderer ,它將在一個orange需要被記錄時被調用。

    對象渲染類似的類的結構繼承。例如,假設oranges是fruits,如果你注冊了一個FruitRenderer,所有的水果包括oranges將被FruitRenderer所渲染。除非你注冊了一個orange。

    對象渲染必須實現ObjectRenderer接口。

    4. 配置

    插入日志請求到應用程序的代碼中需要大量的預先計劃和最終努力。觀察顯示大約4%的代碼是用來輸出的。

    因此,大小適度的程序都被嵌入有成千個日志輸出語句。為了以無需手工的方式管理這些日志的輸出狀態,給日志輸出以編號和規范變得勢在必行。

    Log4j在程序中有充分的可配置性。然而,用配置文件配置Log4j具有更大的彈性。目前,它的配置文件支持xml和java properties(key=value)文件兩種格式。

    讓我們以一個例子來演示它是如何做的。假定有一個用了Log4j的程序MyApp。


    import com.foo.Bar;

    // Import Log4j classes.

    import org.apache.Log4j.Logger;

    import org.apache.Log4j.BasicConfigurator;

    public class MyApp {

    // Define a static logger variable so that it references the

    // Logger instance named "MyApp".

    static Logger logger = Logger.getLogger(MyApp.class);

    public static void main(String[] args) {

    // Set up a simple configuration that logs on the console.

    BasicConfigurator.configure();

    logger.info("Entering application.");

    Bar bar = new Bar();

    bar.doIt();

    logger.info("Exiting application.");

    }

    }

    MyApp以引入Log4j的相關類開始,接著它定義了一個靜態logger變量,并給予值為"MyApp"類的全路徑名稱。

    MYApp用了定義在包com.foo中的類Bar.

    package com.foo;

    import org.apache.Log4j.Logger;

    public class Bar {

    static Logger logger = Logger.getLogger(Bar.class);

    public void doIt() {

    logger.debug("Did it again!");

    }

    }

    調用BasicConfigurator.configure()方法創建了一個相當簡單的Log4j的設置。它加入一

    個ConsoleAppender到根logger。輸出將被采用了"%-4r [%t] %-5p %c %x - %m%n"模式

    的PatternLayout所格式化。

    注意,根logger默認被分配了Level.DEBUG的級別。

    MyApp的輸出為:

    0 [main] INFO MyApp - Entering application.

    36 [main] DEBUG com.foo.Bar - Did it again!

    51 [main] INFO MyApp - Exiting application.

    隨后的圖形描述了在調用BasicConfigurator.configure()方法后MyApp的對象圖。





    一邊要提醒的是,Log4j的子logger只連接到已經存在的它們的父代。特別的是,名為

    com.foo.bar的logger是直接連接到根logger,而不是圍繞著沒用的com或com.foo

    logger。這顯著的提高了程序性能并且減少的內存占用。

    MyApp類配置Log4j是通過調用BasicConfigurator.configure 方法。其它的類僅僅

    需要引入org.apache.Log4j.Logger 類,找到它們希望用的logger,并且用它就行。

    以前的例子通常輸出同樣的日志信息。幸運的是,修改MyApp是容易的,以便日志輸

    出可以在運行時刻被控制。這里是一個小小修改的版本。



    import com.foo.Bar;

    import org.apache.Log4j.Logger;

    import org.apache.Log4j.PropertyConfigurator;

    public class MyApp {

    static Logger logger = Logger.getLogger(MyApp.class.getName());

    public static void main(String[] args) {

    // BasicConfigurator replaced with PropertyConfigurator.

    PropertyConfigurator.configure(args[0]);

    logger.info("Entering application.");

    Bar bar = new Bar();

    bar.doIt();

    logger.info("Exiting application.");

    }

    }

    修改后的 MyApp通知程序調用PropertyConfigurator()方法解析一個配置文件,并且根

    據這個配置文件來設置日志。

    這里是一個配置文件的例子,它將產生同以前BasicConfigurator 基本例子一樣

    的輸出結果。

    # Set root logger level to DEBUG and its only appender to A1.

    Log4j.rootLogger=DEBUG, A1

    # A1 is set to be a ConsoleAppender.

    Log4j.appender.A1=org.apache.Log4j.ConsoleAppender

    # A1 uses PatternLayout.

    Log4j.appender.A1.layout=org.apache.Log4j.PatternLayout

    Log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

    假設我們不在對com.foo包的任何類的輸出感興趣的話,隨后的配置文件向我們展示

    了實現這個目的的方法之一。

    Log4j.rootLogger=DEBUG, A1

    Log4j.appender.A1=org.apache.Log4j.ConsoleAppender

    Log4j.appender.A1.layout=org.apache.Log4j.PatternLayout

    # Print the date in ISO 8601 format

    Log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n

    # Print only messages of level WARN or above in the package com.foo.

    Log4j.logger.com.foo=WARN

    以這個配置文件配置好的MyApp將輸出如下:

    2000-09-07 14:07:41,508 [main] INFO MyApp - Entering application.

    2000-09-07 14:07:41,529 [main] INFO MyApp - Exiting application.

    當logger com.foo.bar沒有被分配一個級別,它將從com.foo繼承,在配置文件中

    它被設置了WARN的級別。在Bar.doIt方法中定義的log為DEBUG級別,低于WARN,

    因此doIt() 方法的日志請求被禁用。

    這里是另外一個配置文件,它使用了多個appenders.

    Log4j.rootLogger=debug, stdout, R

    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

    Log4j.appender.R=org.apache.Log4j.RollingFileAppender

    Log4j.appender.R.File=example.log

    Log4j.appender.R.MaxFileSize=100KB

    # Keep one backup file

    Log4j.appender.R.MaxBackupIndex=1

    Log4j.appender.R.layout=org.apache.Log4j.PatternLayout

    Log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n

    以這個配置文件調用加強了的MyApp類將輸出如下信息.

    INFO [main] (MyApp2.java:12) - Entering application.

    DEBUG [main] (Bar.java:8) - Doing it again!

    INFO [main] (MyApp2.java:15) - Exiting application.

    另外,因為根logger有被分配第二個appender,所以輸出也將被定向到example.log文件。

    這個文件大小達到100kb時將自動備份。備份時老版本的example.log文件自動被移到

    文件example.log.1中。

    注意我們不需要重新編譯代碼就可以獲得這些不同的日志行為。我們一樣可以容易

    的使日志輸出到UNIX Syslog daemon, 重定向所有的com.foo到NT Event logger,

    或者轉發日志到一個遠程的Log4j服務器,它根據本地server的策略來進行日志輸出。例

    如轉發日志事件到第二個Log4j服務器.



    5. 默認的初始化過程
    Log4j類庫不對它的環境做任何假設。特別是沒有默認的Log4j appender。在一些特別

    的有著良好定義的環境下,logger的靜態inializer將嘗試自動的配置Log4j。

    java語言的特性保證類的靜態initializer當且僅當裝載類到內存之時只會被調用一次。

    要記住的重要一點是,不同的類裝載器可能裝載同一個類的完全不同的拷貝。

    這些同樣類的拷貝被虛擬機認為是完全不相干的。

    默認的initialization是非常有用的,特別是在一些應用程序所依靠的運行環境被準確的

    定位的情況下。例如,同一樣的應用程序可以被用做一個標準的應用程序,或一個

    applet,或一個在web-server控制下的servlet。

    準確的默認的initialization原理被定義如下:

    1.設置系統屬性Log4j.defaultInitOverride為"false"以外的其它值,那么Log4j將

    跳過默認的initialization過程。

    2.設置資源變量字符串給系統屬性Log4j.configuration。定義默認initialization

    文件的最好的方法是通過系統屬性Log4j.configuration。萬一系統屬性

    Log4j.configuration沒有被定義,那么設置字符串變量resource 給它的默認值

    Log4j.properties。

    3.嘗試轉換resource 變量為一個URL。

    4.如果變量resource的值不能被轉換為一個URL,例如由于MalformedURLException違

    例,那么通過調用

    org.apache.Log4j.helpers.Loader.getResource(resource, Logger.class) 方法從

    classpath中搜索resource,它將返回一個URL,并通知"Log4j.properties"的值是一個錯

    誤的URL。

    看See Loader.getResource(java.lang.String) 查看搜索位置的列表。

    5.如果沒有URL被發現,那么放棄默認的initialization。否則用URL配置Log4j。

    PropertyConfigurator將用來解析URL,配置Log4j,除非URL以".xml"為結尾。

    在這種情況下的話DOMConfigurator將被調用。你可以有機會定義一個自定義的

    configurator。

    系統屬性Log4j.configuratorClass 的值取自你的自定義的類名的全路徑。

    你自定義的configurator必須實現configurator接口。



    6. 配置范例
    6.1 Tomcat下的初始化
    默認的Log4j initialization典型的應用是在web-server 環境下。在tomcat3.x和tomcat4.x

    下,你應該將配置文件Log4j.properties放在你的web應用程序的WEB-INF/classes 目錄

    下。

    Log4j將發現屬性文件,并且以此初始化。這是使它工作的最容易的方法。

    你也可以選擇在運行tomcat前設置系統屬性Log4j.configuration 。對于tomcat 3.x,

    TOMCAT_OPTS 系統變量是用來設置命令行的選項。對于tomcat4.0,用系統環境變

    量CATALINA_OPTS 代替了TOMCAT_OPTS。

    Example 1

    UNIX 命令行

    export TOMCAT_OPTS="-DLog4j.configuration=foobar.txt"

    告訴Log4j用文件foobar.txt作為默認的配置文件。這個文件應該放在WEB-INF/classes

    目錄下。這個文件將被PropertyConfigurator所讀。每個web-application將用不同的默認

    配置文件,因為每個文件是和它的web-application 相關的。

    Example 2

    UNIX 命令行

    export TOMCAT_OPTS="-DLog4j.debug -DLog4j.configuration=foobar.xml"

    告訴Log4j輸出Log4j-internal的調試信息,并且用foobar.xml作為默認的配置文件。

    這個文件應該放在你的web-application的WEB-INF/classes 目錄下。因為有.xml的

    擴展名,它將被DOMConfigurator所讀。每個web-application將用不同的默認

    配置文件。因為每個文件都和它所在的web-application 相關的。

    Example 3

    UNIX 命令行

    set TOMCAT_OPTS=-DLog4j.configuration=foobar.lcf -DLog4j.configuratorClass=com.foo.BarConfigurator

    告訴Log4j用文件foobar.lcf作為默認的配置文件。這個文件應該放在你的

    web-application的WEB-INF/classes 目錄下。因為定義了Log4j.configuratorClass 系統屬

    性,文件將用自定義的com.foo.barconfigurator類來解析。每個web-application將用不

    同的默認配置文件。因為每個文件都和它所在的web-application 相關的。

    Example 4

    UNIX 命令行

    set TOMCAT_OPTS=-DLog4j.configuration=file:/c:/foobar.lcf

    告訴Log4j用文件foobar.lcf作為默認的配置文件。這個配置文件用URL file:/c:/foobar.lcf

    定義了全路徑名。這樣同樣的配置文件將被所有的web-application所用。

    不同的web-application將通過它們自己的類裝載器來裝載Log4j。這樣,每個Log4j的環

    境將獨立的運作,而沒有任何的相互同步。例如:在多個web-application中定義了

    完全相同的輸出源的FileAppenders將嘗試寫同樣的文件。結果好象是缺乏安全性的。

    你必須確保每個不同的web-application的Log4j配置沒有用到同樣的系統資源。



    6.2 Servlet 的初始化
    用一個特別的servlet來做Log4j的初始化也是可以的。如下是一個例子:

    package com.foo;

    import org.apache.Log4j.PropertyConfigurator;

    import javax.servlet.http.HttpServlet;

    import javax.servlet.http.HttpServletRequest;

    import javax.servlet.http.HttpServletResponse;

    import java.io.PrintWriter;

    import java.io.IOException;

    public class Log4jInit extends HttpServlet {

    public void init() {

    String prefix = getServletContext().getRealPath("/");

    String file = getInitParameter("Log4j-init-file");

    // if the Log4j-init-file is not set, then no point in trying

    if(file != null) {

    PropertyConfigurator.configure(prefix+file);

    }

    }

    public void doGet(HttpServletRequest req, HttpServletResponse res) {

    }

    }

    在web.xml中定義隨后的servlet為你的web-application。

    <servlet>

    <servlet-name>Log4j-init</servlet-name>

    <servlet-class>com.foo.Log4jInit</servlet-class>

    <init-param>

    <param-name>Log4j-init-file</param-name>

    <param-value>WEB-INF/classes/Log4j.lcf</param-value>

    </init-param>

    <load-on-startup>1</load-on-startup>

    </servlet>

    寫一個初始化的servlet是最有彈性的初始化Log4j的方法。代碼中沒有任何限制,你可

    以在servlet的init方法中定義它。

    7. Nested Diagnostic Contexts

    在現實世界中的系統經常不得不同時處理多個客戶端請求。在這樣的一個典型的多線程的系統中,不同的線程將處理不同的客戶端。Logging特別能夠適應這種復雜的分布式的應用程序的調試和跟蹤。一個常見的區分每個客戶端所輸出的Logging的方法是為每個客戶端實例化一個新的獨立的Logger。這導致Logger的大量產生,管理的成本也超過了logging本身。

    唯一標識每個log請求是一個輕量級的技術。Neil Harrison 在名為“Patterns for Logging Diagnostic Messages”的書中描述了這個方法in Pattern Languages of Program Design 3, edited by R. Martin, D. Riehle, and F. Buschmann (Addison-Wesley, 1997).

    為了唯一標識每個請求,用戶把上下文信息推入NDC(Nested Diagnostic Context)中。

    NDC類示例如下:

    public class NDC {

    // Used when printing the diagnostic

    public static String get();

    // Remove the top of the context from the NDC.

    public static String pop();

    // Add diagnostic context for the current thread.

    public static void push(String message);

    // Remove the diagnostic context for this thread.

    public static void remove();

    }

    NDC如同一個堆棧樣管理每個線程。注意所有the org.apache.log4j.NDC 類的方法都是靜態的。假設NDC輸出被開啟,每次一個log 請求被生成時,適當的log4j組件為將要輸出log的線程包含完整的NDC堆棧。這是在沒有用戶的干預的情況下做到的,用戶只負責在NDC中定位正確的信息,通過在代碼中正確位置插入很少的push和pop方法就行了。相反的,在代碼中per-client實現方法有著很大變化。

    為了演示這個點,讓我們以一個發送內容到匿名客戶端的servlet為例。這個servlet可以在開始執行每個其他代碼前的初始化時建立NDC。上下文信息可以是客戶主機的名字和其他的請求中固有的信息。

    典型的信息包括cookies。因此,即使servlet同時為多個客戶同時提供服務,log 被同樣的代碼初始化,例如屬于同一個logger,依然可以被區別,因為每個客戶請求將有不同的NDC堆棧。與之相比,Contrast this with the complexity of passing a freshly instantiated logger to all code exercised during the client′s request。

    不過,一些詭異的程序,例如虛擬主機的web server記錄日志,不是一般的依靠虛擬主機的上下文,還要依靠軟件的組件發出請求。近來log4j的發布版本支持多層的樹形結構。這個增強允許每個虛擬主機可以處理在樹型結構中屬于它自己的logger。

    8. 優化
    一個經常引用的依靠于logging的參數是可以計算的花費。這是一個合理的概念,一個適度的應用程序可能產生成千上萬個日志請求。許多努力花在測量和調試logging的優化上。Log4j要求快速和彈性:速度最重要,彈性是其次。

    用戶應該注意隨后的優化建議。

    1.日志為禁用時,日志的優化。

    當日志被徹底的關閉,一個日志請求的花費等于一個方法的調用加上整數的比較時間。

    在233mhz的Pentium II 機器上這個花費通常在5-50納秒之間。

    然而,方法調用包括參數構建的隱藏花費。

    例如,對于logger cat,

    logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));

    引起了構建信息參數的花費,例如,轉化整數i和entry[i]到一個string,并且連接中間字符串,不管信息是否被輸出。這個參數的構建花費可能是很高,它主要決定于被調用的參數的大小。

    避免參數構建的花費應如下,

    if(logger.isDebugEnabled() {

    logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));

    }

    如果logger的debug被關閉這將不會招致參數構建的花費。另一方面,如果logger是debug的話,它將產生兩次判斷 logger是否能用的花費。一次是在debugenabled,一次是debug。這是無關緊要的,因為判斷日志的能用 只占日志實際花費時間的約1%。

    在Log4j里,日志請求在Logger 類的實例里。Logger 是一個類,而不是一個接口。這大量的減少了在方法調用上的彈性化的花費。

    當然用戶采用預處理或編譯時間技術去編譯出所有的日志聲明。這將導致完美的執行成效。然而因為二進制應用程序不包括任何的日志聲明的結果,日志不可能對那個二進制程序開啟。以我的觀點,以這種較大的代價來換取較小的性能優化是不值得的。

    2。當日志狀態為啟用時,日志的優化。

    這是本質上的優化logger的層次。當日志狀態為開,Log4j依然需要比較請求的級別與logger的級別。然而, logger可能沒有被安排一個級別;它們將從它們的father繼承。這樣,在繼承之前,logger可能需要搜索它的ancestor。

    這里有一個認真的努力使層次的搜索盡可能的快。例如,子logger僅僅連接到它的存在的father logger。

    在先前展示的BasicConfigurator 例子中,名為com.foo.bar 的logger是連接到跟根logger,因此繞過 了不存在的logger com和com.foo。這將顯著的改善執行的速度,特別是解析logger的層結構時。

    典型的層次結構的解析的花費是logger徹底關閉時的三倍。

    3.日志信息的輸出時,日志的優化。

    這是主要花費在日志輸出的格式化和發送它到它的輸出源上。這里我們再一次的付出努力以使格式化執行的盡可能快。同appender一樣。實際上典型的花費大約是100-300毫秒。

    詳情看org.apache.log4.performance.Logging。

    雖然Log4j有許多特點,但是它的第一個設計目標還是速度。一些Log4j的組件已經被重寫過很多次以改善性能。不過,投稿者經常提出了新的優化。你應該滿意的知道,以SimpleLayout的配置執行測試已經展示了Log4j的輸出同System.out.println一樣快。

    9. 總結
    Log4j是一個用java寫成的流行的日志包。一個它與眾不同的特點是在logger中的繼承的概念。用logger的繼承可以以任意的間隔控制日志的狀態輸出。這個減少了體積和最小化日志的代價。

    易管理性是Log4j API的優點之一。只要日志定義被加入到代碼中,它們就可以用配置文件來控制。它們可以有選擇的被禁用,并且發送到不同的多個輸出源上,以用戶選擇好的格式。

    Log4j的包被設計成,不需花費沉重的執行代價就可以保留它們在產品中。

    posted on 2006-04-06 23:18 zeroone0 閱讀(684) 評論(0)  編輯  收藏 所屬分類: 編程

    主站蜘蛛池模板: 337P日本欧洲亚洲大胆精品| 亚洲欧洲综合在线| 爱爱帝国亚洲一区二区三区| 女人被男人躁的女爽免费视频| 亚洲伊人精品综合在合线| 亚欧在线精品免费观看一区| 在线免费观看亚洲| 亚洲免费在线视频播放| 亚洲av午夜精品无码专区| 女人18毛片水真多免费看| 亚洲国产精品日韩av不卡在线| 国产国产人免费人成免费视频| 国产成人va亚洲电影| 亚洲成AV人在线观看网址| 国产精品极品美女自在线观看免费 | 国产大片线上免费观看| 亚洲乱妇老熟女爽到高潮的片 | 国产羞羞的视频在线观看免费| 精品亚洲一区二区| 亚洲精品在线免费看| 亚洲欧美成人综合久久久| 亚洲精品和日本精品| 小日子的在线观看免费| 久久精品国产亚洲αv忘忧草| 天天摸夜夜摸成人免费视频| 男女交性无遮挡免费视频| 亚洲AV日韩精品久久久久久| 青娱分类视频精品免费2| 久久亚洲色WWW成人欧美| 亚洲人成影院在线无码按摩店 | 天天摸天天操免费播放小视频| 一级毛片免费在线观看网站| 亚洲人成电影福利在线播放 | 国产亚洲精品影视在线产品| 在免费jizzjizz在线播| MM1313亚洲国产精品| 久久精品国产精品亚洲蜜月| a级毛片无码免费真人| 香蕉免费一级视频在线观看| 亚洲人成电影网站| 亚洲午夜精品第一区二区8050|