认识Spring 中的日志

这篇文章你将了解到Spring生态中日志框架是如何演化集成的

Spring Boot 日志

众说周知,Spring Boot 统一了日志框架,统一使用Logback进行日志输出,不管内部依赖框架使用的何种日志,最终都以Logback输出,他为什么需要统一呢。

我们通过引入spring-boot-starter 依赖先来看看:

认识Spring 中的日志_第1张图片
可见,通过spring-boot-starter引入了spring-boot、spring-boot-autoconfigure、spring-boot-starter-logging、jakarta.annotation-api、spring-core等;其中spring-boot-starter-logging主要引入日志相关的依赖,有logback-classic、log4j-to-slf4j、jul-to-slf4j,正因为有这些依赖spring boot 才能够统一日志框架,我们先看看这几个依赖的作用。

logback-classic: logback的主要日志依赖  
log4j-to-slf4j:将lo4j日志桥接到了slf4j日志框架,spring 框架默认使用的,所以需要该依赖  
jul-to-slf4j:将jul日志桥接到了slf4j日志框架,tomcat框架默认使用的,jul 即Java Util Logging

正因为Spring Boot 使用到了Spring、Tomcat,但两者都各自使用了不同的日志框架,后面可能还有其他依赖框架,混乱的日志框架势必会对开发人员造成困扰。

这里我们看到两个依赖都桥接到了slf4j,那什么是slf4j?大部分人应该都知道slf4j是一种抽象日志框架,要能够输出日志,slf4j还需要绑定到具体的日志框架,比如logback-classic。那为什么会出来slf4j呢。

到目前为止Java生态有很多的日志框架:logback、log4j、log4j2、simpleLog、JUL等。这么多的日志框架,你想象一下,如果开始的时候你使用的JUL打印日志,随着新技术的发展又出来了log4j,另一波开发人员又引入了log4j,后面又出现了log4j2,多种日志框架充斥着系统,改配置就得改不同的文件,给维护带来很多麻烦。

Spring 日志

后来出现了Commons Logging(JCL),其通过统一的写法统一了框架日志,不需要根据不同的日志框架,不同的日志编码。

 private Log log = LogFactory.getLog(CLASS.class);

通过如上编码,JCL会去寻找合适的日志框架,通过找到的日志框架打印日志。

但是随着时间的推移,JCL 在2014年后就没更新(2023年JCL又开始更新了一版1.3.0),后面被越来越多的人弃用,但是就如上面说的Spring 还是一直使用的JCL作为默认日志框架。

在Spring框架中有一个spring-jcl依赖,其中LogAdapter有一个静态方法块,用来选择要加载的Log API

	private static final String LOG4J_SPI = "org.apache.logging.log4j.spi.ExtendedLogger";

	private static final String LOG4J_SLF4J_PROVIDER = "org.apache.logging.slf4j.SLF4JProvider";

	private static final String SLF4J_SPI = "org.slf4j.spi.LocationAwareLogger";

	private static final String SLF4J_API = "org.slf4j.Logger";


	private static final LogApi logApi;

static {
		if (isPresent(LOG4J_SPI)) {
			if (isPresent(LOG4J_SLF4J_PROVIDER) && isPresent(SLF4J_SPI)) {
				// log4j-to-slf4j bridge -> we'll rather go with the SLF4J SPI;
				// however, we still prefer Log4j over the plain SLF4J API since
				// the latter does not have location awareness support.
				logApi = LogApi.SLF4J_LAL;
			}
			else {
				// Use Log4j 2.x directly, including location awareness support
				logApi = LogApi.LOG4J;
			}
		}
		else if (isPresent(SLF4J_SPI)) {
			// Full SLF4J SPI including location awareness support
			logApi = LogApi.SLF4J_LAL;
		}
		else if (isPresent(SLF4J_API)) {
			// Minimal SLF4J API without location awareness support
			logApi = LogApi.SLF4J;
		}
		else {
			// java.util.logging as default
			logApi = LogApi.JUL;
		}
	}

如上,有一句话很有意思,
log4j-to-slf4j bridge -> we'll rather go with the SLF4J SPI; however, we still prefer Log4j over the plain SLF4J API since the latter does not have location awareness support.
如果存在log4j-to-slf4j 桥接器,那么就选择SLF4J ,但是我们还是更喜欢使用Log4j而不是SLF4J API,因为它没有位置感知支持。

LOG4J_SPI 存在于 log4j包中
LOG4J_SLF4J_PROVIDER 存在于log4j-to-slf4j中
SLF4J_SPI 存在于slf4j中
SLF4J_API存在于slf4j中

什么是位置感知?网上没有什么资料说这个问题?但我们可以问下大模型,看看怎么回答的。
认识Spring 中的日志_第2张图片
好像也没错,SLF4J只是一个日志的抽象,没有具体实现,确实不能记录日志的发生位置~。

SLF4J

说回SLF4J,使用方式也跟JCL一样,只需要同样的代码,就能使用不同的日志框架。
Logger logger = LoggerFactory.getLogger(Wombat.class);
但是它比JCL更先进,只需要更换不同的绑定器,而不是将日志实现硬编码在代码中

SLF4J里面有还有两个概念,绑定器和桥接器。
绑定器:SLF4J绑定具体的日志实现框架,比如logback-classic是logback的绑定器https://slf4j.org/manual.html

认识Spring 中的日志_第3张图片

桥接器:将以前的日志框架桥接到SLF4J中,使用SLF4J来确定具体的日志框架,更多的桥接器可以到官网中查看。https://slf4j.org/legacy.html

认识Spring 中的日志_第4张图片
SLF4J起到了一个桥梁的工作,如同一个交通指挥员,指挥不同的日志框架如何进行转换,比JCL更灵活。


作者其他文章推荐:
基于Spring Boot 3.1.0 系列文章

  1. Spring Boot 源码阅读初始化环境搭建
  2. Spring Boot 框架整体启动流程详解
  3. Spring Boot 系统初始化器详解
  4. Spring Boot 监听器详解
  5. Spring Boot banner详解
  6. Spring Boot 属性配置解析
  7. Spring Boot 属性加载原理解析
  8. Spring Boot 异常报告器解析
  9. Spring Boot 3.x 自动配置详解

你可能感兴趣的:(Spring,5.x,spring,java,云原生,spring,boot)