SpringBoot3 基础教程之二常规配置

大纲

YAML 配置

YAML 是 “YAML Ain’t a Markup Language”(YAML 不是一种标记语言)。在开发这种语言时,YAML 的意思其实是:”Yet Another Markup Language”(是另一种标记语言)。

YAML 配置方式的优势

  • 设计目标,就是方便人类读写
  • 层次分明,更适合做配置文件
  • 使用 .yml.yaml 作为文件后缀,如 application.yml

Properties 配置方式的弊端

  • 痛点:SpringBoot 集中化管理配置(application.properties
  • 问题:配置内容多了以后,难以阅读和修改,层级结构辨识度不高

基本语法

  • 基础语法

    • 大小写敏感
    • 使用缩进表示层级关系,使用空格分割键值对 k: v
    • 缩进时不允许使用 Tab 键,只允许使用空格
    • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
    • # 符号表示注释,从这个字符一直到行尾,都会被解析器忽略
  • 支持的写法

    • 对象:键值对的集合,如映射(map)/ 哈希(hash) / 字典(dictionary)
    • 数组:一组按次序排列的值,如序列(sequence) / 列表(list)
    • 纯量:单个的、不可再分的值,如字符串、数字、布尔、日期

使用案例

  • Java 配置属性类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Data
@Component
@ConfigurationProperties(prefix = "person")
public class Person {

private String name;
private Integer age;
private Date birthDay;
private Boolean like;
private Child child; // 嵌套对象
private List<Dog> dogs; // 数组(里面是对象)
private Map<String, Cat> cats; // 表示 Map

}

@Data
public class Child {
private String name;
private Integer age;
private Date birthDay;
private List<String> text; // 数组
}

@Data
public class Dog {
private String name;
private Integer age;
}

@Data
public class Cat {
private String name;
private Integer age;
}
  • Properties 表示法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
person.name=张三
person.age=18
person.birthDay=2010/10/12 12:12:12
person.like=true
person.child.name=李四
person.child.age=12
person.child.birthDay=2018/10/12
person.child.text[0]=abc
person.child.text[1]=def
person.dogs[0].name=小黑
person.dogs[0].age=3
person.dogs[1].name=小白
person.dogs[1].age=2
person.cats.c1.name=小蓝
person.cats.c1.age=3
person.cats.c2.name=小灰
person.cats.c2.age=2
  • YAML 表示法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
person:
name: 张三
age: 18
birthDay: 2010/10/10 12:12:12
like: true
child:
name: 李四
age: 20
birthDay: 2018/10/10
text: ["abc","def"]
dogs:
- name: 小黑
age: 3
- name: 小白
age: 2
cats:
c1:
name: 小蓝
age: 3
c2: {name: 小绿,age: 2} # 对象也可以用{}表示

最佳实践

  • birthDay 推荐写为 birth-day
  • 文本
    • 单引号不会转义【\n 会显示为普通字符】
    • 双引号会转义【\n 会显示为换行符】
  • 大文本
    • | 开头,大文本写在下层,会保留文本格式,换行符正确显示
    • > 开头,大文本写在下层,会折叠换行符(替换为空格符)
  • 多文档合并
    • 使用 --- 可以把多个 YAML 文档合并在一个文档中,每个文档区依然认为是内容独立

日志配置

本章节所需的案例代码,可以直接从 GitHub 下载对应章节 spring-boot3-03

日志简介

  • 1、Spring 使用 commons-logging 作为内部日志,但底层日志实现是开放的,可对接其他日志框架。
  • 2、Spring 5 及以后的版本,commons-logging 被 Spring 自己直接重写了,包名是 spring-jcl
  • 3、SpringBoot 支持 JULLog4jLogback 等日志框架,默认使用的日志框架是 Logback
  • 4、SpringBoot 提供了默认的控制台输出配置,也可以配置输出为文件。
  • 5、虽然日志框架很多,但是开发人员不用担心,因为使用 SpringBoot 默认的日志配置就能工作的很好。

SpringBoot 是怎么把日志默认配置好的?

  • 1、每个场景启动器 spring-boot-starter-xxx,都会导入 spring-boot-starter 核心场景启动器
  • 2、核心场景启动器导入了 spring-boot-starter-logging,包含了日志的所用功能
  • 3、SpringBoot 默认使用了 SLF4J + Logback 组合作为默认的底层日志实现
  • 4、日志是系统一启动就要用的,而 xxxAutoConfiguration 自动配置类只用于在系统启动完成后导入组件,属于后来用的,因此不适用于日志配置
  • 5、SpringBoot 的日志配置是利用监听器机制实现,底层是通过实现 ApplicationContextInitializer 接口来配置好日志的
  • 6、SpringBoot 的所有日志配置都可以通过修改配置文件实现变更,以 logging 为前缀开始项的所有配置内容

日志框架

  • 日志门面:JCL (Jakarta Commons Logging)SLF4J (Simple Logging Facade for Java)jboss-logging
  • 日志实现:Log4jLog4j2Slf4j-SimpleLogbackJUL (java.util.logging)

日志格式

1
2
2023-06-11T22:34:17.511+08:00  INFO 4944 --- [main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-06-11T22:34:17.511+08:00 INFO 4944 --- [main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.7]
  • 默认的日志输出格式
    • 时间和日期:毫秒级精度
    • 日志级别:ERROR,WARN,INFO,DEBUG,TRACE
    • 进程 ID
    • 消息分割符:---
    • 线程名称: 使用 [] 包含
    • Logger 名: 通常是产生日志的类名
    • 消息: 日志记录的内容

特别注意: Logback 没有 FATAL 级别,对应的是 ERROR 级别。

  • 更改日志输出的格式
    • 默认值:参照 spring-boot 包的 additional-spring-configuration-metadata.json 文件,点击查看源码
    • 默认的日志输出格式值:%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}
    • 日志的输出格式值可修改为:%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{15} ===> %msg%n,配置示例如下
1
2
3
4
# 更改日志的输出格式
logging:
pattern:
console: '%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{15} ===> %msg%n'

日志记录

  • 第一种写法,使用 SLF4J 的 API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MainApplication {

private static final Logger logger = LoggerFactory.getLogger(MainApplication.class);

public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
logger.info("Hello World!");
}

}
  • 第二种写法,使用 Lombok 的 @Slf4j 注解
