摘要: 原创出处 http://www.iocoder.cn/Spring-Boot/Logging/ 「芋道源码」欢迎转载,保留摘要,谢谢!
本文在提供完整代码示例,可见 https://github.com/YunaiV/SpringBoot-Labs 的 lab-37 目录。
原创不易,给点个 Star 嘿,一起冲鸭!
在开始讲解在 Spring Boot 如何使用日志框架之前,我们想来了解下 Java 日志框架的生态,以便我们更深入的入门。当然,胖友也可以选择跳过,直接从「2. 快速入门」小节开始。
在 Java 日志框架的生态中,存在多种的日志实现框架。例如说:
JUL
Java 自带
java.util.logging
组件的简称。
Apache Log4j1
Apache Log4j2
Logback
那么,对于 Spring、Hibernate 等框架,会有打日志的需求,那么就需要选择相应的日志框架。但是,它们无论选择任一一个日志框架,可能使用 Spring、Hibernate 等框架的项目,希望选择另外一个日志框架。此时,项目中就需要添加多个日志框架的配置文件,十分麻烦不便。
所幸,在 Java 日志框架的生态中,存在多种日志门面框架,基于 Facade Pattern 设计模式的思想,提供通用的日志 API 给调用方,而自己去实现不同日志框架的适配。例如说:
SLF4J
Simple Logging Facade for Java
JCL
Apache Commons Logging
jboss-logging
这样就变成,Spring 采用 JCL 日志门面框架,Hibernate 采用 jboss-logging 日志门面框架,而将选择具体的日志实现框架的权利,交给使用者。
当然,也有框架,是自己实现简单的日志门面功能。例如说:
所以,在 Java 日志框架的生态中,一共存在两种角色:日志门面框架和日志实现框架。下面,我们把它们整理成下图:
不过要注意,日志门面框架仅提供通用的日志 API 给调用方,不包括每个日志实现框架的配置。因此我们在使用时,还是需要添加我们使用的具体的日志实现框架的配置文件。 也就是说,日志门面框架的作用是,帮助我们在项目中,统一日志实现框架的使用。
另外,即使在自己项目的代码中,如果有打日志的需求,也是推荐使用日志门面框架的 API ,而不是直接调用日志实现框架。在《阿里巴巴 Java 开发手册》中,也提出了这样的日志规约:
目前,比较主流的选择是,选择 SLF4J 作为日志门面框架,Logback 作为日志实现框。
那么,SLF4J 是如何实现对多个日志框架的适配的呢?在《SLF4J 文档 —— Bridging legacy APIs》中,已经详细讲解了。
我们先来看 SLF4J + Logback 的组合。如下图所示:
我们再来看 SLF4J + Log4j1 的组合。如下图所示:
slf4j-log4j12
库,因为 Log4j1 并未内置对 SLF4J 框架的实现,所以 SLF4J 只能自己实现。如下图所示:在 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}
日志级别:ERROR
、WARN
、INFO
、DEBUG
或 TRACE
。${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-spring.xml
、logback-spring.groovy
、logback.xml
、logback.groovy
配置文件。log4j2-spring.xml
、log4j2.xml
配置文件。logging.properties
配置文件。logging.level.*
:日志等级,通过使用 logging.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
文件的依赖如下:
|
spring-boot-starter-log4j2
:使用 SLF4J + Log4j2 的组合。其 pom.xml
文件的依赖如下:
|
默认情况下,spring-boot-starter
推荐采用 spring-boot-starter-logging
,即 SLF4J + Logback 的组合。因为其 pom.xml
文件引入了 spring-boot-starter-logging
。
示例代码对应仓库:lab-37-logging-demo 。
本小节,我们来进行下 Spring Boot 集成日志功能的快速入门,使用 SLF4J + Logback 的组合。主要功能包括:
下面,我们就来搭建一个 lab-37-logging-demo 项目。
在 pom.xml
文件中,引入相关依赖。
|
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
。在 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.path
或 logging.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 定义的日志级别为 TRACE
、DEBUG
、INFO
、WARN
、ERROR
、FATAL
、OFF
。
默认情况下,控制台和日志文件的日志级别 INFO
。即,INFO
、WARN
、ERROR
级别的日志,都会记录到控制台和日志文件中。
可以通过使用 logging.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 简单测试」详细测试说明。
创建 Application 类,用于启动示例项目。代码如下:
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } |
在 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"); } } |
① 测试“颜色输出”
执行 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
日志应该不会输出。示例代码对应仓库: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 项目。 酱紫,我们也能少写点代码,哈哈哈~
修改 pom.xml
文件中,额外引入 spring-boot-starter-actuator
依赖。如下:
|
修改 application.yaml
配置文件,额外增加 Spring Boot Actuator 相关配置。如下:
management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 |
本小节,我们会将名字为 "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」,也可以动态修改日志级别。
示例代码对应仓库:lab-37-logging-debug 。
默认情况下,Spring Boot 只会记录 ERROR
、WARN
和 INFO
级别的日志。可能有时我们启动 Spring Boot 失败时,需要通过启动 Spring Boot 调试模式:
DEBUG
级别的日志,方便排查原因目前,有两种方式开启 Spring Boot 应用的调试模式:
--debug
标识,进行开启。例如说 $ java -jar myapp.jar --debug
。debug = true
配置项,进行开启。下面,我们就来看看动态修改日志级别的示例。考虑到不污染「2. 快速入门」 的示例,我们在 lab-37-logging-demo 项目的基础上,复制出一个 lab-37-logging-debug 项目。 酱紫,我们也能少写点代码,哈哈哈~
修改 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 |
debug = true
配置项,开启调试模式。logging.level.*
配置项,使用 "cn.iocoder.springboot.lab37.loggingdemo.controller"
的 Logger 的日志级别,恢复为默认的 LOGGER
。① 调用 Application#main(Object[] args)
方法,启动 Spring Boot 应用。在控制台上,我们可以看到核心 Logger 打印了 DEBUG
级别的日志。如下图:
② 使用浏览器,http://127.0.0.1:8080/demo/debug/ 地址,查看打印的日志。如下图:
DEBUG
级别的日志。"cn.iocoder.springboot.lab37.loggingdemo.controller"
的 Logger 的日志级别并未变成 DEBUG
级别,打印出 DEBUG
级别的日志,符合预期。Spring Boot 提供了日志分组功能,可以相关 Logger 组合在一起,以便可以统一配置它们的日志级别。例如说:Spring Boot 内置了两个分组:
日志分组名 | 对应 Logger |
---|---|
web | org.springframework.core.codec 、org.springframework.http 、org.springframework.web |
sql | org.springframework.jdbc.core 、org.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.
配置项,来定义一个日志分组,其值为 Logger 们。logging.level.
配置项,来定义日志分组的日志级别。示例代码对应仓库:lab-37-logging-multi-env 。
同一个项目,在不同的环境下,日志配置可能存在差异性。例如说:
DEBUG
级别的日志级别,打印更多的日志,以便排查问题。INFO
级别的日志级别,毕竟生产环境下访问量比较大,日志量会很大。因为我们可以在配置文件中,进行日志配置,所以我们可以基于 Spring Profiles 机制,每个环境对应一个 Profile 对应一个配置文件。
下面,我们开始本小节的示例。考虑到不污染上述的示例,我们新建一个 lab-37-logging-multi-env 项目。
和「2.1 引入依赖」」一致,见 pom.xml
文件。
我们会创建两个配置文件,分别对应开发环境和测试环境。
① 开发环境
创建 application-dev.yaml
配置文件,添加开发环境的日志相关配置,如下:
spring: application: name: demo-application # 应用名 logging: # 日志级别 level: cn: iocoder: springboot: lab37: loggingdemo: DEBUG |
logging.file.path
或 logging.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
。创建 Application 类,用于启动示例项目。代码如下:
@SpringBootApplication public class Application { private static Logger logger = LoggerFactory.getLogger(Application.class); public static void main(String[] args) { SpringApplication.run(Application.class, args); // |
处,打印 DEBUG
级别的日志,测试下不同环境下的日志级别。通过 IDEA 的 Active profiles
选项,设置启动的 Spring Boot 应用的 Profile 。如下图所示:
① 测试开发环境
设置 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 |
处,打印的 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) |
处,打印的 DEBUG
日志,并未输出。符合预期~/Users/yunai/logs/$demo-application.log
日志文件,新增相应的日志。示例代码对应仓库:lab-37-logging-logback 。
在「1.3 Spring Boot 对日志框架的封装」中,我们可以通过配置文件的 logging.config
配置项,设置日志实现框架的具体配置文件。在 Spring Boot 中,默认情况下,Logback 读取 logback-spring.xml
、logback-spring.groovy
、logback.xml
、logback.groovy
配置文件。一般情况下,我们是使用 logback-spring.xml
配置文件。
Spring Boot 额外提供了 Logback 的两个 XML 标签的扩展。
① 拓展一:
标签
通过
标签,设置标签内的 Logback 的配置,需要符合指定的 Spring Profile 才可以生效。通过该标签,我们也可以实现「6. 不同环境下的日志配置」的效果。示例如下:
|
② 拓展二:
标签
通过
标签,可以从 Spring Boot 配置文件中读取配置项。示例如下:
|
下面,我们开始本小节的示例,和「6. 不同环境下的日志配置」小节的效果等价。考虑到不污染上述的示例,我们新建一个 lab-37-logging-logback 项目。
和「2.1 引入依赖」」一致,见 pom.xml
文件。
在 application.yml
中,仅添加应用名的配置即可,如下:
spring: application: name: demo-application # 应用名 |
在 logback-spring.xml
中,添加开发环境和生产环境的 Logback 配置,如下:
|
<1>
处,引入 Spring Boot 默认的 logback XML 配置文件 defaults.xml
。在其中,定义了控制台的日志格式 CONSOLE_LOG_PATTERN
、日志文件的日志格式 FILE_LOG_PATTERN
、Tomcat 等常用 Logger 的日志级别。<2.1>
处,配置控制台的 Appender。<2.2>
处,配置日志文件的 Appender。这里,我们可以看到 Spring Boot
拓展标签的使用。<3.1>
处,通过
标签,设置测试环境的独有配置。<3.2>
处,通过
标签,设置生产环境的独有配置。创建 Application 类,用于启动示例项目。代码如下:
@SpringBootApplication public class Application { private static Logger logger = LoggerFactory.getLogger(Application.class); public static void main(String[] args) { SpringApplication.run(Application.class, args); // |
处,打印 DEBUG
级别的日志,测试下不同环境下的日志级别。通过 IDEA 的 Active profiles
选项,设置启动的 Spring Boot 应用的 Profile 。如下图所示:
① 测试开发环境
设置 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 |
处,打印的 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) |
处,打印的 DEBUG
日志,并未输出。符合预期~/Users/yunai/logs/$demo-application.log
日志文件,新增相应的日志。示例代码对应仓库: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.xml
、log4j2.xml
配置文件。一般情况下,我们是使用 log4j2-spring.xml
配置文件。
友情提示:Log4j2 也是支持「2. 快速入门」小节中,采用 Spring Boot 配置文件的方式, 只是艿艿想稍微**“复杂”**下本小节的示例。
下面,我们开始本小节的示例,和「2. 快速入门」小节的效果等价。考虑到不污染上述的示例,我们新建一个 lab-37-logging-log4j2 项目。
在 pom.xml
文件中,引入相关依赖。
|
在 application.yml
中,仅添加应用名的配置即可,如下:
spring: application: name: demo-application # 应用名 |
在 log4j2-spring.xml
中,添加 Log4j2 配置,如下:
|
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》 文章。
创建 Application 类,用于启动示例项目。代码如下:
@SpringBootApplication public class Application { private static Logger logger = LoggerFactory.getLogger(Application.class); public static void main(String[] args) { SpringApplication.run(Application.class, args); // |
处,打印 DEBUG
级别的日志,测试下不同环境下的日志级别。执行 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 |
处,打印的 DEBUG
日志,成功输出。符合预期另外,如果胖友想不同环境下的 Log4j2 日志配置的配置不同,并且使用不同的 Log4j2 配置文件,则可以基于「6. 不同环境下的日志配置」的方案之上,每个 logging.config
配置项设置对应的环境的 Log4j2 配置文件。例如说,开发环境对应 log4j2-spring-dev.xml
配置文件、生产环境对应 log4j2-spring-prod.xml
配置文件。
在项目中,我们需要记录 HTTP 访问日志。一般来说,有三种方式:
对于方式一,Spring Web 内置了 CommonsRequestLoggingFilter 过滤器,已经实现。如果日志内容不是胖友所需,胖友可以参考自定义实现。
对于方式二,艿艿的 Onemall 提供了 AccessLogInterceptor 拦截器,已经实现。个人比较推荐这种方式。
对于方式三,本小节会基于 Controller 进行 Spring AOP 切面,记录访问日志。
下面,我们就来看看基于 AOP 实现访问日志的示例。考虑到不污染「2. 快速入门」 的示例,我们在 lab-37-logging-demo 项目的基础上,复制出一个 lab-37-logging-aop 项目。 酱紫,我们也能少写点代码,哈哈哈~
修改 pom.xml
文件中,额外引入 spring-boot-starter-aop
依赖。如下:
|
在 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"); } } |
创建 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
日志。对 Spring AOP 不了解的胖友,可以简单看看 《彻底征服 Spring AOP 之理论篇》 文章。
执行 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] |
如果胖友想要存储 HTTP 访问日志到数据库中,方便的后续查询使用,可以考虑采用如下几种方案:
在生产环境下,我们会集群部署我们的应用。那么我们可能需要登录多台服务器,查看不用应用节点下的日志,这样会非常不方便。所以一般情况下,我们会采用 ELK 搭建日志平台,将日志进行采集,统一存储。感兴趣的胖友,可以阅读 《芋道 Spring Boot 日志平台 ELK 入门》
这里额外在推荐一些 Logger 相关的不错的内容: