Spring Boot 日志:项目的“行车记录仪”
一、什么是Spring Boot日志
(一)日志引入
在正式介绍日志之前,我们先来看看上篇文章中(Spring Boot 配置文件)中的验证码功能的一个代码片段:

这是一段校验用户输入的验证码是否正确的后端代码, 仔细看,其中有以下三种异常情况,程序会返回false:
(1)用户输入的验证码为空或者没有输入验证码
(2)session为空
(3)验证码超时
那么,当程序运行时,程序返回false时,我们并不知道到底是属于哪一种异常情况,因此,我们借助println方法,打印相关信息,以此来定位返回false返回的是哪种情况,比如,当控制台打印 ” 验证码超时 “ 时,我们就知道,此时返回false是因为验证码已经超时。
通过这种打印信息的方式,可以帮助我们精准定位问题出现的原因,这里打印出来的信息,称为日志!
随着项目复杂度提升,我们对日志打印有更高需求,不仅用于定位排查问题,还需记录用户操作记录(部分审计公司有此要求)、记录用户喜好,将日志持久化后用于数据分析等。但 println 无法很好满足需求,需使用专门日志框架。
当我们启动项目时,是不是经常会看到如下图的信息,其实这就是日志框架生成的日志!

(二)日志与行车记录仪
我们可以将日志类比为生活中的行车记录仪,它们在功能和作用上有很多相似之处:
记录功能:行车记录仪会持续记录车辆行驶过程中的各种信息,比如行驶路线、速度、时间以及周围的路况等,就像日志会记录 Spring Boot 应用程序运行过程中的各种信息,包括方法调用、参数传递、执行时间和发生的事件等。
问题排查:当车辆发生事故或者出现一些异常情况时,车主可以通过查看行车记录仪的记录来还原当时的场景,分析事故发生的原因或者车辆出现问题的可能因素。类似地,当 Spring Boot 应用出现故障或错误时,开发人员和运维人员可以通过查看日志来了解应用在故障发生前后的运行情况,快速定位问题所在,比如是哪个方法出现了异常,在什么时间、什么条件下出现的等。
行为追溯:如果遇到一些纠纷,例如在判断是否发生追尾等事故责任时,行车记录仪的记录可以作为重要的证据,清晰地呈现车辆的行驶状态和相对位置等情况,帮助确定责任归属。在 Spring Boot 应用中,日志也可以用于追溯用户的操作行为和系统的响应过程,对于一些需要审计或者追踪的场景非常有用,比如记录用户的登录登出时间、对重要数据的修改操作等,以便在需要时进行查询和核对。
性能评估:通过行车记录仪记录的行驶数据,比如在不同路段的行驶速度、停车次数等,还可以分析车辆的行驶性能和效率,判断是否存在行驶路线不合理或者车辆本身性能问题等。同样,通过分析 Spring Boot 应用的日志,能够评估应用的性能状况,例如某个接口的响应时间是否过长,数据库查询的频率是否过高,从而为性能优化提供依据。
(三)简单打印一个日志
接下来让我们学习一下如何使用日志框架来打印一个日志!


由于程序是在main方法中运行的,因此线程名称是main;此外,在获取日志对象的时候,我们填入LoggerController.class作为参数,因此日志的来源类就是它,这样做可以让我们更加精准地定位日志来源于哪个类!
二、门面模式
(一)什么是门面模式
在很久之前,市场上存在多种日志框架,如 Log4j、Logback、java.util.logging 等,它们的使用方式各不相同,比如日志框架1是使用log1()方法来打印日志的,日志框架2是使用log2()方法来打印日志的。
那么在一个大型项目中,可能会有多个团队或开发者参与开发,如果没有统一的日志记录规范,不同团队可能会使用不同的日志框架,会导致代码风格和日志记录方式混乱。
那么,该如何解决这个问题呢?
虽然各大框架的使用方式不同,但是,我们是不是可以使用一个平台,让这个平台集成各种不同的日志框架,然后将不同框架中功能相同的方法,对外提供一个统一的接口,供程序员使用,这样就解决了代码风格不一致的问题。这种模式就叫 " 门面模式 "(这里的平台就被称为——日志门面)。