1
2
3
4
5
6
7
8
<dependencies>
<!-- 引入 Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
1
2
3
4
5
6
7
8
9
10
11
12
13
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@Slf4j
@SpringBootApplication
public class MainApplication {

public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
log.info("Hello World!");
}

}

日志级别

  • 日志级别由低到高:TRACE,DEBUG,INFO,WARN,ERROR,FATAL,OFF
    • TRACE:追踪框架详细流程日志,一般不使用
    • DEBUG:开发调试细节日志
    • INFO:关键、感兴趣信息日志
    • WARN:警告但不是错误的信息日志,比如版本过时
    • ERROR:业务错误日志,比如出现各种异常
    • FATAL:致命错误日志,比如 JVM 系统崩溃
    • OFF:关闭所有日志记录

提示

  • SpringBoot 默认的日志级别是 INFO
  • SpringBoot 只会打印指定日志级别及以上级别的日志。
  • 不指定日志级别的所有类,默认都使用 root 指定的日志级别。
  • 日志级别的配置
    • 1、在 application.properties 配置文件中,可以通过 logging.level.<logger-name>=<level> 指定日志级别。
    • 2、level 的可取值范围:TRACE,DEBUG,INFO,WARN,ERROR,FATAL,OFF,它们都定义在 LogLevel 枚举类中。
    • 3、rootlogger-nameroot,所以可以配置 logging.level.root=warn,代表所有未指定日志级别的类都使用 rootwarn 日志级别。

日志分组

精确调整某个包下类的日志级别,一般的写法如下:

1
2
3
logging:
level:
com.clay.shop: info

建议将相关的 logger 分组写在一起,SpringBoot 也支持这样统一配置,比如 Tomcat 相关的日志统一设置:

1
2
3
4
5
logging:
group:
tomcat: org.apache.catalina,org.apache.coyote,org.apache.tomcat
level:
tomcat: info

SpringBoot 预定义了两个日志分组:

