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,然後返回給用戶。