《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Spring-Boot/Logging/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

本文在提供完整代码示例,可见 https://github.com/YunaiV/SpringBoot-Labslab-37 目录。

原创不易,给点个 Star 嘿,一起冲鸭!

1. 概述

在开始讲解在 Spring Boot 如何使用日志框架之前,我们想来了解下 Java 日志框架的生态,以便我们更深入的入门。当然,胖友也可以选择跳过,直接从「2. 快速入门」小节开始。

1.1 日志框架

在 Java 日志框架的生态中,存在多种的日志实现框架。例如说:

那么,对于 Spring、Hibernate 等框架,会有打日志的需求,那么就需要选择相应的日志框架。但是,它们无论选择任一一个日志框架,可能使用 Spring、Hibernate 等框架的项目,希望选择另外一个日志框架。此时,项目中就需要添加多个日志框架的配置文件,十分麻烦不便。

所幸,在 Java 日志框架的生态中,存在多种日志门面框架,基于 Facade Pattern 设计模式的思想,提供通用的日志 API 给调用方,而自己去实现不同日志框架的适配。例如说:

这样就变成,Spring 采用 JCL 日志门面框架,Hibernate 采用 jboss-logging 日志门面框架,而将选择具体的日志实现框架的权利,交给使用者。

当然,也有框架,是自己实现简单的日志门面功能。例如说:

所以,在 Java 日志框架的生态中,一共存在两种角色:日志门面框架日志实现框架。下面,我们把它们整理成下图:门面 + 实现

不过要注意,日志门面框架提供通用的日志 API 给调用方,不包括每个日志实现框架的配置。因此我们在使用时,还是需要添加我们使用的具体的日志实现框架的配置文件。😈 也就是说,日志门面框架的作用是,帮助我们在项目中,统一日志实现框架的使用。

另外,即使在自己项目的代码中,如果有打日志的需求,也是推荐使用日志门面框架的 API ,而不是直接调用日志实现框架。在《阿里巴巴 Java 开发手册》中,也提出了这样的日志规约:日志规约

目前,比较主流的选择是,选择 SLF4J 作为日志门面框架,Logback 作为日志实现框

1.2 SLF4J 原理

那么,SLF4J 是如何实现对多个日志框架的适配的呢?在《SLF4J 文档 —— Bridging legacy APIs》中,已经详细讲解了。

我们先来看 SLF4J + Logback 的组合。如下图所示:

SLF4J + Logback 的组合

  • 【红框】将使用其它日志门面框架的,替换成 SLF4J 日志门面框架。例如说,Spring 采用 JCL 日志门面框架,就是通过该方式来解决的。
    • jcl-over-slf4j 库,将调用 JCL 打日志的地方,适配成调用 SLF4J API。实现原理是,jcl-over-slf4j 通过在包名/类名/方法名上和 JCL 保持完全一致,内部重写来调用的是 SLF4J 的 API 。如下图所示:
  • 【绿框】将使用其它日志实现框架的,替换成 SLF4J 日志门面框架。
    • log4j-over-slf4j 库,将调用 Log4j1 打日志的地方,适配成调用 SLF4J API。实现原理是,同 jcl-over-slf4j 库。如下图所示:
    • jul-over-slf4j 库,将调用 JUL 打日志的地方,适配成调用 SLF4J API。实现原理是,实现自定义的 java.util.logging.Handler 处理器,来调用的是 SLF4J 的 API 。如下图所示:
  • 【黄框】SLF4J 日志门面框架,调用具体的日志实现框架,打印日志。
    • logback-classic 库,内置了对 SLF4J 框架的实现。如下图所示:

我们再来看 SLF4J + Log4j1 的组合。如下图所示:

SLF4J + Log4J1 的组合

  • 整体实现原理来说,和 SLF4J + Logback 的组合是一致的。
  • 【绿框】slf4j-log4j12 库,因为 Log4j1 并未内置对 SLF4J 框架的实现,所以 SLF4J 只能自己实现。如下图所示:

1.3 Spring Boot 对日志框架的封装

在 Spring Boot 内部,保持和 Spring 一致,采用 JCL 日志门面框架来记录日志。但是,其 logging 包下,提供了对 JUL、Log4j2、Logback 日志实现框架的封装,提供默认的日志配置。我们以 Logback 举例子,其在 DefaultLogbackConfiguration 类中,定义了文件日志格式如下:

// DefaultLogbackConfiguration.java

private static final String FILE_LOG_PATTERN = "%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} "
+ "${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}";

  • 日志输出示例如下:

    2019-12-29 17:24:57.935  INFO 97606 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
    2019-12-29 17:24:57.938 INFO 97606 --- [ main] c.i.s.lab36.prometheusdemo.Application : Started Application in 2.624 seconds (JVM running for 3.228)
    2019-12-29 17:24:58.412 INFO 97606 --- [on(4)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'

  • 文件日志格式解释:

    • %d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} 日期和时间:毫秒精度。
    • ${LOG_LEVEL_PATTERN:-%5p} 日志级别:ERRORWARNINFODEBUGTRACE
    • ${PID:- } 进程 ID。
    • --- 分隔符:用于区分实际日志内容的开始。
    • [%t] 线程名称:在方括号中(可能会截断控制台输出)。
    • %-40.40logger{39} 日志记录器名称:这通常是源类名称(通常为缩写)。
    • %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx} 日志内容。

