java日志:四、slf4j使用

java日志:四、slf4j使用

1 介绍:日志门面

当系统变得更加复杂的时候,日志就容易发生混乱。随着系统开发的进行,可能会更新不同的日志框架。造成当前系统中存在不同的日志依赖,难以统一管理和控制。即便强制要求所有模块使用相同的日志框架,系统难免使用其它类似spring,mybatis等其它的第三方框架,它们依赖于我们规定不同的日志框架,且它们自身的日志系统就有着不一致性,依然会出现日志体系的混乱。

所以我们需要借鉴JDBC思想,为日志系统提供一套门面,就可以面向这些接口规范来开发,避免了直接依赖具体的日志框架。

常见的日志门面:JCL、slf4j;常见的日志实现:JUL、Log4j、logback、log4j2

日志门面和日志实现的关系:

用户-》日志接口(Commons-logging、Slf4j)-》日志实现(Log4j、Java.logging、Slf4j-nop、Slf4j-simple、Logback…)

日志框架出现的历史顺序:log4j->JUL->JCL->slf4j->logback->log4j2

常用组合:slf4j+log4j2 ;logback+log4j2

2 SLF4J的使用

简单日志门面(Simple Logging Facade For Java)主要是为了给Java日志访问提供一套标准、规范的API框架,其主要意义在于提供接口,具体的实现可以交由其它日志框架,例如log4j和logback等。当然slf4j自己也提供了功能较为简单的实现,但是一般很少用到。对于一般的java项目而言,日志框架会选择slf4j-api作为门面,配上具体的实现框架(log4j、logback等),中间使用桥接器完成桥接。

官网:https://www.slf4j.org/ 查看:
java日志:四、slf4j使用_第1张图片
而且类路径要绑定具体的日志实现框架,否则无操作去实现,因为slf4j只是接口。

SLF4J是目前市面上最流行的日志门面。SLF4J主要提供以下两大功能:
1 日志框架的绑定
2 日志框架的桥接

3 依赖配置

pom.xml中配置slf4j依赖:

<!--  slf4j 日志门面  -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.32</version>
</dependency>
<!--  slf4j 内置的简单实现      -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.7.32</version>
</dependency>

配置junit依赖:

<!-- 只希望junit在src下的main中使用 -->
<dependency>
    <artifactId>junit</artifactId>
    <version>4.13.1</version>
    <scope>compile</scope>
</dependency>
<!-- 只希望junit在src下的test中使用-->
<groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.1</version>
    <scope>test</scope>
</dependency>
<!--如果希望junit在main和test下均可使用,去掉scope即可-->

4 SLF4J初识

package com.base7;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SLF4JTest {
    public static final Logger LOGGER= LoggerFactory.getLogger(SLF4JTest.class);
    @Test
    public void testLog(){
        //日志输出
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");

        //使用占位符输出日志信息
        String name="xiaoxu";
        Integer age=34;
        //slf4j的占位符,不用在{}中输入0、1、2等等
        LOGGER.info("用户:{},年龄:{}",name,age);
        
        //将系统的异常信息输出
        try {
            int i=1/0;
        } catch (Exception e) {
//            e.printStackTrace();
//          LOGGER中输出,可以逗号隔开(类似python的print),但是System.out.println只能+拼接
            LOGGER.error("出现异常:",e);
        }
    }
}

java日志:四、slf4j使用_第2张图片
5 SLF4J绑定日志的实现(Binding)

如前所述,SLF4J支持各种日志框架。SLF4J发行版附带了几个称为"SLF4J绑定"的jar文件,每个绑定对应一个受支持的框架。

使用slf4j的日志绑定流程:

1.添加slf4j-api依赖
2.使用slf4j的API在项目中进行统一的日志记录
3.绑定具体的日志实现框架:
3.1绑定已经实现了slf4j的日志框架,直接添加对应依赖
3.2绑定没有实现slf4j的日志框架,先添加日志的适配器,再添加实现类的依赖
4.slf4j有且仅有一个日志实现框架的绑定(如果出现多个默认使用第一个依赖日志实现)

打开官网的用户手册:
java日志:四、slf4j使用_第3张图片
java日志:四、slf4j使用_第4张图片
5.1 logback日志实现

添加依赖:

<!-- logback日志实现 -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.5</version>
</dependency>

但是执行依然使用的是simpleLoggerFactory:
java日志:四、slf4j使用_第5张图片
如果希望执行的是logback的日志,就需要注释掉先前的slf4j-simple依赖,然后在pom.xml中重新执行ctrl+shift+o:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.32</version>
</dependency>
<!--        <dependency>-->
<!--            <groupId>org.slf4j</groupId>-->
<!--            <artifactId>slf4j-simple</artifactId>-->
<!--            <version>1.7.32</version>-->
<!--        </dependency>-->
<!-- logback日志实现 -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.5</version>
</dependency>