上面说的 slf4j 其实就是日志门面,它为所有开发者提供了一套统一的日志记录接口,无论底层使用哪种日志框架,开发者都使用相同的方式记录日志,有助于保持代码的一致性和规范性,降低代码的学习和维护成本。
门面模式(Facade Pattern)又称为外观模式,提供了一个统一的接口,用来访问子系统中的一群接口。其主要特征是定义了一个高层接口,让子系统更容易使用。
(二)门面模式的实现
下面我们来实现一个简单的门面模式,我们想象这样一个场景,当我们回家时,我们可能会打开客厅的灯,打开卧室的灯、打开走廊的灯……
如果没有使用门面模式,我们需要一个一个走过去相应的场所去打开灯,那么,如果使用门面模式,我们就可以通过一个总开关,一次性打开所有地方的灯……
1、我们先定义客厅、卧室、走廊三个类,类中都有一个on()方法,用于打开灯
客厅类:

卧室类:

走廊类:

2、传统模式下开灯:
传统模式下开灯,我们需要在main方法中创建客厅、卧室、走廊三个类的实例,然后调用其 on()方法开灯,特别麻烦。


3、门面模式下开灯:
首先,我们定义一个门面,里面集成了客厅、卧室、走廊。

现在我们开灯的时候,就只需要实例Facade对象的实例,然后调用其on()方法即可开灯,比之前方便不少!


在这个案例中,我们可以把客厅、卧室、走廊类比为不同的日志框架,我们发现,即便是框架里面的方法名称改变了,比如客厅打开灯的方法名不是on(),而变成了livingOn()了,此时用户也无需去操心这种方法名变化对自己开发有什么影响,因为门面对外提供的接口还是on(),框架改变产生的影响,只需要门面去考虑,只要门面对外提供的接口不变,用户就无需去操心框架的改变带来的影响。
三、日志级别
(一)日志格式说明
在前面打印日志的例子中,我简单介绍了一下日志的格式,接下来,我再正式的详细介绍一下日志格式,带大家深入了解一下,日志内容究竟包含了什么信息?

这段日志是典型的 Spring Boot 应用程序启动日志,以下是对其格式的说明:
时间戳:日志的开头是时间戳,例如 2025-01-28T10:11:09.772+08:00,表示日志记录的时间,精确到毫秒,并且包含了时区信息(+08:00 表示东八区)。
日志级别:紧接着时间戳的是日志级别,如 INFO,表示日志的重要程度。常见的日志级别还有 DEBUG、WARN、ERROR 等。
进程 ID:9852 是进程 ID,标识生成该日志的进程。
线程名称:[spring_setting_test3] 是线程名称,表明该日志是由哪个线程产生的。通常是源代码的类名。
日志记录器名称:中括号后面的内容,如 main 是日志记录器名称,用于标识日志的来源。
日志内容:冒号后面的部分是具体的日志信息,描述了应用程序在启动过程中发生的事件,
(二)日志级别
上面在介绍日志格式的时候,我们看到了“ 日志格式 “,那么,啥是日志级别呢?
日志级别:是在软件开发和系统运维中用于对日志信息进行分类和分级的一种标准机制,大白话来说就是:日志级别就是对日志消息轻重程度进行了分级。
就好像天气预报根据降雨的严重情况,对雨进行分级:小雨、大雨、暴雨、雷阵雨……同样,日志消息也有轻重缓急之分,有的消息只会记录一些小事,有些日志消息会记录一些程序奔溃的错误消息。
日志的级别从高到低依次为:FATAL、ERROR、WARN、INFO、DEBUG、TRACE

FATAL:致命信息,表示需要立即被处理的系统级错误。
ERROR:错误信息,级别较高的错误日志信息,但仍然不影响系统的继续运行。
WARN:警告信息,不影响使用,但需要注意的问题。
INFO:普通信息,用于记录应用程序正常运行时的一些信息,例如系统启动完成、请求处理完成等。
DEBUG:调试信息,需要调试时候的关键信息打印。
TRACE:追踪信息,比 DEBUG 更细粒度的信息事件 (除非有特殊用意,否则请使用 DEBUG 级别替代)
(三)日志级别配置
默认情况下,日志框架只会打印 info级别 或 info 级别以上级别的日志消息,因为如果打印info级别以下的日志消息记录的是一些繁琐的小事,因此默认不打印出来。


观察上面的main方法,我们打印了除了FATAL级别的日志消息(日志框架没有提供FATAL级别的日志打印),但是运行程序时,只打印 INFO 级别或 INFO 级别以上的日志消息。
其实,这是日志配置的默认输出级别是 INFO ,程序只会默认输入这个级别或以上级别的日志,那么,既然是默认的,我们也可以自定义设置默认级别为其他级别。
1、在yml文件中配置日志输出级别为DEBUG:

