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

Logback中如何在StructuredTaskScope的fork任务中传递MDC值?

在Java 21+的StructuredTaskScope中传递MDC上下文

核心思路

借助Java 21的ScopedValue作为MDC上下文的载体——它会自动被StructuredTaskScope的fork任务继承,再通过自定义Logback的MDCAdapter让日志框架直接读取这个ScopedValue中的上下文,实现MDC跨任务的自动传递。

步骤1:用ScopedValue绑定MDC上下文

先定义全局的ScopedValue存储MDC键值对,再在StructuredTaskScope内部绑定当前线程的MDC上下文:

import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope;
import java.util.function.Supplier;
import org.slf4j.MDC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ResponseHandler {
    private static final Logger logger = LoggerFactory.getLogger(ResponseHandler.class);
    // 用于承载MDC上下文的ScopedValue
    private static final ScopedValue<Map<String, String>> MDC_CONTEXT = ScopedValue.newInstance();

    public Response handle() throws ExecutionException, InterruptedException {
        // 捕获当前线程的MDC快照
        Map<String, String> currentMdc = MDC.getCopyOfContextMap();
        
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            // 绑定ScopedValue,后续fork的所有任务都会继承这个MDC上下文
            ScopedValue.where(MDC_CONTEXT, currentMdc).run(() -> {
                Supplier<String> user = scope.fork(() -> {
                    logger.info("1");
                    return "user_123";
                });
                Supplier<Integer> order = scope.fork(() -> {
                    logger.info("2");
                    return 456;
                });

                scope.join().throwIfFailed();
                logger.info("3");
            });

            return new Response(user.get(), order.get());
        }
    }

    // 示例Response类
    public record Response(String user, Integer order) {}
}

步骤2:自定义ScopedMDCAdapter适配Logback

替换Logback默认的ThreadLocal实现,改成读取ScopedValue中的MDC上下文:

import ch.qos.logback.classic.spi.MDCAdapter;
import ch.qos.logback.core.spi.ContextAwareBase;
import java.util.Map;
import java.util.Set;

public class ScopedMDCAdapter extends ContextAwareBase implements MDCAdapter {
    // 复用之前定义的ScopedValue
    private static final ScopedValue<Map<String, String>> MDC_CONTEXT = ResponseHandler.MDC_CONTEXT;

    @Override
    public void put(String key, String val) {
        Map<String, String> context = MDC_CONTEXT.get();
        if (context != null) {
            context.put(key, val);
        }
    }

    @Override
    public String get(String key) {
        Map<String, String> context = MDC_CONTEXT.get();
        return context != null ? context.get(key) : null;
    }

    @Override
    public void remove(String key) {
        Map<String, String> context = MDC_CONTEXT.get();
        if (context != null) {
            context.remove(key);
        }
    }

    @Override
    public void clear() {
        Map<String, String> context = MDC_CONTEXT.get();
        if (context != null) {
            context.clear();
        }
    }

    @Override
    public Map<String, String> getCopyOfContextMap() {
        Map<String, String> context = MDC_CONTEXT.get();
        return context != null ? Map.copyOf(context) : null;
    }

    @Override
    public void setContextMap(Map<String, String> contextMap) {
        throw new UnsupportedOperationException("ScopedValue上下文不可直接修改,请通过ScopedValue.where绑定新上下文");
    }

    @Override
    public Set<String> getKeys() {
        Map<String, String> context = MDC_CONTEXT.get();
        return context != null ? context.keySet() : Set.of();
    }
}

步骤3:配置Logback使用自定义Adapter

在应用启动时通过系统属性指定,或者在logback.xml中配置:

  • 代码方式(放在应用启动入口):
System.setProperty("logback.mdcAdapter", "com.yourpackage.ScopedMDCAdapter");
  • 配置文件方式(logback.xml):
<configuration>
    <!-- 指定自定义MDC适配器 -->
    <mdcAdapter class="com.yourpackage.ScopedMDCAdapter"/>
    
    <!-- 其他日志配置... -->
</configuration>

注意事项

  • ScopedValue是不可变的,若需要在fork任务中修改MDC,需通过ScopedValue.where重新绑定新的不可变Map,避免并发问题。
  • 该方案完全适配结构化并发模型,不会出现MDC上下文泄漏的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 01:55:03