Kotlin协程结合OpenTelemetry与Spring WebFlux时上下文丢失致NPE
Kotlin协程+Spring WebFlux+OpenTelemetry日志NPE问题解决
问题核心
挂起函数恢复时协程上下文丢失,导致kotlin.coroutines.jvm.internal.ContinuationImpl.getContext触发空指针异常,仅在使用协程挂起操作(如delay、coroutineScope)时出现,无协程或用runBlocking时正常。
解决方案
1. 修复依赖版本兼容性
当前Kotlin协程1.9.0与Kotlin 2.0.20存在兼容性冲突,同时OpenTelemetry扩展版本需与BOM保持一致。调整build.gradle.kts依赖:
dependencies { implementation("org.springframework.boot:spring-boot-starter-webflux") implementation("io.github.oshai:kotlin-logging:7.0.0") implementation("io.opentelemetry.instrumentation:opentelemetry-spring-boot-starter") runtimeOnly("io.opentelemetry.instrumentation:opentelemetry-logback-mdc-1.0:2.6.0-alpha") implementation("io.micrometer:micrometer-tracing-bridge-otel") implementation("io.micrometer:micrometer-registry-prometheus") implementation("io.opentelemetry:opentelemetry-exporter-zipkin") implementation("org.springframework.boot:spring-boot-starter-actuator") // 适配Kotlin 2.0.20的协程稳定版本 implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.8.1") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-slf4j:1.8.1") // 与OpenTelemetry BOM版本对齐 implementation("io.opentelemetry:opentelemetry-extension-kotlin:2.6.0") implementation("io.projectreactor.kotlin:reactor-kotlin-extensions") testImplementation(kotlin("test")) }
2. 强制绑定OpenTelemetry上下文到协程
手动将OpenTelemetry上下文注入协程上下文,确保挂起恢复时上下文不丢失:
import io.github.oshai.kotlinlogging.KotlinLogging import kotlinx.coroutines.delay import kotlinx.coroutines.withContext import io.opentelemetry.extension.kotlin.asContextElement import io.opentelemetry.api.GlobalOpenTelemetry import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RestController @RestController class MyReactiveController { private val logger = KotlinLogging.logger {} private val otel = GlobalOpenTelemetry.get() @GetMapping("/hello") suspend fun hello(): String { logger.info { "Log from controller" } // 绑定当前OpenTelemetry上下文到协程 withContext(otel.currentContext().asContextElement()) { delay(50) logger.info { "Log from controller 2" } } return "Hello World" } }
3. 临时禁用OpenTelemetry MDC集成
若上述方案无效,可临时切换到Micrometer Tracing的MDC传递:
- 移除
runtimeOnly("io.opentelemetry.instrumentation:opentelemetry-logback-mdc-1.0:2.6.0-alpha") - 依赖
micrometer-tracing-bridge-otel自动传递traceId到MDC
验证
启动应用后调用/hello接口,确认日志正常输出且无NPE,同时检查所有日志的traceId一致,说明上下文传递正常。
内容的提问来源于stack exchange,提问作者Alexander
相关产品推荐
相关产品推荐

