java日志門面之JCL和SLF4J詳解
前言
什么時(shí)日志門面?
隨著系統(tǒng)開發(fā)的進(jìn)行,可能會(huì)更新不同的日志框架,造成當(dāng)前系統(tǒng)中存在不同的日志依賴,讓我們難以統(tǒng)一的管理和控制。借鑒JDBC的思想,為日志系統(tǒng)也提供一套門面,那么我們就可以面向這些接口規(guī)范來開發(fā),避免了直接依賴具體的日志框架。這樣我們的系統(tǒng)在日志中,就存在了日志的門面和日志的實(shí)現(xiàn)。
常見的日志框架及日志門面
- 常見日志門面:JCL、slf4j
- 常見日志實(shí)現(xiàn):JUL、log4j、logback、log4j2
- 框架誕生順序:log4j --> JUL -->
JCL-->slf4j--> logback --> log4j2
日志門面和日志實(shí)現(xiàn)的關(guān)系

一、JCL
1、JCL簡(jiǎn)介
- 全稱為
Jakarta Commons Logging,是Apache提供的一個(gè)通用日志API common-logging會(huì)通過動(dòng)態(tài)查找的機(jī)制,在程序運(yùn)行時(shí)自動(dòng)找出log4j,或者jdk自帶的jul- 使用它的好處就是,代碼依賴是common-logging而非log4j的API, 避免了和具體的日志API直接耦合,在有必要時(shí),可以更改日志實(shí)現(xiàn)的第三方庫(kù)(
不改變代碼,只修改依賴) - JCL有兩個(gè)基本的抽象類:Log(日志記錄器),LogFactory(日志工廠負(fù)責(zé)創(chuàng)建Log實(shí)例)
2、快速入門
jcl依賴
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class JCLTest {
@Test
public void test01() {
Log log = LogFactory.getLog(JCLTest.class);
log.info("info");
}
}
只導(dǎo)入commons-logging的輸出結(jié)果
- 此時(shí)沒有任何第三方日志框架,我們使用的就是jdk自帶的
JUL

導(dǎo)入commons-logging并添加log4j依賴和log4j.properties配置文件輸出結(jié)果
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
#配置根節(jié)點(diǎn)logger
log4j.rootLogger=info,console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%-10p] [%t] [%d{yyyy-MM-dd HH:mm:ss:SSS}] [%l] %m%n

3、 JCL原理
- 通過LogFactory動(dòng)態(tài)加載Log實(shí)現(xiàn)類
- Jdk14Logger就是jdk自帶JUL日志框架(因?yàn)镴UL從jdk1.4開始提供)
- SimpleLog是日志門面JCL自帶日志,功能簡(jiǎn)單一般不用

- 日志門面創(chuàng)建公共接口org.apache.commons.logging.Log
- 日志實(shí)現(xiàn)類Logger實(shí)現(xiàn)接口Log,這樣對(duì)外暴露的Log不變,只需要?jiǎng)討B(tài)加載不同的Logger
- 如果只導(dǎo)入JCL門面,不導(dǎo)入其他日志實(shí)現(xiàn),那么日志實(shí)現(xiàn)為jdk自帶JUL
- 如果導(dǎo)入JCL門面,并導(dǎo)入log4j依賴,那么日志實(shí)現(xiàn)為log4j,log4j優(yōu)先級(jí)最高

二、SLF4J
1、SLF4J簡(jiǎn)介
- 簡(jiǎn)單日志門面(Simple Logging Facade For Java)
SLF4J主要是為了給Java日志訪問提供一套標(biāo)準(zhǔn)、規(guī)范的API框架 - 主要意義在于
提供接口,具體的實(shí)現(xiàn)可以交由其他日志框架,例如log4j和logback等 - slf4j自己也提供了功能較為簡(jiǎn)單的實(shí)現(xiàn),但是一般很少用到
- SLF4J最重要的兩個(gè)功能就是對(duì)于
日志框架的綁定以及日志框架的橋接
2、快速入門
slf4j依賴
<!--slf4j 核心依賴-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!--slf4j 自帶的簡(jiǎn)單日志實(shí)現(xiàn) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
</dependency>
- slf4j-api只提供api,具體日志實(shí)現(xiàn)由slf4j-simple提供
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SLF4JTest {
@Test
public void test01(){
Logger logger = LoggerFactory.getLogger(SLF4JTest.class);
logger.trace("trace信息");
logger.debug("debug信息");
logger.info("info信息");
logger.warn("warn信息");
logger.error("error信息");
}
}
輸出結(jié)果:

