commons-logging和Log4j使用總結

Apache通用日誌工具commons-logging和Log4j使用總結

 

前言

 

       在程序開發中,有時候爲了跟蹤代碼的運行情況,需要在程序代碼中加入一些打印代碼運行情況的日誌代碼,在早期的C語言中,一般都是用printf()函數來實現這些信息的輸出,在Java語言中用System..out.println()來實現控制檯信息輸出。這種輸出的信息功能非常有限,如果要輸出到文件,就很麻煩。並且,如果以後想去掉(不再輸出)這些信息,還需要手動的一行一行去改。當然,也有一種解決辦法可以實現處理這個問題,在Java中,可以這麼寫:

 
------------------------------------------------------
package org.lavasoft.test1;
 
 
public class LogUtil {
    private static boolean logSwitch=true;  //日誌開關,默認爲開
    public static void log(String smg){
        if(logSwitch){
            System.out.println(smg);
        }
    }
}
------------------------------------------------------
package org.lavasoft.test1;
 
public class ManualLogApp {
    public static void main(String[]args){
        new ManualLogApp().test();
    }
 
    public void test(){
        LogUtil.log("我在手動打日誌,呵呵!");
    }
}


通過這兩個類,就實現一個簡單的日誌工具。當你不想打印日誌的時候,可以在LogUtil中改logSwitch的值爲flase,那麼日誌打印功能就關閉了。如果你願意,你可以將這個開關放到一個配置文件中去修改。還可以讓日誌打印到文件,但是當你修改實現這個功能的時候,這個日誌工具會變得相當的複雜。

 


爲了解決上面的問題,Apache針對不同的語言平臺爲做了一系列日誌工具包,可應用於java、.net、php、c++,這些日誌包都是免費的,使用非常方便,可以極大提高編程效率。並且,Apache爲了讓衆多的日誌工具有一個相同操作方式,還實現做了一個通用日誌工具包commons-logging

 

commons-logging是爲那些需要建立在不同環境下使用不同日誌架構的組件或庫的開發者創建的,其中包括Apache Log4j以及Java log的日誌架構。把日誌信息commons-logging的Log接口,並由commons-logging在運行時決定使用哪種日誌架構。

現在,以我見,Apache通用日誌工具commons-logging和Log4j已經成爲Java日誌的標準工具。

 

下面我對Log4j(對Java平臺的)和通用日誌工具包commons-logging的應用做一個總結。

 

一、 Log4j的架構

 

Log4j系統的三大板塊:日誌寫入器、日誌輸出終端、日誌佈局模式。

 

 Logger類是日誌包的核心,Logger的名稱是大小寫敏感的,並且名稱之間有繼承關係。子名由父名做前綴,用點號“.”分隔,如x.y是x.y.z的父親Logger。

 

Logger系統中有個根logger,是所有logger的祖先,它總是存在的,並且不可以通過名字獲取,可以通過Logger.getRootLogger()來獲取。

 

獲取Logger對象的方法很多,可以參考API文檔:

static Logger getLogger(Class clazz)

          Shorthand for getLogger(clazz.getName()).

static Logger getLogger(String name)

          Retrieve a logger named accordingto the value of the name parameter.

static Logger getLogger(String name, LoggerFactory factory)

          Like getLogger(String) exceptthat the type of logger instantiated depends on the type returned by the LoggerFactory.makeNewLoggerInstance(java.lang.String)method of the factory parameter.

static Logger getRootLogger()

          Return the root logger forthe current logger repository.

 

在某對象中,用該對象所屬的類作爲參數,調用Logger.getLogger(Class clazz)以獲取logger對象被認爲是目前所知最理智的命名logger方法。

 

用同名參數調用Logger.getLogger(Class clazz)將返回同一個logger對象。Logger的創建可以按照任意順序,log4j將自動維護logger的繼承樹。

 

二、 Log4j的日誌級別(Level)

 

每個logger都有一個日誌級別,用來控制日誌的輸出。未分配級別的logger將自動繼承它最近的父logger的日誌級別。Logger的由低到高級別如下:

ALL  <  DEBUG  <  INFO  <  WARN  <  ERROR  <  FATAL  <  OFF

 

Logger的級別越低,輸出的日誌越詳細。

 

三、 Log4j的輸出終端(Appender接口)

 

