1. 程式人生 > >Java日誌框架——JCL

Java日誌框架——JCL

JCL,全稱為"Jakarta Commons Logging",也可稱為"Apache Commons Logging"。

一、JCL原理

1、基本原理

JCL這個日誌框架跟Log4J,Java Logging API等日誌框架不同。JCL採用了設計模式中的“介面卡模式”,它對外提供統一的介面,然後在適配類中將對日誌的操作委託給具體的日誌框架,比如Log4J,Java Logging API等。

在JCL中對外有兩個統一的介面,分別是Log和LogFactory。

Log的繼承體系如圖1:

               圖1


LogFactory的繼承體系如圖2:

                                                          圖2


Log4JLogger,Jdk14Logger等是適配類。

在Log4JLogger類中,包含有"org.apache.log4j.Logger"類,即Log4J中的Logger類,因而對Log4JLogger類中的日誌操作方法的呼叫會被委託給"org.apache.log4j.Logger"類執行
在Jdk14Logger類中,包含有"java.util.logging.Logger"類,即Java Logging API中的Logger類,因而對Jdk14Logger類中的日誌操作方法的呼叫會被委託給"java.util.logging.Logger"類執行

2、具體載入步驟

在執行以下Java程式碼語句的時候,經歷了哪些步驟?

Log log = LogFactory.getLog(Main.class);
log.error("Hello World");

1)通過檢視原始碼可以發現,執行"LogFactory.getLog(Main.class)"語句的時候,最終是執行LogFactoryImp類中的discoverLogImplementation方法,在該方法中有如下程式碼語句:
for(int i = 0; i < classesToDiscover.length && result == null; ++i)
 {
        result = this.createLogFromClass(classesToDiscover[i], logCategory, true);
}

其中classesToDiscover的值如圖3所示:

                                                                                        圖3



這個過程就是依次去判斷類路徑中是否存在Log4J依賴,JDK依賴等。就是經常說的JCL執行時動態查詢具體日誌框架的過程。
2)在1)中discoverLogImplementation方法找到具體的日誌框架依賴之後,會去生成相應的介面卡類例項。比如找到了Log4J日誌框架依賴,那麼會生成一個Log4JLogger介面卡類例項(以A來表示它),並將其返回。最後該介面卡類例項,被賦值給"Log log = LogFactory.getLog(Main.class)"中的log物件。
3)執行log.error("Hello World");語句,實際上是執行A中的error(Object message)方法,而該方法中會去委託"A中所包含的org.apache.log4j.Logger類例項"進行處理。

二、基本原理擴充套件

1、本質上說,NoOpLog和SimpleLog不是介面卡類,因為它們自身實現日誌操作功能,而不是委託給其他日誌框架。


2、關於JCL有兩個包,分別是:commons-logging:commons-logging:1.1和commons-logging:commons-logging-api:1.1
這兩者的主要差別在於前者比後者擁有更多的介面卡類
前者中的介面卡體系見圖1
後者中的介面卡體系見圖4

                                                               圖4



3、在commons-logging:commons-logging:1.1和commons-logging:commons-logging-api:1.1的pom.xml檔案中,可以發現它們有對具體日誌框架的依賴,比如在commons-logging:commons-logging:1.1的pom.xml中有如下片段:

<dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.12</version>
</dependency>
<dependency>
      <groupId>avalon-framework</groupId>
      <artifactId>avalon-framework</artifactId>
      <version>4.1.3</version>
</dependency>
即包含有對Log4J和avalon-framework(也是一個具體的日誌框架)的依賴。其實想想也是如此,因為JCL中含有Log4JLogger,Jdk14Logger等介面卡類,在這些介面卡類中含有對對應的具體的日誌框架的依賴,比如在Log4JLogger類中,有如下片段:
package org.apache.commons.logging.impl;

import java.io.Serializable;
import org.apache.commons.logging.Log;
import org.apache.log4j.Logger;
import org.apache.log4j.Priority;

