slf4j與jul、log4j1、log4j2、logback的集成原理
系列文章已完成,目錄如下:
jdk-logging log4j logback日志系統(tǒng)實(shí)現(xiàn)機(jī)制原理介紹
commons-logging與jdk-logging、log4j1、log4j2、logback的集成原理
slf4j、jcl、jul、log4j1、log4j2、logback大總結(jié)
slf4j
先從一個(gè)簡單的使用案例來說明
1 簡單的使用案例
private static Logger logger=LoggerFactory.getLogger(Log4jSlf4JTest.class); public static void main(String[] args){ if(logger.isDebugEnabled()){ logger.debug("slf4j-log4j debug message"); } if(logger.isInfoEnabled()){ logger.debug("slf4j-log4j info message"); } if(logger.isTraceEnabled()){ logger.debug("slf4j-log4j trace message"); } }
上述Logger接口、LoggerFactory類都是slf4j自己定義的。
2 使用原理
LoggerFactory.getLogger(Log4jSlf4JTest.class)的源碼如下:
public static Logger getLogger(String name) { ILoggerFactory iLoggerFactory = getILoggerFactory(); return iLoggerFactory.getLogger(name); }
上述獲取Log的過程大致分成2個(gè)階段
- 獲取ILoggerFactory的過程 (從字面上理解就是生產(chǎn)Logger的工廠)
- 根據(jù)ILoggerFactory獲取Logger的過程
下面來詳細(xì)說明:
1 獲取ILoggerFactory的過程
又可以分成3個(gè)過程:
所以slf4j與其他實(shí)際的日志框架的集成jar包中,都會含有這樣的一個(gè)org/slf4j/impl/StaticLoggerBinder.class類文件,并且提供一個(gè)ILoggerFactory的實(shí)現(xiàn)
- 1.1 從類路徑中尋找org/slf4j/impl/StaticLoggerBinder.class類
ClassLoader.getSystemResources("org/slf4j/impl/StaticLoggerBinder.class")
如果找到多個(gè),則輸出 Class path contains multiple SLF4J bindings,表示有多個(gè)日志實(shí)現(xiàn)與slf4j進(jìn)行了綁定
下面看下當(dāng)出現(xiàn)多個(gè)StaticLoggerBinder的時(shí)候的輸出日志(簡化了一些內(nèi)容):
SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [slf4j-log4j12-1.7.12.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [logback-classic-1.1.3.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [slf4j-jdk14-1.7.12.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.htmlmultiple_bindings for an explanation. SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
- 1.2 "隨機(jī)選取"一個(gè)StaticLoggerBinder.class來創(chuàng)建一個(gè)單例
StaticLoggerBinder.getSingleton()
這里的"隨機(jī)選取"可以見官方文檔說明:
SLF4J API is designed to bind with one and only one underlying logging framework at a time. If more than one binding is present on the class path, SLF4J will emit a warning, listing the location of those bindings
The warning emitted by SLF4J is just that, a warning. Even when multiple bindings are present,SLF4J will pick one logging framework/implementation and bind with it. The way SLF4J picks a binding is determined by the JVM and for all practical purposes should be considered random
- 1.3 根據(jù)上述創(chuàng)建的StaticLoggerBinder單例,返回一個(gè)ILoggerFactory實(shí)例
StaticLoggerBinder.getSingleton().getLoggerFactory()
2 根據(jù)ILoggerFactory獲取Logger的過程
這就要看具體的ILoggerFactory類型了,下面的集成來詳細(xì)說明
slf4j與jdk-logging集成
1 需要的jar包
slf4j-api
slf4j-jdk14
對應(yīng)的maven依賴為:
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.12</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jdk14</artifactId> <version>1.7.12</version> </dependency>
2 使用案例
private static final Logger logger=LoggerFactory.getLogger(JulSlf4jTest.class); public static void main(String[] args){ if(logger.isDebugEnabled()){ logger.debug("jul debug message"); } if(logger.isInfoEnabled()){ logger.info("jul info message"); } if(logger.isWarnEnabled()){ logger.warn("jul warn message"); } }
上述的Logger、LoggerFactory都是slf4j自己的API中的內(nèi)容,沒有jdk自帶的logging的蹤影,然后打出來的日志卻是通過jdk自帶的logging來輸出的,如下:
四月 28, 2015 7:33:20 下午 com.demo.log4j.JulSlf4jTest main
信息: jul info message
四月 28, 2015 7:33:20 下午 com.demo.log4j.JulSlf4jTest main
警告: jul warn message
3 使用案例原理分析
先看下slf4j-jdk14 jar包中的內(nèi)容:
從中可以看到:
- 的確是有org/slf4j/impl/StaticLoggerBinder.class類
- 該StaticLoggerBinder返回的ILoggerFactory類型將會是JDK14LoggerFactory
- JDK14LoggerAdapter就是實(shí)現(xiàn)了slf4j定義的Logger接口
下面梳理下整個(gè)流程:
1 獲取ILoggerFactory的過程
由于類路徑下有org/slf4j/impl/StaticLoggerBinder.class,所以會選擇slf4j-jdk14中的StaticLoggerBinder來創(chuàng)建單例對象并返回ILoggerFactory,來看下StaticLoggerBinder中的ILoggerFactory是什么類型:
private StaticLoggerBinder() { loggerFactory = new org.slf4j.impl.JDK14LoggerFactory(); }
所以返回了JDK14LoggerFactory的實(shí)例
2 根據(jù)ILoggerFactory獲取Logger的過程
來看下JDK14LoggerFactory是如何返回一個(gè)slf4j定義的Logger接口的實(shí)例的,源碼如下:
java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger(name); Logger newInstance = new JDK14LoggerAdapter(julLogger);
可以看到,就是使用jdk自帶的logging的原生方式來先創(chuàng)建一個(gè)jdk自己的java.util.logging.Logger實(shí)例,參見jdk-logging的原生寫法
然后利用JDK14LoggerAdapter將上述的java.util.logging.Logger包裝成slf4j定義的Logger實(shí)例
所以我們使用slf4j來進(jìn)行編程,最終會委托給jdk自帶的java.util.logging.Logger去執(zhí)行。
slf4j與log4j1集成
1 需要的jar包
slf4j-api
slf4j-log4j12
log4j
maven依賴分別為:
<!-- slf4j --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.12</version> </dependency> <!-- slf4j-log4j --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.12</version> </dependency> <!-- log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
2 使用案例
第一步:編寫log4j.properties配置文件,放到類路徑下
log4j.rootLogger = debug, console log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} %m%n
配置文件的詳細(xì)內(nèi)容不是本博客關(guān)注的重點(diǎn),不再說明,自行搜索
第二步:代碼中如下使用
private static Logger logger=LoggerFactory.getLogger(Log4jSlf4JTest.class); public static void main(String[] args){ if(logger.isDebugEnabled()){ logger.debug("slf4j-log4j debug message"); } if(logger.isInfoEnabled()){ logger.info("slf4j-log4j info message"); } if(logger.isTraceEnabled()){ logger.trace("slf4j-log4j trace message"); } }
補(bǔ)充說明:
1 配置文件同樣可以隨意放置,如log4j1原生方式加載配置文件的方式log4j1原生開發(fā)
2 注意兩者方式的不同:
slf4j: Logger logger=LoggerFactory.getLogger(Log4jSlf4JTest.class); log4j: Logger logger=Logger.getLogger(Log4jTest.class);
slf4j的Logger是slf4j定義的接口,而log4j的Logger是類。LoggerFactory是slf4j自己的類
3 使用案例原理分析
先來看下slf4j-log4j12包中的內(nèi)容:
- 的確是有org/slf4j/impl/StaticLoggerBinder.class類
- 該StaticLoggerBinder返回的ILoggerFactory類型將會是Log4jLoggerFactory
- Log4jLoggerAdapter就是實(shí)現(xiàn)了slf4j定義的Logger接口
來看下具體過程:
1 獲取對應(yīng)的ILoggerFactory
從上面的slf4j的原理中我們知道:ILoggerFactory是由StaticLoggerBinder來創(chuàng)建出來的,所以可以簡單分成2個(gè)過程:
1.1 第一個(gè)過程:slf4j尋找綁定類StaticLoggerBinder
使用ClassLoader來加載 "org/slf4j/impl/StaticLoggerBinder.class"這樣的類的url,然后就找到了slf4j-log4j12包中的StaticLoggerBinder
1.2 第二個(gè)過程:創(chuàng)建出StaticLoggerBinder實(shí)例,并創(chuàng)建出ILoggerFactory
源碼如下:
StaticLoggerBinder.getSingleton().getLoggerFactory()
以slf4j-log4j12中的StaticLoggerBinder為例,創(chuàng)建出的ILoggerFactory為Log4jLoggerFactory
2 根據(jù)ILoggerFactory獲取Logger的過程
來看下Log4jLoggerFactory是如何返回一個(gè)slf4j定義的Logger接口的實(shí)例的,源碼如下:
org.apache.log4j.Logger log4jLogger; if (name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME)) log4jLogger = LogManager.getRootLogger(); else log4jLogger = LogManager.getLogger(name); Logger newInstance = new Log4jLoggerAdapter(log4jLogger);
2.1 我們可以看到是通過log4j1的原生方式,即使用log4j1的LogManager來獲取,引發(fā)log4j1的加載配置文件,然后初始化,最后返回一個(gè)org.apache.log4j.Logger log4jLogger,參見log4j1原生的寫法
2.2 將上述的org.apache.log4j.Logger log4jLogger封裝成Log4jLoggerAdapter,而Log4jLoggerAdapter是實(shí)現(xiàn)了slf4j的接口,所以我們使用的slf4j的Logger接口實(shí)例(這里即Log4jLoggerAdapter)都會委托給內(nèi)部的org.apache.log4j.Logger實(shí)例
slf4j與log4j2集成
1 需要的jar包
slf4j-api
log4j-api
log4j-core
log4j-slf4j-impl (用于log4j2與slf4j集成)
對應(yīng)的maven依賴分別是:
<!-- slf4j --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.12</version> </dependency> <!-- log4j2 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.2</version> </dependency> <!-- log4j-slf4j-impl --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.2</version> </dependency>
2 使用案例
第一步:編寫log4j2的配置文件log4j2.xml,簡單如下:、
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </Appenders> <Loggers> <Root level="debug"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>
第二步:使用方式
private static Logger logger=LoggerFactory.getLogger(Log4j2Slf4jTest.class); public static void main(String[] args){ if(logger.isTraceEnabled()){ logger.trace("slf4j-log4j2 trace message"); } if(logger.isDebugEnabled()){ logger.debug("slf4j-log4j2 debug message"); } if(logger.isInfoEnabled()){ logger.info("slf4j-log4j2 info message"); } }
3 使用案例原理分析
先來看下log4j-slf4j-impl包中的內(nèi)容:
- 的確是有org/slf4j/impl/StaticLoggerBinder.class類
- 該StaticLoggerBinder返回的ILoggerFactory類型將會是Log4jLoggerFactory(這里的Log4jLoggerFactory與上述log4j1集成時(shí)的Log4jLoggerFactory是不一樣的)
- Log4jLogger就是實(shí)現(xiàn)了slf4j定義的Logger接口
來看下具體過程:
1 獲取對應(yīng)的ILoggerFactory
1.1 第一個(gè)過程:slf4j尋找綁定類StaticLoggerBinder
使用ClassLoader來加載 "org/slf4j/impl/StaticLoggerBinder.class"這樣的類的url,然后就找到了log4j-slf4j-impl包中的StaticLoggerBinder
1.2 第二個(gè)過程:創(chuàng)建出StaticLoggerBinder實(shí)例,并創(chuàng)建出ILoggerFactory
log4j-slf4j-impl包中的StaticLoggerBinder返回的ILoggerFactory是Log4jLoggerFactory
2 根據(jù)ILoggerFactory獲取Logger的過程
來看下Log4jLoggerFactory是如何返回一個(gè)slf4j定義的Logger接口的實(shí)例的,源碼如下:
@Override protected Logger newLogger(final String name, final LoggerContext context) { final String key = Logger.ROOT_LOGGER_NAME.equals(name) ? LogManager.ROOT_LOGGER_NAME : name; return new Log4jLogger(context.getLogger(key), name); } @Override protected LoggerContext getContext() { final Class<?> anchor = ReflectionUtil.getCallerClass(FQCN, PACKAGE); return anchor == null ? LogManager.getContext() : getContext(ReflectionUtil.getCallerClass(anchor)); }
上述獲取LoggerContext的過程也是log4j2的原生方式:
LogManager.getContext()
該操作會去加載log4j2的配置文件,引發(fā)log4j2的初始化
2.1 我們可以看到是通過log4j2的原生方式,即使用log4j2的LoggerContext來獲取,返回一個(gè)org.apache.logging.log4j.core.Logger即log4j2定義的Logger接口實(shí)例,參見log4j2原生的寫法
2.2 將上述的org.apache.logging.log4j.core.Logger封裝成Log4jLogger,而Log4jLogger是實(shí)現(xiàn)了slf4j的Logger接口的,所以我們使用的slf4j的Logger接口實(shí)例(這里即Log4jLogger)都會委托給內(nèi)部的log4j2定義的Logger實(shí)例。
slf4j與logback集成
1 需要的jar包
slf4j-api
logback-core
logback-classic(已含有對slf4j的集成包)
對應(yīng)的maven依賴為:
<!-- slf4j-api --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.12</version> </dependency> <!-- logback --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.1.3</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.1.3</version> </dependency>
2 使用案例
第一步:編寫logback的配置文件logback.xml,簡單如下:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="STDOUT" /> </root> </configuration>
第二步:使用方式
private static final Logger logger=LoggerFactory.getLogger(LogbackTest.class); public static void main(String[] args){ if(logger.isDebugEnabled()){ logger.debug("slf4j-logback debug message"); } if(logger.isInfoEnabled()){ logger.info("slf4j-logback info message"); } if(logger.isTraceEnabled()){ logger.trace("slf4j-logback trace message"); } }
3 使用案例原理分析
先來看下logback-classic包中與slf4j集成的內(nèi)容:
- 的確是有org/slf4j/impl/StaticLoggerBinder.class類
- 該StaticLoggerBinder返回的ILoggerFactory類型將會是LoggerContext(logback的對象)
- logback自己定義的ch.qos.logback.classic.Logger類就是實(shí)現(xiàn)了slf4j定義的Logger接口
1 獲取對應(yīng)的ILoggerFactory
1.1 第一個(gè)過程:slf4j尋找綁定類StaticLoggerBinder
使用ClassLoader來加載 "org/slf4j/impl/StaticLoggerBinder.class"這樣的類的url,然后就找到了logback-classic包中的StaticLoggerBinder
1.2 第二個(gè)過程:創(chuàng)建出StaticLoggerBinder實(shí)例,并創(chuàng)建出ILoggerFactory
logback-classic包中的StaticLoggerBinder返回的ILoggerFactory是LoggerContext(logback的對象)
創(chuàng)建出單例后,同時(shí)會引發(fā)logback的初始化,這時(shí)候logback就要去尋找一系列的配置文件,嘗試加載并解析。
2 根據(jù)ILoggerFactory獲取Logger的過程
來看下LoggerContext(logback的對象)是如何返回一個(gè)slf4j定義的Logger接口的實(shí)例的:
該LoggerContext(logback的對象)返回的ch.qos.logback.classic.Logger(logback的原生Logger對象)就是slf4j的Logger實(shí)現(xiàn)類。
結(jié)尾
本篇文章講解了slf4j與jdk-logging、log4j1、log4j2、logback的集成原理,下一篇也是最后一篇來總結(jié)下
- 各種jar包的總結(jié)
- commons-logging、slf4j與其他日志框架的集成總結(jié)
- 實(shí)現(xiàn)已有的日志框架無縫切換到別的日志框架(如已使用log4j進(jìn)行日志記錄的代碼最終轉(zhuǎn)到logback來輸出)
- jar包沖突說明
以上就是slf4j與jul、log4j1、log4j2、logback的集成原理的詳細(xì)內(nèi)容,更多關(guān)于slf4j與jul log4j1 log4j2 logback集成原理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決IDEA啟動(dòng)springboot項(xiàng)目報(bào)錯(cuò)java.lang.ClassNotFoundException:?jav
這篇文章主要介紹了解決IDEA啟動(dòng)springboot項(xiàng)目報(bào)錯(cuò)java.lang.ClassNotFoundException:?javax.servlet.ServletContext問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01IDEA設(shè)置Maven自動(dòng)編譯model的實(shí)現(xiàn)方法
本文主要介紹了IDEA設(shè)置Maven自動(dòng)編譯model的實(shí)現(xiàn)方法, maven結(jié)構(gòu)的項(xiàng)目,我們在每次修改代碼后都會需要手動(dòng)編譯,本文就可以解決這個(gè)問題,感興趣的可以了解一下2023-08-08Spring中Websocket身份驗(yàn)證和授權(quán)的實(shí)現(xiàn)
在Web應(yīng)用開發(fā)中,安全一直是非常重要的一個(gè)方面,本文主要介紹了Spring中Websocket身份驗(yàn)證和授權(quán)的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08Java+swing實(shí)現(xiàn)經(jīng)典貪吃蛇游戲
貪吃蛇(也叫做貪食蛇)游戲是一款休閑益智類游戲,有PC和手機(jī)等多平臺版本。既簡單又耐玩。本文將通過java的swing來實(shí)現(xiàn)這一游戲,需要的可以參考一下2022-01-01