运行main方法:
我们发现,虽然设置默认输出级别为debug,但是运行mian方法时,却没有打印出来debug级别的日志,这是为什么呢?
这是因为,你手动运行main方法,配置文件对main方法是不起作用的,我们得通过url访问调用main方法,这样配置文件才会起作用。
代码修改:

Postman访问调用main方法:


2、接下来我们在yml文件中配置日志输出级别为TRACE:

Postman访问调用main方法:


补充:因为FATAL级别的消息是致命消息,一旦出现,系统就无法正常运行。只要用户看到系统崩溃,就知道系统已经出现了致命错误,所有无需打印出来告知用户,因此日志框架没有提供FATAL级别的日志打印。
四、日志(补充)
(一)日志持久化
我们发现,一旦程序结束运行,我们之前的日志消息就全部被清空了。那么我们能不能生成一个日志文件,让它来记录日志信息呢?
有的,兄弟,有的,像生成日志文件这样的的方法我们有两种,并且都是通过配置文件配置即可自动生成!
配置方法1:yml 配置日志文件名:
#配置日志文件名存储历史日志
logging:file:name: logger/springBoot.log
配置后启动项目,日志框架会生成 logger/springBoot.log 日志文件

配置方法2:yml配置日志文件路径:
#配置日志文件路径:会在D:/temp 路径下生成日志文件
logging:file:path: D:/temp
配置后启动项目,日志框架会在D:/temp路径下自动生成日志文件

(二)更简单的日志输出
前面教学的打印日志,我们需要创建一个日志对象,才可以打印日志,那么有没有什么更好的方法来打印日志呢?也是有的,兄弟!我们可以使用 @Slf4j 注解
旧方法:

新方法代码: 无需创建日志对象啦!直接使用!

Postman访问调用print方法:

