slf4j的使用研究

OK,现在我们来使用slf4j。

  • 概念

SLF4J,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,它只服务于各种各样的日志系统。按照官方的说法,SLF4J是一个用于日志系统的简单Facade,允许最终用户在部署其应用时使用其所希望的日志系统。

实际上,SLF4J所提供的核心API是一些接口以及一个LoggerFactory的工厂类。从某种程度上,SLF4J有点类似JDBC,不过比JDBC更简单,在JDBC中,你需要指定驱动程序,而在使用SLF4J的时候,不需要在代码中或配置文件中指定你打算使用

那个具体的日志系统。如同使用JDBC基本不用考虑具体数据库一样,SLF4J提供了统一的记录日志的接口,只要按照其提供的方法记录即可,最终日志的格式、记录级别、输出方式等通过具体日志系统的配置来实现,因此可以在应用中灵活切换日志

系统。

  • 使用条件

如果你开发的是类库或者嵌入式组件,那么就应该考虑采用SLF4J,因为不可能影响最终用户选择哪种日志系统。在另一方面,如果是一个简单或者独立的应用,确定只有一种日志系统,那么就没有使用SLF4J的必要。假设你打算将你使用log4j的产

品卖给要求使用logback的用户时,面对成千上万的log4j调用的修改,相信这绝对不是一件轻松的事情。但是如果开始便使用SLF4J,那么这种转换将是非常轻松的事情。

 


说白了,slf4j和common-logging一个意思,就是简单的日志门面,方便我们在不动代码的前提下随意切换我们的日志框架。在部署的时候,选择不同的日志系统包,就可自动转换到不同的日志系统上。

比如:选择JDK自带的日志系统,则只需要将slf4j-api-1.5.10.jar和slf4j-jdk14-1.5.10.jar放置到classpath中即可,如果中途无法忍受JDK自带的日志系统了,想换成log4j的日志系统,仅需要用slf4j-log4j12-1.5.10.jar替换slf4j-jdk14-1.5.10.jar即可

(当然也需要log4j的jar及配置文件)。当然如果这个时候觉得log4j的性能不是太好,出于性能考虑想换成logback的日志系统的话,也只是需要将logback的core包和classic包替换原来的log4j包就OK(当然也需要logback的配置文件)。


  • OK,现在举一个完整的例子。下面先贴出完整的代码:

pom文件:

[html]  view plain  copy
  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
  2.     <modelVersion>4.0.0</modelVersion>  
  3.   
  4.     <groupId>org.linkinpark.commons</groupId>  
  5.     <artifactId>linkin-log-test</artifactId>  
  6.     <version>0.0.1-SNAPSHOT</version>  
  7.     <packaging>jar</packaging>  
  8.   
  9.     <name>linkin-log-test</name>  
  10.     <url>http://maven.apache.org</url>  
  11.   
  12.     <properties>  
  13.         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
  14.     </properties>  
  15.   
  16.     <dependencies>  
  17.         <!-- slf4j依赖 -->  
  18.         <dependency>  
  19.             <groupId>org.slf4j</groupId>  
  20.             <artifactId>slf4j-api</artifactId>  
  21.             <version>1.7.12</version>  
  22.         </dependency>  
  23.         <!-- log4j依赖 -->  
  24.         <dependency>  
  25.             <groupId>org.slf4j</groupId>  
  26.             <artifactId>slf4j-log4j12</artifactId>  
  27.             <version>1.7.12</version>  
  28.         </dependency>  
  29.         <dependency>  
  30.             <groupId>log4j</groupId>  
  31.             <artifactId>log4j</artifactId>  
  32.             <version>1.2.17</version>  
  33.         </dependency>  
  34.         <!-- logback依赖 -->  
  35.         <dependency>  
  36.             <groupId>ch.qos.logback</groupId>  
  37.             <artifactId>logback-classic</artifactId>  
  38.             <version>1.1.2</version>  
  39.         </dependency>  
  40.         <dependency>  
  41.             <groupId>ch.qos.logback</groupId>  
  42.             <artifactId>logback-core</artifactId>  
  43.             <version>1.1.2</version>  
  44.         </dependency>  
  45.         <!-- slf4j自带的简单日志 -->  
  46.         <dependency>  
  47.             <groupId>org.slf4j</groupId>  
  48.             <artifactId>slf4j-simple</artifactId>  
  49.             <version>1.7.12</version>  
  50.         </dependency>  
  51.         <!-- jdk自带的日志 -->  
  52.         <dependency>  
  53.             <groupId>org.slf4j</groupId>  
  54.             <artifactId>slf4j-jdk14</artifactId>  
  55.             <version>1.7.12</version>  
  56.         </dependency>  
  57.         <!-- common-logging日志框架 -->  
  58.         <dependency>  
  59.             <groupId>org.slf4j</groupId>  
  60.             <artifactId>slf4j-jcl</artifactId>  
  61.             <version>1.7.12</version>  
  62.         </dependency>  
  63.         <dependency>  
  64.             <groupId>commons-logging</groupId>  
  65.             <artifactId>commons-logging</artifactId>  
  66.             <version>1.2</version>  
  67.         </dependency>  
  68.         <!-- junit依赖 -->  
  69.         <dependency>  
  70.             <groupId>junit</groupId>  
  71.             <artifactId>junit</artifactId>  
  72.             <version>4.12</version>  
  73.             <scope>test</scope>  
  74.         </dependency>  
  75.     </dependencies>  
  76. </project>  

