文章目录
- 具体步骤
- 附录
笔者的操作环境:
Spring Cloud Alibaba:2022.0.0.0-RC2
Spring Cloud:2022.0.0
Spring Boot:3.0.2
Nacos 2.2.3
Maven 3.8.3
JDK 17.0.7
IntelliJ IDEA 2022.3.1 (Ultimate Edition)
具体步骤
-
因为 Spring Boot 已经内置了 Logback,所以需要先将 Logback 移除。移除的方法是在 Spring Boot 依赖包中移除 Logback。
<exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions>
比如就像这样:
<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>
【踩坑提醒】
如何使用了 Maven 多模块,必须在所有依赖 Logback 的依赖包中将 Logback 排除,否则 Spring Boot 启动时会发生如下报错:
SLF4J: Class path contains multiple SLF4J providers. SLF4J: Found provider [ch.qos.logback.classic.spi.LogbackServiceProvider@4f51b3e0] SLF4J: Found provider [org.apache.logging.slf4j.SLF4JServiceProvider@4b9e255] SLF4J: See https://www.slf4j.org/codes.html#multiple_bindings for an explanation. SLF4J: Actual provider is of type [ch.qos.logback.classic.spi.LogbackServiceProvider@4f51b3e0]
比如,下面这两个 Spring Boot 依赖包中都依赖了 Logback,所以都要排除 Logback。
<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-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency>
如果想确定自己的 Maven 模块有没有依赖 Logback,可以在 IntelliJ IDEA 中查看。
比方说,下面这种情况就属于同时依赖了 Logback、Log4j2,这样 Spring Boot 就会在启动时报错。
-
引入与 Spring Boot 适配的 Log4j2 依赖包。
<!-- 设置 SLF4J 与之绑定的日志包。无需提供 SLF4J 的 JAR 包,因为 Lombok 已经提供了 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> <version>Spring Boot 的版本</version> </dependency>
此依赖包的版本与 Spring Boot 是一致的。如果不清楚应该使用什么版本,可以去 Maven 仓库中查询。Maven 仓库官网:https://mvnrepository.com/
【提示】
对于不使用 Spring Boot 的项目,使用的是如下经典 Log4j2 依赖配置。使用 Spring Boot 之后,此依赖配置是多余的。
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>Log4j2 的版本</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>Log4j2 的版本</version> </dependency> <!-- 设置 SLF4J 与之绑定的日志包。无需提供 SLF4J 的 JAR 包,因为 Lombok 已经提供了 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>Log4j2 的版本</version> </dependency>
读者可以自行检查,Spring Boot 适配的 Log4j2 依赖包已经包含了上述经典 Log4j2 依赖配置。
-
提供 Log4j2 的日志配置文件。
如果不提供此配置,则 Spring Boot 会提供一个默认配置。通常,默认配置也不是很糟糕,但它有一个严重的问题,它不会将日志输出至文件来备份。因此,不能使用默认配置。
一个示例的 Log4j2 的日志配置如下,读者可以自行变更为自己喜欢的配置。
<?xml version="1.0" encoding="UTF-8"?> <configuration status="OFF"> <Properties> <property name="console_log_pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} [%level] [%t] %l %n %m%n</property> <property name="file_log_pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} [%level] [%t] %C.%M[%L line] %n %m%n</property> <property name="every_file_size">20MB</property> </Properties> <appenders> <Console name="Console" target="SYSTEM_OUT"> <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="${console_log_pattern}"/> </Console> <RollingFile name="DEBUG" fileName="./log/log4j2/debug.log" filePattern="./log/log4j2/debug_log_archive/debug-%d{yyyy-MM-dd}-%i.log.zip"> <PatternLayout pattern="${file_log_pattern}"/> <Policies> <SizeBasedTriggeringPolicy size="${every_file_size}"/> </Policies> <Filters> <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/> </Filters> </RollingFile> <RollingFile name="INFO" fileName="./log/log4j2/info.log" filePattern="./log/log4j2/info_log_archive/info-%d{yyyy-MM-dd}-%i.log.zip"> <PatternLayout pattern="${file_log_pattern}"/> <Policies> <SizeBasedTriggeringPolicy size="${every_file_size}"/> </Policies> <Filters> <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/> </Filters> </RollingFile> <RollingFile name="WARN" fileName="./log/log4j2/warn.log" filePattern="./log/log4j2/warn_log_archive/warn-%d{yyyy-MM-dd}-%i.log.zip"> <PatternLayout pattern="${file_log_pattern}"/> <Policies> <SizeBasedTriggeringPolicy size="${every_file_size}"/> </Policies> <Filters> <ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/> </Filters> </RollingFile> <RollingFile name="ERROR" fileName="./log/log4j2/error.log" filePattern="./log/log4j2/error_log_archive/error-%d{yyyy-MM-dd}-%i.log.zip"> <PatternLayout pattern="${file_log_pattern}"/> <Policies> <SizeBasedTriggeringPolicy size="${every_file_size}"/> </Policies> <Filters> <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/> </Filters> </RollingFile> </appenders> <loggers> <!-- 属性 level 是用于设置最低需要输出的日志输出级别 --> <root level="DEBUG"> <appender-ref ref="Console"/> <appender-ref ref="DEBUG"/> <appender-ref ref="INFO"/> <appender-ref ref="WARN"/> <appender-ref ref="ERROR"/> </root> </loggers> </configuration>
-
Log4j2 的日志配置文件编写完成之后,可以放在 Maven 模块的
resource
目录下,如下图所示。然后,在 Spring Boot 配置文件(如
application.yml
)中使用如下代码引入该配置。logging: config: classpath:log4j2.xml
如果 Log4j2 的日志配置文件名为
log4j2.xml
或log4j2-spring.xml
,且放在resource
目录下,那就算是不在 Spring Boot 配置文件中引入此 Log4j2 的日志配置,Spring Boot 也会自动读取该 Log4j2 的日志配置。不过,最好还是显式地引入此配置。
【注意】
如果使用了 Maven 多模块,则此 Log4j2 的日志配置文件和 Spring Boot 配置文件只能放在 Spring Boot 入口模块中。不要在一种没有程序启动入口的 Maven 库模块中放置此配置文件。
-
前面已经导入了 Log4j2,现在来考虑 SLF4J。
SLF4J 是一种门面日志,它只要发现导入了 Log4j2,它就会自动使用它。
幸运的是,有一个众所周知的插件叫 Lombok,它已经内置了 SLF4J。因此只要使用 Lombok,就可以不需要引入 SLF4J 依赖。
-
引入 Lombok 的依赖代码如下。
<!-- 注意:Lombok 不会在依赖中被继承 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>Lombok 的版本</version> <scope>provided</scope> </dependency>
-
然后,在需要使用 SLF4J 的类上使用注解
@Slf4j
。这样就可以直接在代码中使用log.xxx(...)
来使用 Log4j2 日志了。@Slf4j public class UserAvatarController { // ...省略其它内容... public void fun(HttpServletRequest request, HttpServletResponse response) { log.info("fun called"); }
附录
-
Log4j2
-
Log4j2 的开源地址:https://github.com/apache/logging-log4j2
-
Log4j2 官方文档:https://logging.apache.org/log4j/2.x/manual/index.html
-
-
SLF4J
-
SLF4J 官网:https://www.slf4j.org/
-
SLF4J 官方文档:https://www.slf4j.org/docs.html
-