public class Log4JLogger implements Log, Serializable {}
以上這點表明,在專案中,我們只要包含了對commons-logging:commons-logging:1.1或者commons-logging:commons-logging-api:1.1的依賴,就會包含所有對它們所支援的具體日誌框架的依賴。因而在JCL執行時動態查詢具體日誌框架的過程中,能夠找到所有所支援的具體日誌框架,根據具體的查詢演算法,選定某一個返回。
但是為了更加清晰準確,我們應該還是得在專案中包含對具體日誌框架的依賴比較好,比如要使用“JCL+Log4J”的組合方案,那麼在專案的pom.xml中,應該包含以下片段:
<dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1</version>
</dependency>
<dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
</dependency>

4、在JCL中一般情況下,需要有兩個配置檔案,一個是JCL自身的,另外一個是具體日誌框架的。比如在JCL中,具體的日誌框架使用Log4J,那麼需要有"commons-logging.properties"和"log4j.properties"這兩個檔案

5、由上述第3點可以知道,最終使用的具體日誌框架由查詢演算法決定,這降低了我們對日誌框架使用的控制度。我們也可以通過JCL的配置檔案,即"commons-logging.properties",來明確指定最終使用的具體日誌框架,達到精準控制定義的目標。具體是配置檔案中的"org.apache.commons.logging.Log"屬性。
比如在"commons-logging.properties"檔案中,配置
org.apache.commons.logging.Log = org.apache.commons.logging.impl.Log4JLogger

那麼顯式指定使用Log4J這個具體日誌框架,然後也生成"org.apache.commons.logging.impl.Log4JLogger"的一個例項

三、JCL如何使用的具體例子

1、JCL+Log4J

1.1、專案中的pom.xml配置

<dependencies>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
</dependencies>

1.2、commons-logging.properties和log4j.properties兩個檔案的內容

"commons-logging.properties"檔案內容如下:

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

"log4j.properties"檔案內容如下:

# Root logger option
log4j.rootLogger=INFO, stdout
 
# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
1.3、Java程式碼
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


public class Main {
    public static void main(String[] args) {
        Log log = LogFactory.getLog(Main.class);
        log.error("Hello World");
        System.out.println(log.getClass());
    }
}

1.4、輸出結果

如圖5

                                                      圖5


2、JCL+Log4J

2.1、專案中的pom.xml配置

<dependencies>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
</dependencies>

2.2、commons-logging.properties和log4j.properties兩個檔案的內容

"commons-logging.properties"檔案內容如下:

org.apache.commons.logging.Log = org.apache.commons.logging.impl.Log4JLogger
"log4j.properties"檔案不存在


2.3、Java程式碼

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


public class Main {
    public static void main(String[] args) {
        Log log = LogFactory.getLog(Main.class);
        log.error("Hello World");
        System.out.println(log.getClass());
    }
}

2.4、輸出結果

如圖6

                                                         圖6


3、JCL+Java Logging API

3.1、專案中的pom.xml配置

<dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1</version>
</dependency>
<!--對JDK的依賴無需在pom.xml中配置-->

3.2、commons-logging.propertie和logging.properties兩個檔案的內容

"commons-logging.properties"檔案內容如下:

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

"logging.properties"檔案是Java Logging API預設的配置檔名稱,預設路徑是JDK_HOME/jre/lib/logging.properties

3.3、Java程式碼

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


public class Main {
    public static void main(String[] args) {
        Log log = LogFactory.getLog(Main.class);
        log.error("Hello World");
        System.out.println(log.getClass());
    }
}

3.4、輸出結果

如圖7

                                                       圖7


四、其他
1、在專案中使用JCL的好處是降低與具體日誌框架的耦合,可以靈活改變使用的具體日誌框架
2、經典的日誌框架組合為:JCL+Log4J

3、Spring專案中就選用了JCL框架

參考文獻:

[1]http://commons.apache.org/proper/commons-logging/guide.html

[2]http://www.javapractices.com/topic/TopicAction.do?Id=143