You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot自定义日志Starter:如何从HTTP请求动态填充Logback自定义字段?

解决方案:利用MDC + 请求拦截器动态注入tenantId

这个需求是日志标准化场景里的常见问题,核心思路是借助**MDC(Mapped Diagnostic Context)**存储请求上下文变量,结合Spring的请求拦截机制来实现动态tenantId的注入,具体步骤如下:

1. 编写请求拦截器,提取tenantId并存入MDC

MDC是Logback提供的线程上下文存储工具,能让我们在日志中动态获取当前线程的上下文变量。我们需要先从HTTP请求中提取tenantId(可根据实际从请求头、参数或Cookie中获取),存入MDC后,在请求结束时清理MDC,避免线程池复用导致的脏数据。

创建拦截器类:

import org.slf4j.MDC;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TenantIdInterceptor implements HandlerInterceptor {
    // 自定义tenantId的请求头key,可根据业务调整
    private static final String TENANT_ID_HEADER = "X-Tenant-Id";
    private static final String MDC_TENANT_KEY = "tenantId";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 从请求头获取tenantId,也可改为从请求参数、Cookie等位置获取
        String tenantId = request.getHeader(TENANT_ID_HEADER);
        // 为空时设置默认值,避免日志字段缺失
        if (tenantId == null || tenantId.trim().isEmpty()) {
            tenantId = "unknown";
        }
        // 将tenantId存入MDC
        MDC.put(MDC_TENANT_KEY, tenantId);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 请求结束后移除MDC中的tenantId,防止线程复用导致数据污染
        MDC.remove(MDC_TENANT_KEY);
    }
}

2. 注册拦截器到Spring容器

需要把上面的拦截器配置到Spring Boot的WebMvc上下文,让它对请求生效:

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 WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册拦截器,拦截所有请求路径,可根据业务调整拦截范围
        registry.addInterceptor(new TenantIdInterceptor())
                .addPathPatterns("/**");
    }
}

3. 修改logback.xml中的customFields配置

把原来硬编码的tenantId替换为MDC中的变量,LogstashEncoder支持用%X{key}引用MDC值,还可以通过:-设置默认值:

<encoder class="net.logstash.logback.encoder.LogstashEncoder">
    <!-- 从MDC中获取tenantId,无值时用unknown兜底 -->
    <customFields>{"tenantId":"%X{tenantId:-unknown}"}</customFields>
</encoder>

额外注意事项

  • 异步线程场景:如果项目中使用@Async等异步方法,MDC上下文不会自动传递到异步线程。由于你已经集成了Sleuth,它默认会帮你传递MDC上下文,但如果是自定义线程池,需要手动配置TaskDecorator来传递MDC:
import org.slf4j.MDC;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskDecorator;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.Map;
import java.util.concurrent.Executor;

@Configuration
public class AsyncConfig {
    @Bean(name = "asyncExecutor")
    public Executor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 配置线程池基础参数
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(20);
        executor.setThreadNamePrefix("Async-");
        // 添加TaskDecorator传递MDC上下文
        executor.setTaskDecorator(runnable -> {
            Map<String, String> mdcContext = MDC.getCopyOfContextMap();
            return () -> {
                try {
                    if (mdcContext != null) {
                        MDC.setContextMap(mdcContext);
                    }
                    runnable.run();
                } finally {
                    MDC.clear();
                }
            };
        });
        executor.initialize();
        return executor;
    }
}
  • 合法性校验:如果tenantId是敏感业务字段,建议在拦截器中添加校验逻辑,确保传入的tenantId符合业务规范,避免恶意注入。

完成以上配置后,下游项目引入你的Starter时,只要请求携带对应字段的tenantId,日志中的tenantId就会自动替换为请求中的值,无值时则使用默认的unknown填充。

内容的提问来源于stack exchange,提问作者keepmoving

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 07:46:38