同时,允许通过 logging. 配置项来,进行自定义设置。例如说:

  • 自定义日志格式

    • logging.pattern.file :文件的日志格式。
    • logging.pattern.console :控制台的日志格式。
    • logging.pattern.dateformat :日期和时间。
    • logging.pattern.level :日志级别。
    • logging.pattern.pid : 进程 ID。
    • logging.exception-conversion-word :记录异常时使用的转换字
  • 自定义日志文件

    • logging.file.max-history :日志文件要保留的归档的最大天数。
    • logging.file.max-size :日志文件的最大大小。
    • logging.file :日志文件名。
    • logging.path :日志文件路径。
  • logging.config :日志框架使用的配置文件地址。因为根据不同的日志实现框架,Spring Boot 按如下“约定规则”加载配置文件:

    • Logback :对应 logback-spring.xmllogback-spring.groovylogback.xmllogback.groovy 配置文件。
    • Log4j2 :对应 log4j2-spring.xmllog4j2.xml 配置文件。
    • JUL :对应 logging.properties 配置文件。
  • logging.level.* :日志等级,通过使用 logging.level.<logger-name>=<level> 来设置。例如说:

    logging.level.root=WARN
    logging.level.org.springframework.web=DEBUG
    logging.level.org.hibernate=ERROR

虽然 logging. 自定义配置项很多,但是一般情况下,我们只使用 logging.level.* 配置项,设置不同 Logger 的日志级别。

目前,Spring Boot 内置了两个日志相关的 Starter :

  • spring-boot-starter-logging :使用 SLF4J + Logback 的组合。其 pom.xml 文件的依赖如下:

    <dependencies>
    <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    </dependency>
    <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-to-slf4j</artifactId>
    </dependency>
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
    </dependency>
    </dependencies>

  • spring-boot-starter-log4j2 :使用 SLF4J + Log4j2 的组合。其 pom.xml 文件的依赖如下:

    <dependencies>
    <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    </dependency>
    <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    </dependency>
    <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-jul</artifactId>
    </dependency>
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
    </dependency>
    </dependencies>

默认情况下,spring-boot-starter 推荐采用 spring-boot-starter-logging ,即 SLF4J + Logback 的组合。因为其 pom.xml 文件引入了 spring-boot-starter-logging

2. 快速入门

示例代码对应仓库:lab-37-logging-demo

本小节,我们来进行下 Spring Boot 集成日志功能的快速入门,使用 SLF4J + Logback 的组合。主要功能包括:

  • 控制台打印日志。
  • 控制台颜色输出。
  • 日志文件打印日志。
  • 自定义日志级别。

下面,我们就来搭建一个 lab-37-logging-demo 项目。

2.1 引入依赖

pom.xml 文件中,引入相关依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>lab-37-logging-demo</artifactId>

<dependencies>
<!-- 实现对 Spring MVC 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

</project>

  • 引入 spring-boot-starter-web 依赖的原因,是因为稍后我们会在「2.4 DemoController」中,定义 HTTP API 接口,方便安我们进行自定义日志级别的测试。
  • 为什么并未引入 spring-boot-starter-logging 依赖呢?因为 spring-boot-starter-web 包含了 spring-boot-starter ,而 spring-boot-starter 又已经包含了 spring-boot-starter-logging

2.2 配置文件

application.yml 中,添加日志相关配置,如下:

spring:
application:
name: demo-application # 应用名

logging:
# 日志文件配置
file:
# path: /Users/yunai/logs/ # 日志文件路径。
name: /Users/yunai/logs/${spring.application.name}.log # 日志文件名。
max-history: 7 # 日志文件要保留的归档的最大天数。默认为 7 天。
max-size: 10MB # 日志文件的最大大小。默认为 10MB 。

# 日志级别
level:
cn:
iocoder:
springboot:
lab37:
loggingdemo:
controller: DEBUG

① 日志文件

logging.file.* 配置项下,设置 Spring Boot 日志文件

默认情况下,Spring Boot 日志只会打印到控制台。所以需要通过 logging.file.pathlogging.file.name 配置项来设置。不过要注意,这两者是二选一,并不是共同作用。

  • logging.file.name :日志文件全路径名。可以是相对路径,也可以是绝对路径。这里,我们设置为 "/Users/yunai/logs/${spring.application.name}.log" ,绝对路径。
  • logging.file.path :日志文件目录。会在该目录下,创建 spring.log 日志文件。例如说:/Users/yunai/logs/
  • 上述两者都配置的情况下,以 logging.file.name 配置项为准。

② 日志级别

logging.level.* 配置项,设置自定义的日志级别。Spring Boot 定义的日志级别TRACEDEBUGINFOWARNERRORFATALOFF

默认情况下,控制台和日志文件的日志级别 INFO。即,INFOWARNERROR 级别的日志,都会记录到控制台和日志文件中。

可以通过使用 logging.level.<logger-name>=<level> 配置项,自定义 Logger 对应的日志级别。这里,我们设置了名字为 "cn.iocoder.springboot.lab37.loggingdemo.controller" 的 Logger 的日志级别,为 DEBUG

③ 颜色输出

如果我们的控制台支持 ANSI ,则可以使用颜色输出,来提高可读性。通过 spring.output.ansi.enabled 配置项,设置 ANSI 功能的状态,有三种选项:

  • ALWAYS :启用 ANSI 颜色的输出。
  • NEVER :禁用 ANSI 颜色的输出。
  • DETECT :自动检测控制台是否支持 ANSI 功能。如果支持则进行开启,否则则进行禁用。默认情况下,使用 DETECT 这种选项。

默认情况下,Spring Boot 已经配置好颜色输出的日志格式,我们并不需要做自定义,所以这里也就不多做介绍了。我们只需要知道,不同日志级别,对应不同的颜色的映射关系即可:

级别 颜色
FATAL 红(Red)
ERROR 红(Red)
WARN 黄(Yellow)
INFO 绿(Green)
DEBUG 绿(Green)
TRACE 绿(Green)

①、②、③ 配置项,我们也会在「2.5 简单测试」详细测试说明。

2.3 Application

创建 Application 类,用于启动示例项目。代码如下:

@SpringBootApplication
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

}

2.4 DemoController

cn.iocoder.springboot.lab37.loggingdemo.controller 包路径下,创建 DemoController 类,提供不同 API 接口,打印不同级别的日志。代码如下:

// DemoController.java

@RestController
@RequestMapping("/demo")
public class DemoController {

private Logger logger = LoggerFactory.getLogger(getClass());

@GetMapping("/debug")
public void debug() {
logger.debug("debug");
}

@GetMapping("/info")
public void info() {
logger.info("info");
}

@GetMapping("/error")
public void error() {
logger.error("error");
}

}

2.5 简单测试

① 测试“颜色输出”

执行 Application#main(String[] args) 方法,启动项目。控制台输出带有颜色的日志,如下图所示:颜色输出

② 测试“日志文件”

查看日志文件 /Users/yunai/logs/demo-application.log ,已经有打印日志。日志内容如下:

// ... 省略其它日志

2020-01-01 15:19:41.991 INFO 7553 --- [main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-01-01 15:19:42.130 INFO 7553 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-01-01 15:19:42.132 INFO 7553 --- [main] c.i.s.lab37.loggingdemo.Application : Started Application in 1.549 seconds (JVM running for 2.053)

③ 测试“日志级别”

使用浏览器,访问 /demo/debug/demo/info/demo/error 三个接口。控制台输出日志如下:

2020-01-01 15:25:05.630 DEBUG 7553 --- [nio-8080-exec-2] c.i.s.l.l.controller.DemoController      : debug
2020-01-01 15:25:14.717 INFO 7553 --- [nio-8080-exec-4] c.i.s.l.l.controller.DemoController : info
2020-01-01 15:25:19.590 ERROR 7553 --- [nio-8080-exec-5] c.i.s.l.l.controller.DemoController : error

  • 符合预期。因为我们配置 "cn.iocoder.springboot.lab37.loggingdemo.controller"的 Logger 的日志级别,为 DEBUG 。否则,按照 Spring Boot 默认日志级别为 INFO 的情况下,DEBUG 日志应该不会输出。

3. 动态修改日志级别

示例代码对应仓库:lab-37-logging-actuator

在排查线上运行问题时,我们可能需要动态修改 Logger 的日志级别。一般情况下,我们需要手动修改配置文件的 Logger 的日志级别,然后重启应用,以使修改的 Logger 的日志级别生效。

显然,这样的操作过程略微繁琐,一点都不骚气。所以本小节,我们来通过《芋道 Spring Boot 监控端点 Actuator 入门》「14. loggers 端点」,使用该端点提供的 POST /actuator/loggers/{name} 接口,修改指定 Logger 的日志级别。

下面,我们就来看看动态修改日志级别的示例。考虑到不污染「2. 快速入门」 的示例,我们在 lab-37-logging-demo 项目的基础上,复制出一个 lab-37-logging-actuator 项目。😈 酱紫,我们也能少写点代码,哈哈哈~

3.1 引入依赖

修改 pom.xml 文件中,额外引入 spring-boot-starter-actuator 依赖。如下:

<!-- 实现对 Actuator 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

3.2 配置文件

修改 application.yaml 配置文件,额外增加 Spring Boot Actuator 相关配置。如下:

management:
endpoints:
# Actuator HTTP 配置项,对应 WebEndpointProperties 配置类
web:
exposure:
include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。

3.3 简单测试

本小节,我们会将名字为 "cn.iocoder.springboot.lab37.loggingdemo.controller" 的 Logger 的日志级别,修改成 INFO 级别,实现 /demo/debug 接口无法打印出 DEBUG 日志。

调用 Application#main(Object[] args) 方法,启动 Spring Boot 应用。

① 使用浏览器,访问 http://127.0.0.1:8080/actuator/loggers/cn.iocoder.springboot.lab37.loggingdemo.controller/ 地址,获得该 Logger 的日志级别。响应结果如下:

{
"configuredLevel": "DEBUG",
"effectiveLevel": "DEBUG"
}

  • 日志级别是 DEBUG

② 使用浏览器,访问 http://127.0.0.1:8080/demo/debug/ 地址,打印出 DEBUG 日志如下:

2020-01-01 16:15:54.019 DEBUG 8507 --- [nio-8080-exec-3] c.i.s.l.l.controller.DemoController      : debug

③ 使用 Postman ,请求 POST http://127.0.0.1:8080/actuator/loggers/cn.iocoder.springboot.lab37.loggingdemo.controller/ 地址,修改该 Logger 的日志级别。如下图:修改日志级别

④ 使用浏览器,访问 http://127.0.0.1:8080/actuator/loggers/cn.iocoder.springboot.lab37.loggingdemo.controller/ 地址,获得该 Logger 的日志级别。响应结果如下:

{
"configuredLevel": "INFO",
"effectiveLevel": "INFO"
}

  • 日志级别修改成了 INFO

⑤ 使用浏览器,访问 http://127.0.0.1:8080/demo/debug/ 地址,并未打印出日志。符合预期~

另外,如果胖友有使用 Spring Boot Admin 监控工具,可以参考《芋道 Spring Boot 监控工具 Admin 入门》「3.2 Loggers」,也可以动态修改日志级别。

4. 调试模式

示例代码对应仓库:lab-37-logging-debug

默认情况下,Spring Boot 会记录 ERRORWARNINFO 级别的日志。可能有时我们启动 Spring Boot 失败时,需要通过启动 Spring Boot 调试模式

  • 核心 Logger(内嵌容器、Hibernate 和 Spring Boot 等等)打印 DEBUG 级别的日志,方便排查原因
  • 应用中的其它 Logger 还是保持原有的日志级别。

目前,有两种方式开启 Spring Boot 应用的调试模式:

  • 通过启动参数带上 --debug 标识,进行开启。例如说 $ java -jar myapp.jar --debug
  • 通过配置文件的 debug = true 配置项,进行开启。

下面,我们就来看看动态修改日志级别的示例。考虑到不污染「2. 快速入门」 的示例,我们在 lab-37-logging-demo 项目的基础上,复制出一个 lab-37-logging-debug 项目。😈 酱紫,我们也能少写点代码,哈哈哈~

4.1 配置文件

修改 application.yaml 配置文件,额外增加调试模式相关的配置。增加如下:

spring:
application:
name: demo-application # 应用名

logging:
# 日志文件配置
file:
# path: /Users/yunai/logs/ # 日志文件路径。
name: /Users/yunai/logs/${spring.application.name}.log # 日志文件名。
max-history: 7 # 日志文件要保留的归档的最大天数。默认为 7 天。
max-size: 10MB # 日志文件的最大大小。默认为 10MB 。

# 调试模式
debug: true

  • 相比「2.2 配置文件」来说,主要有两点变化。
  • 第一点,增加 debug = true 配置项,开启调试模式。
  • 第二点,删除 logging.level.* 配置项,使用 "cn.iocoder.springboot.lab37.loggingdemo.controller" 的 Logger 的日志级别,恢复为默认的 LOGGER

4.2 简单测试

① 调用 Application#main(Object[] args) 方法,启动 Spring Boot 应用。在控制台上,我们可以看到核心 Logger 打印了 DEBUG 级别的日志。如下图:调试模式 - 启动日志

② 使用浏览器,http://127.0.0.1:8080/demo/debug/ 地址,查看打印的日志。如下图:调试模式 - 请求日志

  • 在控制台上,我们可以看到核心 Logger 打印了 DEBUG 级别的日志。
  • "cn.iocoder.springboot.lab37.loggingdemo.controller" 的 Logger 的日志级别并未变成 DEBUG 级别,打印出 DEBUG 级别的日志,符合预期

5. 日志分组

Spring Boot 提供了日志分组功能,可以相关 Logger 组合在一起,以便可以统一配置它们的日志级别。例如说:Spring Boot 内置了两个分组:

日志分组名 对应 Logger
web org.springframework.core.codecorg.springframework.httporg.springframework.web
sql org.springframework.jdbc.coreorg.hibernate.SQL

后续,我们仅需配置 logging.level.sql = DEBUG,就可以将 sql 日志分组对应的 Logger 们的日志级别,一次性修改成 DEBUG 级别。

同时,我们可以自定义日志分组。配置文件示例如下:

logging:
group:
tomcat: org.apache.catalina, org.apache.coyote, org.apache.tomcat
level:
tomcat: DEBUG

  • 通过 logging.group.<group-name> 配置项,来定义一个日志分组,其值为 Logger 们。
  • 通过 logging.level.<group-name> 配置项,来定义日志分组的日志级别。

6. 不同环境下的日志配置

示例代码对应仓库:lab-37-logging-multi-env

同一个项目,在不同的环境下,日志配置可能存在差异性。例如说:

  • 在开发环境下,无需日志文件,只需要打印日志到控制台。同时,使用 DEBUG 级别的日志级别,打印更多的日志,以便排查问题。
  • 在生产环境下,需要日志文件,可选打印日志到控制台。同时,使用 INFO 级别的日志级别,毕竟生产环境下访问量比较大,日志量会很大。

因为我们可以在配置文件中,进行日志配置,所以我们可以基于 Spring Profiles 机制,每个环境对应一个 Profile 对应一个配置文件。

下面,我们开始本小节的示例。考虑到不污染上述的示例,我们新建一个 lab-37-logging-multi-env 项目。

6.1 引入依赖

「2.1 引入依赖」」一致,见 pom.xml 文件。

6.2 配置文件

我们会创建两个配置文件,分别对应开发环境和测试环境。

① 开发环境

创建 application-dev.yaml 配置文件,添加开发环境的日志相关配置,如下:

spring:
application:
name: demo-application # 应用名

logging:
# 日志级别
level:
cn:
iocoder:
springboot:
lab37:
loggingdemo: DEBUG

  • 未添加 logging.file.pathlogging.file.name 配置项,所以不会创建日志文件。
  • 添加 logging.level.cn.iocoder.springboot.lab37.loggingdemo = DEBUG 配置项,设置该 Logger 日志级别为 DEBUG

② 生产环境

创建 application-prod.yaml 配置文件,添加生产环境的日志相关配置,如下:

spring:
application:
name: demo-application # 应用名

logging:
# 日志文件配置
file:
name: /Users/yunai/logs/${spring.application.name}.log # 日志文件名。

  • 添加 logging.file.name 配置项,所以会创建日志文件。
  • 未添加 logging.level.* 配置项,所以整个日志级别为默认的 LOGGER

6.3 Application

创建 Application 类,用于启动示例项目。代码如下:

@SpringBootApplication
public class Application {

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

public static void main(String[] args) {
SpringApplication.run(Application.class, args);

// <X> 打印日志
logger.debug("just do it");
}

}

  • <X> 处,打印 DEBUG 级别的日志,测试下不同环境下的日志级别。

6.4 简单测试

通过 IDEA 的 Active profiles 选项,设置启动的 Spring Boot 应用的 Profile 。如下图所示:IDEA

① 测试开发环境

设置 Profile 为 dev,启动 Spring Boot 应用。控制台打印日志如下:

2019-12-30 21:43:19.736  INFO 12531 --- [           main] c.i.s.lab37.loggingdemo.Application      : Started Application in 1.361 seconds (JVM running for 1.764)
2019-12-30 21:43:19.737 DEBUG 12531 --- [ main] c.i.s.lab37.loggingdemo.Application : just do its

  • 我们在 <X> 处,打印的 DEBUG 日志,成功输出。符合预期~

② 测试生产环境

设置 Profile 为 prod,启动 Spring Boot 应用。控制台打印日志如下:

2019-12-30 21:44:35.292  INFO 12551 --- [           main] c.i.s.lab37.loggingdemo.Application      : Started Application in 1.393 seconds (JVM running for 1.837)

  • 我们在 <X> 处,打印的 DEBUG 日志,并未输出。符合预期~
  • 同时,查看 /Users/yunai/logs/$demo-application.log 日志文件,新增相应的日志。

7. Logback 扩展

示例代码对应仓库:lab-37-logging-logback

「1.3 Spring Boot 对日志框架的封装」中,我们可以通过配置文件的 logging.config 配置项,设置日志实现框架的具体配置文件。在 Spring Boot 中,默认情况下,Logback 读取 logback-spring.xmllogback-spring.groovylogback.xmllogback.groovy 配置文件。一般情况下,我们是使用 logback-spring.xml 配置文件。

Spring Boot 额外提供了 Logback 的两个 XML 标签的扩展。

① 拓展一:<springProfile /> 标签

通过 <springProfile /> 标签,设置标签内的 Logback 的配置,需要符合指定的 Spring Profile 才可以生效。通过该标签,我们也可以实现「6. 不同环境下的日志配置」的效果。示例如下:

<springProfile name="staging">
<!-- configuration to be enabled when the "staging" profile is active -->
</springProfile>

<springProfile name="dev | staging">
<!-- configuration to be enabled when the "dev" or "staging" profiles are active -->
</springProfile>

<springProfile name="!production">
<!-- configuration to be enabled when the "production" profile is not active -->
</springProfile>

② 拓展二:<springProperty /> 标签

通过 <springProperty /> 标签,可以从 Spring Boot 配置文件中读取配置项。示例如下:

<!-- 从 Spring Boot 配置文件中,读取 spring.application.name 应用名 -->
<springProperty name="applicationName" scope="context" source="spring.application.name" />
<!-- 日志文件的路径 -->
<property name="LOG_FILE" value="/Users/yunai/logs/${applicationName}.log"/>

下面,我们开始本小节的示例,和「6. 不同环境下的日志配置」小节的效果等价。考虑到不污染上述的示例,我们新建一个 lab-37-logging-logback 项目。

7.1 引入依赖

「2.1 引入依赖」」一致,见 pom.xml 文件。

7.2 应用配置文件

application.yml 中,仅添加应用名的配置即可,如下:

spring:
application:
name: demo-application # 应用名

7.3 Logback 配置文件

logback-spring.xml 中,添加开发环境和生产环境的 Logback 配置,如下:

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

<!-- <1> -->
<!-- 引入 Spring Boot 默认的 logback XML 配置文件 -->
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>

<!-- <2.1> -->
<!-- 控制台 Appender -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- 日志的格式化 -->
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>

<!-- 2.2 -->
<!-- 从 Spring Boot 配置文件中,读取 spring.application.name 应用名 -->
<springProperty name="applicationName" scope="context" source="spring.application.name" />
<!-- 日志文件的路径 -->
<property name="LOG_FILE" value="/Users/yunai/logs/${applicationName}.log"/>
<!-- 日志文件 Appender -->
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}</file>
<!--滚动策略,基于时间 + 大小的分包策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
<maxHistory>7</maxHistory>
<maxFileSize>10MB</maxFileSize>
</rollingPolicy>
<!-- 日志的格式化 -->
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>

