Java日誌框架--JCL(Jakarta Commons Logging)

JCL,全稱爲"Jakarta Commons Logging",也可稱爲"Apache Commons Logging",是Apache提供的一個通用日誌API。JCL採用了設計模式中的“適配器模式”,它是爲“所有的Java日誌實現”提供的一個統一的接口,然後在適配類中將對日誌的操作委託給具體的日誌框架,它自身也提供一個日誌的實現,但是功能非常弱(SimpleLog)。所以一般不會單獨使用它。它允許開發人員使用不同的具體日誌實現工具:Log4j,jdk自帶的日誌(JUL)

JCL有兩個基本的抽象類:Log(基本記錄器)LogFactory(負責創建Log實例)

Log的繼承體系如圖1:

LogFactory的繼承體系如圖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"類運行

《jcl 與 jul、log4j1、log4j2、logback 集成》:https://jybzjf.iteye.com/blog/2238792
《Commons-Logging 存在的 ClassLoader 問題詳解》:https://yq.aliyun.com/articles/46888

一、JCL入門

JCL+Java Logging API工程構建:

1.構建maven工程:JCL的jar包

<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>

2.創建TestDemo:JCL接口在沒有其他日誌實現的情況,會自動實現jdk自帶的日誌(JUL)

  @Test
    public void testQuick() throws Exception{
//        獲取log日誌記錄器對象
        Log log = LogFactory.getLog(JCLTest.class);
//        日誌記錄輸出
        log.info("hello jcl");
    }

JCL+Log4J工程構建:

1.使用JCL接口實現log4j日誌

首先在Maven工程中添加log4j日誌實現的jar包

        <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

2.然後創建的配置文件commons-logging.properties和log4j.properties兩個文件的內容

log4j.properties配置文件內容如下:

#配置根  Loggers控制日誌的輸出級別與日誌是否輸出
log4j.rootLogger=trace,console

#配置輸出到控制檯  Appenders是指定日誌的輸出方式
log4j.appender.console=org.apache.log4j.ConsoleAppender
#指定輸出控制檯
log4j.appender.console.Target = System.out
#指定佈局,輸出日誌的格式
log4j.appender.console.layout=org.apache.log4j.PatternLayout
#指定佈局的參數
log4j.appender.console.layout.ConversionPattern=[%-10p] %r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n

commons-logging.properties配置文件內容如下:

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

3.最後進行Demo測試,JCL會調用log4j實現,進行實現log4j的日誌記錄

  @Test
    public void testQuick() throws Exception{
//        獲取log日誌記錄器對象
        Log log = LogFactory.getLog(JCLTest.class);
//        日誌記錄輸出
        log.info("hello jcl");
    }

commons-logging 綁定日誌實現

LogFactory.getLog(JclTest.class) 的源碼如下:

public static Log getLog(Class clazz) throws LogConfigurationException {
    return getFactory().getInstance(clazz);
}

上述獲取 Log 的過程大致分成 2 個階段

  • 獲取 LogFactory 的過程 (從字面上理解就是生產 Log 的工廠)。commons-logging 默認提供的 LogFactory 實現:LogFactoryImpl
  • 根據 LogFactory 獲取 Log 的過程。commons-logging 默認提供的 Log 實現:Jdk14Logger、Log4JLogger、SimpleLog。

來看下 commons-logging 包中的大概內容:

二、我們爲什麼要使用日誌門面

1.面向接口開發,不再依賴具體的實現類。減少代碼的耦合

2.項目通過導入不同的日誌實現類,可以靈活的切換日誌框架

3.統一API,方便開發者學習和使用

4.統一配置便於項目日誌的管理

三、JCL原理

1.通過LogFactory動態加載Log實現類

2.1獲取 LogFactory 的過程

從下面幾種途徑來獲取 LogFactory

(1) 系統屬性中獲取

System.getProperty("org.apache.commons.logging.LogFactory")

(2) 使用 java 的 SPI 機制

對於 java 的 SPI 機制,詳細內容可以自行搜索,這裏不再說明。搜尋路徑如下:

META-INF/services/org.apache.commons.logging.LogFactory

簡單來說就是搜尋哪些 jar 包中含有搜尋含有上述文件,該文件中指明瞭對應的 LogFactory 實現

(3) 從 commons-logging 的配置文件

commons-logging 也是可以擁有自己的配置文件的,名字爲 commons-logging.properties,只不過目前大多數情況下,我們都沒有去使用它。如果使用了該配置文件,嘗試從配置文件中讀取屬性 "org.apache.commons.logging.LogFactory" 對應的值

(4) 默認的 org.apache.commons.logging.impl.LogFactoryImpl

LogFactoryImpl 是 commons-logging 提供的默認實現

2.2根據 LogFactory 獲取 Log 的過程

這時候就需要尋找底層是選用哪種類型的日誌

就以 commons-logging 提供的默認實現爲例,來詳細看下這個過程:

(1) 從 commons-logging 的配置文件中尋找 Log 實現類的類名

從commons-logging.properties 配置文件中尋找屬性爲 "org.apache.commons.logging.Log" 對應的 Log 類名

(2) 從系統屬性中尋找 Log 實現類的類名

System.getProperty("org.apache.commons.logging.Log")

(3) 如果上述方式沒找到,則從 classesToDiscover 屬性中尋找

classesToDiscover 屬性值如下:

private static final String[] classesToDiscover = {
    "org.apache.commons.logging.impl.Log4JLogger",
    "org.apache.commons.logging.impl.Jdk14Logger",
    "org.apache.commons.logging.impl.Jdk13LumberjackLogger",
    "org.apache.commons.logging.impl.SimpleLog"
};

3.獲取具體的日誌實現,通過查看源代碼可以發現,執行"LogFactory.getLog(Main.class)"語句的時候,最終是執行LogFactoryImp類中的discoverLogImplementation方法,在該方法中有如下代碼語句:

for(int i = 0; i < classesToDiscover.length && result == null; ++i)
 {
        result = this.createLogFromClass(classesToDiscover[i], logCategory, true);
}

它會嘗試根據上述類名,依次進行創建,如果能創建成功,則使用該 Log,然後返回給用戶。

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