log4j.propetites配置文件:

[html]  view plain  copy
  1. log4j.rootLogger=DEBUG,console  
  2.   
  3. # 以下是rootLogger的配置,子类默认继承,但是子类重写下面配置=rootLogger+自己配置,我晕  
  4. #输出到控制台     
  5. log4j.appender.console=org.apache.log4j.ConsoleAppender    
  6. #设置输出样式     
  7. log4j.appender.console.layout=org.apache.log4j.PatternLayout   
  8. #日志输出信息格式为  
  9. log4j.appender.console.layout.ConversionPattern=[%-d{yyyy-MM-dd HH:mm:ss}]-[%t-%5p]-[%C-%M(%L)]: %m%n   


logback配置文件:

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2.   
  3. <configuration debug="true" scan="true" scanPeriod="30 seconds">  
  4.   
  5.     <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">  
  6.         <encoder>  
  7.             <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n  
  8.             </pattern>  
  9.         </encoder>  
  10.     </appender>  
  11.   
  12.     <root level="DEBUG">  
  13.         <appender-ref ref="STDOUT" />  
  14.     </root>  
  15. </configuration>  

Java测试代码:

[java]  view plain  copy
  1. package org.linkinpark.commons.slf4j;  
  2.   
  3. import org.junit.Test;  
  4. import org.slf4j.Logger;  
  5. import org.slf4j.LoggerFactory;  
  6.   
  7. /** 
  8.  * @创建作者: LinkinPark 
  9.  * @创建时间: 2016年3月1日 
  10.  * @功能描述: slf4j测试类 
  11.  */  
  12. public class Slf4jTest  
  13. {  
  14.     private static Logger logger = LoggerFactory.getLogger(Slf4jTest.class);  
  15.   
  16.     @Test  
  17.     public void test()  
  18.     {  
  19.         logger.debug("debug()方法,看下这里logger的实例是:{}", logger.getClass());  
  20.         logger.info("info()方法,看下这里logger的实例是:{}", logger.getClass());  
  21.         logger.error("error()方法,看下这里logger的实例是:{}", logger.getClass());  
  22.     }  
  23. }  


1,我们来一个一个测试,假如我们现在不想用任何的额外的日志框架,只想用slf4j来输出日志。slf4j也给我们提供了一个简单的simple日志框架。注释掉别的pom依赖,只打开slf4j-simple的依赖。运行上面的测试控制台输出如下:

[java]  view plain  copy
  1. [main] INFO org.linkinpark.commons.slf4j.Slf4jTest - info()方法,看下这里logger的实例是:class org.slf4j.impl.SimpleLogger  
  2. [main] ERROR org.linkinpark.commons.slf4j.Slf4jTest - error()方法,看下这里logger的实例是:class org.slf4j.impl.SimpleLogger  

OK,没问题,观察日志输出,我们也看到了这个时候slf4j使用的是slf4j自带的日志简单日志。

2,现在我们想使用JDK自带的日志框架来输出日志。去掉pom文件中多余的日志框架的依赖,然后添加slf4j-jdk14的依赖,运行上面的测试控制台输出如下:

[java]  view plain  copy
  1. 三月 012016 1:51:44 下午 org.linkinpark.commons.slf4j.Slf4jTest test  
  2. 信息: info()方法,看下这里logger的实例是:class org.slf4j.impl.JDK14LoggerAdapter  
  3. 三月 012016 1:51:44 下午 org.linkinpark.commons.slf4j.Slf4jTest test  
  4. 严重: error()方法,看下这里logger的实例是:class org.slf4j.impl.JDK14LoggerAdapter  