<!-- 3.1 -->
<!-- 测试环境,独有的配置 -->
<springProfile name="dev">
<!-- 设置 Appender -->
<root level="INFO">
<appender-ref ref="console"/>
</root>

<!-- 设置 "cn.iocoder.springboot.lab37.loggingdemo" 的 Logger 的日志级别为 DEBUG -->
<logger name="cn.iocoder.springboot.lab37.loggingdemo" level="DEBUG"/>
</springProfile>

<!-- 3.2 -->
<!-- 生产环境,独有的配置 -->
<springProfile name="prod">
<!-- 设置 Appender -->
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="file"/>
</root>
</springProfile>

</configuration>

  • <1> 处,引入 Spring Boot 默认的 logback XML 配置文件 defaults.xml。在其中,定义了控制台的日志格式 CONSOLE_LOG_PATTERN、日志文件的日志格式 FILE_LOG_PATTERN、Tomcat 等常用 Logger 的日志级别。
  • <2.1> 处,配置控制台的 Appender。
  • <2.2> 处,配置日志文件的 Appender。这里,我们可以看到 Spring Boot <springProperty /> 拓展标签的使用。
  • <3.1> 处,通过 <springProfile name="dev" /> 标签,设置测试环境的独有配置。
  • <3.2> 处,通过 <springProfile name="prod" /> 标签,设置生产环境的独有配置。