Log4j提供了以下幾個實現:

 org.apache.log4j.ConsoleAppender(控制檯)

 org.apache.log4j.FileAppender(文件)

 org.apache.log4j.DailyRollingFileAppender(每天都產生一個日誌文件)

 org.apache.log4j.RollingFileAppender(文件大小達到指定尺寸時產生一個新的日誌文件,文件名稱上會自動添加數字序號。)

 org.apache.log4j.WriterAppender(將日誌信息以流的格式發送到任意指定的地方)

 

默認情況下,子logger將繼承父logger的所有appenders。

 

rootlogger擁有目標爲system.out的consoleAppender,故默認情況下,所有的logger都將繼承該appender。

 

四、 Log4j的輸出佈局模式(Layout接口)

Log4j提供Layout有以下幾種:

 org.apache.log4j.HTMLLayout(以HTML表格形式佈局)

 org.apache.log4j.PatternLayout(可以靈活地指定佈局模式)

 org.apache.log4j.SimpleLayout(包含日誌信息的級別和信息字符串)

 org.apache.log4j.TTCCLayout(包含日誌產生的時間、線程、類別等信息)

 

Log4j採用類似C語言中的printf函數的打印格式格式化日誌信息。打印參數如下:

%m:輸出代碼中指定的消息。

%p:輸出優先級。

%r:輸入自應用啓動到輸出該log信息耗費的毫秒數。

%c:輸出所屬的類目,通常就是所在類的全名。

%t:輸出產生該日誌線程的線程名。

%n:輸出一個回車換行符。Windows平臺爲“\r\n”,UNIX爲“\n”。

%d:輸出日誌時間點的日期或時間,默認格式爲ISO8601,推薦使用“%d{ABSOLUTE}”,這個輸出格式形如:“2007-05-07 18:23:23,500”,符合中國人習慣。

%l:輸出日誌事件發生的位置,包括類名、線程名,以及所在代碼的行數。

 

五、 Log4j的配置

 

在實際使用中,Log4j一般是通過配置文件配置使用的。配置文件有兩種,Java properties和XML文件。一般都選用properties文件來配置,因爲簡潔易讀。下面只介紹Java properties的配置方式。

 

對Log4j的配置就是對rootLogger和子Logger的配置。主要的配置項爲:rootLogger、輸出終端、輸出佈局模式。

 

所有的配置項都必須以log4j開頭。

 

下面給出一個配置文件的示例,用以說明配置的方法。

 

log4j.properties

-------------------------------------------------------------------------------------------------

1 ### logger的配置 ###

 2 #配置根logger

 3 log4j.rootLogger=INFO,stdout

 4 #配置子logger:org.lavasoft(在org.lavasoft包中類的日誌在沒有指定子logger名的情況下使用這個logger)

 5 log4j.logger.org.lavasoft=ERROR,file

 6 #配置子logger:org.lavasoft.test(在org.lavasoft.test包中類的日誌在沒有指定子logger名的情況下使用這個logger)

 7 log4j.logger.org.lavasoft.test=ERROR,file1,stdout

 8

 9 ### direct log messages to stdout### (標準的終端輸出)

10 #控制檯輸出

11 log4j.appender.stdout=org.apache.log4j.ConsoleAppender

12 #自定義輸出佈局

13 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

14 #輸出的格式

15 log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L- %m%n

16

17 ### direct messages to file ttt.log ### (輸入到文件ttt.log的配置)

18 #輸出到滾動文件

19 log4j.appender.file=org.apache.log4j.RollingFileAppender

20 #輸出文件最大爲10M

21 log4j.appender.file.MaxFileSize=10MB

22 #輸出文件最大序號爲10

23 log4j.appender.file.MaxBackupIndex=10

24 #輸出文件路徑

25 log4j.appender.file.File=C:/ttt.log

26 #自定義輸出佈局

27 log4j.appender.file.layout=org.apache.log4j.PatternLayout

28 #輸出格式

29 log4j.appender.file.layout.ConversionPattern=%d %-5p [%t] (%13F:%L) %3x- %m%n

30

31 ### direct messages to file ttt.log ### (輸入到文件ttt1.log的配置)

32 #輸出到滾動文件

33 log4j.appender.file1=org.apache.log4j.RollingFileAppender

34 #輸出文件最大爲10M

35 log4j.appender.file1.MaxFileSize=10MB

36 #輸出文件最大序號爲10

37 log4j.appender.file1.MaxBackupIndex=10

38 #輸出文件路徑

39 log4j.appender.file1.File=C:/ttt1.log