OK,没问题,观察日志输出,我们也看到这个时候slf4j使用的是JDK自带的日志框架,实际运行中在上面的slf4j-jdk14中有一个桥接类,slf4j用该类桥接到了JDK自带的日志框架中。

3,现在我们想使用log4j来输出日志。去掉pom文件中多余的日志框架的依赖,然后添加slf4j-log4j12和log4j2个依赖到pom中,运行上面的测试控制台输出如下:

[java]  view plain  copy
  1. log4j:WARN No appenders could be found for logger (org.linkinpark.commons.slf4j.Slf4jTest).  
  2. log4j:WARN Please initialize the log4j system properly.  
  3. log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.  
OK,错误已经很明显了,没有找见log4j的配置文件,所以没有appender来控制日志的输出。我们将log4j.propertites配置文件丢在我们项目根路径下继续运行上面的测试:

[java]  view plain  copy
  1. [2016-03-01 13:56:15]-[main-DEBUG]-[org.linkinpark.commons.slf4j.Slf4jTest-test(19)]: debug()方法,看下这里logger的实例是:class org.slf4j.impl.Log4jLoggerAdapter  
  2.  [2016-03-01 13:56:15]-[main- INFO]-[org.linkinpark.commons.slf4j.Slf4jTest-test(20)]: info()方法,看下这里logger的实例是:class org.slf4j.impl.Log4jLoggerAdapter  
  3.  [2016-03-01 13:56:15]-[main-ERROR]-[org.linkinpark.commons.slf4j.Slf4jTest-test(21)]: error()方法,看下这里logger的实例是:class org.slf4j.impl.Log4jLoggerAdapter  
  4.    

OK,没问题,观察日志输出,我们也看到这个时候slf4j使用了log4j来做日志框架。和上面第2点类似,这里slf4j也是用到了一个名叫Log4jLoggerAdapter的桥接类桥接到log4j的。

4,现在我们想使用logback来输出日志。去掉pom文件中多余的日志框架的依赖,添加logback-core和logback-classic的依赖到pom中,运行上面的测试,控制台输出如下:

[java]  view plain  copy
  1. 13:59:29.797 [main] DEBUG o.linkinpark.commons.slf4j.Slf4jTest - debug()方法,看下这里logger的实例是:class ch.qos.logback.classic.Logger  
  2. 13:59:29.801 [main] INFO  o.linkinpark.commons.slf4j.Slf4jTest - info()方法,看下这里logger的实例是:class ch.qos.logback.classic.Logger  
  3. 13:59:29.802 [main] ERROR o.linkinpark.commons.slf4j.Slf4jTest - error()方法,看下这里logger的实例是:class ch.qos.logback.classic.Logger  
OK,日志正常输出,但是我们发现了这里使用的其实的logback自带的默认的控制台输出的简单的日志类,我们现在添加logback.xml到项目的classpath中,继续运行测试:

[java]  view plain  copy
  1. 14:00:43,794 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]  
  2. 14:00:43,795 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]  
  3. 14:00:43,795 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [file:/Users/LinkinPark/WorkSpace/linkin-log-test/target/classes/logback.xml]  
  4. 14:00:43,861 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - Setting ReconfigureOnChangeFilter scanning period to 30 seconds  
  5. 14:00:43,861 |-INFO in ReconfigureOnChangeFilter{invocationCounter=0} - Will scan for changes in [[/Users/LinkinPark/WorkSpace/linkin-log-test/target/classes/logback.xml]] every 30 seconds.   
  6. 14:00:43,861 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - Adding ReconfigureOnChangeFilter as a turbo filter  
  7. 14:00:43,863 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]  
  8. 14:00:43,865 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]  
  9. 14:00:43,882 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property  
  10. 14:00:43,910 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG  
  11. 14:00:43,911 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]  
  12. 14:00:43,911 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.  
  13. 14:00:43,912 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@5ebec15 - Registering current configuration as safe fallback point  
  14. 14:00:43.916 [main] DEBUG o.linkinpark.commons.slf4j.Slf4jTest - debug()方法,看下这里logger的实例是:class ch.qos.logback.classic.Logger  
  15. 14:00:43.919 [main] INFO  o.linkinpark.commons.slf4j.Slf4jTest - info()方法,看下这里logger的实例是:class ch.qos.logback.classic.Logger  
  16. 14:00:43.920 [main] ERROR o.linkinpark.commons.slf4j.Slf4jTest - error()方法,看下这里logger的实例是:class ch.qos.logback.classic.Logger  