7.4 Application

创建 Application 类,用于启动示例项目。代码如下:

@SpringBootApplication
public class Application {

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

public static void main(String[] args) {
SpringApplication.run(Application.class, args);

// <X> 打印日志
logger.debug("just do it");
}

}

  • <X> 处,打印 DEBUG 级别的日志,测试下不同环境下的日志级别。

7.5 简单测试

通过 IDEA 的 Active profiles 选项,设置启动的 Spring Boot 应用的 Profile 。如下图所示:IDEA

① 测试开发环境

设置 Profile 为 dev,启动 Spring Boot 应用。控制台打印日志如下:

2019-12-30 23:53:44.469  INFO 14215 --- [           main] c.i.s.lab37.loggingdemo.Application      : Started Application in 1.583 seconds (JVM running for 2.069)
2019-12-30 23:53:44.469 DEBUG 14215 --- [ main] c.i.s.lab37.loggingdemo.Application : just do it

  • 我们在 <X> 处,打印的 DEBUG 日志,成功输出。符合预期~

② 测试生产环境

设置 Profile 为 prod,启动 Spring Boot 应用。控制台打印日志如下:

2019-12-30 23:54:19.202  INFO 14228 --- [           main] c.i.s.lab37.loggingdemo.Application      : Started Application in 1.38 seconds (JVM running for 1.781)

  • 我们在 <X> 处,打印的 DEBUG 日志,并未输出。符合预期~
  • 同时,查看 /Users/yunai/logs/$demo-application.log 日志文件,新增相应的日志。

