写在前面
Hello大家好,今日是2024年的第一天,祝大家元旦快乐🎉 2024第一篇文章从SpringBoot日志开始
在我们日常的公司开发中,难免都会存在着大大小小的BUG,不可能会有公司说我们的项目做出来是没有BUG的,那既然或多或少会BUG的话,要如何去发现BUG呢?
💬 接下去就让我们来看看日志到底是个什么东西?
记录错误日志和警告日志(发现和定位问题):
记录用户的登录日志:
记录系统的操作日志:
程序执行日志:
Spring Boot 项?在启动的时候默认就有?志输出,如下图所示:
💬 那有同学就会说,既然都有日志了,为什么还要再去学习呢?
此时我便提出了以下几个问题
下?我们?起来找寻这些问题的答案🔍
有的同学认为我们日常在写代码时使用
System.out.println()
输出的内容就是日志,那我们可以用这个方法来打印看看
@RequestMapping("/sayhi")
public String sayHi(){
System.out.println("打印日志");
return "hello world ->" + myconfig;
}
💬 那么接下来我来介绍如何通过得到日志对象来打印日志
在这之前呢,我们要先来聊聊前面所提到的【日志框架】,首先你要知道的一点是:SpringBoot是内置了日志框架的
Slf4j
Slf4j
再根据系统的配置决定要调用的具体框架是什么👉这个Slf4j
呢就类似于 房产中介🏠,我们在买房的时候不可能一家家地去找,而是通过房产中介来进行对接以此可以看到不同的户型
log4j 1
是比较多的,后面就慢慢升级成log4j 2
了? 注意:在去年log4j
被报出了漏洞,而且漏洞很严重,它可以通过这个漏洞直接把你的应用服务给关掉。那这个其实对许多公司来说是很大的一个问题,这里的话就不细讲了,有兴趣的同学可以去了解一下 链接
logback
这个日志框架了,它就有人维护比较稳定一些,不过它的写法就不太一样了,类名、方法名都不一样了,所以我们直接去对接这个框架的话成本是非常高的,但如果此时我们有了Slf4j
这个门面就不一样了💡解耦
了💬 我们也可以去验证一下SpringBoot是否真的内置了这两个框架,那可以观察到确实是有的
在大致了解了SpringBoot中内置的框架后,我们就可以去试试看要如何获取到当前程序的日志
?不过对于个日志对象的定义可以非常有讲究的
👉 那可以很清楚地知道,我们和这个日志对象一定是类级别的,因为要在整个类的任何方法都能获取到
共有的
还是私有的
,那可以很明确,每个类都有自己的日志对象,所以它肯定是私有的,所以要用private
来进行修饰static
来进行修饰final
关键字作为修饰那么再加上一步步的修饰后,我们就要通过
Slf4j
所提供的接口去新建出一个日志对象了,注意在这里我们要选择org.slf4j
这个包下的,不要导?错包!
getLogger
这个方法,这里一共有两种重写方法,我们先选择Class<?> clazz
以下是它的源码,我们后面通过查看打印出来日志的形式来进行观察:
public static Logger getLogger(Class<?> clazz) {
Logger logger = getLogger(clazz.getName());
if (DETECT_LOGGER_NAME_MISMATCH) {
Class<?> autoComputedCallingClass = Util.getCallingClass();
if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(), autoComputedCallingClass.getName()));
Util.report("See https://www.slf4j.org/codes.html#loggerNameMismatch for an explanation");
}
}
return logger;
}
// 打印日志
logger.trace("test2 trace");
logger.debug("test2 debug");
logger.info("test2 info");
logger.warn("test2 warn");
logger.error("test2 error");
trace
开始打印的,而是从info
开始打印的,这个的话就要追溯到 日志等级的优先级 和 默认级别 了,这个我们在下一模块会展开详细讲解看到了打印出来的日志信息后,相信有很多读者并不是很了解每一条日志信息到底想告诉我们什么,现在就让我们来分析一下这些日志信息吧
c.e.controller
这个名称有点熟悉呢?没错,它就是我们在这个包下创建类的时候自动导入的包名,前面两个c
和e
即为缩写package com.example.demo.controller;
Test2Controller
了,这个的话就要去回忆我们上面为 工厂类LoggerFactory 中的getLogger
这个方法所传入的当前类类名,所以SpringBoot靠着这些很好地识别到并打印出了相应的日志信息还记得我们在上面所谈到的
getLogger
这个方法的第二种形式吗,我们一起再来看看
getLogger
方法@RestController // = @Controller + @ResponseBody
public class StudentController {
private static final Logger logger = LoggerFactory.getLogger("StudentController");
@RequestMapping("/stu/sayhi")
public String sayhi(){
logger.info("student info");
logger.error("student error");
return "student sayhi";
}
}
name
来说所并不会展现出完整的包名路径,而是只有一个类名,所以想看到怎么样的日志信息读者可以自己来进行控制
以下是它的源码,供阅读参考
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
整体地介绍完日志消息后,我们再来详细地介绍一下其中的一个东西叫做【日志级别】
error
,那么就可以只看程序的报错?志了,对于普通的调试?志和业务?志就可以忽略了,从?节省开发者信息筛选的时间。trace
:微量,少许的意思,级别最低;
debug
:需要调试时候的关键信息打印;
info
:普通的打印信息(默认?志级别);
warn
:警告,不影响使?,但需要注意的问题;
error
:错误信息,级别较?的错误?志信息;
fatal
:致命的,因为代码异常导致程序退出执?的事件。
?志级别的顺序:
💬 级别越高所能接收到的消息就越少了,比如说error
只能接收到error
和fatal
级别的日志
清楚各个日志级别后,我们就来配置文件中实际地来操作一下吧💻
“logging.level”
配置项即可,如下所示:# 日志级别设置
logging:
level:
root: error
error
controller
层中的所触发的日志信息# 日志级别设置
logging:
level:
root: error
com:
example:
demo:
controller: trace
先把项目重启一下我们可以看到,任何多余的日志都没有看到,就等待我们去访问
接着去访问一下就可以看到打印出来了相关的日志信息,而且很干净,并没有任何杂志
💬 所以在学习了日志级别后我们就可以清除掉配置?件中的原先?志设置,从而随心地去控制日志的打印内容
以上的?志都是直接输出在控制台上的,然?在?产环境上咱们需要将?志保存下来,以便出现问题之后追溯问题,把?志保存下来的过程就叫做【持久化】
想要将?志进?持久化,只需要在配置?件中 指定?志的存储?录 或者是 指定?志保存?件名 之后,Spring Boot 就会将控制台的?志写到相应的?录或?件下了
home
文件夹下# 日志保存路径
logging:
file:
path: D:\\home
spring.log
的文件就出现了,点进去一看确实出现了我们上面在控制台中所出现的日志信息我们来看看几位同学的问题(???(???(???*)
💬 那有同学就说,那在项目重启之后会不会丢失呢?
💬 那在重启项目后再去运行,存储的日志会不会将之前的日志覆盖掉呢?
append
追加的过程,而不会产生一个覆盖的现象我们可以将项目重启后再来访问一下看看,便可以观察到上一次的日志信息确实还存留,并且追加上了这一次的日志信息📰
💬 如果一直像上面那样追加,如果文件变得越来越大怎么办呢?
那么除了可以设置日志的保存路径外呢,我们还可以去配置?志?件的?件名,可以不用系统默认的,自己也可以起名哦~
springboot.log
,不过要记得取名字的同时也要带上路径哦,否则就就无法输出到指定的路径了,就会直接保存在当前项目中# 日志保存名称
logging:
file:
name: D:\\home\\springboot.log
来运行一下看看,确实可以看到出现了一个名为springboot.log
的文件
💬 那这个时候又有同学问了:什么东西都存在文件里,万一系统被入侵了文件不是很容易丢失吗?
对于生产级别日志分类 —— 根据业务场景来定
📚 【综合练习】:将 controller 包下 error 级别以上的?志保存到 log_all.log 下,将 service 下warn 级别以上的?志保存到 log_all.log 下
每次都使? LoggerFactory.getLogger(xxx.class) 很繁琐,且每个类都添加?遍,也很麻烦,这?讲?种更好?的?志输出?式,使? lombok 来更简单的输出。
EditStarters
pom.xml
依赖文件中通过这个插件去生成相关的lombok依赖
然后在文件中就会多出来以下依赖了
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
@slf4j
这个注解去代替前面繁琐的日志对象创建过程了,这个注解会给当前的类提供一个log
对象,使用这个对象就可以去调用对应日志级别了@RequestMapping("/log/sayhi")
public String sayhi(){
log.info("info log");
log.warn("warn log");
log.error("error log");
return "log sayhi";
}
然后我们来运行一下试试,便可以观察到与前面获取到的日志信息是一样的~
但是光这样子是不够的,我们要做到的是 知其然,知其所以然。对于
lombok
来说,它呢是编译期间的一个框架
.class
为后缀的字节码文件,将其放到JVM虚拟机上去运行就可以让程序跑起来了lombok
框架支持后,它就会与普通的java
程序一同进行编译,然后生成.class
为后缀的字节码文件那怎么去证实呢?我们一起来看看
target
文件中去进行查看,然后打开内部的controller
文件.class
为后缀的字节码文件,将其拖入到IDEA中观察一下🔍@Slf4j
注解的缘故,出现了我们在前面所学习的Logger
接口所新建出来的对象,不过这里叫做log
当然,lombok 这个框架可不知那么一个注解,它的功能还是很强大的,其中的很多注解都可以帮助我们快速地去进行开发
注解 | 作用 |
---|---|
@Getter | ?动添加 getter ?法 |
@Setter | ?动添加 setter ?法 |
@ToString | ?动添加 toString?法 |
@EqualsAndHashCode | ?动添加 equals 和 hashCode ?法 |
@NoArgsConstructor | ?动添加?参构造?法 |
@AllArgsConstructor | ?动添加全属性构造?法,顺序按照属性的定义顺序 |
@NonNull | 属性不能为 null |
@RequiredArgsConstructor | ?动添加必需属性的构造?法,final + @NonNull 的属性为必需 |
注解 | 作用 |
---|---|
@Data | @Getter + @Setter + @ToString +@EqualsAndHashCode +@RequiredArgsConstructor +@NoArgsConstructor |
注解 | 作用 |
---|---|
@Slf4j | 添加?个名为 log 的?志,使? slf4j |
接下去来总结一下本文所学习的内容📖
JDBC
有着异曲同工之妙Logger接口
,然后通过调用其中的方法并传入对应的参数来获取到对应的日志对象,接着再通过这个对象去调用【日志级别】来进行输出trace
:微量,少许的意思,级别最低;debug
:需要调试时候的关键信息打印;info
:普通的打印信息(?默认?志级别?);warn
:警告,不影响使?,但需要注意的问题;error
:错误信息,级别较?的错误?志信息;fatal
:致命的,因为代码异常导致程序退出执?的事件。@Slf4j
注解和 log
对象我们可以实现快速的打印?定义?志,当然我们还去好好地探究了一番这个注解究竟怎么一回事,做到了 “知其然,知其所以然”以上就是本文要介绍的所有内容,诚挚感谢您对本文的观看🌹🌹🌹