背后原理:
@Slf4j注解 的作用是自动生成日志对象。在使用 @Slf4j 注解的类中,它相当于帮开发者自动添加了private final Logger log = LoggerFactory.getLogger(当前类名.class); 这行代码。 通过LoggerFactory.getLogger(当前类名.class)语句,以当前类作为参数初始化日志记录器,这样在打印日志时,就能明确日志信息出自哪个类。
相关文章:
Spring Boot 日志:项目的“行车记录仪”
一、什么是Spring Boot日志 (一)日志引入 在正式介绍日志之前,我们先来看看上篇文章中(Spring Boot 配置文件)中的验证码功能的一个代码片段: 这是一段校验用户输入的验证码是否正确的后端代码,…...
Spring Boot 实现文件上传和下载
文章目录 Spring Boot 实现文件上传和下载一、引言二、文件上传1、配置Spring Boot项目2、创建文件上传控制器3、配置文件上传大小限制 三、文件下载1、创建文件下载控制器 四、使用示例1、文件上传2、文件下载 五、总结 Spring Boot 实现文件上传和下载 一、引言 在现代Web应…...
慕课:若鱼1919的视频课程:Java秒杀系统方案优化 高性能高并发实战,启动文档
代码: Javahhhh/miaosha191: 运行成功了慕课若鱼1919的视频课程:Java秒杀系统方案优化 高性能高并发实战https://github.com/Javahhhh/miaosha191 https://github.com/Javahhhh/miaosha191 miaosha项目启动文档 需安装的配置环境: VMwar…...
React第二十七章(Suspense)
Suspense Suspense 是一种异步渲染机制,其核心理念是在组件加载或数据获取过程中,先展示一个占位符(loading state),从而实现更自然流畅的用户界面更新体验。 应用场景 异步组件加载:通过代码分包实现组件…...
虚幻基础08:组件接口
能帮到你的话,就给个赞吧 😘 文章目录 作用 作用 组件接口:可以直接调用对方的组件接口,而无需转换为actor。 实现对象间的通知。 A 通知 B 做什么。...
iPhone SE(第三代) 设备详情图
目录 产品宣传图内部图——后设备详细信息 产品宣传图 内部图——后 设备详细信息 信息收集于HubWeb.cn...
2025苹果CMS v10短剧模板源码
文件不到70kb,加载非常快 无配置,没有详情页,上传就可以直接使用 使用教程:上传到网站template目录并解压、进入网站后台选择模板 注意:默认调用ID为1的数据和扩展分类,建议新建站使用 源码下载…...
2007-2020年各省国内专利申请授权量数据
2007-2020年各省国内专利申请授权量数据 1、时间:2007-2020年 2、来源:国家统计局、统计年鉴 3、指标:行政区划代码、地区名称、年份、国内专利申请授权量(项) 4、范围:31省 5、指标解释:专利是专利权的简称&…...
第一天-嵌入式应用开发介绍
首先,我们来介绍一下嵌入式的发展路线,虽然嵌入式的知识点众多,但是总体上来说,嵌入式分为以下两条主要路线: 单片机开发ArmLinux开发 当然,还有其他的一些例如FPGA这种的我们就不计算在内了,F…...
约瑟夫问题(信息学奥赛一本通-2037)
【题目描述】 N个人围成一圈,从第一个人开始报数,数到M的人出圈;再由下一个人开始报数,数到M 的人出圈;…输出依次出圈的人的编号。 【输入】 输入N和M。 【输出】 输出一行,依次出圈的人的编号。 【输入样…...
WPF5-x名称空间
1. x名称空间2. x名称空间内容3. x名称空间内容分类 3.1. x:Name3.2. x:Key3.3. x:Class3.4. x:TypeArguments 4. 总结 1. x名称空间 “x名称空间”的x是映射XAML名称空间时给它取的名字(取XAML的首字母),里面的成员(如x:Class、…...
一个python项目中的文件和目录的作用是什么?scripts,venv,predict的具体含义
今天学习SadTalker的项目,但目录和文件不知道都是干什么的,总结记录下,方便后续使用。 目录 1. docs: 作用: 这个文件夹通常包含项目的文档。文档可能包括用户指南、API 文档、开发文档等。 2. examples: 作用: 这里通常包含一些示例代码…...
python学opencv|读取图像(四十八)使用cv2.bitwise_xor()函数实现图像按位异或运算
【0】基础定义 按位与运算:两个等长度二进制数上下对齐,全1取1,其余取0。 按位或运算:两个等长度二进制数上下对齐,有1取1,其余取0。 按位取反运算:一个二进制数,0变1,1变0。 按…...
YOLOv11-ultralytics-8.3.67部分代码阅读笔记-block.py
block.py ultralytics\nn\modules\block.py 目录 block.py 1.所需的库和模块 2.class DFL(nn.Module): 3.class Proto(nn.Module): 4.class HGStem(nn.Module): 5.class HGBlock(nn.Module): 6.class SPP(nn.Module): 7.class SPPF(nn.Module): 8.class C1(nn…...
c++多态
1.多态的概念 通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状态。 2.多态的定义及实现 2.1多态的构成条件 多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为…...
ResNeSt: Split-Attention Networks 参考论文
参考文献 [1] Tensorflow Efficientnet. https://github.com/tensorflow/tpu/tree/master/models/official/efficientnet. Accessed: 2020-03-04. 中文翻译:[1] TensorFlow EfficientNet. https://github.com/tensorflow/tpu/tree/master/models/official/efficien…...
Blazor-选择循环语句
今天我们来说说Blazor选择语句和循环语句。 下面我们以一个简单的例子来讲解相关的语法,我已经创建好了一个Student类,以此类来进行语法的运用 因为我们需要交互性所以我们将类创建在*.client目录下 if 我们做一个学生信息的显示,Gender为…...
从AD的原理图自动提取引脚网络的小工具
这里跟大家分享一个我自己写的小软件,实现从AD的原理图里自动找出网络名称和引脚的对应。存成文本方便后续做表格或是使用简单行列编辑生成引脚约束文件(如.XDC .UCF .TCL等)。 我们在FPGA设计中需要引脚锁定文件,就是指示TOP层…...
苍穹外卖使用MyBatis-Plus
系列博客目录 文章目录 系列博客目录一、修改sky-take-out项目的pom.xml文件1.修改lombok依赖的版本号2.修改spring-boot-starter-parent父工程的版本号3.增加依赖 二、修改sky-server模块的pom.xml文件1.增加mysql连接的依赖(版本为8.0以上)2.增加两个依…...
Baklib引领数字化内容管理转型提升企业运营效率
内容概要 在数字化迅速发展的背景下,企业正面临着前所未有的内容管理挑战。传统的内容管理方式已难以适应如今的信息爆炸,企业需要更加高效、智能的解决方案以应对复杂的数据处理需求。Baklib作为行业的先锋,以其创新技术对数字化内容管理进…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...
tauri项目,如何在rust端读取电脑环境变量
如果想在前端通过调用来获取环境变量的值,可以通过标准的依赖: std::env::var(name).ok() 想在前端通过调用来获取,可以写一个command函数: #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...