8. 集成 Log4j2

示例代码对应仓库:lab-37-logging-log4j2

「2. 快速入门」小节中,我们已经使用 spring-boot-starter-logging 依赖,实现 SFL4J + Logback 打日志的功能。而在本小节,我们将使用 spring-boot-starter-log4j2 依赖,实现 SFL4J + Log4j2 打日志的功能。

考虑到在「7. Logback 扩展」小节中,采用了使用 Logback 配置文件的方式,所以本小节,我们也采用 Log4j2 配置文件的方式。在 Spring Boot 中,默认情况下,Log4j2 读取 log4j2-spring.xmllog4j2.xml 配置文件。一般情况下,我们是使用 log4j2-spring.xml 配置文件。

友情提示:Log4j2 也是支持「2. 快速入门」小节中,采用 Spring Boot 配置文件的方式,😈 只是艿艿想稍微**“复杂”**下本小节的示例。

下面,我们开始本小节的示例,和「2. 快速入门」小节的效果等价。考虑到不污染上述的示例,我们新建一个 lab-37-logging-log4j2 项目。

8.1 引入依赖

pom.xml 文件中,引入相关依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>lab-37-logging-log4j2</artifactId>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<!-- 去掉对 spring-boot-starter-logging 的依赖-->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- 实现对 SLF4J + Log4j2 的依赖的引入 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

<!-- 实现对 Spring MVC 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

</project>

  • 具体每个依赖的作用,胖友自己认真看下艿艿添加的所有注释噢

8.2 应用配置文件

application.yml 中,仅添加应用名的配置即可,如下:

spring:
application:
name: demo-application # 应用名

8.3 Log4j2 配置文件

log4j2-spring.xml 中,添加 Log4j2 配置,如下:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Properties>
<Property name="PID">????</Property>
<Property name="LOG_EXCEPTION_CONVERSION_WORD">%xwEx</Property>
<Property name="LOG_LEVEL_PATTERN">%5p</Property>
<Property name="LOG_DATEFORMAT_PATTERN">yyyy-MM-dd HH:mm:ss.SSS</Property>
<!-- 1.1 -->
<Property name="FILE_LOG_BASE_PATH">/Users/yunai/logs</Property>
<Property name="APPLICATION_NAME">demo-application</Property>

<!-- 1.2 -->
<!-- 控制台的日志格式 -->
<Property name="CONSOLE_LOG_PATTERN">%clr{%d{${LOG_DATEFORMAT_PATTERN}}}{faint} %clr{${LOG_LEVEL_PATTERN}} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>
<!-- 1.3 -->
<!-- 日志文件的日志格式 -->
<Property name="FILE_LOG_PATTERN">%d{${LOG_DATEFORMAT_PATTERN}} ${LOG_LEVEL_PATTERN} ${sys:PID} --- [%t] %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>
</Properties>

<Appenders>
<!-- 2.1 -->
<!-- 控制台的 Appender -->
<Console name="Console" target="SYSTEM_OUT" follow="true">
<PatternLayout pattern="${sys:CONSOLE_LOG_PATTERN}" />
</Console>

<!-- 2.2 -->
<!-- 日志文件的 Appender -->
<RollingFile name="File" fileName="${FILE_LOG_BASE_PATH}/${sys:APPLICATION_NAME}"
filePattern="${FILE_LOG_BASE_PATH}/${sys:APPLICATION_NAME}-%d{yyyy-MM-dd-HH}-%i.log.gz">
<!-- 日志的格式化 -->
<PatternLayout>
<Pattern>${sys:FILE_LOG_PATTERN}</Pattern>
</PatternLayout>
<!--滚动策略,基于时间 + 大小的分包策略 -->
<Policies>
<SizeBasedTriggeringPolicy size="10 MB" />
</Policies>
</RollingFile>
</Appenders>

