1.1 簡介
與commons-logging相同,slf4j也是一個通用的日誌接口,在程序中與其餘日誌框架結合使用,並對外提供服務。java
Simple Logging Facade for Java簡稱 slf4j,Java簡單日誌門面系統。在咱們的代碼中,不須要顯式指定具體日誌框架(例如:java.util.logging、logback、log4j),而是使用slf4j的API來記錄日誌即可,最終日誌的格式、記錄級別、輸出方式等經過具體日誌框架的配置來實現,所以能夠在應用中靈活切換日誌系統。mysql
若是你對上面所說的,仍然不太理解。那麼,簡單的說slf4j能夠理解爲JDBC,都是提供接口服務,只不過比JDBC更爲直觀、簡單些。在程序中,JDBC須要單獨指定具體的數據庫實現(例如:mysql),而slf4j並不須要。spring
接下來,咱們講解下關於slf4j具體的使用。sql
1.2 slf4j結構
![](http://static.javashuo.com/static/loading.gif)
上面的截圖,展現的是slf4j搭配log4j使用。數據庫
Logger:slf4j日誌接口類,提供了trace < debug < info < warn < error這5個級別對應的方法,主要提供了佔位符{}的日誌打印方式;apache
Log4jLoggerAdapter:Logger適配器,主要對org.apache.log4j.Logger對象的封裝,佔位符{}日誌打印的方式在此類中實現;api
LoggerFactory:日誌工廠類,獲取實際的日誌工廠類,獲取相應的日誌實現對象;app
lLoggerFactory:底層日誌框架中日誌工廠的中介,再其實現類中,經過底層日誌框架中的日誌工廠獲取對應的日誌對象;框架
StaticLoggerBinder:靜態日誌對象綁定,在編譯期肯定底層日誌框架,獲取實際的日誌工廠,也就是lLoggerFactory的實現類;性能
1.3 使用
同爲Java日誌接口框架,相對於commons-logging來講,slf4j的使用有點特殊。
commons-logging無需在pom.xml文件中單獨引入日誌實現框架,即可進行日誌打印。可是,slf4j並不支持此功能,必須在pom.xml中單獨引入底層日誌實現。
【SLF4J + log4j】使用:
須要在pom.xml文件中添加依賴:
//slf4j: <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.20</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>
聲明測試代碼:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; /**** ** SLF4J **/ public class Slf4jLog { final static Logger logger = LoggerFactory.getLogger(Slf4jLog.class); public static void main(String[] args) { logger.trace("Trace Level."); logger.info("Info Level."); logger.warn("Warn Level."); logger.error("Error Level."); } }
接下來,在classpath下定義配置文件:log4j.xml:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> <log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/' > <appender name="myConsole" class="org.apache.log4j.ConsoleAppender"> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="[%d{dd HH:mm:ss,SSS\} %-5p] [%t] %c{2\} - %m%n" /> </layout> <!--過濾器設置輸出的級別--> <filter class="org.apache.log4j.varia.LevelRangeFilter"> <param name="levelMin" value="debug" /> <param name="levelMax" value="error" /> <param name="AcceptOnMatch" value="true" /> </filter> </appender> <!-- 根logger的設置--> <root> <priority value ="debug"/> <appender-ref ref="myConsole"/> </root> </log4j:configuration>
咱們仍是用上面的代碼,無需作改變,運行結果爲:
[15 16:04:06,371 DEBUG] [main] slf4j.SLF4JLog - Debug Level. [15 16:04:06,371 INFO ] [main] slf4j.SLF4JLog - Info Level. [15 16:04:06,371 WARN ] [main] slf4j.SLF4JLog - Warn Level. [15 16:04:06,371 ERROR] [main] slf4j.SLF4JLog - Error Level.
須要在pom.xml文件中添加依賴:
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jdk14</artifactId> <version>1.7.21</version> </dependency>
咱們仍是用上面的代碼,無需作改變,運行結果爲:
七月 15, 2016 3:30:02 下午 com.chanshuyi.slf4j.Slf4jJDKLog main 信息: Info Level. 七月 15, 2016 3:30:02 下午 com.chanshuyi.slf4j.Slf4jJDKLog main 警告: Warn Level. 七月 15, 2016 3:30:02 下午 com.chanshuyi.slf4j.Slf4jJDKLog main 嚴重: Error Level.
【SLF4J + LogBack】使用:
須要在pom.xml文件中添加依賴:
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.1.7</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.1.7</version> </dependency>
配置 logback.xml 文件:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <layout class="ch.qos.logback.classic.PatternLayout"> <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern> </layout> </appender> <logger name="com.chanshuyi" level="TRACE"/> <root level="warn"> <appender-ref ref="STDOUT" /> </root> </configuration>
咱們仍是用上面的代碼,無需作改變,運行結果爲:
16:08:01.040 [main] TRACE com.chanshuyi.slf4j.SLF4JLog - Trace Level. 16:08:01.042 [main] DEBUG com.chanshuyi.slf4j.SLF4JLog - Debug Level. 16:08:01.043 [main] INFO com.chanshuyi.slf4j.SLF4JLog - Info Level. 16:08:01.043 [main] WARN com.chanshuyi.slf4j.SLF4JLog - Warn Level. 16:08:01.043 [main] ERROR com.chanshuyi.slf4j.SLF4JLog - Error Level.
![](http://static.javashuo.com/static/loading.gif)
- 具體的接入方式參見下圖
上圖,是官方文檔中slf4j與其餘日誌框架相結合的使用狀況,具體總結以下:
logback:logback-classic 、logback-core java.util.logging.Logging:slf4j-jdk14 commons-logging:jcl-over-slf4j
其中,commons-logging比較特殊。因爲commons-logging誕生的比較早,一些年限久遠的系統大致上都使用了commons-logging和log4j的日誌框架組合,大名鼎鼎的spring框架也依然在使用commons-logging框架。那麼,此時你的新系統若是想使用slf4j該如何處理?
這會,就須要引入jcl-over-slf4j.jar包了,它會將commons-logging的「騙入」到slf4j中來,實現日誌框架結合;
1.4 slf4j靜態綁定原理
雖然commons-logging和slf4j都是日誌服務接口,可是二者對於底層日誌框架綁定的方式相差甚遠。在第一篇日誌系統的文章中,筆者已經介紹過,commons-logging是基於動態綁定來實現與日誌框架的結合,也就是說在編譯期間咱們的程序並不知道底層的實現是什麼,只有在運行期間才進行獲取;
與commons-logging不一樣的是,slf4j是基於靜態綁定來實現與日誌框架的結合,在編譯期間咱們的程序就已經知道使用了哪一種日誌實現。
1.5 slf4j和commons-logging比較
(1)slf4j使用了靜態綁定方式,實現了與底層日誌框架的結合, 避免了commons-logging中因爲類加載器不一樣致使的日誌加載失敗狀況的發生;
(2)slf4j支持參數化日誌打印,也就是佔位符{}的方式。去除了commons-logging中的isDebugEnabled(), isInfoEnabled()等方法的日誌級別檢查代碼,極大的提升了代碼可讀性;而且,佔位符的方式也延緩了構建日誌信息(String的開銷),提升了內存的使用性;
在commons-logging中,咱們常常須要些這樣的代碼:
if (logger.isDebugEnabled()) { logger.debug("我是: " + name); }
而在slf4j中,咱們能夠這樣寫:
logger.debug("我是: {}",name);
在commons-logging中,是要符合日記級別,咱們就進行字符串的拼接;而在slf4j中,咱們不進行字符串拼接操做,而是使用StringBuffer來完成的替換。這不只下降了內存消耗並且預先下降了CPU去處理字符串鏈接命令的時間,提升了程序的性能。
1.6 slf4j搭配commons-logging使用原理
在前面的小節中,咱們提到了slf4j爲了兼容老代碼,是能夠跟commons-logging結合使用的,須要在pom.xml文件中引入jcl-over-slf4j.jar包。具體實現過程以下:
測試代碼:(引入的依舊爲commons-logging對象,無需改變)
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Test; public class commons_loggingDemo { Log log= LogFactory.getLog(commons_loggingDemo.class); @Test public void test() throws IOException { log.debug("Debug info."); log.info("Info info"); log.warn("Warn info你好"); log.error("Error info"); log.fatal("Fatal info"); } }
引入pom依賴:(除了原有的commons-logging和log4j依賴外,還須要添加slf4j-api、jcl-over-slf4j、slf4j-log4j12依賴)
!-- commons-logging --> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.3</version> </dependency> <!--將commons-logging引入到slf4j中去--> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.20</version> </dependency> <!--log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!--slf4j-log4j --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.12</version> </dependency> <!--slf4j --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.20</version> </dependency>
日誌配置文件: (均爲commons-logging時期配置,無需爲slf4j作任何改變)
commons-logging.properties配置文件: #日誌對象: org.apache.commons.logging.Log=org.apache.log4j.Logger #日誌工廠: org.apache.commons.logging.LogFactory=org.apache.commons.logging.impl.LogFactoryImpl log4j.xml配置文件: <log4j:configuration> <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender"> <param name="Target" value="System.out" /> <param name="ImmediateFlush" value="true"/> <param name="encoding" value="UTF-8"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d %t %-5p (%c:%L) - %m%n"/> </layout> </appender> <root> <priority value="debug" /> <appender-ref ref="CONSOLE" /> </root> </log4j:configuration>
實現原理:
將commons-logging的輸出引入到jcl-over-slf4j中,再轉向slf4j,緊接着進入到slf4j-log4j12,最終進入到log4j;