2.1、輸出動(dòng)態(tài)信息
- Logger方法:
public void info(String format, Object... arguments);
@Test
public void test02() {
Logger logger = LoggerFactory.getLogger(SLF4JTest.class);
String name = "zs";
int age = 23;
// logger.info("學(xué)生信息-姓名:"+name+";年齡:"+age); 字符串拼接,效率低
// logger.info("學(xué)生信息-姓名:{},年齡:{}",new Object[]{name,age}); 老方式,代碼冗余
logger.info("學(xué)生信息-姓名:{},年齡:{}", name, age);// 新方式,簡(jiǎn)單
}
輸出結(jié)果:

2.2、異常信息的處理
- Logger方法:
public void info(String msg, Throwable t);
@Test
public void test03() {
Logger logger = LoggerFactory.getLogger(SLF4JTest.class);
try {
Class.forName("aaa");
} catch (ClassNotFoundException e) {
// 打印棧追蹤信息
// e.printStackTrace();
logger.info("具體錯(cuò)誤是:", e);
}
}
輸出結(jié)果:

3、綁定日志的實(shí)現(xiàn)
使用slf4j綁定日志的流程
- 添加slf4j-api的依賴
- 使用slf4j的API在項(xiàng)目中進(jìn)行統(tǒng)一的日志記錄
- 綁定具體的日志實(shí)現(xiàn)框架
- 綁定已經(jīng)實(shí)現(xiàn)了slf4j的日志框架,直接添加對(duì)應(yīng)依賴
- 綁定沒有實(shí)現(xiàn)slf4j的日志框架,先添加日志的適配器,再添加實(shí)現(xiàn)類的依賴
- slf4j有且
僅有一個(gè)日志實(shí)現(xiàn)框架的綁定(如果出現(xiàn)多個(gè)默認(rèn)使用第一個(gè)依賴日志實(shí)現(xiàn))
使用slf4j綁定日志的原理
public class StaticLoggerBinder implements LoggerFactoryBinder- 這個(gè)類負(fù)責(zé)靜態(tài)
org.slf4j.LoggerFactory類與相應(yīng)的日志實(shí)現(xiàn)ILoggerFactory類綁定 - 只要有這個(gè)類就可以實(shí)現(xiàn)slf4j門面+對(duì)應(yīng)日志實(shí)現(xiàn),包名都是org.slf4j.impl
3.1、slf4j實(shí)現(xiàn)slf4j-simple和logback
slf4j-simple和logback都是slf4j門面出現(xiàn)后才有的日志實(shí)現(xiàn),所以這兩生來就有自己的StaticLoggerBinder類。

依賴
<!--slf4j 核心依賴-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- logback依賴 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!--slf4j 自帶的簡(jiǎn)單日志實(shí)現(xiàn) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
</dependency>
導(dǎo)包
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
- 如果有多個(gè)日志實(shí)現(xiàn)的話,會(huì)出紅色警告,默認(rèn)使用
先導(dǎo)入的實(shí)現(xiàn) - 無論使用slf4j自己的slf4j-simple還是logback,導(dǎo)包都不會(huì)變,都用的以上兩個(gè)
@Test
public void test04(){
Logger logger = LoggerFactory.getLogger(SLF4JTest.class);
logger.trace("trace信息");
logger.debug("debug信息");
logger.info("info信息");
logger.warn("warn信息");
logger.error("error信息");
}
輸出結(jié)果:

3.2、slf4j綁定適配器實(shí)現(xiàn)log4j
- 由于log4j是在
slf4j之前出品的日志框架實(shí)現(xiàn),所以并沒有遵循slf4j的API規(guī)范 - 之前集成的logback,是
slf4j之后出品的日志框架實(shí)現(xiàn),就是按照slf4j的標(biāo)準(zhǔn)指定的API,所以我們導(dǎo)入依賴就能用 - 如果想要使用slf4j門面,需要綁定一個(gè)
適配器slf4j-log4j12
依賴
<!--slf4j 核心依賴-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!--
導(dǎo)入log4j適配器依賴
slf4j-log4j12依賴的slf4j-api,slf4j-api可以不用重復(fù)導(dǎo)入依賴
-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
導(dǎo)包
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
@Test
public void test05(){
Logger logger = LoggerFactory.getLogger(SLF4JTest.class);
logger.trace("trace信息");
logger.debug("debug信息");
logger.info("info信息");
logger.warn("warn信息");
logger.error("error信息");
}
log4j.properties
#指定日志的輸出級(jí)別與輸出端
log4j.rootLogger=info,console
# 配置appender輸出方式:輸出到控制臺(tái)
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%-10p] [%t] [%d{yyyy-MM-dd HH:mm:ss:SSS}] [%l] %m%n
輸出結(jié)果:

