Java日志体系(五) slf4j 接口,适配器,实现;SLF4J+ log4j SLF4J + JUL SLF4J + LogBack SLF4|8月更文挑战

724 阅读4分钟

这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战

官网

www.slf4j.org/ www.slf4j.org/manual.html

简介

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

Simple Logging Facade for Java简称 slf4j,Java简单日志门面系统。

在这里插入图片描述 在这里插入图片描述

结构 日志门面 适配器 实现类

  • Logger:slf4j日志接口类,提供了trace < debug < info < warn < error这5个级别对应的方法,主要提供了占位符{}的日志打印方式;
  • Log4jLoggerAdapter:Logger适配器,主要对org.apache.log4j.Logger对象的封装,占位符{}日志打印的方式在此类中实现;
  • LoggerFactory:日志工厂类,获取实际的日志工厂类,获取相应的日志实现对象;
  • lLoggerFactory:底层日志框架中日志工厂的中介,再其实现类中,通过底层日志框架中的日志工厂获取对应的日志对象;
  • StaticLoggerBinder:静态日志对象绑定,在编译期确定底层日志框架,获取实际的日志工厂,也就是lLoggerFactory的实现类;

slf4j和commons-logging 日志接口比较

  • slf4j是基于静态绑定来实现与日志框架的结合,在编译期间我们的程序就已经知道使用了哪种日志实现
  • commons-logging是基于动态绑定来实现与日志框架的结合,也就是说在编译期间我们的程序并不知道底层的实现是什么,只有在运行期间才进行获取

slf4j使用了静态绑定方式,实现了与底层日志框架的结合, 避免了commons-logging中由于类加载器不同导致的日志加载失败情况的发生;

slf4j支持参数化日志打印,也就是占位符{}的方式。去除了commons-logging中的isDebugEnabled(), isInfoEnabled()等方法的日志级别检查代码,极大的提高了代码可读性;并且,占位符的方式也延缓了构建日志信息(String的开销),提高了内存的使用性;

在commons-logging中,我们经常需要些这样的代码:

if (logger.isDebugEnabled()) {
    logger.debug("我是: " + name);
}

而在slf4j中,我们可以这样写:

logger.debug("我是: {}",name);

在commons-logging中,是要符合日记级别,我们就进行字符串的拼接;而在slf4j中,我们不进行字符串拼接操作,而是使用StringBuffer来完成的替换。这不仅降低了内存消耗而且预先降低了CPU去处理字符串连接命令的时间,提高了程序的性能。

示例

创建maven工程

SLF4J+ log4j

添加POM依赖

<dependencies>
        <!--slf4j start ...-->

        <!-- slf4j-api + slf4j-log4j12适配器 + log4j实现 组成完成的slf4j+log4j的日志体系 -->
        <!--slf4j日志接口-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.26</version>
        </dependency>
        <!--log4j适配器-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>
        
        <!--log4j-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <!--slf4j end...-->
    </dependencies>

测试类Slf4jTest

public class Slf4jTest {
    @Test
    public void slf4jForLog4j(){
        Logger logger = LoggerFactory.getLogger(Slf4jTest.class);
        logger.error("Error Level.");
        logger.warn("Warn Level.");
        logger.info("Info Level.");
        logger.debug("Debug Level.");
        logger.trace("Trace Level.");
    }
}

输出结果:

Log4j 20:38:13,490 ERROR Slf4jTest:19 - Error Level.
Log4j 20:38:13,492  WARN Slf4jTest:20 - Warn Level.
Log4j 20:38:13,492  INFO Slf4jTest:21 - Info Level.
Log4j 20:38:13,492 DEBUG Slf4jTest:22 - Debug Level.
Log4j 20:38:13,492 TRACE Slf4jTest:23 - Trace Level.

log4j.properties文件

注意这边使用了之前的log4j.properties文件。==注意日志输出级别,不然不一定能输出你想要的日志==

# 日志输出级别DEBUG 输出目的地 FILE CONSOLE
log4j.rootLogger = ALL, FILE, CONSOLE

#将日志信息输出到对应的磁盘文件中
log4j.appender.FILE=org.apache.log4j.FileAppender
#将日志输出到D盘的logs/log.out文件中
log4j.appender.FILE.File=D:/logs/log.out
#请求的日志消息被立即输出,默认为true
log4j.appender.FILE.ImmediateFlush=true
#指定日志输出的最低级别,默认为DEBUG;如果日志请求的级别低于此级别,则不会输出此请求日志信息
log4j.appender.FILE.Threshold = DEBUG
#将新增日志追加到文件中,默认为true为不覆盖,false为覆盖
log4j.appender.FILE.Append=true
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.conversionPattern=Log4j  %d{ABSOLUTE} %5p %c{1}:%L - %m%n

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.ImmediateFlush=true
log4j.appender.CONSOLE.Threshold = ALL
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.encoding=UTF-8
log4j.appender.CONSOLE.layout.conversionPattern=Log4j %d{ABSOLUTE} %5p %c{1}:%L - %m%n

输出的日志文件:如下 在这里插入图片描述 日志文件少了 Log4j 20:38:13,492 TRACE Slf4jTest:23 - Trace Level. 那是因为配置文件中log4j.appender.FILE.Threshold = DEBUG的级别为DEBUG;

