zoukankan      html  css  js  c++  java
  • Java日志体系(四)slf4j

    1.1 简介

                        

    与commons-logging相同,slf4j也是一个通用的日志接口,在程序中与其他日志框架结合使用,并对外提供服务。

    Simple Logging Facade for Java简称 slf4j,Java简单日志门面系统。在我们的代码中,不需要显式指定具体日志框架(例如:java.util.logging、logback、log4j),而是使用slf4j的API来记录日志便可,最终日志的格式、记录级别、输出方式等通过具体日志框架的配置来实现,因此可以在应用中灵活切换日志系统。

    如果你对上面所说的,仍然不太理解。那么,简单的说slf4j可以理解为JDBC,都是提供接口服务,只不过比JDBC更为直观、简单些。在程序中,JDBC需要单独指定具体的数据库实现(例如:mysql),而slf4j并不需要。

    接下来,我们讲解下关于slf4j具体的使用。

    1.2 slf4j结构

                  

    上面的截图,展示的是slf4j搭配log4j使用。

    Logger:slf4j日志接口类,提供了trace < debug < info < warn < error这5个级别对应的方法,主要提供了占位符{}的日志打印方式;

    Log4jLoggerAdapter:Logger适配器,主要对org.apache.log4j.Logger对象的封装,占位符{}日志打印的方式在此类中实现;

    LoggerFactory:日志工厂类,获取实际的日志工厂类,获取相应的日志实现对象;

    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.
    【SLF4J + JDKLog】使用:
    需要在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来说,它只提供了一个核心模块--slf4j-api,这个模块下只有日志接口,没有具体的实现,所以在实际开发总需要单独添加底层日志实现。但是,这些底层日志类实际上跟slf4j并没有任何关系,因此slf4j又通过增加一层日志中间层来转换相应的实现,例如上文中的slf4j-log4j12。
     
            
    • 具体的接入方式参见下图
          •    

    上图,是官方文档中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;
    参考:https://www.jianshu.com/p/e3b2de9e418b

  • 相关阅读:
    实践是检验真理的唯一标准——《精益创业实战》让你的成功飞起来
    蛇年谈程序员发展之路
    蛇年,你使用Python语言编程了吗?
    由《30天自制操作系统》引发的漫画创作
    《七周七语言:理解多种编程范型》书评整理
    精益创业实战
    一本为程序员量身打造的面试逻辑题宝典——《程序员面试逻辑题解析》
    从1.5k到18k, 一个程序员的5年成长之路
    Comparator and Comparable及Collections and Collection的用法和区别
    线程同步
  • 原文地址:https://www.cnblogs.com/caoweixiong/p/11273378.html
Copyright © 2011-2022 走看看