<Loggers>
<!-- 3.1 -->
<!-- 常用组件的 Logger 的日志级别 -->
<Logger name="org.apache.catalina.startup.DigesterFactory" level="error" />
<Logger name="org.apache.catalina.util.LifecycleBase" level="error" />
<Logger name="org.apache.coyote.http11.Http11NioProtocol" level="warn" />
<logger name="org.apache.sshd.common.util.SecurityUtils" level="warn"/>
<Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="warn" />
<Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="error" />
<Logger name="org.hibernate.validator.internal.util.Version" level="warn" />
<logger name="org.springframework.boot.actuate.endpoint.jmx" level="warn"/>

<!-- 3.2 -->
<!-- 自定义的 Logger 的日志级别 -->
<logger name="cn.iocoder.springboot.lab37.loggingdemo" level="debug"/>

<!-- 3.3 -->
<!-- 设置 Appender ,同时 ROOT 的日志级别为 INFO -->
<Root level="info">
<AppenderRef ref="Console" />
<AppenderRef ref="File" />
</Root>
</Loggers>
</Configuration>

  • 参考 Spring Boot 默认的 log4j2 XML 配置文件 log4j2-file.xml,进行修改。
  • <1.1> 处,定义日志文件的基础路径和文件名。因为 Spring Boot 并未提供 Log4j2 拓展,无法像「7. Logback 拓展」一样直接读取 Spring Boot 配置文件,所以这里我们只能直接定义。
  • <1.2> 处,定义了控制台的日志格式。
  • <1.3> 处,定义了日志文件的日志格式。
  • <2.1> 处,配置控制台的 Appender 。
  • <2.2> 处,配置日志文件的 Appender。
  • <3.1> 处,设置常用组件的 Logger 的日志级别。
  • <3.2> 处,自定义的 Logger 的日志级别。这里,我们设置名字为 "cn.iocoder.springboot.lab37.loggingdemo" 的 Logger 的日志级别为 DEBUG
  • <3.3> 处,设置 Root 的 Appender 为控制台和日志文件,日志级别为 INFO

可能胖友对 Log4j2 日志实现框架不是很了解,后续可以看看 《聊一聊 log4j2 配置文件 log4j2.xml》 文章。

8.4 Application

创建 Application 类,用于启动示例项目。代码如下:

@SpringBootApplication
public class Application {

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

public static void main(String[] args) {
SpringApplication.run(Application.class, args);

// <X> 打印日志
logger.debug("just do it");
}

}

  • <X> 处,打印 DEBUG 级别的日志,测试下不同环境下的日志级别。

8.5 简单测试

执行 Application#main(String[] args) 方法,启动 Spring Boot 应用。控制台打印日志如下:

2020-01-01 11:25:34.512  INFO 16166 --- [           main] c.i.s.l.l.Application                    : Started Application in 1.442 seconds (JVM running for 2.393)
2020-01-01 11:25:34.513 DEBUG 16166 --- [ main] c.i.s.l.l.Application : just do it

  • 我们在 <X> 处,打印的 DEBUG 日志,成功输出。符合预期

另外,如果胖友想不同环境下的 Log4j2 日志配置的配置不同,并且使用不同的 Log4j2 配置文件,则可以基于「6. 不同环境下的日志配置」的方案之上,每个 logging.config 配置项设置对应的环境的 Log4j2 配置文件。例如说,开发环境对应 log4j2-spring-dev.xml 配置文件、生产环境对应 log4j2-spring-prod.xml 配置文件。

9. 访问日志

在项目中,我们需要记录 HTTP 访问日志。一般来说,有三种方式:

  • 方式一,通过 Filter 过滤器
  • 方式二,通过 Spring MVC HandlerInterceptor 处理器过滤器
  • 方法三,通过 Spring AOP 切面

对于方式一,Spring Web 内置了 CommonsRequestLoggingFilter 过滤器,已经实现。如果日志内容不是胖友所需,胖友可以参考自定义实现。

对于方式二,艿艿的 Onemall 提供了 AccessLogInterceptor 拦截器,已经实现。个人比较推荐这种方式。

对于方式三,本小节会基于 Controller 进行 Spring AOP 切面,记录访问日志。

下面,我们就来看看基于 AOP 实现访问日志的示例。考虑到不污染「2. 快速入门」 的示例,我们在 lab-37-logging-demo 项目的基础上,复制出一个 lab-37-logging-aop 项目。😈 酱紫,我们也能少写点代码,哈哈哈~

9.1 引入依赖

修改 pom.xml 文件中,额外引入 spring-boot-starter-aop 依赖。如下:

<!-- 实现对 Spring AOP 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

9.2 DemoController

cn.iocoder.springboot.lab37.loggingdemo.controller 包路径下,创建 DemoController 类,提供不同 API 接口,打印不同级别的日志。代码如下:

// DemoController.java

@RestController
@RequestMapping("/demo")
public class DemoController {

private Logger logger = LoggerFactory.getLogger(getClass());

@GetMapping("/debug")
public void debug() {
logger.debug("debug");
}

@GetMapping("/info")
public void info() {
logger.info("info");
}

@GetMapping("/error")
public void error() {
logger.error("error");
}

}

9.3 HttpAccessAspect

创建 HttpAccessAspect 类,实现对 Controller 进行 Spring AOP 切面,记录访问日志。代码如下:

// HttpAccessAspect.java

