环境
- 操作系统:Ubuntu 20.04
- 开发工具:IntelliJ IDEA 2022.1 (Community Edition)
- JDK:17.0.1
准备工作
在 https://start.spring.io/ 创建项目 test0509_2 ,添加 Spring Web 依赖。
创建package controller ,并创建class HelloController 如下:
package com.example.test0509_2.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
public class HelloController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@GetMapping("/test")
public Map<String, String> test() {
logger.trace("trace");
logger.debug("debug");
logger.info("info");
logger.warn("warn");
logger.error("error");
return Map.of("key1", "value1");
}
}
运行程序,然后打开浏览器,访问 http://localhost:8080/test ,最后查看命令行,结果如下:
2022-05-10 20:19:38.506 INFO 76371 --- [nio-8080-exec-3] c.e.t.controller.HelloController : info
2022-05-10 20:19:38.507 WARN 76371 --- [nio-8080-exec-3] c.e.t.controller.HelloController : warn
2022-05-10 20:19:38.508 ERROR 76371 --- [nio-8080-exec-3] c.e.t.controller.HelloController : error
可见,默认的log级别是 INFO 。
Spring Boot默认使用 SLF4J + Logback 的组合:
SLF4J :log的门面(抽象);Logback :log的实现;
项目的依赖关系如下:
可见, spring-boot-starter 包依赖于 spring-boot-starter-logging 包,所以我们可以在Spring Boot项目里直接使用log。而 spring-boot-starter-logging 包又依赖于 logback 、 log4j 、 jul (即Java Util Logging,JDK自带的log功能)这三个包,这是log的三种实现,Spring默认使用的是 Logback 。
设置Spring Boot的log级别
要设置整个Spring Boot应用的log级别,可以给应用程序添加命令行参数,或者通过配置文件来设置。比如,添加命令行参数 --trace (或者在 application.properties 文件中添加 trace=true ):
运行程序,可见命令行输出了很多 TRACE 和 DEBUG 的log。
同理,也可以设置成 DEBUG 级别。
注意:当启用trace或者debug模式时,Spring Boot的核心log为 TRACE 或者 DEBUG 级别,但是应用程序的log级别并不受影响。刷新浏览器页面,命令行的输出仍然是 INFO 级别的log。
测试完毕,别忘了还原设置,以免影响到下面的测试。
设置应用程序的log级别
打开 application.properties 文件,添加内容如下:
logging.level.root=WARN
再次运行程序,刷新浏览器页面,可见命令行只有 WARN 和 ERROR 的log。
注意:该配置也会影响Spring Boot的核心log,Spring Boot启动时的 INFO log都不见了。
上面设置了应用程序整体的log级别,事实上也可以设置特定包或者类的log级别,如下:
logging.level.root=WARN
logging.level.<package全名>=WARN
logging.level.<class全名>=WARN
......
测试完毕,别忘了还原设置,以免影响到下面的测试。
配置log格式
logging.pattern.console logging.pattern.file
注意:只有使用Logback时才有效。
输出log到文件
可通过如下配置:
logging.file.name :文件名logging.file.path :文件目录
注意:
- 如果只指定
path ,不指定 name ,则默认文件名为 spring.log ; - 如果只指定
name ,不指定 path ,则在当前目录下生成log文件;
注:参见上面图片中的 Working directory 设置,当前目录是项目的根目录。
例如:
logging.file.path=.
logging.file.name=mylog.log
当使用 Logback 时,可配置如下参数:
logging.logback.rollingpolicy.max-file-size :设置文件大小,默认为10MB;logging.logback.rollingpolicy.max-history :设置保留天数,默认为7天;- ……
对于其它log实现,则需要修改对应的配置文件,比如 Log4j ,就需要修改 log4j.xml / log4j-spring.xml 文件进行配置。
运行程序,可见当前目录(即项目的根目录)里面的 mylog.log 文件生效了,同时命令行log仍然生效。
Log group
配置 logging.group ,把多个package和class放到一个group里统一设置级别,方便管理。例如:
logging.group.<groupname>=xxx,xxx,......
logging.level.<groupname>=trace
显式指定log实现(关闭log)
Spring Boot默认使用Logback作为log实现。可以通过系统属性 org.springframework.boot.logging.LoggingSystem 显式指定log实现。其值可以是 LoggingSystem 实现类的全名,也可以指定为 none ,关闭log。
我们来尝试关闭log。 Run -> Edit Configurations... ,在 VM options 处,添加 -Dorg.springframework.boot.logging.LoggingSystem=none ,如下图:
注意:默认并没有显示 VM options 编辑框,需要点击 Modify options ,然后勾选 Add VM Options 。
注意: VM options 即系统属性(System Property),可以理解为JVM的环境变量,通过 -D<name>=<value> 来设置。
运行程序,我测试的效果为:
- 文件log确实关闭了(Spring Boot核心log和应用程序log都关闭了);
- 命令行log不但没有关闭,反而级别变成了
DEBUG (Spring Boot核心log和应用程序log都变成 DEBUG 级别了);
用命令行运行 java -jar -Dorg.springframework.boot.logging.LoggingSystem=none ./target/test0509_2-0.0.1-SNAPSHOT.jar 也是同样效果。
注:为何要通过系统属性,而不能通过 application.properties 文件来配置 org.springframework.boot.logging.LoggingSystem 呢?Spring Boot的官方文档(https://docs.spring.io/spring-boot/docs/2.6.7/reference/htmlsingle/#features.logging.custom-log-configuration)如是说:
Logging在ApplicationContext创建之前就初始化了,因此无法通过Spring的 @Configuration 文件的 @PropertySources 来控制logging。改变或者关闭logging system的唯一方法是通过系统属性。
我测试过,如果清空 VM options ,同时把 org.springframework.boot.logging.LoggingSystem=none 添加到 application.properties 文件里,确实命令行log和文件log都不生效。
Log实现的配置文件
前面的测试都没有涉及log实现的配置文件。事实上Logback,Log4j2等log实现,也支持配置文件。Spring Boot默认从类加载路径的根路径下加载log配置文件。Spring Boot自动识别并加载的配置文件如下:
- Logback:
logback-spring.xml 、 logback.xml 等 - Log4j2:
log4j2-spring.xml 、 log4j2.xml 等 - JUL:
logging.properties
注:Spring Boot推荐使用带 -spring 后缀的配置文件。
可以通过 logging.config 系统属性来指定log配置文件。
比如,我们创建 logback-spring.xml 文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<property name="LOG_FILE"
value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}"/>
<include resource="org/springframework/boot/logging/logback/file-appender.xml" />
<root level="INFO">
<appender-ref ref="FILE" />
</root>
</configuration>
注: :- 是Logback语法,用来作为属性名和默认值之间的分隔符。本例中 LOG_FILE 的设置,我没仔细研究其作用。看起来貌似并不影响log:如果 application.properties 里设置了 logging.file.name ,就会用指定的命名,否则就用默认值 spring.log 命名。
注意,配置文件指定了log级别为 INFO ,且只用文件log(没有命令行log)。至于Log文件的路径和命名,在 application.properties 文件中指定(参见上面的配置)。
把 logback-spring.xml 文件复制到任意目录,比如项目的根目录下。
由于该文件不在类加载路径的根路径下,我们需要显式告诉Spring Boot来加载它。指定 VM options 为 -Dlogging.config=logback-spring.xml 。运行程序,项目的根目录(即当前目录)里面的 logback-spring.xml 配置文件就会生效。该配置文件配置了log级别为 INFO ,且只用文件log。
可以看到,命令行确实没有输出了,同时项目的根目录(即当前目录)里面的 mylog.log 文件生效了。
同样的问题:能否不使用系统属性,而使用 application.properties 文件来配置呢?我测试过,如果清空 VM options ,同时把 logging.config=logback-spring.xml 添加到 application.properties 文件里,它仍然生效:命令行log没有了,同时 mylog.log 文件生效了。
Spring Boot属性 VS. 系统属性
具体对应关系,参见Spring官网: https://docs.spring.io/spring-boot/docs/2.6.7/reference/htmlsingle/#features.logging.custom-log-configuration
隐式指定log实现
前面提到,可以通过系统属性 org.springframework.boot.logging.LoggingSystem 来显式指定log实现。如果不指定,则Spring Boot默认会根据依赖关系,自动选取log实现。
在本文开头的项目依赖关系图里,我们看到 spring-boot-starter-logging 包依赖于 logback 、 log4j 、 jul ,即log的三种实现,其中 Logback 优先级较高,所以Spring默认使用的是 Logback 。
下面我们来尝试通过改变依赖关系来指定log实现。修改 pom.xml 文件,在 spring-boot-starter 下添加 exclusion ,排除对 spring-boot-starter-logging 的依赖,同时添加对 spring-boot-starter-log4j2 的依赖,如下:
<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>
注:添加对 spring-boot-starter-log4j2 的依赖,就会自动传递对 SLF4J 的依赖。
刷新Maven依赖关系,如下图:
可见已经没有对 Logback 的依赖了。
Maven -> Compile ,重新编译项目,然后去掉所有logging相关的配置,只保留 application.properties 文件里的log文件配置:
logging.file.path=.
logging.file.name=mylog.log
运行程序,可见和Logback的log并没什么区别,在命令行和 mylog.log 文件都输出了 INFO 级别的log。
可以在debug模式下设置断点观察Logger:
可见这是 Log4jLogger 。
补一张之前使用 Logback 时的图: 可见这是Logback的Logger。
注意:如果使用了其它log实现,就不要再指定跟Logback相关的配置,否则会有问题。比如,在 application.properties 文件中添加如下配置:
logging.config=logback-spring.xml
运行程序,则命令行只有 ERROR 级别的log,而文件log无效了。不知道为啥,但总之,如果使用某种log实现,就不要再指定其它log实现的配置了,以免产生不必要的问题。
参考
|