SLF4J + JUL

pom.xml添加 jul适配器

记得把上面log4j的适配器删除

<!--jul适配器-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-jdk14</artifactId>
    <version>1.5.6</version>
</dependency>

输出结果

三月 21, 2021 8:49:07 下午 cn.ling.logs.Slf4jTest slf4jForLog4j
严重: Error Level.
三月 21, 2021 8:49:07 下午 cn.ling.logs.Slf4jTest slf4jForLog4j
警告: Warn Level.
三月 21, 2021 8:49:07 下午 cn.ling.logs.Slf4jTest slf4jForLog4j
信息: Info Level.

Process finished with exit code 0

SLF4J + LogBack

添加POM依赖

<!--slf4j日志接口-->
<dependency>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-api</artifactId>
     <version>1.7.26</version>
 </dependency>

 <!--logback适配器-->
 <dependency>
     <groupId>ch.qos.logback</groupId>
     <artifactId>logback-classic</artifactId>
     <version>1.2.3</version>
 </dependency>
 <!--logback实现-->
 <dependency>
     <groupId>ch.qos.logback</groupId>
     <artifactId>logback-core</artifactId>
     <version>1.2.3</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>logback %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>

执行结果

21:49:51,386 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
21:49:51,386 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
21:49:51,387 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [file:/D:/Sunxy_Space/LogLearn/target/classes/logback.xml]
21:49:51,444 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
21:49:51,444 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
21:49:51,448 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
21:49:51,478 |-WARN in ch.qos.logback.core.ConsoleAppender[STDOUT] - This appender no longer admits a layout as a sub-component, set an encoder instead.
21:49:51,478 |-WARN in ch.qos.logback.core.ConsoleAppender[STDOUT] - To ensure compatibility, wrapping your layout in LayoutWrappingEncoder.
21:49:51,478 |-WARN in ch.qos.logback.core.ConsoleAppender[STDOUT] - See also http://logback.qos.ch/codes.html#layoutInsteadOfEncoder for details
21:49:51,479 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [cn.ling.logs] to TRACE
21:49:51,479 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to WARN
21:49:51,479 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]
21:49:51,479 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
21:49:51,480 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@6df97b55 - Registering current configuration as safe fallback point

logback 21:49:51.482 [main] ERROR cn.ling.logs.Slf4jTest - Error Level.
logback 21:49:51.483 [main] WARN  cn.ling.logs.Slf4jTest - Warn Level.
logback 21:49:51.483 [main] INFO  cn.ling.logs.Slf4jTest - Info Level.
logback 21:49:51.483 [main] DEBUG cn.ling.logs.Slf4jTest - Debug Level.
logback 21:49:51.483 [main] TRACE cn.ling.logs.Slf4jTest - Trace Level.

SLF4J + JCL

添加POM依赖

<!--slf4j日志接口-->
 <dependency>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-api</artifactId>
     <version>1.7.26</version>
 </dependency>

 <!--jcl适配器-->
 <dependency>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-jcl</artifactId>
     <version>1.7.25</version>
 </dependency>
 <!--JCL日志框架-->
 <dependency>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
     <version>1.2</version>
 </dependency>

 <!--log4j-->
 <dependency>
     <groupId>log4j</groupId>
     <artifactId>log4j</artifactId>
     <version>1.2.17</version>
 </dependency>

配置文件commons-logging.properties

# JCL依赖log4j
#org.apache.commons.logging.Log = org.apache.commons.logging.impl.Log4JLogger

# JCL依赖JUL
org.apache.commons.logging.Log = org.apache.commons.logging.impl.Jdk14Logger

执行结果

三月 21, 2021 10:36:40 下午 cn.ling.logs.Slf4jTest error
严重: Error Level.
三月 21, 2021 10:36:40 下午 cn.ling.logs.Slf4jTest warn
警告: Warn Level.
三月 21, 2021 10:36:40 下午 cn.ling.logs.Slf4jTest info
信息: Info Level.

Process finished with exit code 0

==上面POM中也加载了log4j的jar包,为什么没有使用log4j的打印呢?== 因为commons-logging.properties 配置文件中使用的是JUL的依赖。

修改commons-logging.properties 依赖

# JCL依赖log4j
org.apache.commons.logging.Log = org.apache.commons.logging.impl.Log4JLogger

# JCL依赖JUL
#org.apache.commons.logging.Log = org.apache.commons.logging.impl.Jdk14Logger

输出结果


Log4j 22:40:39,473 ERROR Slf4jTest:451 - Error Level.
Log4j 22:40:39,475  WARN Slf4jTest:356 - Warn Level.
Log4j 22:40:39,475  INFO Slf4jTest:260 - Info Level.
Log4j 22:40:39,475 DEBUG Slf4jTest:166 - Debug Level.
Log4j 22:40:39,475 TRACE Slf4jTest:70 - Trace Level.

Process finished with exit code 0

总结

SLF4J日志接口(日志门面) ==到== SLF4J各种适配器 ==到== 具体的日志实现

如果这是从上到下结构的话,SLF4J还提供了从下到上的实现方式。

如果你已经看到这里,请为博主点个赞,编辑不易,谢谢支持