OK,没问题,观察日志输出,我们看到这个时候slf4j使用logback来做日志框架。其实slf4j和logback也是相处最融洽的2套日志管理框架,建议以后使用这2个的组合。

5,现在我们想使用common-logging来输出日志。前面关于common-logging我也已经整理过了,如果项目的classpath中存在log4j的包,那么久使用log4j,如果不存在则使用JDK自带的logging。slf4j同样支持common-logging来作为日志输出。

去掉pom文件中多余的日志框架的依赖,添加slf4j-jcl和commons-logging的依赖到pom中。

这里我们先暂时不添加log4j到我们的项目,运行上面的测试,控制台输出如下:

[java]  view plain  copy
  1. 三月 012016 2:05:18 下午 org.linkinpark.commons.slf4j.Slf4jTest info  
  2. 信息: info()方法,看下这里logger的实例是:class org.slf4j.impl.JCLLoggerAdapter  
  3. 三月 012016 2:05:18 下午 org.linkinpark.commons.slf4j.Slf4jTest error  
  4. 严重: error()方法,看下这里logger的实例是:class org.slf4j.impl.JCLLoggerAdapter  
OK,没问题,现在slf使用common-logging来输出日志,这里slf用了一个JCLLoggerAdapter来做桥接,桥接到了common-logging包的日志输出去的。由于这里的项目没有log4j的包,所以common-logging使用JDK的logging来输出

现在我们添加log4j到我们的项目中,继续运行上面的测试,控制台输出如下:

[java]  view plain  copy
  1. [2016-03-01 14:06:56]-[main-DEBUG]-[org.slf4j.impl.JCLLoggerAdapter-debug(186)]: debug()方法,看下这里logger的实例是:class org.slf4j.impl.JCLLoggerAdapter  
  2.  [2016-03-01 14:06:56]-[main- INFO]-[org.slf4j.impl.JCLLoggerAdapter-info(281)]: info()方法,看下这里logger的实例是:class org.slf4j.impl.JCLLoggerAdapter  
  3.  [2016-03-01 14:06:56]-[main-ERROR]-[org.slf4j.impl.JCLLoggerAdapter-error(471)]: error()方法,看下这里logger的实例是:class org.slf4j.impl.JCLLoggerAdapter  
  4.    
OK,没问题,现在控slf正常的使用了log4j来作为common-logging的实现来输出了日志。观察日志输出,我们也发现整理并不是直接使用的log4j,而是使用的JCLLoggerAdapter桥接到common-logging,然后common-logging自己选择的log4j来控制的输出,这点要注意,和上面的第3点不一样的,别搞混淆了。


  • 总结:

通过上面这个比较详细的例子,我们看到了,我们使用slf4j来统一管理的我们的代码,我们不停的切换了多种日志框架来作为我们的日志输出,但是我们的业务代码,Java类中的那些日志输出代码一点都不用去改,这也真是slf4j最迷人的地方。它完美的整合了自己的一个简单日志,JDK自带的日志,log4j,logback,common-logging。只不顾我们在转换日志输出的时候,可能会用到一些中间的桥接jar包。

当然,我仔细有看过这些桥接类的maven依赖,比如:

slf4j-log4j12:它本身就会依赖slf4j-api和log4j,maven依赖的jar包是可以传递的,所以也可以不用人工的去添加这些jar包的。
slf4j-jcl:它本身就会依赖slf4j-api和common-logging,maven依赖的jar包是可以传递的,所以也可以不用人工的去添加这些jar包的。
slf4j-jdk14:它本身就会依赖slf4j-api,maven依赖的jar包是可以传递的,所以也可以不用人工的去添加这些jar包的。


slf还有一个比较直接的吸引人的地方就是Java代码中输出日志的Java写法,性能很好,如果配合logback使用据说是log4j性能的10倍,特别是有字符串连接的时候。

在使用Commons Logging时,我们经常会看到以下方法的写法:

[java]  view plain  copy
  1. if (logger.isDebugEnabled())  
  2. {  
  3.     logger.info("Loading XML bean definitions from " + encodedResource.getResource());  
  4. }  

存在isDebugEnabled()的判断逻辑是为了在避免多余的字符串拼接,即如果不存在isDebugEnabled()判断,即使当前日志级别为ERROR时,在遇到logger.info()调用时,它还会先拼接日志消息的字符串,然后进入该方法内,才发现这个日志语句不用打印。而这种多余的拼接不仅浪费了多余的CPU操作,而且会增加GC的负担。SLF4J则提供以下的方式来解决这个问题:

[java]  view plain  copy
  1. logger.info("Loading XML bean definitions from {}", encodedResource.getResource());  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值