本篇我们主要学习一下SpringBoot日志框架,在项目的开发中,日志是必不可少的一个记录事件的组件,不管是记录运行情况还是追踪线上问题,都离不开对日志的分析,所以也会相应的在项目中实现和构建我们所需要的日志框架。而市面上常见的日志框架有很多,比如:JCL、SLF4J、Jboss-logging、jUL、log4j、log4j2、logback等等,我们该如何选择呢?
通常情况下,日志是由一个抽象层+实现层的组合来搭建的。
现有的一些抽象层框架和实现层框架:
日志-抽象层 | 日志-实现层 |
---|---|
JCL(Jakarta Commons Logging)、SLF4J(Simple Logging Facade for Java)、jboss-logging | jul(java.util.logging)、log4j、logback、log4j2 |
为什么日志是由一个抽象层+实现层的组合来搭建的?
主要是因为日志框架也会需要进行迭代,且市面上有各种各样的日志框架,如果每次升级或者切换日志框架都是直接修改实现层的话成本太高,接入过于复杂,很不友好,分成抽象层和实现层就是为了升级或者换日志框架只需要换实现类即可,方便快速安全
日志抽象层怎么选?
JCL是由Jakarta团队编写的,所以叫JCL但是2014年后已经不更新了
jboss-logging是给一些特定的项目使用的,不适合日常的使用
所以抽象层的日志最好是使用SLF4J
日志实现层怎么选?
jul是java util下的工具,功能不够强大
logback是log4j的升级加强版,同时他们和SLF4J是同一个作者,使用起来会更方便
log4j2功能也很强大,是apache编写的,但是现在使用不广泛
所以:日志实现层就用logback
SpringBoot的默认框架:
Spring 框架选择使用了 JCL 作为默认日志输出。而 Spring Boot 默认选择了 SLF4J 结合 LogBack
在开发的时候不应该直接使用日志实现类,应该使用日志的抽象层
首先引入依赖
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
官方给出的简单示例:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}
下图是 SLF4J 结合各种日志框架的官方示例,从图中可以清晰的看出 SLF4J API 永远作为日志的门面, 直接应用与应用程序中
从上面的图中我们可以看到:
(1).开发都是基于SLF4J API进行开发,但是日志的底层实现是由其他的实现框架实现的
(2).像log4j这种老的日志框架在最开始开发的时候是不知道会有SLF4J这种抽象层框架的,所以他们在结合的时候中间需要一个适配器的jar包(适配器的实现思想是:实现SLF4J的一些相关方法,然后再实现中调用log4j的具体实现),使用jul(java.util.logging)也一样需要导入对应的适配器jar包
(3).SLF4J也有一些自己的实现框架:slf4j-simple.jar、slf4j-nop.jar
注意:由于每一个日志的实现框架都有自己的配置文件,所以在使用SLF4j之后,配置文件还是要使用实现日志框架的配置文件
一般情况下,在项目中存在着各种不同的第三方jar,并且他们的日志选择也可能不仅相同,显然这样是不利于我们使用的,比如:A项目(slf4J + logback): Spring(commons logging)、Hibernate(jboss-logging)、mybatis…,那么如果我们想为项目设置统一的日志框架该怎么办呢?
在SLF4J官方,也给了我们参考的例子
从图中我们可以得到一种统一日志框架使用的方式,可以使用一种和要替换的日志框架类一样的jar进行替换,这样不至于原来的第三方jar报错,而这个替换的jar其实使用了SLF4J API,这样项目中的日志就都可以通过SLF4J API结合自己选择的框架进行日志输出。
统一日志框架使用步骤归纳如下:
在 Spring Boot 的 Maven 依 赖里可以清楚的看到 Spring Boot排除了其他日志框架
我们自行排除依赖是也只需要按照图中的方式就好了
排除了以后项目肯定就会报错,那么我们就需要引入中间包来进行替换
Spring Boot 是使用了SLF4J+logback 的日志框架组合,查看Spring Boot项目的Maven 依赖关系可以看到 Spring Boot 的核心启动器 spring-boot-starter 引入了 spring-boot-starter-logging
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>2.4.0.RELEASE</version>
</dependency>
而 spring-boot-starter-logging 的 Maven 依赖主要引入了 logback-classic (包含了日志框架 Logback 的实现),log4j-to-slf4j (在 log4j 日志框架作者开发此框架的时候还没有想到使用日志抽象层进行开 发,因此出现了 log4j 向 slf4j 转换的工具),jul-to-slf4j ( Java 自带的日志框架转换为 slf4j)
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.10</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
<version>2.17.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.33</version>
<scope>compile</scope>
</dependency>
</dependencies>
从上面的分析,Spring Boot 对日志框架的使用已经是清晰明了了,我们使用 IDEA 工具查看 Maven 依赖关系,可以清晰的看到日志框架的引用
由此可见,Spring Boot 可以自动的适配日志框架,而且底层使用 SLF4j + LogBack 记录日志,如果我们自行引入其他框架,需要排除其日志框架
这个就看我们具体选择哪一种了:比如选择logback就直接引入logback,如果选择log4j的话要同时导入log4j和log4j的适配器jar
从上面的分析,发现 Spring Boot 默认已经使用了SLF4J + LogBack . 所以我们在不进行任何额外操作的情况下就可以使用 SLF4J + Logback 进行日志输出。
编写Java 测试类进行测试。
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class LogbackTest {
Logger logger = LoggerFactory.getLogger(getClass());
@Test
public void testLog() {
logger.trace("Trace 日志...");
logger.debug("Debug 日志...");
logger.info("Info 日志...");
logger.warn("Warn 日志...");
logger.error("Error 日志...");
}
}
已知日志级别从小到大为 trace < debug < info < warn < error .
运行得到输出如下:
由此可见 Spring Boot 的默认级别就是INFO也就是root级别
从上面的日志结合 Logback 日志格式可以知道 Spring Boot 默认日志格式是
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n # %d{yyyy-MM-dd HH:mm:ss.SSS} 时间
# %thread 线程名称
# %-5level 日志级别从左显示5个字符宽度
# %logger{50} 类名
# %msg%n 日志信息加换行
至于为什么 Spring Boot 的默认日志输出格式是这样?
我们可以在 Spring Boot 的源码里找到答案。
可以直接在配置文件编写日志相关配置
# 日志配置
# 指定具体包的日志级别(这里就是com.lagou包的日志级别)
logging.level.com.lagou=debug
# 控制台和日志文件输出格式
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
# 日志输出路径,默认文件spring.log
logging.file.path=spring.log
#logging.file.name=log.log
关于日志的输出路径,可以使用 logging.file 或者 logging.path 进行定义
因为 Log4j 日志框架已经年久失修,原作者都觉得写的不好,所以下面演示替换日志框架为Log4j2 的方式。
首先我们排除掉spring-boot-starter-logging,如下:
再执行一下:
可以看到并没有日志的输出,说明排除成功了
接下来我们再切换成log4j2
切换成功啦!