SpringBoot 3.2.0 基于Logback定制日志框架

日志门面和日志实现

日志门面(如Slf4j)就是一个标准,同JDBC一样来制定"规则",把不同的日志系统的实现进行了具体的抽象化,只提供了统一的日志使用接口。而Logback、log4j等具体的日志系统就如同MySQL驱动、PGSQL驱动一样,才是日志功能的真正实现。

SpringBoot默认日志框架

SpringBoot使用Slf4j作为日志门面,Logback作为默认的日志实现。在SpringBoot的pom.xml中,依赖为:

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

此依赖在SpringBoot Starter包中包含,所以在SpringBoot项目中导入 spring-boot-starter 后可直接使用:

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
</dependencies>

打印日志

  1. 基于LoggerFactory创建日志记录器实例
java 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
​
@SpringBootApplication
public class LoggingApplication {
​
    private static final Logger logger = LoggerFactory.getLogger(LoggingApplication.class);
​
    public static void main(String[] args) {
        logger.info("LoggingApplication start...");
        SpringApplication.run(LoggingApplication.class, args);
        logger.info("LoggingApplication end...");
    }
}
  1. 引入lombok依赖,通过注解@Slf4j创建日志记录器实例
xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>
java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
​
@Slf4j
@SpringBootApplication
public class LoggingApplication {
    public static void main(String[] args) {
        log.info("LoggingApplication start...");
        SpringApplication.run(LoggingApplication.class, args);
        log.info("LoggingApplication end...");
    }
}

配置Logback

默认配置

SpringBoot推荐将配置文件名称命名为logback-spring.xml表示这是SpringBoot下Logback专用的配置,可以使用SpringBoot 的高级Profile功能,它的内容类似于这样:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 配置信息 -->
</configuration>

最外层由configuration包裹,一旦编写,那么就会替换默认的配置,所以如果内部什么都不写的话,那么会导致我们的SpringBoot项目没有配置任何日志输出方式,控制台也不会打印日志。

也可在配置文件中指定要使用的日志配置,如application.yml中通过配置 logging.config指定要使用的日志配置。

yml 复制代码
logging:
  config: classpath:logback-spring.xml

基于SpringBoot默认Logback配置进行定制

SpringBoot 的默认Logback文件可在依赖项中找到。路径为:org/springframework/boot/logging/logback/defaults.xml

在配置文件中对默认配置进行引用,及编写定制配置覆盖默认配置。

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 引用Spring Boot 默认日志配置 -->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
​
    <!-- 控制台日志打印格式 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="MyLogPattern %clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr(${LOGGED_APPLICATION_NAME:-}[%15.15t]){faint} %clr(${LOG_CORRELATION_PATTERN:-}){faint}%clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
​
    <!-- 日志输出到控制台 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>${CONSOLE_LOG_THRESHOLD}</level>
        </filter>
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>${CONSOLE_LOG_CHARSET}</charset>
        </encoder>
    </appender>
​
    <!-- 指定日志输出级别 -->
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>

配置日志信息输出到文件

logback-spring.xml 添加如下内容:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 引用Spring Boot 默认日志配置 -->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <!-- 日志文件输出位置 -->
    <property name="LOG_PATH" value="./logs"/>
    <!-- 日志文件名 -->
    <property name="LOG_FILE" value="${LOG_PATH}/spring-boot.log"/>