與slf4j-simple和logback的實(shí)現(xiàn)原理其實(shí)一樣,都是在相同包下org.slf4j.impl下創(chuàng)建StaticLoggerBinder類將slf4j門面與日志實(shí)現(xiàn)綁定起來。log4j需要適配器slf4j-log4j12,JUL需要適配器slf4j-jdk14

3.2、@Slf4j注解
- 源代碼
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class LogTest {
@Test
public void test() {
log.info("hello world");
}
}
- 編譯后class

4、橋接舊的日志框架
橋接解決的問題:當(dāng)系統(tǒng)中存在之前的日志API,可以通過橋接轉(zhuǎn)換到slf4j的實(shí)現(xiàn)
- 先去除之前老的日志框架的依賴
- 添加SLF4J提供的橋接組件
- 為項(xiàng)目添加SLF4J的具體實(shí)現(xiàn)
4.1、log4j日志重構(gòu)為slf4j+logback的組合
重構(gòu)前
依賴
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
log4j.properties
#配置根節(jié)點(diǎn)logger
log4j.rootLogger=trace,console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.conversionPattern=[%-10p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
導(dǎo)包
import org.apache.log4j.Logger;
@Test
public void test07(){
Logger logger = Logger.getLogger(Log4jTest.class);
logger.info("info信息");
}
- 查看Logger類源碼,屬于log4j包下的類

重構(gòu)后
- 依賴:刪除log4j依賴,添加slf4j提供的橋接組件和logback依賴
<!-- log4j相關(guān)的橋接器 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
<!-- logback依賴 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
- 導(dǎo)包:依然不變
import org.apache.log4j.Logger;
@Test
public void test08(){
Logger logger = Logger.getLogger(Log4jTest.class);
logger.info("info信息");
}
- 查看Logger類源碼,屬于log4j-over-slf4j包下的類

如此操作,看上去依賴還是org.apache.log4j.Logger,實(shí)際已不再是log4j包下的類,日志實(shí)現(xiàn)也變成了slf4j門面可以隨意搭配的方式了。
總結(jié)
到此這篇關(guān)于java日志門面之JCL和SLF4J詳解的文章就介紹到這了,更多相關(guān)java日志JCL和SLF4J內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入了解SparkSQL中數(shù)據(jù)的加載與保存
這篇文章主要為大家詳細(xì)介紹了SparkSQL中數(shù)據(jù)的加載與保存的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解下2023-11-11
Java在讀取文件內(nèi)容的時(shí)候,如何判斷出空白行的操作
這篇文章主要介紹了Java在讀取文件內(nèi)容的時(shí)候,如何判斷出空白行的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09
SpringBoot詳細(xì)講解如何創(chuàng)建及刷新Spring容器bean
前面看spring源碼時(shí)可以發(fā)現(xiàn)refresh()方法十分重要。在這個(gè)方法中會(huì)加載beanDefinition,同時(shí)創(chuàng)建bean對(duì)象。那么在springboot中有沒有使用這個(gè)refresh()方法呢2022-06-06
SpringMVC實(shí)現(xiàn)Controller的三種方式總結(jié)
這篇文章主要介紹了SpringMVC實(shí)現(xiàn)Controller的三種方式總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
mybatis-plus實(shí)現(xiàn)四種lambda表達(dá)式方式
使用了lambda表達(dá)式 可以通過方法引用的方式來使用實(shí)體字段名的操作,本文主要介紹了mybatis-plus實(shí)現(xiàn)四種lambda表達(dá)式方式,具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06
基于java Springboot實(shí)現(xiàn)教務(wù)管理系統(tǒng)詳解
這篇文章主要介紹了Java 實(shí)現(xiàn)簡(jiǎn)易教務(wù)管理系統(tǒng)的代碼,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08
java實(shí)現(xiàn)微博后臺(tái)登錄發(fā)送微博
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)微博后臺(tái)登錄發(fā)送微博的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-07-07

