日志是程序的重要组成部分。想象⼀下,如果程序报错了,不让你打开控制台查看⽇志,那么你能找到报错的原因吗?
答案是否定的,写程序不是买彩票,不能完全靠猜,因此⽇志对于我们来说,最主要的⽤途就是排除和定位问题。
除了发现和定位问题之外,我们还可以通过⽇志实现以下功能:
以上这些都是⽇志提供的⾮常实⽤的功能。
Spring Boot 项⽬在启动的时候默认就有⽇志输出,如下图所示:
以上内容就是 Spring Boot 输出的控制台⽇志信息。
通过上述⽇志信息我们能得到以下 一个结论 和 三个问题:
下⾯我们⼀起来寻找这些问题的答案 ~
开发者⾃定义打印⽇志的实现步骤:
1)得到日志对象;
2)使用日志对象提供的方法,输出自定义的日志内容。
System.out.println(); 也算是打印了日志,但是:
a. 只有打印的内容,没有其他相关信息,比如打印的时间、打印的类 (出处)…
b. 不能实现不同环境下的行为控制 (设置日志打印级别)。
在程序中获取⽇志对象需要使⽤⽇志⼯⼚ LoggerFactory,如下代码所示:
// 1.得到日志对象
private static Logger log = LoggerFactory.getLogger(TestController.class);
日志工厂需要将每个类的类型传递进去,这样我们才知道⽇志的归属类,才能更⽅便、更直观的定位到问题类。
约定大于配置:(注意事项)
因为 Spring Boot 中内置了⽇志框架 slf4j,所以咱们可以直接在程序中调用 slf4j 来输出⽇志。
⽇志对象的打印⽅法有很多种:info、debug、warn… 如下代码所示:
package com.example.demo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestLogging {
// 1.得到日志对象
private static Logger logger = LoggerFactory.getLogger(TestLogging.class);
@RequestMapping("/hello")
public void sayHi() {
// 2.使用日志对象提供的方法进行日志打印
logger.trace("Hello,i am trace.");
logger.debug("Hello,i am debug.");
logger.info("Hello,i am info.");
logger.warn("Hello,i am warn");
logger.error("Hello,i am error");
}
}
启动并运行,通过 http://localhost:8080/hello 访问,此时控制台打印出日志信息:
为什么 trace() 和 debug() 的打印日志没有显示呢?这与日志级别相关 ~~
⽇志的级别分为:
级别越高接收到的消息就越少,如设置了 warn 就只能收到 warn、error、fatal 级别的日志了。
总共有 6 种级别,打印的时候为什么只有 5 种,而没有 fatal 呢?
因为 fatal 是 “致命” 的,只会在代码异常导致程序退出的时候才记录,自己没法自定义 ~~
日志的输出级别默认是 info。
在上面我们自定义了五个等级的打印日志,却只看到了三个:
这是因为此时是默认日志级别 info,怎样进行日志级别设置呢?
日志级别配置只需要在配置⽂件中设置 “logging.level” 配置项即可,如下所示:
logging:
level:
root: trace
这是配置跟路径的日志级别。
而日志级别的设置可以是非常精细的 (根据包):
logging:
level:
root: warn
com:
example:
demo: debug
重新启动运行,此时再通过 http://localhost:8080/hello 访问:
这样就可以自定义设置日志级别了 ~~
以上的⽇志都是输出在控制台上的,然⽽在⽣产环境上咱们需要将⽇志保存下来,以便出现问题之后追溯问题,把⽇志保存下来的过程就叫做持久化。
想要将⽇志进⾏持久化,只需要在配置⽂件中指定⽇志的存储⽬录或者是指定⽇志保存⽂件名,Spring Boot 就会将控制台的⽇志写到相应的⽬录或⽂件下了。
# 设置⽇志⽂件的⽂件名
logging:
file:
name: C:\yyhjava_project\tmp\yyh.log
只指定文件名的话,就是生成在项目的根路径 ~~
配置完成后,启动并运行,此时在 C:\yyhjava_project\tmp\ 路径下:
就生成了 yyh.log 日志文件。打开它:
此时通过 http://localhost:8080/hello 访问:
得到结论:日志是追加的,不会覆盖 ~~
重新运行依然是追加:
一直追加,文件不是会特别大吗?
并不是。当日志比较大的时候,会自动分隔成多个文件。
# 设置⽇志⽂件的⽬录
logging:
file:
path: C:\yyhjava_project\tmp\
配置完成后,启动并运行,此时在 C:\yyhjava_project\tmp\ 路径下:
生成了 spring.log 日志文件 (默认文件名就是 spring.log)。
各种性质与文件名方式生成的一致 ~~
每次都使用 LoggerFactory.getLogger(xxx.class) 很繁琐,且每个类都添加一遍也很麻烦,这⾥讲一种更好⽤的⽇志输出⽅式,使⽤ lombok 来更简单的输出。
1)添加 lombok 框架支持
2)使用 @slf4j 注解输出日志
package com.example.demo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
@RequestMapping("/p")
public class TestLogging {
@RequestMapping("/log")
public void loggerTest() {
log.error("------------------- error -----------------");
}
}
通过 http://localhost:8080/p/log 访问后,查看控制台:
也成功打印出了日志。
注意: 使用 @Slf4j 注解,在程序中使⽤ log 对象即可输入日志,并且只能使⽤ log 对象才能输出,这是 lombok 提供的对象名。
lombok 能够打印日志的秘密就在 target ⽬录⾥⾯,target 为项目最终执行的代码,查看 target 目录如下:
基本注解
注解 | 作用 |
---|---|
@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 |