40 #自定義輸出佈局

41 log4j.appender.file1.layout=org.apache.log4j.PatternLayout

42 #輸出格式

43 log4j.appender.file1.layout.ConversionPattern=%d %-5p [%t] (%13F:%L) %3x- %m%n

44

-------------------------------------------------------------------------------------------------

 

下面對以上的文件解說,並給出一個應用例子:

logger的配置語法爲:級別,輸入終端1,輸出終端2,…

根logger的配置項爲:log4j.rootLogger

 

子logger的配置項爲:

log4j.logger.<子logger名>

 

在上面的配置項中,配置了一個根logger和兩個子logger,名稱爲:org.lavasoft和org.lavasoft.test,通過該名稱,利用Logger.getLogger(String name)可以獲取這兩個logger對象。

 

子logger的名字一般都以包名來配置,這樣當在程序通過類名獲取logger的對象就是與本類包名相同的子logger,這樣可以方便控制某個包下面logger的輸出。

 

但是一般不建議設置子logger,一個通用的根logger足夠用了。由於繼承關係,並且可以在任何地方通過子logger的名字獲取logger對象,這樣容易導致混亂,比如在org.lavasoft包下的類直接獲取了名爲org.lavasoft.test子logger的對象,這是不合理的(雖然日誌可以打印出來),因爲如果直接在類中用Logger.getLogger(Classclazz)方式來獲取Logger對象,則獲取到的應該是名爲org.lavasoft的logger對象,而不會得到名爲org.lavasoft.test的logger對象。

 

以上的配置文件純粹是爲了說明問題才那麼寫的。

 

六、 Log4j與通用日誌包commons-logging的結合

 

其實commons-logging中默認都支持Log4j,因此只要同時加載commons-logging包和log4j包,可以不用配置即可用在應用中使用commons-logging的接口方法。

 

當然,標準的應用的是需要的配置,如果你log4j則這個配置是可選的。下面我說明如何通過配置文件來組合commons-logging和log4j。

 

配置文件內容很簡單,就指定一個日誌實現類即可,下面是個示例文件:

 

commons-logging.properties

--------------------------------------------------------------------------------

org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger

--------------------------------------------------------------------------------

 

在使用Log4j作爲日誌工具的時候,commons-logging.properties的配置可以不要,在下面例子中,你可以嘗試刪除此文件,而不影響日誌輸出。

 

log4j.properties

--------------------------------------------------------------------------------

### logger的配置 ###

#配置根logger

log4j.rootLogger=info,stdout

#配置子logger:org.lavasoft(在org.lavasoft包中類的日誌在沒有指定子logger名的情況下使用這個logger)

log4j.logger.org.lavasoft=ERROR,file

#配置子logger:org.lavasoft.test(在org.lavasoft.test包中類的日誌在沒有指定子logger名的情況下使用這個logger)

log4j.logger.org.lavasoft.test=ERROR,file1,stdout

 

### direct log messages to stdout ### (標準的終端輸出)

#控制檯輸出

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

#自定義輸出佈局

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

#輸出的格式

log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L -%m%n

 

### direct messages to file ttt.log ### (輸入到文件ttt.log的配置)

#輸出到滾動文件

log4j.appender.file=org.apache.log4j.RollingFileAppender

#輸出文件最大爲10M

log4j.appender.file.MaxFileSize=10MB

#輸出文件最大序號爲10

log4j.appender.file.MaxBackupIndex=10

#輸出文件路徑

log4j.appender.file.File=C:/ttt.log

#自定義輸出佈局

log4j.appender.file.layout=org.apache.log4j.PatternLayout

#輸出格式

log4j.appender.file.layout.ConversionPattern=%d %-5p [%t] (%13F:%L) %3x - %m%n

 

### direct messages to file ttt.log ### (輸入到文件ttt1.log的配置)

#輸出到滾動文件

log4j.appender.file1=org.apache.log4j.RollingFileAppender

#輸出文件最大爲10M

log4j.appender.file1.MaxFileSize=10MB

#輸出文件最大序號爲10

log4j.appender.file1.MaxBackupIndex=10

#輸出文件路徑

log4j.appender.file1.File=C:/ttt1.log

#自定義輸出佈局

log4j.appender.file1.layout=org.apache.log4j.PatternLayout

#輸出格式

log4j.appender.file1.layout.ConversionPattern=%d %-5p [%t] (%13F:%L) %3x -%m%n