​
    <!-- 日志输出到文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>${FILE_LOG_THRESHOLD}</level>
        </filter>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
            <charset>${FILE_LOG_CHARSET}</charset>
        </encoder>
        <file>${LOG_FILE}</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}</fileNamePattern>
            <cleanHistoryOnStart>${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart>
            <maxFileSize>${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB}</maxFileSize>
            <totalSizeCap>${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0}</totalSizeCap>
            <maxHistory>${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7}</maxHistory>
        </rollingPolicy>
    </appender>
​
    <!-- 指定日志输出级别,以及启动的Appender -->
    <root level="INFO">
        <appender-ref ref="FILE"/>
    </root>
</configuration>

MDC机制

Slf4j官方解释

MDC全称Mapped Diagnostic Context(映射诊断上下文),"映射诊断上下文" 本质上是由日志记录框架维护的映射,其中应用程序代码提供键值对,然后可以由日志记录框架将其插入日志消息中。MDC数据在过滤消息或触发某些操作时也非常有用。

通俗的说就是可以将特定的信息(如请求Id,链路Id),通过MDC机制注入到当前日志线程的上下文中,将信息在日志中记录、输出。

MDC的使用

MDC 常用的三个方法:

  • MDC.put(key,value) 注入值
  • MDC.remove(key) 移除指定的MDC注入信息
  • MDC.clear() 移除所有当前线程的MDC注入信息

MDC注入属性配置:

logback-spring.xml文件的输出格式中添加需要注入的KEY信息与代码注入的信息相对应。假定我需要注入当前用户的Id信息,且设置key为user-id

代码:

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
​
import java.util.UUID;
​
@Slf4j
@SpringBootApplication
public class LoggingApplication {
    public static void main(String[] args) {
        SpringApplication.run(LoggingApplication.class, args);
        String mdcKey = "user-id";
        log.info("Logging before MDC put...");
        MDC.put("mdcKey", UUID.randomUUID().toString());
        log.info("Logging after MDC put...");
        MDC.clear();
        log.info("Logging after MDC clear...");
    }
}
​

logback-spring.xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 引用Spring Boot 默认日志配置 -->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
​
    <!-- 控制台日志打印格式 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="[%X{user-id}] %clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr(${LOGGED_APPLICATION_NAME:-}[%15.15t]){faint} %clr(${LOG_CORRELATION_PATTERN:-}){faint}%clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
​
    <!-- 日志输出到控制台 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>${CONSOLE_LOG_THRESHOLD}</level>
        </filter>
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>${CONSOLE_LOG_CHARSET}</charset>
        </encoder>
    </appender>
​
    <!-- 指定日志输出级别 -->
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>

使用方式:

  1. 通过注册拦截器或过滤器,在业务开始前获取到相关信息(请求Id,操作人Id),将相关信息通过MDC自动注入到当前线程中,实现关键信息记录。
  2. 在创建子线程时,需要通过MDC.getCopyOfContextMap()方法获取到当前线程的MDC注入相关信息,传递给子线程,才能在子线打印日志信息时获取到父线程MDC的注入信息。

Logback参考配置

xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <!-- Spring Boot 默认日志配置 -->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <!-- 日志文件输出位置 -->
    <property name="LOG_PATH" value="./logs"/>
    <!-- 日志文件名 -->
    <property name="LOG_FILE" value="${LOG_PATH}/spring-boot.log"/>
    <!-- 归档日志名 -->
    <property name="FILE_NAME_PATTERN" value="${LOG_FILE}-%d{yyyyMMdd}.%i.gz"/>
    <!-- 在启动时清除历史日志 -->
    <property name="LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START" value="false"/>
    <!-- 单个日志文件最大大小 -->
    <property name="LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE" value="100MB"/>
    <!-- 日志总大小 -->
    <property name="LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP" value="0"/>
    <!-- 日志保留天数 -->
    <property name="LOGBACK_ROLLINGPOLICY_MAX_HISTORY" value="7"/>
    <!-- 控制台日志编码格式 -->
    <property name="CONSOLE_LOG_CHARSET" value="UTF-8"/>
    <!-- 控制台日志打印格式 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} [%X{user-id}] [%X{request-id}] [%X{client-ip}] %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr(${LOGGED_APPLICATION_NAME:-}[%15.15t]){faint} %clr(${LOG_CORRELATION_PATTERN:-}){faint}%clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
    <!-- 控制台日志输出级别 -->
    <property name="CONSOLE_LOG_THRESHOLD" value="INFO"/>
    <!-- 日志文件输出打印格式 -->
    <property name="FILE_LOG_PATTERN"
              value="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} [%X{user-id}] [%X{request-id}] [%X{client-ip}] ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- ${LOGGED_APPLICATION_NAME:-}[%t] ${LOG_CORRELATION_PATTERN:-}%-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
    <!-- 日志文件输出编码格式 -->
    <property name="FILE_LOG_CHARSET" value="UTF-8"/>
    <!-- 日志文件输出级别 -->
    <property name="FILE_LOG_THRESHOLD" value="INFO"/>
​
​
    <!-- 日志输出到控制台 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>${CONSOLE_LOG_THRESHOLD}</level>
        </filter>
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>${CONSOLE_LOG_CHARSET}</charset>
        </encoder>
    </appender>
​
    <!-- 日志输出到文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>${FILE_LOG_THRESHOLD}</level>
        </filter>
        <!-- 日志打印配置 -->
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
            <charset>${FILE_LOG_CHARSET}</charset>
        </encoder>
        <!-- 日志文件名 -->
        <file>${LOG_FILE}</file>
        <!-- 日志输出配置 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 日志归档名 -->
            <fileNamePattern>${FILE_NAME_PATTERN}</fileNamePattern>
            <!-- 在启动时清除日志 -->
            <cleanHistoryOnStart>${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START}</cleanHistoryOnStart>
            <!-- 单个文件大小 -->
            <maxFileSize>${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE}</maxFileSize>
            <!-- 日志总大小 -->
            <totalSizeCap>${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP}</totalSizeCap>
            <!-- 日志保留天数 -->
            <maxHistory>${LOGBACK_ROLLINGPOLICY_MAX_HISTORY}</maxHistory>
        </rollingPolicy>
    </appender>
​
    <!-- 指定日志输出级别,以及启动的Appender -->
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
    </root>
</configuration>
相关推荐
0xDevNull17 分钟前
Linux 中 Nginx 代理 Redis 的详细教程
redis·后端
GetcharZp33 分钟前
告别 Nginx 手动配置!这款 Go 语言开发的云原生网关,才是容器化时代的真香神器!
后端
RuoyiOffice42 分钟前
SpringBoot+Vue3 企业考勤如何处理法定假期?节假日方案、调休补班与工作日判断链路拆解
spring boot·后端·vue·anti-design-vue·ruoyioffice·假期·人力
xmjd msup1 小时前
spring security 超详细使用教程(接入springboot、前后端分离)
java·spring boot·spring
Vane11 小时前
从零开发一个AI插件,经历了什么?
人工智能·后端
952362 小时前
SpringBoot统一功能处理
java·spring boot·后端
rleS IONS2 小时前
SpringBoot中自定义Starter
java·spring boot·后端
DevilSeagull2 小时前
MySQL(2) 客户端工具和建库
开发语言·数据库·后端·mysql·服务
TeDi TIVE3 小时前
springboot和springframework版本依赖关系
java·spring boot·后端
雨辰AI3 小时前
SpringBoot3 + 人大金仓 V9 微服务监控实战|Prometheus+Grafana+SkyWalking 全链路监控
数据库·后端·微服务·grafana·prometheus·skywalking