Spring WebFlux协程中SLF4J MDC记录请求ID的上下文丢失问题求解
解决方案
根因说明
你当前的问题核心是直接操作ThreadLocal实现的MDC,没有将MDC上下文绑定到协程的调用链中:Spring WebFlux协程模式下请求处理会在多个线程间切换,ThreadLocal的内容不会跟着协程传递,因此R2DBC执行时切换线程后MDC内容自然丢失。你之前手动加的MDCContext()只在test方法的局部代码块生效,且过滤器中未提前将MDC内容绑定到协程上下文,局部添加的MDCContext()也获取不到正确的MDC值。
具体修复步骤
1. 修正MDC过滤器实现,全局绑定协程级MDC上下文
直接在过滤器中构造MDCContext并通过withContext绑定到整个请求的协程调用链,后续所有协程切换都会自动传递MDC内容,不需要在业务代码中手动处理:
import kotlinx.coroutines.slf4j.MDCContext import kotlinx.coroutines.withContext suspend fun filterMDC(request: ServerRequest, next: suspend (ServerRequest) -> ServerResponse): ServerResponse { // 提前构造所有需要存入MDC的参数 val mdcMap = mapOf( REQUEST_ID_MDC_PARAM to (request.headers().firstHeader(REQUEST_ID_HEADER_NAME) ?: UUID.randomUUID().toString()), RESOURCE_MDC_PARAM to "${request.methodName()} ${request.uri()}" // 其他需要存入MDC的参数都可以在这里加 ) // 将MDC上下文绑定到当前协程的整个调用链 return withContext(MDCContext(mdcMap)) { next(request) } }
2. 移除业务代码中重复的MDCContext绑定
过滤器已经全局绑定后,test方法中不需要再手动加withContext(MDCContext()),代码可简化为:
suspend fun test(serverRequest: ServerRequest): ServerResponse { logger.info("foo" to "bar") { "foobar" } // 直接调用repository,MDC会自动传递到R2DBC执行的线程中 val user = testRepository.getUser() return ServerResponse.ok().bodyValueAndAwait(user.let(::toApiResponse)) }
3. 确认依赖引入
确保项目中引入了协程SLF4J适配依赖,版本和你项目使用的kotlin协程版本保持一致:
// Gradle 示例 implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-slf4j:1.7.3'
<!-- Maven 示例 --> <dependency> <groupId>org.jetbrains.kotlinx</groupId> <artifactId>kotlinx-coroutines-slf4j</artifactId> <version>1.7.3</version> </dependency>
验证方式
你可以在TestRepository.getUser方法中添加测试日志,直接打印MDC中的参数即可确认上下文是否传递成功:
import org.slf4j.MDC suspend fun getUser(): User { logger.info("查询用户信息,请求ID:{}", MDC.get(REQUEST_ID_MDC_PARAM)) return r2dbcEntityTemplate .select<User>() .from("users") .awaitOne() }
内容的提问来源于stack exchange,提问作者MrAndre
相关产品推荐
相关产品推荐

