【Spring】日志:使用 spring 时日志的多种选择

前言:由于篇幅所限,关于日志的基础内容请看我的这篇文章 【Java杂记】日志:Java日志体系,从应用场景到发展历史

Spring 底层选择的是 JCL做为日志门面 + JUL 作为日志实现,而 SpringBoot 选择的是 SLF4J 做为日志门面 + Logback 作为日志实现。那我们本篇就来看看在 Spring 中到底可以如何组合使用各种日志框架+日志实现。

1.spring 底层日志框架 JCL+ spring 底层的日志实现 JUL

首先,引入 spring-context 依赖

<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context</artifactId>
     <version>4.3.20.RELEASE</version>
</dependency>

然后我们简单写一个测试类,目的是观察系统日志和应用日志

public class MainClass {
	
    // import java.util.logging.Logger;
    private static Logger logger = Logger.getLogger(MainClass.class.getName());

    public static void main(String[] args) {
    	// 打印系统日志
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
		// 打印应用日志
        logger.info("let's go");
        context.start();
    }
}

下图是控制台打印的日志:

2.spring 底层日志框架 JCL + 引入并配置 Log4j

首先引入 log4j 的依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.20.RELEASE</version>
</dependency>
<!-- 引入log4j的依赖后,日志框架在寻找实现时就会优先使用log4j而不是jul -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

测试的代码不用变,因为这里我们使用的日志框架还是 JCL,只不过 Logger 的变成了 log4j 的

public class MainClass {
	
    // import org.apache.log4j.Logger;
    // 如果这里还是用 JUL 的话,打印出来的用户应用日志还是上图红色的
    private static Logger logger = Logger.getLogger(MainClass.class.getName());

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);

        logger.info("let's go");
        context.start();
    }
}

这里还一定注意,我们必须在resource目录下创建一个 log4j.properties 去对 log4j 进行初始配置,如果没有的话,Log4j 就无法正常打印日志。

PS:Log4j 启动时会自动去resource目录寻找名为 log4j 的配置文件并加载,相当于一种约定。

#定义LOG输出级别
#CONSOLE 和 FILE 是日志输出地方(也可以取别的名字,但必须大写)
log4j.rootLogger=INFO, CONSOLE, FILE

#定义日志输出目的地为控制台(显示方式:控制台,普通方式)
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
#可以灵活的指定日志输出格式,下面一行是指定具体的格式
log4j.appender.CONSOLE.layout.ConversionPattern=[%p] %d{yyyy-MM-dd HH:mm:ss} - %m%n

log4j.appender.FILE = org.apache.log4j.FileAppender
log4j.appender.FILE.layout = org.apache.log4j.PatternLayout
log4j.appender.FILE.Append = true
log4j.appender.FILE.File=log/info.log
log4j.appender.FILE.layout.ConversionPattern=[%p] %d{yyyy-MM-dd HH:mm:ss} - %m%n

#显示SQL语句日志配置(info不显示,debug显示执行的sql语句)
log4j.logger.com.xupt.yzh=info

日志打印结果如下

3.引入日志框架 Slf4j + 引入并配置 Log4j

首先引入 slf4j 及 log4j 的相关依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.20.RELEASE</version>
</dependency>
<!-- log4j的日志实现 -->
<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.28</version>
</dependency>
<!-- 该包是转换包,实现slf4j-api接口,调用log4j的实现 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.25</version>
</dependency>

因为早期 log4j 压根不知道有一slf4j 的日志门面,所以加入适配器 slf4j-log412.jar 实现 slf4j 的接口,真正实现功能调用。

我们的 log4j.jar 测试代码需要在创建日志 Logger 那变一下,因为日志门面这个中间层从 jcl 变成 slf4j 了

public class MainClass {
 
    // import org.slf4j.Logger;
    // import org.slf4j.LoggerFactory;
    // slf4j 是通过 LoggerFactory 去获取 Logger
    private static Logger logger = LoggerFactory.getLogger(MainClass.class.getName());
    
    // private static Logger logger = Logger.getLogger(MainClass.class.getName());

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);

        logger.info("let's go");
        context.start();
    }
}

因为引入了 Log4j,所以底层调用的还是 log4j,只不过用户使用的日志门面变了

4.引入日志框架 Slf4j + 引入并配置 Logback

还是首先引入 slf4j 和 logback 的依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.20.RELEASE</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>1.2.3</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.28</version>
</dependency>

测试代码同上面第三种情况,因为使用的日志门面都是 slf4j

public class MainClass {
 
    // import org.slf4j.Logger;
    // import org.slf4j.LoggerFactory;
    // slf4j 是通过 LoggerFactory 去获取 Logger
    private static Logger logger = LoggerFactory.getLogger(MainClass.class.getName());
    
    // private static Logger logger = Logger.getLogger(MainClass.class.getName());

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);

        logger.info("let's go");
        context.start();
    }
}

同 Log4j,这里的 Logback 必须配置。我们在 resource 目下新建一个 logback.xml 去配置日志格式,级别等

同 Log4j,Logback 启动时也会自动在 resource 目录下寻找名为 logback 的配置文件去加载。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    
	<!-- 设置如何输出到控制台 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
	
    <!-- 日志级别 -->
    <root level="DEBUG">
        <appender-ref ref="CONSOLE" />
    </root>
</configuration>

日志输出结果如下


问题:如果我同时引入了 Log4j 和 Logback,那 slf4j 到底会调用哪个日志实现?log4j > logback > jul

5.Spring4.x 底层使用的日志技术

在这里插入图片描述

我们从这里可以看出来,spring4.x 获取的日志对象中,Logger 对象是 JCL 的 ,而他底层搭配的技术点就是先去找 Log4j的日志实现,若没有找到,底层去找 jdk 的日志框架,压根不支持 Logback,Log4j2 的日志技术。

6.Spring5.x 底层使用的日志技术

 private static LogApi logApi = LogApi.JUL; 
 static {   
     ClassLoader cl = LogFactory.class.getClassLoader();  
     try {  
         // 第一步:先尝试去加载 log4j2的日志框架    
         cl.loadClass("org.apache.logging.log4j.spi.ExtendedLogger"); 
         logApi = LogApi.LOG4J;  
     }  
     catch (ClassNotFoundException ex1) {   
         try {   
             // 第二步:尝试去加载 LogApi.SLF4J_LAL     
             cl.loadClass("org.slf4j.spi.LocationAwareLogger"); 
             logApi = LogApi.SLF4J_LAL;  
         }   
         catch (ClassNotFoundException ex2) {    
             try {   
                 // 尝试去加载slf4j的日志实现   
                 cl.loadClass("org.slf4j.Logger");   
                 logApi = LogApi.SLF4J;  
             }    
             catch (ClassNotFoundException ex3) {   
                 // 使用原生的JUL    
             }   
         } 
     }  
 } 
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A minor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值