核心目的
console 控台看日志 dev 阶段还行,不适合真正的产线阶段。
希望引入整合 log4j2
并且日志引入 tid
ERROR 日志级别单独的文件,方便问题排查。
实现
包排除
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 排除默认 logback -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- log4j2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
</dependencies>
TID
把 每个请求的 TID(Trace ID) 也记录到日志里,这是企业级系统非常常见的做法。
我们可以通过 Log4j2 的 ThreadContext(MDC) 来实现。整体方案如下:
1️⃣ 添加 TID 到每个请求
在 Spring Boot 后端创建一个 拦截器,给每个请求生成一个唯一 TID,并放入 Log4j2 的 ThreadContext:
package com.github.houbb.passport.plateform.backend.interceptor;
import org.apache.logging.log4j.ThreadContext;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;
@Component
public class TraceIdInterceptor implements HandlerInterceptor {
private static final String TID_KEY = "tid";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String tid = UUID.randomUUID().toString().replace("-", "");
ThreadContext.put(TID_KEY, tid);
response.setHeader("X-TID", tid); // 可返回给前端
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
ThreadContext.remove(TID_KEY); // 避免内存泄漏
}
}
注册拦截器:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private TraceIdInterceptor traceIdInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(traceIdInterceptor).addPathPatterns("/**");
}
}
2️⃣ Log4j2 配置优化(支持 TID、按天归档、错误日志分离)
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="60">
<Properties>
<Property name="LOG_HOME">/app/logs/passport-plateform</Property>
<Property name="PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [TID=%X{tid}] %-5level %logger{36} - %msg%n</Property>
</Properties>
<Appenders>
<!-- 通用日志,按天滚动 -->
<RollingFile name="RollingFile" fileName="${LOG_HOME}/app.log"
filePattern="${LOG_HOME}/archive/app-%d{yyyy-MM-dd}.log.gz">
<PatternLayout pattern="${PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
<DefaultRolloverStrategy max="30"/>
</RollingFile>
<!-- 错误日志单独输出 -->
<RollingFile name="ErrorFile" fileName="${LOG_HOME}/error.log"
filePattern="${LOG_HOME}/archive/error-%d{yyyy-MM-dd}.log.gz">
<PatternLayout pattern="${PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
<DefaultRolloverStrategy max="30"/>
<Filters>
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
</RollingFile>
<!-- 控制台输出 -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${PATTERN}"/>
</Console>
</Appenders>
<Loggers>
<!-- 默认日志 -->
<Root level="info">
<AppenderRef ref="RollingFile"/>
<AppenderRef ref="ErrorFile"/>
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
特点:
- 每条日志都会输出 TID:
[TID=xxxx] - 日志分为普通日志和错误日志,按天归档压缩
- 控制台输出保留开发可视化
- 日志文件路径可灵活修改
3️⃣ 使用方式
- 保证
pom.xml已经引入spring-boot-starter-log4j2并排除 logback - 在
application.properties配置:
logging.config=classpath:log4j2.xml
- Spring Boot 启动后,每条请求日志都自动带上 TID,便于全链路跟踪
