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结构

上面的截图,展现的是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.

- 具体的接入方式参见下图
上图,是官方文档中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;