@Component
@Aspect
public class HttpAccessAspect {

private Logger logger = LoggerFactory.getLogger(getClass());

@Around("@within(org.springframework.stereotype.Controller)" +
"|| @within(org.springframework.web.bind.annotation.RestController)")
public Object around(ProceedingJoinPoint point) throws Throwable {
// <1.1> 获取类名
String className = point.getTarget().getClass().getName();
// <1.2> 获取方法
String methodName = point.getSignature().getName();
// <1.3> 记录开始时间
long beginTime = System.currentTimeMillis();
// 记录返回结果
Object result = null;
Exception ex = null;
try {
// <2.1> 执行方法
result = point.proceed();
return result;
} catch (Exception e) {
// <2.2> 记录异常
ex = e;
throw e;
} finally {
// <3.1> 计算消耗时间
long costTime = System.currentTimeMillis() - beginTime;
// <3.2> 发生异常,则打印 ERROR 日志
if (ex != null) {
logger.error("[className: {}][methodName: {}][cost: {} ms][args: {}][发生异常]",
className, methodName, point.getArgs(), ex);
// <3.3> 正常执行,则打印 INFO 日志
} else {
logger.info("[className: {}][methodName: {}][cost: {} ms][args: {}][return: {}]",
className, methodName, costTime, point.getArgs(), result);
}
}
}

}

  • 上,添加 @Aspect 注解,标记它是一个切面类。同时,添加 @Component 注解,保证被 Spring 扫描到,创建一个 Bean 对象。
  • 方法上,添加 @Around(...) 注解,切入 @Controller@RestController 注解的 Controller 类的方法的调用。通过 @Around 注解,我们可以手动控制切入点的方法的调用,从而方便的计算执行时长。
  • <1.1><1.2><1.3> 处,获取类名、方法、开始时间。
  • <2.1> 处,执行切入点的方法的调用,并记录返回结果。如果发生异常,则在 <2.2> 处,进行记录。
  • <3.1> 处,计算消耗时间。
  • <3.2> 处,发生异常,则打印 ERROR 日志。
  • <3.3> 处,正常执行,则打印 INFO 日志。
  • 这里艿艿仅仅提供了较为简单的示例,更加接近实际项目所使用的,可以参考 RequestLogAspect 类。

对 Spring AOP 不了解的胖友,可以简单看看 《彻底征服 Spring AOP 之理论篇》 文章。

9.4 简单测试

执行 Application#main(String[] args) 方法,启动项目。

使用浏览器,访问 /demo/debug 接口,打印日志如下:

2020-01-01 14:43:33.284 DEBUG 18146 --- [nio-8080-exec-6] c.i.s.l.l.controller.DemoController      : debug
2020-01-01 14:43:33.285 INFO 18146 --- [nio-8080-exec-6] c.i.s.l.l.aspect.HttpAccessAspect : [className: cn.iocoder.springboot.lab37.loggingdemo.controller.DemoController][methodName: debug][cost: 1 ms][args: []][return: null]

  • HttpAccessAspect 成功切入 Controller 方法的调用,并记录访问日志。

如果胖友想要存储 HTTP 访问日志到数据库中,方便的后续查询使用,可以考虑采用如下几种方案:

  • 使用 ELK 方案,通过 Logstash 采集日志文件,持久化到 Elasticsearch 数据库中,最终使用 Kibana 查询与分析。
  • 使用 Logback 提供的 DBAppender ,将日志写入到数据库中。
  • 不使用 Logger ,而是直接使用 DAO 操作,存储访问日志到数据库。例如说,AccessLogInterceptor 拦截器。

666. 彩蛋

在生产环境下,我们会集群部署我们的应用。那么我们可能需要登录多台服务器,查看不用应用节点下的日志,这样会非常不方便。所以一般情况下,我们会采用 ELK 搭建日志平台,将日志进行采集,统一存储。感兴趣的胖友,可以阅读 《芋道 Spring Boot 日志平台 ELK 入门》

这里额外在推荐一些 Logger 相关的不错的内容:

文章目录
  1. 1. 1. 概述
    1. 1.1. 1.1 日志框架
    2. 1.2. 1.2 SLF4J 原理
    3. 1.3. 1.3 Spring Boot 对日志框架的封装
  2. 2. 2. 快速入门
    1. 2.1. 2.1 引入依赖
    2. 2.2. 2.2 配置文件
    3. 2.3. 2.3 Application
    4. 2.4. 2.4 DemoController
    5. 2.5. 2.5 简单测试
  3. 3. 3. 动态修改日志级别
    1. 3.1. 3.1 引入依赖
    2. 3.2. 3.2 配置文件
    3. 3.3. 3.3 简单测试
  4. 4. 4. 调试模式
    1. 4.1. 4.1 配置文件
    2. 4.2. 4.2 简单测试
  5. 5. 5. 日志分组
  6. 6. 6. 不同环境下的日志配置
    1. 6.1. 6.1 引入依赖
    2. 6.2. 6.2 配置文件
    3. 6.3. 6.3 Application
    4. 6.4. 6.4 简单测试
  7. 7. 7. Logback 扩展
    1. 7.1. 7.1 引入依赖
    2. 7.2. 7.2 应用配置文件
    3. 7.3. 7.3 Logback 配置文件
    4. 7.4. 7.4 Application
    5. 7.5. 7.5 简单测试
  8. 8. 8. 集成 Log4j2
    1. 8.1. 8.1 引入依赖
    2. 8.2. 8.2 应用配置文件
    3. 8.3. 8.3 Log4j2 配置文件
    4. 8.4. 8.4 Application
    5. 8.5. 8.5 简单测试
  9. 9. 9. 访问日志
    1. 9.1. 9.1 引入依赖
    2. 9.2. 9.2 DemoController
    3. 9.3. 9.3 HttpAccessAspect
    4. 9.4. 9.4 简单测试
  10. 10. 666. 彩蛋