如何在 Spring Boot 中集成日志框架 SLF4J、Log4j

文章目录

笔者的操作环境:

  • 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)

具体步骤

  1. 因为 Spring Boot 已经内置了 Logback,所以需要先将 Logback 移除。移除的方法是在 Spring Boot 依赖包中移除 Logback。

    xml 复制代码
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>

    比如就像这样:

    xml 复制代码
    <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 启动时会发生如下报错:

    txt 复制代码
    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。

    xml 复制代码
    <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>
    xml 复制代码
    <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 就会在启动时报错。


  2. 引入与 Spring Boot 适配的 Log4j2 依赖包。

    xml 复制代码
    <!-- 设置 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 之后,此依赖配置是多余的。

    xml 复制代码
    <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 依赖配置。


  3. 提供 Log4j2 的日志配置文件。

    如果不提供此配置,则 Spring Boot 会提供一个默认配置。通常,默认配置也不是很糟糕,但它有一个严重的问题,它不会将日志输出至文件来备份。因此,不能使用默认配置。

    一个示例的 Log4j2 的日志配置如下,读者可以自行变更为自己喜欢的配置。

    xml 复制代码
    <?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>
  4. Log4j2 的日志配置文件编写完成之后,可以放在 Maven 模块的 resource 目录下,如下图所示。

    然后,在 Spring Boot 配置文件(如 application.yml)中使用如下代码引入该配置。

    yml 复制代码
    logging:
      config: classpath:log4j2.xml

    如果 Log4j2 的日志配置文件名为 log4j2.xmllog4j2-spring.xml,且放在 resource 目录下,那就算是不在 Spring Boot 配置文件中引入此 Log4j2 的日志配置,Spring Boot 也会自动读取该 Log4j2 的日志配置。不过,最好还是显式地引入此配置。


    【注意】

      如果使用了 Maven 多模块,则此 Log4j2 的日志配置文件和 Spring Boot 配置文件只能放在 Spring Boot 入口模块中。不要在一种没有程序启动入口的 Maven 库模块中放置此配置文件


  5. 前面已经导入了 Log4j2,现在来考虑 SLF4J。

    SLF4J 是一种门面日志,它只要发现导入了 Log4j2,它就会自动使用它。

    幸运的是,有一个众所周知的插件叫 Lombok,它已经内置了 SLF4J。因此只要使用 Lombok,就可以不需要引入 SLF4J 依赖。

  6. 引入 Lombok 的依赖代码如下。

    xml 复制代码
    <!-- 注意:Lombok 不会在依赖中被继承 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>Lombok 的版本</version>
        <scope>provided</scope>
    </dependency>
  7. 然后,在需要使用 SLF4J 的类上使用注解 @Slf4j。这样就可以直接在代码中使用 log.xxx(...) 来使用 Log4j2 日志了。

    java 复制代码
    @Slf4j
    public class UserAvatarController {
        // ...省略其它内容...
        public void fun(HttpServletRequest request, HttpServletResponse response) {
            log.info("fun called");
    }

附录

相关推荐
Java陈序员21 小时前
企业级!一个基于 Java 开发的开源 AI 应用开发平台!
spring boot·agent·mcp
杨运交1 天前
[041][公共模块]分布式唯一ID生成器设计与实现:一款灵活可扩展的雪花算法框架
spring boot
Flittly2 天前
【AgentScope Java新手村系列】(14)人机交互
java·spring boot·spring
Flynt3 天前
从Spring Boot 4.0升到4.1,我在Maven和gRPC上栽了跟头
java·spring boot·后端
掉鱼的猫4 天前
Spring Boot → Solon 注解迁移实战指南:一张对照表说清楚
java·spring boot
人活一口气5 天前
Spring Boot与AIGC的完美结合:从零搭建智能内容生成平台
java·spring boot·aigc
java小白小8 天前
SpringBoot(01): 初识SpringBoot,从Spring的痛点说起
spring boot
用户3169353811838 天前
如何从零编写一个 Spring Boot Starter
spring boot
程序员晓琪9 天前
约定大于配置:基于 Java 包名自动生成 API 版本路由的最佳实践
java·spring boot·后端
Flittly9 天前
【AgentScope Java新手村系列】(11)中断与恢复
java·spring boot·spring