NameLoggers
weborg.springframework.core.codecorg.springframework.httporg.springframework.weborg.springframework.boot.actuate.endpoint.weborg.springframework.boot.web.servlet.ServletContextInitializerBeans
sqlorg.springframework.jdbc.coreorg.hibernate.SQLorg.jooq.tools.LoggerListener

文件输出

SpringBoot 默认只会将日志输出到控制台,如果想额外记录到文件,可以在 application.properties 中添加 logging.file.name 或者 logging.file.path 配置项。

logging.file.namelogging.file.path配置示例配置效果
未指定未指定仅控制台输出
指定未指定my.log 或者 /var/log/my.log写入指定文件,可以加文件路径(可选)
未指定指定/var/log写入指定目录,文件名默认为 spring.log
指定指定logging.file.name 的配置为准
1
2
3
4
5
logging:
level:
com.clay.shop: info
file:
name: /tmp/logs/shop/app.log

日志归档

文件归档与滚动切割

  • 归档:每天的日志单独存到一个文档中。
  • 切割:每个文件 10MB,超过大小就切割成另外一个文件。
  • 1、每天的日志应该独立分割出来存档。如果使用 Logback(SpringBoot 默认整合),可以通过 application.properties 文件指定日志滚动规则。
  • 2、如果是其他日志系统,需要自行配置(例如使用 Log4j2,则添加 log4j2.xmllog4j2-spring.xml 配置文件)
  • 3、SpringBoot 默认支持的滚动规则配置如下:
配置项描述
logging.logback.rollingpolicy.file-name-pattern 日志存档的文件名格式(默认值:${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz
logging.logback.rollingpolicy.clean-history-on-start 应用启动时是否清除以前的存档文件(默认值:false)
logging.logback.rollingpolicy.max-file-size 存档前,每个日志文件的最大大小(默认值:10MB)
logging.logback.rollingpolicy.total-size-cap 日志文件被删除之前,可以容纳的最大大小(默认值:0B)。设置 1GB,则磁盘存储超过 1GB 日志后就会删除旧的日志文件
logging.logback.rollingpolicy.max-history 日志文件保存的最大天数(默认值:7)

自定义配置

一般情况下,SpringBoot 项目修改 application.properties 配置文件,就可以调整日志的所有行为。如果不够,还可以自定义日志系统的配置文件。比如:

日志系统自定义配置文件
Logbacklogback-spring.xmllogback-spring.groovylogback.xmllogback.groovy
Log4j2log4j2-spring.xmllog4j2.xml
JUL (java.util.logging)logging.properties

如果可能,建议在日志配置文件的名称中使用 -spring 变量,比如 logback-spring.xml 而不是 logback.xml。这是因为如果使用标准的文件名称,Spring 无法完全控制日志的初始化过程。

最佳实践

如果需要自己写日志配置文件,那么日志配置文件的名称建议加上 -spring.xml,例如 logback-spring.xml,而不是 logback.xml

切换日志框架

  • 切换 Log4j2 日志框架
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
  • Log4j2 支持 XMLYAMLJSON 格式的配置文件
格式 Jar 包依赖文件名
YAMLcom.fasterxml.jackson.core:jackson-databind + com.fasterxml.jackson.dataformat:jackson-dataformat-yamllog4j2.yamllog4j2.yml
JSONcom.fasterxml.jackson.core:jackson-databindlog4j2.jsonlog4j2.jsn
XMLlog4j2-spring.xmllog4j2.xml

最佳项目实践

  • 1、导入任何第三方框架,首先排除它的日志包,因为 SpringBoot 底层默认配置好了日志。
  • 2、SpringBoot 项目修改 application.properties 配置文件,就可以调整日志的所有行为。如果不够,可以编写日志框架的配置文件,并将它放在项目的类路径下就行,比如 logback-spring.xmllog4j2-spring.xml
  • 3、如需对接专业日志系统,也就是需要把 Logback 记录的日志灌到 Kafka 之类的中间件,这和 SpringBoot 没关系,都是日志框架自己的配置,修改配置文件即可实现。
  • 4、在业务代码中使用 slf4j-api 记录日志,不要再使用 System.out.println() 了。