Spring Boot微服务:如何将请求头参数设为Log4j查找变量?
实现方案:用MDC+Log4j2自动注入track_number到日志
完全可以实现,不用在每个方法里手动传递参数,核心是借助MDC(映射诊断上下文)——这是Log4j等日志框架专门用于存储请求级上下文数据的机制,具体步骤如下:
1. 在请求入口将track_number存入MDC
用Spring的请求拦截器,在请求进入Controller前把track_number放入MDC,请求结束后清除(避免ThreadLocal内存泄漏):
1.1 编写拦截器
import org.slf4j.MDC; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class TrackNumberInterceptor implements HandlerInterceptor { private static final String TRACK_NUMBER_KEY = "track_number"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 从请求头获取track_number,无值时设默认值 String trackNumber = request.getHeader(TRACK_NUMBER_KEY); MDC.put(TRACK_NUMBER_KEY, trackNumber != null ? trackNumber : "unknown"); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 请求结束后清除MDC中的值,防止线程复用导致数据污染 MDC.remove(TRACK_NUMBER_KEY); } }
1.2 注册拦截器
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 { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new TrackNumberInterceptor()) // 拦截所有请求,可根据实际业务调整路径 .addPathPatterns("/**"); } }
2. 修改Log4j2.xml配置,在日志格式中引用MDC的track_number
修改日志输出模板,加入%X{track_number}(%X是Log4j2引用MDC变量的语法),示例配置:
<Configuration status="INFO"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <!-- 格式中加入track_number,无值时显示unknown --> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %X{track_number:-unknown} - %msg%n"/> </Console> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>
3. 业务代码中正常打日志即可
不管是Controller、Service还是Repo层,直接用Lombok的@Slf4j注解打日志,不需要额外传递track_number:
import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @Slf4j @Service public class DemoService { public void doBusinessLogic() { // 日志会自动带上当前请求的track_number log.info("开始执行核心业务逻辑"); log.debug("调试信息:当前请求追踪号已自动注入"); } }
注意事项:异步线程的上下文传递
如果服务使用了@Async异步方法,由于MDC基于ThreadLocal实现,异步线程会丢失上下文,需要手动传递:
- 自定义TaskDecorator:
import org.slf4j.MDC; import org.springframework.core.task.TaskDecorator; import java.util.Map; public class MdcTaskDecorator implements TaskDecorator { @Override public Runnable decorate(Runnable runnable) { // 捕获当前线程的MDC上下文 Map<String, String> mdcContext = MDC.getCopyOfContextMap(); return () -> { try { // 将MDC上下文设置到异步线程中 if (mdcContext != null) { MDC.setContextMap(mdcContext); } runnable.run(); } finally { // 清除异步线程的MDC MDC.clear(); } }; } }
- 配置线程池时绑定Decorator:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; @Configuration @EnableAsync public class AsyncConfig { @Bean(name = "asyncExecutor") public Executor asyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(20); executor.setThreadNamePrefix("AsyncThread-"); // 绑定MDC装饰器,传递上下文 executor.setTaskDecorator(new MdcTaskDecorator()); executor.initialize(); return executor; } }
配置后,异步方法的日志也能正常带上track_number。
内容的提问来源于stack exchange,提问作者Jishnu Prathap
相关产品推荐
相关产品推荐

