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)语句,以当前类作为参数初始化日志记录器,这样在打印日志时,就能明确日志信息出自哪个类。  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/962266.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【大厂AI实践】OPPO:大规模知识图谱及其在小布助手中的应用

导读:OPPO知识图谱是OPPO数智工程系统小布助手团队主导、多团队协作建设的自研大规模通用知识图谱,目前已达到数亿实体和数十亿三元组的规模,主要落地在小布助手知识问答、电商搜索等场景。 本文主要分享OPPO知识图谱建设过程中算法相关的技…

机器学习周报-文献阅读

文章目录 摘要Abstract 1 相关知识1.1 WDN建模1.2 掩码操作(Masking Operation) 2 论文内容2.1 WDN信息的数据处理2.2 使用所收集的数据构造模型2.2.1 Gated graph neural network2.2.2 Masking operation2.2.3 Training loss2.2.4 Evaluation metrics 2…

工具的应用——安装copilot

一、介绍Copilot copilot是一个AI辅助编程的助手,作为需要拥抱AI的程序员可以从此尝试进入,至于好与不好,应当是小马过河,各有各的心得。这里不做评述。重点在安装copilot的过程中遇到了一些问题,然后把它总结下&…

后盾人JS--闭包明明白白

延伸函数环境生命周期 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> <…

顺启逆停程序

两台电机用Q0.0和Q0.1表示&#xff0c;分别有自身的启动和停止按钮&#xff0c;第一台电机启动后&#xff0c;第二台电机才能启动。停止时&#xff0c;第二台电机停止后&#xff0c;第一台电机才能停止。 1. 按下按钮SB1&#xff0c;接触器KM1线圈得电吸合&#xff0c;主触点…

登录授权流程

发起一个网络请求需要&#xff1a;1.请求地址 2.请求方式 3.请求参数 在检查中找到request method&#xff0c;在postman中设置同样的请求方式将登录的url接口复制到postman中&#xff08;json类型数据&#xff09;在payload中选择view parsed&#xff0c;将其填入Body-raw中 …

CUDA学习-内存访问

一 访存合并 1.1 说明 本部分内容主要参考: 搞懂 CUDA Shared Memory 上的 bank conflicts 和向量化指令(LDS.128 / float4)的访存特点 - 知乎 1.2 share memory结构 图1.1 share memory结构 放在 shared memory 中的数据是以 4 bytes(即 32 bits)作为 1 个 word,依…

基于Springboot的社区药房管理系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业多年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了多年的设计程序开发&#xff0c;开发过上千套设计程序&#xff0c;没有什么华丽的语言&#xff0c;只有实…

【力扣系列题目】最后一块石头的重量 分割回文串 验证回文串 等差数列划分{最大堆 背包 动态规划}

文章目录 七、最后一块石头的重量最后一块石头的重量【堆】[最后一块石头的重量 II](https://leetcode.cn/problems/last-stone-weight-ii/)【背包】 八、分割回文串分割回文串【分割子串方案数量】[分割回文串 II](https://leetcode.cn/problems/omKAoA/)【最少分割次数】[分割…

KIMI K1.5:用大语言模型扩展强化学习(论文翻译)

文章目录 KIMI K1.5技术报告摘要 1. 引言2. 方法&#xff1a;基于大语言模型的强化学习2.1 强化学习提示集整理2.2 长思维链监督微调2.3 强化学习2.3.1 问题设定2.3.2 策略优化2.3.3 长度惩罚2.3.4 采样策略2.3.5 训练方法的更多细节 2.4 长到短&#xff1a;短思维链模型的上下…

【Linux系统】进程间通信:实现命名管道通信

认识命名管道通信 命名管道通信的结构图示&#xff1a; 图中的 Server 和 Client 是不同的进程&#xff0c; Server 负责发送数据&#xff0c; Client 则是接收数据&#xff0c;进程之间通过命名管道进行数据通信 准备工作&#xff1a; 创建以下文件 Server.hpp #服务器类的…

SpringBoot Web开发(SpringMVC)

SpringBoot Web开发&#xff08;SpringMVC) MVC 核心组件和调用流程 Spring MVC与许多其他Web框架一样&#xff0c;是围绕前端控制器模式设计的&#xff0c;其中中央 Servlet DispatcherServlet 做整体请求处理调度&#xff01; . 除了DispatcherServletSpringMVC还会提供其他…

Linux《基础指令》

在之前的Linux《Linux简介与环境的搭建》当中我们已经初步了解了Linux的由来和如何搭建Linux环境&#xff0c;那么接下来在本篇当中我们就要来学习Linux的基础指令。在此我们的学习是包括两个部分&#xff0c;即指令和关于Linux的基础知识&#xff1b;因此本篇指令和基础知识的…

我的求职面经:(1)C++里指针和数组的区别

经典问题&#xff1a; char s1[]"hello"; char *s2"hello"; 1、s1的值是放在栈上的&#xff0c;值是可以修改的&#xff0c;而hello是一个字符串常量放在静态存储区是不能修改的。 2、内存大小不一样 #include<stdio.h>int main(){char s1[]&quo…

react中如何获取dom元素

实现代码 const inputRef useRef(null) inputRef.current.focus()

【LLM】Deepseek本地部署学习

文章目录 1. 访问ollama官网安装平台2. 选择配置3. 下载和运行 1. 访问ollama官网安装平台 https://ollama.com/ 2. 选择配置 参考以下配置要求 3. 下载和运行 ollama run deepseek-r1:7b

deepseek R1 14b显存占用

RTX2080ti 11G显卡&#xff0c;模型7b速度挺快&#xff0c;试试14B也不错。 7B显存使用5.6G&#xff0c;14B显存刚好够&#xff0c;出文字速度差不多。 打算自己写个移动宽带的IPTV播放器&#xff0c;不知道怎么下手&#xff0c;就先问他了。

【漫话机器学习系列】064.梯度下降小口诀(Gradient Descent rule of thume)

梯度下降小口诀 为了帮助记忆梯度下降的核心原理和关键注意事项&#xff0c;可以用以下简单口诀来总结&#xff1a; 1. 基本原理 损失递减&#xff0c;梯度为引&#xff1a;目标是让损失函数减少&#xff0c;依靠梯度指引方向。负梯度&#xff0c;反向最短&#xff1a;沿着负…

让万物「听说」:AI 对话式智能硬件方案和发展洞察

本文整理自声网 SDK 新业务探索组技术负责人&#xff0c;IoT 行业专家 吴方方 1 月 18 日在 RTE 开发者社区「Voice Agent 硬件分享会」上的分享。本次主要介绍了 AI 对话式智能硬件的发展历程&#xff0c;新一波 AI 浪潮所带来的创新机遇、技术挑战以及未来的展望。 在语音交…

SpringBoot 日志

目录 一. 日志概述 二. 日志的使用 1. 打印日志 (1) 获取日志对象 (2) 输出要打印的内容 2. 日志框架简介 (1) 门面模式简介 (2) SLF4J 框架简介 3. 日志的格式 4. 日志的级别 5. 日志配置 (1) 配置日志级别 (2) 日志持久化存储 ① 配置日志文件名 ② 配置日志的…