目录
一、SpringBoot日志框架的介绍
Spring Boot是一种微框架,因为其易于集成多个日志框架而闻名。Spring Boot中默认采用的日志框架是Logback,但是用户也可以通过添加依赖来使用其他流行的框架,例如Log4j2和Java Util Logging。
Spring Boot通过使用Commons Logging框架来进行桥接,使得开发者可以轻松地在多个日志框架之间进行切换或者集成多个框架。
在Spring Boot的配置文件中,可以使用logging.level属性来配置日志输出级别,以及使用logging.file和logging.path属性来配置日志输出路径。
此外,Spring Boot也提供了一些方便的特性,例如可定制的日志格式和便于实现访问日志的功能。
二、使用SpringBoot日志的好处
为什么要使用日志框架去记录日志,因为记录日志可以让你的系统以及一些行为在发生错误的时候能够快速的定位并且解决。
1、记录操作轨迹、监控系统运行状况、回溯系统故障。
例如,记录操作的行为和操作的轨迹数据,可以数据化的分析用户偏好,有助于优化业务逻辑,为用户提供个性化的服务。比如:通过access.log,记录用户的操作频度和跳转连接,有助于分析用户的后续行为。
2、全面有效的日志系统有助于建立完善的应用监控体系,由此工程师可以实时的监控系统的运行状况,及时预警,避免故障的发生
例如,监控系统运行的状况,通过日志的记录,来对服务器的使用状态,如内存、CPU等使用情况;应用的运行情况,如响应时间QPS等交互状态;应用的错误信息,如空指针、SQL异常等监控。比如,在CPU使用率大于60%的时候,四核服务器中的load大于4的时候发出报警,提醒工程师及时处理,避免发生故障。
3、当系统发生线上问题的时候,完整的现场的日志有助于工程师快速的定位并且解决问题。
例如,当系统内存溢出的时候,如果日志系统记录了问题的发生堆信息,就可以通过这个日志分析是什么对象在大量的产生并且没有释放内存,回溯系统故障,从而定位问题。
三、SpringBoot日志框架的使用
在实际的项目开发中,日志框架是很重要的框架,不管是记录运行情况还是定位线上问题,都离不开对日志的分析。在 Java 领域里存在着多种日志框架,如 JCL、SLF4J、Jboss-logging、jUL、log4j、log4j2、logback 等等。
虽然,失眠上的日志有很多种,但是这么多的日志其实可以分为二种:
1、日志门面(日志的抽象层)
日志的门面是什么呢 ?简单的讲就是为 Java 日志的访问提供了一套标准和规范的 API 框架,其主要意义在于提供接口。那么是接口,就需要有实现类是实现这个接口,所以下面一种就是日志的实现。
日志门面(日志的抽象层)的一些框架,例如:
JCL(Jakarta Commons Logging)(2014后不再维护中)
jboss-logging (不适合企业项目开发使用)
SLF4j(Simple Logging Facade for Java)
2、日志实现
日志实现,简单来说就是实现日志门面(日志的抽象层)中的API接口。
日志实现的一些框架,例如:
Log4j
JUL(java.util.logging)(java.util.logging 担心被抢市场推出的一种日志框架)
Log4j2 (Apache开发的很强大的日志框架,借助了log4j的名称,但是很多框架都未适配上)
Logback(Log4j同一个人开发的新框架,做了重大的升级)
通常情况下,日志是由一个日志门面与一个日志实现组合搭建而成,Spring Boot 选用 SLF4J + Logback 的组合来搭建日志系统。
SLF4J 是目前市面上最流行的日志门面,使用 Slf4j 可以很灵活的使用占位符进行参数占位,简化代码,拥有更好的可读性。
Logback 是 Slf4j 的原生实现框架,它与 Log4j 出自一个人之手,但拥有比 log4j 更多的优点、特性和更做强的性能,现在基本都用来代替 log4j 成为主流。
在开发的时候不应该直接使用日志实现类,应该使用日志的抽象层。具体怎么使用可以查看官网的SLF4J 手册
下面这张图片是来自官方,我们可以由图可知SFL4J这款优秀的日志框架结合了各种的日志框架,并且还可以清晰的看出来SFL4J API一直都是作为抽象层也就是日志门面。
如果不是很理解,可以去看看官方的解释,我这里就不多做解释了。
3、SpringBoot日志框架的引入
一般情况下,org.springframework.boot:spring-boot-starter-web,都会自带这些日志框架,由图可知。这个spring-boot-starter-web依赖都已经有这个日志框架了,那我为什么还要说引用日志框架呢,因为我的这个spring-boot-starter-web没有这些依赖。
我们从spring-boot-starter-web可以看出来这个springboot的底层,它已经依赖了这3个日志框架,例如: sfl4j、logback、log4j2。
接下来我们引入所需要的依赖框架
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>2.5.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
依赖框架引入完成之后,那么接下来我们就来完成一个简单的日志框架测试类。
但是在测试之前,我还需要讲一下日志的级别和格式
日志级别 | 描述 |
---|---|
trace | 较低的日志级别,通常不会被使用,日志的输出很详细。 |
debug | 程序员调式代码的时候使用,开发过程中打印一些运行信息。 |
info | 记录运维(程序运行)过程的数据 |
warn | 警告信息,潜在的问题信息,在生产日志中,作为给程序员的一种提醒而使用。 |
error | 打印错误日志,但是不会影响程序继续运行。 |
fatal | 致命的,灾难性的错误信息,但是已经不存在了,统一合并为error |
日志的级别等级按照从低到高排序:trace < debug < info < warn < error
4、日志的格式
传统的控制台输出(未加日志框架的情况输出),如下:
2023-05-23 22:37:35.130 INFO 3408 --- [ restartedMain] s.yun.blogSystem.BlogSystemApplication : Starting BlogSystemApplication using Java 17.0.4.1 on LAPTOP-G96HOK5P with PID 3408 (D:\BlogSystem\target\classes started by 20604 in D:\BlogSystem)
2023-05-23 22:37:35.132 INFO 3408 --- [ restartedMain] s.yun.blogSystem.BlogSystemApplication : The following 1 profile is active: "dev"
以上输出的内容有:
-
日期和时间 : 精确到毫秒
-
日志的级别 :例如 (error、info、warn、debug、trace)
-
进程的标识 : 可以查询该服务的具体进程
-
--- : 分割线
-
线程名称 : 可以是用 [...] 框起来 利于查看
-
记录器名称 : 这通常是运行类的名称
-
日志消息
日志格式的配置(我这里是以.properties为主)
# 配置日志服务 根据root根节点设置 代表整体应用的级别
logging.level.root=info
# 输出日志的时间、线程、日志级别、日志记录器名称、日志消息都输出到控制台。
logging.pattern.console=%clr(%d{yyyy/MM/dd-HH:mm:ss}){cyan} %clr(%5p) --- %clr([%15thread]){magenta} %clr(%logger- %msg%n){red}
# 日志存放的位置
logging.file.path=D:/BlogSystem/log/
# 时间、线程、日志级别、日志记录器名称、日志消息都输出到日志文件中
logging.pattern.file=%d{yyyy/MM/dd-HH:mm} [%thread] %-5level %logger- %msg%n
# 指定文件的大小
logging.logback.rollingpolicy.max-file-size= 20KB
第一条配置(logging.level.root)是配置日志的输出级别。
第二条配置(logging.pattern.console)是控制台输出的格式(里面包含很多):
- %clr(%...) {color} : 配置输出字体的颜色(支持的颜色 blue、cyan、faint、green、magenta、red、yellow)加了这个属于指定颜色 {color} ,不加就是系统默认的颜色。
- % d或者%data : 配置日志的日期,默认是ISO8601的标准日期,相当于yyyy-MM-dd HH:mm:ss:SSS ,自己配置的日志日期可以精确到毫秒。
- %level或者%p : 配置日志的级别,日志的级别包括(trace、debug、info、warn、error)。
- %thread或者%t : 配置日志的线程名称 。
- %logger : 配置日志输出的 包名+类名。
- % msg 或者%m: 配置输出日志的内容。
- % n : 换行符,如果不加这个,那么所有的信息都在连接一起输出,可读性很差。
- %M : 配置日志发生的方法名。
- %L : 配置日志调用时所在的行,线下运行的时候不建议使用此参数,因为获取代码的行号性能有损耗。
- % 5 ?? : 配置信息前面加数字可以配置输出的长度,5前面没有加- 那么默认就是往右靠,加了就是往左靠。
第三条(logging.file.path)是配置是日志内容输出的文件名称,也可以不选择指定文件夹,直接logging.file.name = **.log,那么当有日志输出的时候springboot会将日志输出到该文件的最外层。
第四条(logging.pattern.file)是日志输出到文件的内容格式。
第五条(logging.logback.rollingpolicy.max-file-size)是指定日志文件的大小当到了这个大小就将日志输出的内容分割成一个新的.log文件,默认是10MB。
日志级别的日志格式讲完之后,那么我们对这些有了一些了解之后,接下来就是创建一个日志控制层的类或者测试类也可以,我们这里主要以@Controller类为主。
我们创建一个LogController的控制器类,并使用@GetMapping注解来指定请求路径为/LogHello,HTTP请求方法为GET。并在该方法上添加一个@ResponseBody注解,因为我们没有相对应的网页地址,需要将一个Java对象转化为被称为HTTP响应体(HTTP Response Body)的格式,并返回给客户端。
package springboot.yun.blogSystem.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @BelongsProject: BlogSystem
* @BelongsPackage: springboot.yun.blogSystem.controller
* @Author: 云边小屋(My.Tears)
* @CreateTime: 2023-05-23 22:06
* @Description: TODO
* @Version: 1.0
*/
@Controller
public class LogController {
// 获取日志对象
Logger logger = LoggerFactory.getLogger(this.getClass());
@GetMapping("/LogHello")
@ResponseBody
public String LogHello() {
/**
* 日志的级别(由低到高)
* trace < debug < info < warn < error
* 可以指定日志的级别,日志就会在本级别及以上更高级别的所有日志均生效
* (如果不指定,则默认从info级别开始(root权限))
*/
logger.trace("这是trace追踪日志记录");
logger.debug("这是debug调试日志记录");
logger.info("这是info运行日志记录");
logger.warn("这是warn警告日志记录");
logger.error("这是error错误日志记录");
return "LogHello";
}
}
接下来我们启动是springboot主程序类并打开浏览器访问这个路径,此时的idea控制台信息,并未出现日志信息,因为我们并没有访问这个路径。
下面我们打开浏览器访问该请求地址localhost:8080/LogHello 此时浏览器输出了一条信息。
接下来,我们查看idea控制台
此时控制台出现了这几条信息,为什么只输出了这几条呢,因为我的配置文件中日志的输出级别是info 那么我们可以看日志的输出等级级别来看trace < debug < info < warn < error(从低到高排序)因为日志级别默认是info级别的,所以低于此级别的日志是不会打印出来的,并且我们设置的日志级别是info 那么最低的日志级别也是info。
你可以自己测试多种日志级别,这里我就不多做展示了。
下面有种方法不用获取logger对象,可以使用注解的方法来获取对象,方法更加的简单。
package springboot.yun.blogSystem.controller;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @BelongsProject: BlogSystem
* @BelongsPackage: springboot.yun.blogSystem.controller
* @Author: 云边小屋(My.Tears)
* @CreateTime: 2023-05-23 22:06
* @Description: TODO
* @Version: 1.0
*/
@Slf4j // 可以自动生成日志记录器(Logger)代码
@Controller
public class LogController {
// // 获取日志对象
// Logger logger = LoggerFactory.getLogger(this.getClass());
@GetMapping("/LogHello")
@ResponseBody
public String LogHello() {
/**
* 日志的级别(由低到高)
* trace < debug < info < warn < error
* 可以指定日志的级别,日志就会在本级别及以上更高级别的所有日志均生效
* (如果不指定,则默认从info级别开始(root权限))
*/
log.trace("这是trace追踪日志记录");
log.debug("这是debug调试日志记录");
log.info("这是info运行日志记录");
log.warn("这是warn警告日志记录");
log.error("这是error错误日志记录");
return "LogHello";
}
}
这种方法就是使用@Slf4j注解来获取日志对象,只是这样获取的话,有固定的格式。
当你添加@Slf4j该注解的时候log对象会自动创建并初始化,我们可以调用它的不同方法来输出不同级别的日志。
需要注意的是,在使用@Slf4j注解时,需要在项目的构建工具中添加Lombok依赖,并安装Lombok插件。否则,在编译时会提示找不到log对象的错误。
接下来我们来讲一下日志输出到文件中
5、日志持久化
以上所说的,都是将日志输出到控制台,并没有将日志输出到文件上保存下来,然⽽在⽣产环境上我们需要将⽇志保存下来,以便出现问题之后能够快速的查找问题,我们需要把⽇志保存下来,而这个过程就叫做日志的持久化。
由于我们上面已经配置过了文件输出的文件夹,以及输出格式,那么我们接下来就来看看输出到文件的内容。
我们重启一下服务器,然后继承访问localhost:8080/LogHello,来查看日志是否输出到了我们指定的文件夹上,如果有文件那么D:/BlogSystem/log/ 在我的文件夹下应该存在springboot自动帮我创建的.log文件,如果存在我们就打开该文件,观察该日志文件。
上面我访问的第一次,我们继续在访问一次(具体查看时间线),继续观察该文件,可以看到日志是通过追加的方式将日志输出到文件中的,而不是通过覆盖的方法。
接下来我们再来看看,当日志文件达到我设置的那个文件内容最大值的时候,会不会分文件夹,我这里设置的是20KB,设置太小了,不易观察,我这里因为对该路径访问了多次,这里已经分了几次文件了,这个就不好演示了,你可以自己去尝试一下,每访问一次 ,并观察文件一次,直至它达到预设的文件大小,分文件为止。
若发现不足的或者不对的,可私信我。