--------------------------------------------------------------------------------

 

下面是TestLog工程的一個類HelloWorld.java,通過運行該類,可以看到日誌的輸出情況。

 

org.lavasoft.test. HelloWorld.java

--------------------------------------------------------------------------------

package org.lavasoft.test;

 

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

 

/**

 * File Name:   HelloWorld.java

 * Created by:  IntelliJ IDEA.

 * Copyright:   Copyright (c) 2003-2006

 * Company:     LavaSoft( [url]http://lavasoft.blog.51cto.com[/url])

 * Author:      leizhimin

 * Modifier:    leizhimin

 * Date Time:   2007-5-6 17:49:55

 * Readme:

 */

public class HelloWorld {

    public static void main(String args[]){

        Log log1=LogFactory.getLog(HelloWorld.class);

        log1.info("11111111通過class對象來獲取logger對象.");

        log1.error("111111111.....error");

        new HelloWorld().test();

    }

 

    public void test(){

 

        //獲取(配置文件中)名稱爲log4j.logger.org.lavasoft.test的logger對象

        Log log2=LogFactory.getLog("org.lavasoft.test");

        log2.info("2222222222222222");

        log2.error("222222HelloWorld的Error!");

 

        //獲取(配置文件中)名稱爲log4j.logger.org.lavasoft的logger對象

        Log log3=LogFactory.getLog("org.lavasoft");

        log3.info("3333333333333333");

        log3.error("33333..........errrr");

 

        //獲取當前類所在

        Log log4=LogFactory.getLog(this.getClass());

        log4.info("44444444");

        log4.error("44444444 ....error");

    }

}

--------------------------------------------------------------------------------

 

運行org.lavasoft.test.HelloWorld.java,結果如下:

22:45:27,953 ERROR HelloWorld:20 - 111111111.....error

22:45:27,953 ERROR HelloWorld:20 - 111111111.....error

22:45:27,968 ERROR test:29 - 222222HelloWorld的Error!

22:45:27,968 ERROR test:29 - 222222HelloWorld的Error!

22:45:27,968 ERROR lavasoft:34 - 33333..........errrr

22:45:27,968 ERROR HelloWorld:39 - 44444444 ....error

22:45:27,968 ERROR HelloWorld:39 - 44444444 ....error

 

打開C盤,下面有兩個日誌文件ttt.log、ttt1.log,內容如下:

ttt.log

--------------------------------------------------------------------------------

2007-05-08 22:45:27,953 ERROR [main] (HelloWorld.java:20)     - 111111111.....error

2007-05-08 22:45:27,968 ERROR [main] (HelloWorld.java:29)     - 222222HelloWorld的Error!

2007-05-08 22:45:27,968 ERROR [main] (HelloWorld.java:34)     - 33333..........errrr

2007-05-08 22:45:27,968 ERROR [main] (HelloWorld.java:39)     - 44444444 ....error

--------------------------------------------------------------------------------

 

ttt1.log

--------------------------------------------------------------------------------

2007-05-08 22:45:27,953 ERROR [main] (HelloWorld.java:20)     - 111111111.....error

2007-05-08 22:45:27,968 ERROR [main] (HelloWorld.java:29)     - 222222HelloWorld的Error!

2007-05-08 22:45:27,968 ERROR [main] (HelloWorld.java:39)     - 44444444 ....error

--------------------------------------------------------------------------------

 

從兩個日誌文件輸出信息(類、loging的行數)可以看到,通過名稱org.lavasoft.test獲取的logger打印的ERROR級別的日誌記錄在C:/ttt1.log裏面,而通過名稱org.lavasoft獲取的logger打印的ERROR級別的日誌記錄在C:/ttt.log裏面,通過LogFactory.getLog(this.getClass())和LogFactory.getLog(HelloWorld.class);記錄的日誌實質上根據該類所在的包名來獲取logger,因此實質上獲取的是離他最近的logger(離它最近的logger就是該包上定義的logger對象,名稱爲org.lavasoft.test)。

 

其實仔細研究一下輸出的logger就很容易看出其中的所以然,上面這種定義子logger的方法不推薦在項目中使用,一般只定義一個rootlogger就行了。

 

爲了交流研究方便,我把TestLog整個工程的源代碼(用IntellJ IDEA 5.12做的)打包放在附件裏,當然也可以很容易的將此源代碼加入Eclipse或者NetBean工程。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章