java日志門(mén)面之JCL和SLF4J詳解
前言
什么時(shí)日志門(mén)面?
隨著系統(tǒng)開(kāi)發(fā)的進(jìn)行,可能會(huì)更新不同的日志框架,造成當(dāng)前系統(tǒng)中存在不同的日志依賴(lài),讓我們難以統(tǒng)一的管理和控制。借鑒JDBC的思想,為日志系統(tǒng)也提供一套門(mén)面,那么我們就可以面向這些接口規(guī)范來(lái)開(kāi)發(fā),避免了直接依賴(lài)具體的日志框架。這樣我們的系統(tǒng)在日志中,就存在了日志的門(mén)面和日志的實(shí)現(xiàn)。
常見(jiàn)的日志框架及日志門(mén)面
- 常見(jiàn)日志門(mén)面:JCL、slf4j
- 常見(jiàn)日志實(shí)現(xiàn):JUL、log4j、logback、log4j2
- 框架誕生順序:log4j --> JUL -->
JCL
-->slf4j
--> logback --> log4j2
日志門(mén)面和日志實(shí)現(xiàn)的關(guān)系
一、JCL
1、JCL簡(jiǎn)介
- 全稱(chēng)為
Jakarta Commons Logging
,是Apache
提供的一個(gè)通用日志API common-logging
會(huì)通過(guò)動(dòng)態(tài)查找
的機(jī)制,在程序運(yùn)行時(shí)自動(dòng)找出log4j,或者jdk自帶的jul- 使用它的好處就是,代碼依賴(lài)是common-logging而非log4j的API, 避免了和具體的日志API直接耦合,在有必要時(shí),可以更改日志實(shí)現(xiàn)的第三方庫(kù)(
不改變代碼,只修改依賴(lài)
) - JCL有兩個(gè)基本的抽象類(lèi):Log(日志記錄器),LogFactory(日志工廠負(fù)責(zé)創(chuàng)建Log實(shí)例)
2、快速入門(mén)
jcl依賴(lài)
<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í)沒(méi)有任何第三方日志框架,我們使用的就是jdk自帶的
JUL
導(dǎo)入commons-logging并添加log4j依賴(lài)和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原理
- 通過(guò)LogFactory動(dòng)態(tài)加載Log實(shí)現(xiàn)類(lèi)
- Jdk14Logger就是jdk自帶JUL日志框架(因?yàn)镴UL從jdk1.4開(kāi)始提供)
- SimpleLog是日志門(mén)面JCL自帶日志,功能簡(jiǎn)單一般不用
- 日志門(mén)面創(chuàng)建公共接口org.apache.commons.logging.Log
- 日志實(shí)現(xiàn)類(lèi)Logger實(shí)現(xiàn)接口Log,這樣對(duì)外暴露的Log不變,只需要?jiǎng)討B(tài)加載不同的Logger
- 如果只導(dǎo)入JCL門(mén)面,不導(dǎo)入其他日志實(shí)現(xiàn),那么日志實(shí)現(xiàn)為jdk自帶JUL
- 如果導(dǎo)入JCL門(mén)面,并導(dǎo)入log4j依賴(lài),那么日志實(shí)現(xiàn)為log4j,log4j優(yōu)先級(jí)最高
二、SLF4J
1、SLF4J簡(jiǎn)介
- 簡(jiǎn)單日志門(mén)面(Simple Logging Facade For Java)
SLF4J
主要是為了給Java日志訪問(wèn)提供一套標(biāo)準(zhǔn)、規(guī)范的API框架 - 主要意義在于
提供接口
,具體的實(shí)現(xiàn)可以交由其他日志框架,例如log4j和logback等 - slf4j自己也提供了功能較為簡(jiǎn)單的實(shí)現(xiàn),但是一般很少用到
- SLF4J最重要的兩個(gè)功能就是對(duì)于
日志框架的綁定
以及日志框架的橋接
2、快速入門(mén)
slf4j依賴(lài)
<!--slf4j 核心依賴(lài)--> <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的依賴(lài)
- 使用slf4j的API在項(xiàng)目中進(jìn)行統(tǒng)一的日志記錄
- 綁定具體的日志實(shí)現(xiàn)框架
- 綁定已經(jīng)實(shí)現(xiàn)了slf4j的日志框架,直接添加對(duì)應(yīng)依賴(lài)
- 綁定沒(méi)有實(shí)現(xiàn)slf4j的日志框架,先添加日志的適配器,再添加實(shí)現(xiàn)類(lèi)的依賴(lài)
- slf4j有且
僅有一個(gè)
日志實(shí)現(xiàn)框架的綁定(如果出現(xiàn)多個(gè)默認(rèn)使用第一個(gè)依賴(lài)日志實(shí)現(xiàn))
使用slf4j綁定日志的原理
public class StaticLoggerBinder implements LoggerFactoryBinder
- 這個(gè)類(lèi)負(fù)責(zé)靜態(tài)
org.slf4j.LoggerFactory
類(lèi)與相應(yīng)的日志實(shí)現(xiàn)ILoggerFactory
類(lèi)綁定 - 只要有這個(gè)類(lèi)就可以實(shí)現(xiàn)slf4j門(mén)面+對(duì)應(yīng)日志實(shí)現(xiàn),包名都是org.slf4j.impl
3.1、slf4j實(shí)現(xiàn)slf4j-simple和logback
slf4j-simple和logback都是slf4j門(mén)面出現(xiàn)后才有的日志實(shí)現(xiàn),所以這兩生來(lái)就有自己的StaticLoggerBinder類(lèi)。
依賴(lài)
<!--slf4j 核心依賴(lài)--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <!-- logback依賴(lài) --> <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) - 無(wú)論使用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),所以并沒(méi)有遵循slf4j的API規(guī)范 - 之前集成的logback,是
slf4j之后
出品的日志框架實(shí)現(xiàn),就是按照slf4j的標(biāo)準(zhǔn)指定的API,所以我們導(dǎo)入依賴(lài)就能用 - 如果想要使用slf4j門(mén)面,需要綁定一個(gè)
適配器slf4j-log4j12
依賴(lài)
<!--slf4j 核心依賴(lài)--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <!-- 導(dǎo)入log4j適配器依賴(lài) slf4j-log4j12依賴(lài)的slf4j-api,slf4j-api可以不用重復(fù)導(dǎo)入依賴(lài) --> <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
類(lèi)將slf4j門(mén)面與日志實(shí)現(xiàn)綁定
起來(lái)。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、橋接舊的日志框架
橋接解決的問(wèn)題:當(dāng)系統(tǒng)中存在之前的日志API,可以通過(guò)橋接轉(zhuǎn)換到slf4j的實(shí)現(xiàn)
- 先去除之前老的日志框架的依賴(lài)
- 添加SLF4J提供的橋接組件
- 為項(xiàng)目添加SLF4J的具體實(shí)現(xiàn)
4.1、log4j日志重構(gòu)為slf4j+logback的組合
重構(gòu)前
依賴(lài)
<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類(lèi)源碼,屬于log4j包下的類(lèi)
重構(gòu)后
- 依賴(lài):刪除log4j依賴(lài),添加slf4j提供的橋接組件和logback依賴(lài)
<!-- log4j相關(guān)的橋接器 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> <version>1.7.25</version> </dependency> <!-- logback依賴(lài) --> <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類(lèi)源碼,屬于log4j-over-slf4j包下的類(lèi)
如此操作,看上去依賴(lài)還是org.apache.log4j.Logger,實(shí)際已不再是log4j包下的類(lèi),日志實(shí)現(xiàn)也變成了slf4j門(mén)面可以隨意搭配的方式了。
總結(jié)
到此這篇關(guān)于java日志門(mén)面之JCL和SLF4J詳解的文章就介紹到這了,更多相關(guān)java日志JCL和SLF4J內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Boot與Kotlin 整合全文搜索引擎Elasticsearch的示例代碼
本篇文章主要介紹了Spring Boot與Kotlin 整合全文搜索引擎Elasticsearch的示例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01Java二叉搜索樹(shù)遍歷操作詳解【前序、中序、后序、層次、廣度優(yōu)先遍歷】
這篇文章主要介紹了Java二叉搜索樹(shù)遍歷操作,結(jié)合實(shí)例形式詳細(xì)分析了Java二叉搜索樹(shù)前序、中序、后序、層次、廣度優(yōu)先遍歷等相關(guān)原理與操作技巧,需要的朋友可以參考下2020-03-03自己動(dòng)手用Springboot實(shí)現(xiàn)仿百度網(wǎng)盤(pán)的實(shí)踐
本項(xiàng)目基于Springboot開(kāi)發(fā)實(shí)現(xiàn),前端采用BootStrap開(kāi)發(fā)實(shí)現(xiàn),模仿百度網(wǎng)盤(pán)實(shí)現(xiàn)相關(guān)功能,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12SpringBoot整合RocketMQ實(shí)現(xiàn)發(fā)送同步消息
RocketMQ 是一款開(kāi)源的分布式消息中間件,由阿里巴巴開(kāi)源,它具有高可用性、高性能、低延遲等特點(diǎn),廣泛應(yīng)用于阿里巴巴集團(tuán)內(nèi)部以及眾多外部企業(yè)的業(yè)務(wù)系統(tǒng)中,本文給大家介紹了SpringBoot整合RocketMQ實(shí)現(xiàn)發(fā)送同步消息,需要的朋友可以參考下2024-04-04Java實(shí)現(xiàn)分解任意輸入數(shù)的質(zhì)因數(shù)算法示例
這篇文章主要介紹了Java實(shí)現(xiàn)分解任意輸入數(shù)的質(zhì)因數(shù)算法,涉及java數(shù)學(xué)運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下2017-10-10Java輕松入門(mén)冒泡?選擇?插入?希爾?歸并排序算法
這篇文章主要介紹了Java常用的排序算法及代碼實(shí)現(xiàn),在Java開(kāi)發(fā)中,對(duì)排序的應(yīng)用需要熟練的掌握,這樣才能夠確保Java學(xué)習(xí)時(shí)候能夠有扎實(shí)的基礎(chǔ)能力。那Java有哪些排序算法呢?本文小編就來(lái)詳細(xì)說(shuō)說(shuō)Java常見(jiàn)的排序算法,需要的朋友可以參考一下2022-02-02