重新执行打印日志如下:
java日志:四、slf4j使用_第6张图片
5.2 日志开关

将前面用到的slf4j-simple、logback-classic相关的依赖注释掉,保留slf4j-api依赖,以及添加下面的依赖:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-nop</artifactId>
    <version>1.7.32</version>
</dependency>

重新执行SLF4JTest,不会打印日志:
在这里插入图片描述
5.3 log4j日志实现

将日志开关slf4j-nop依赖注释掉,依赖配置如下:

<!--  slf4j日志门面  -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.32</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.30</version>
</dependency>

log4j.properties:

log4j.rootLogger=WARN,conso

log4j.appender.conso=org.apache.log4j.ConsoleAppender

log4j.appender.conso.layout=org.apache.log4j.PatternLayout

log4j.appender.conso.layout.conversionPattern=[%-10p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n

重新执行:
java日志:四、slf4j使用_第7张图片
5.4 jul日志实现

同理,去掉上述slf4j-log4j12的依赖配置,增加如下依赖,然后pom.xml中执行ctrl+shift+o:

<!--  绑定jul日志实现,需要导入适配器  -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-jdk14</artifactId>
    <version>1.7.0</version>
</dependency>

重新执行代码:
java日志:四、slf4j使用_第8张图片
6 SLF4J桥接旧的日志框架(Bridging)

通常,您依赖的某些组件依赖于SLF4J以外的日志记录API。您也可以假设这些组件在不久的将来不会切换到SLF4J。为了解决这种情况,SLF4J附带了几个桥接模块,这些模块将对log4j、JCL和java.util.loggingAPI的调用重定向,就好像它们是SLF4J的API一样。

桥接解决的是项目中日志的遗留问题,当系统中存在之前的日志API,可以通过桥接转接到slf4j的实现。

1 先去除之前老的日志框架的依赖
2 添加SLF4J提供的桥接组件
3 为项目添加SLF4J的具体实现

举个例子,比如以前的项目代码,没有使用日志门面,比如slf4j,直接使用的log4j日志框架:

package com.base7;

import org.junit.Test;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
import org.apache.log4j.Logger;

public class SLF4JTest {
    public static final Logger LOGGER= Logger.getLogger(SLF4JTest.class);
    @Test
    public void testLog(){
        //日志输出
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
    }
}

在这里插入图片描述
如果修改了依赖为logback的日志框架(先注释掉log4j的依赖,放开slf4j-api和logback的依赖):

<!--        <dependency>-->
<!--            <groupId>log4j</groupId>-->
<!--            <artifactId>log4j</artifactId>-->
<!--            <version>1.2.17</version>-->
<!--        </dependency>-->
<!--  slf4j日志门面  -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.32</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.5</version>
</dependency>

代码就会提示错误(如果整个项目各个地方用到,每个地方都去修改就会工作量很大):
java日志:四、slf4j使用_第9张图片
在原有的依赖配置上添加如下依赖:

<!--  配置log4j的桥接器  -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>log4j-over-slf4j</artifactId>
    <version>1.7.32</version>
</dependency>

报错消失:
java日志:四、slf4j使用_第10张图片
运行结果如下(表面上看,导入的是log4j的包,实际上执行效果却是logback的日志打印):
java日志:四、slf4j使用_第11张图片
注意问题:

1 jcl-over-slf4j.jar和slf4j-jcl.jar不能同时部署。前一个jar文件将导致JCL将日志系统的选择委托给SLF4J,后一个jar文件将导致SLF4J将日志系统的选择委托给JCL,从而导致无限循环。

2 log4j-over-slf4j.jar和slf4j-log4j12.jar不能同时出现。

3 jul-to-slf4j.jar和slf4j-jdk14.jar不能同时出现。

4 所有的桥接都只对Logger日志记录器对象有效,如果程序中调用了内部的配置类或者是Appender、Filter等对象,将无法产生效果。

去掉logback的依赖,依赖配置如下(logback、log4j的依赖全部去掉):

<!--  配置log4j的桥接器  -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>log4j-over-slf4j</artifactId>
    <version>1.7.32</version>
</dependency>
<!--  slf4j日志门面  -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.32</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.32</version>
</dependency>

运行出错:
java日志:四、slf4j使用_第12张图片
SLF4J原理解析:

1 SLF4J通过LoggerFactory加载日志具体的实现对象。

2 LoggerFactory在初始化的过程中,会通过performInitialization()方法绑定具体的日志实现。

3 在绑定具体实现的时候,通过类加载器,加载org/slf4j/impl/StaticLoggerBinder.class

4 所以,只要是一个日志实现框架,在org.slf4j.impl包中提供一个自己的StaticLoggerBinder类,在其中提供具体日志实现的LoggerFactory就可以被SLF4J所加载

你可能感兴趣的:(Java,java)