1. 日志介绍
1.1 日志是什么?
日志 就是介绍一个过程的详细记录,项目中的日志 就是项目开发过程的详细记录,代码里的日志 就是程序员记录某个开发过程的详细情况。
代码里的日志是项目中非常重要的组成成分,它的详细程度能够决定系统是否容易维护。
1.2 日志的作用
- 发现问题和定位问题。
- 记录用户的登录日志,方便大数据分析用户信息。
- 记录系统的操作日志,方便恢复数据和定位操作人。
- 记录程序执行的时间,方便为以后优化程序提供数据支持。
1.3 日志格式说明
Spring Boot 项目在启动时默认就有日志输出,如下图所示:
其中日志的格式包含了六个部分,分别是:
- 日志打印的时间
- 日志级别
- 线程ID
- 线程名称
- 执行的类型
- 日志信息
2. 常见日志框架说明
Java 中日志的演化历史:
在 Java 中,最先出现的日志是 Apache 开源社区中的 log4j。后来 Java 开发主体 Sun 公司在 jdk1.4 中增加了 JUL(java.util.logging 包下)日志的实现,由于不同的日志工具之间没有关联,导致替换和统一日志工具变得非常棘手,使得 Java 开发局面比较混乱。
为了解决这个问题,Apache 开源社区提供了一个日志框架 commons-logging 作为日志的抽象,commons-logging 对各种日志接口进行抽象,抽象出了一个接口层,对每个日志实现都进行适配,出色的兼容了主流的日志实现,包括 log4j 和 JUL 等。
后来 log4j 的作者实现了一个更加优雅的日志框架 SLF4J,并为 SLF4J 实现了一个 logback 的具体实现。最后又将 log4j 改造成了新的 log4j2,并让其能够支持 commons-logging 和 SLF4J。
Java 日志体系图:
通过上述 Java 日志演化的历史和体系图,我们可以了解到:
- commons-logging 和 SLF4J 是日志的接口,而没有提供实现。
- log4j 1/2、JUL、logback 是日志的具体实现。
为什么需要日志接口?
接口用于定制规范,可以有多个实现,使用时是面向接口的(导入的包都是 SLF4J 的包或者是 commons-logging 的包,而不是具体某个日志框架中的包),即直接和接口交互,不直接使用实现,所以当需要更换实现的时候,直接更换就可以了,而不用更改代码中的日志相关代码。
3. 日志级别
3.1 日志级别的作用
日志的级别能够筛选符合目标的日志信息。对日志进行分级,能够过滤出自己想看的信息,比如设置日志级别为 error,那么就可以只看到程序的报错日志,而忽略普通的调试日志和业务日志等,节省了开发者的信息筛选时间。
3.2 日志级别的分类
日志级别(从低到高) | 说明 |
---|
trace | 很低的日志级别,一般不会使用。 | debug | 指出细粒度信息事件对调试应用程序是非常有帮助的,主要用于开发过程中打印一些运行信息。 | info | 消息在粗粒度级别上突出强调应用程序的运行过程。打印一些你感兴趣的或者重要的信息,这个可以用于生产环境中输出程序运行的一些重要信息。 | warn | 表明会出现潜在错误的情形,有些信息不是错误信息,但是也要给程序员的一些提示。 | error | 指出虽然发生错误事件,但仍然不影响系统的继续运行。 | fatal | 指出每个严重的错误事件将会导致应用程序的退出。 |
- 默认日志级别为 info。
- 程序会打印高于或等于当前日志级别的日志。
3.3 日志级别的设置
在项目的配置文件中设置 logging.level 就可以设置日志级别。
在 application.properties 中设置日志级别:
# 设置所有目的的日志级别为 error
logging.level.root=error
# 设置 com.example.demo.controller 目录下的日志级别为 debug
logging.level.com.demo.controller=debug
在 application.yml 中设置日志级别:
logging:
server:
root: error
com:
example:
demo:
controller: debug
4. 自定义日志打印
4.1 在程序中得到日志对象
在程序中,日志对象为 Logger ,获取日志对象需要使用日志工厂 LoggerFactory 的 getLogger 方法,代码如下所示:
private Logger logger = LoggerFactory.getLogger(LoginController.class);
这里推荐使用 SLF4J + logback 或者 SLF4J + log4j2,而 Spring Boot 中内置了日志框架 SLF4J,所以在使用的时可以直接使用。上述使用的是 SLF4J 日志框架,所以 Logger 对象是属于 org.slf4j 包下的,导包的时候不要导错!
4.2 使用日志对象打印日志
在要执行的类中,使用日志对象提供的 API 进行日志打印。代码如下所示:
@Controller
@ResponseBody
public class LoginController {
private Logger logger = LoggerFactory.getLogger(LoginController.class);
@RequestMapping("/login")
public String login(String username, String password) {
logger.trace("日志级别 trace");
logger.debug("日志级别 debug");
logger.info("日志级别 info");
logger.warn("日志级别 warn");
logger.error("日志级别 error");
String result = "未知错误!";
if (StringUtils.hasLength(username) && StringUtils.hasLength(password)
&& username.equals("admin") && password.equals("1234")) {
result = "登录成功!";
} else {
result = "登陆失败!用户名或密码错误!";
}
return result;
}
}
上述代码中,由于没有主动设置日志级别,默认日志级别为 info。因为程序只会打印高于或等于当前日志级别的日志,因此并不会打印 trace 的日志信息。而
5. 日志持久化
以上的?志都是输出在控制台上的,然?在?产环境上需要将?志保存下来,以便出现问题之后追溯问题,把?志保存下来的过程就叫做持久化。
想要将?志进?持久化,只需要在配置?件中指定?志的存储?录或者是指定?志保存?件名之后,Spring Boot 就会将控制台的?志写到相应的?录或?件下了。
配置日志文件的保存路径:
logging:
file:
path: C:\Users\bbbbbge\Desktop
配置日志文件的文件名:
logging:
file:
name: C:\Users\bbb bbge\Desktop\spring-boot.log
6. 更简单的日志打印方式 lombok
6.1 lombok 介绍
lombok 是一个 Java 库,能自动插入编辑器并构建工具,简化 Java 开发。通过添加注解的方式,不需要为类编写getter、setter、equals、toString 等方法,同时可以自动创建日志变量。
使用 lombok 前需要在项目中引入其依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<optional>true</optional>
</dependency>
6.2 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 |
6.3 使用 @Slf4j 注解打印日志
通过 lombok 中的 @Slf4j 注解,可以自动添加一个名为 log 的日志对象对象,使得我们不用每次再通过 LoggerFactory.getLogger(xxx.class) 代码的方法去创建一个日志对象。
示例代码:
@Controller
@ResponseBody
@Slf4j
public class LoginController {
@RequestMapping("/login")
public String login(String username, String password) {
log.trace("日志级别 trace");
log.debug("日志级别 debug");
log.info("日志级别 info");
log.warn("日志级别 warn");
log.error("日志级别 error");
String result = "未知错误!";
if (StringUtils.hasLength(username) && StringUtils.hasLength(password)
&& username.equals("admin") && password.equals("1234")) {
result = "登录成功!";
} else {
result = "登陆失败!用户名或密码错误!";
}
return result;
}
}
6.4 lombok 工作原理
通过 lombok,我们只需要添加相应的注解,就能够省略对应的代码。那么 lombok 为何能做到这么神奇的事呢?
我们编写的代码是 Java 文件,而编写完成后需要通过编码生成字节码文件,JVM 通过加载和运行字节码文件才能得到程序的结果。
我们可以观察和比较一下上述代码的源文件和字节码文件中代码的差别:
我们发现,源代码中我们使用的 @Slf4j 注解在字节码文件中消失了,反而字节码文件中多出了使用LoggerFactory.getLogger(xxx.class) 获取日志对象的一行代码,并且创建的日志对象变量叫 log。因此我们可以猜测出 lombok 中的注解在 Java 代码编译的时候能够转换成对应的代码,使得最终的字节码文件能够正常运行。
lombok 实际上是通过 jdk 实现的 JSR 269: Pluggable Annotation Processing API (编译期的注解处理器),在编译期时把 lombok 的注解转换成 Java 代码,相当于在编译期对代码进行了修改。
我们知道 Java 源文件是通过 javac 编译器来编译成字节码文件的,而 javac 编译器的编译过程大致可以分为1个准备和3个处理过程:
- 初始化插入式注解处理器
- 解析与填充符号表
- 插入式注解处理器的注解处理
- 分析与字节码生成
而 lombok 就是实现了插入式注解处理器,通过插入式注解处理器,就可以读取、修改、添加抽象语法树中的任意元素。因此,在 javac 编译的过程中,它产生的作用流程具体如下:
- javac 对源代码进行分析,生成了一棵抽象语法树(AST)。
- 运行过程中调用实现了插入式注解处理器的 lombok 程序。
- lombok 对 AST 进行处理,找到相关注解所在的类对应的语法树,然后修改 AST,增加相应的代码定义的语法树的节点。
- javac 使用修改后的抽象语法树生成字节码文件。
6.5 lombok 的优缺点
优点:
- 能通过注解的形式自动生成构造器、getter、setter、equals、hashcode、toString 等方法,提高了一定的开发效率。
- 让代码变得简洁,不用过多的去关注相应的方法。
- 属性做修改时,也简化了维护为这些属性所生成的 getter、setter 方法等。
缺点:
- 不支持多种参数构造器的重载。
- 虽然省去了手动创建 getter、setter 等方法编写的麻烦,但大大降低了源代码的可读性和完整性,降低了阅读源代码的舒适度。
- 由于是编译器生成的代码,所以不能够调试。
码文件。
|