From db18ff8d550632fee9f7c8b247e12627e225c210 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Wed, 29 Apr 2026 05:56:40 +0200 Subject: [PATCH] fix(spring-boot-jakarta): Skip Kafka autoconfig for OTel agent --- .../KafkaOtelCoexistenceSystemTest.kt | 34 ++++++++----------- .../KafkaOtelCoexistenceSystemTest.kt | 34 ++++++++----------- .../boot/jakarta/SentryAutoConfiguration.java | 5 ++- .../SentryKafkaAutoConfigurationTest.kt | 21 +++++++++++- 4 files changed, 54 insertions(+), 40 deletions(-) diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/src/test/kotlin/io/sentry/systemtest/KafkaOtelCoexistenceSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/src/test/kotlin/io/sentry/systemtest/KafkaOtelCoexistenceSystemTest.kt index 6ede83510e..0f85e81a0a 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/src/test/kotlin/io/sentry/systemtest/KafkaOtelCoexistenceSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/src/test/kotlin/io/sentry/systemtest/KafkaOtelCoexistenceSystemTest.kt @@ -5,22 +5,6 @@ import kotlin.test.Test import kotlin.test.assertEquals import org.junit.Before -/** - * System tests for Kafka queue instrumentation on the OTel Jakarta noagent sample. - * - * The Sentry Kafka auto-configuration (`SentryKafkaQueueConfiguration`) is intentionally suppressed - * when `io.sentry.opentelemetry.SentryAutoConfigurationCustomizerProvider` is on the classpath, so - * the Sentry `SentryKafkaProducer` and `SentryKafkaRecordInterceptor` must not be wired. - * - * These tests produce a Kafka message end-to-end and assert that Sentry-style `queue.publish` / - * `queue.process` spans/transactions are *not* emitted. Any Kafka telemetry in OTel mode must come - * from the OTel Kafka instrumentation, not from the Sentry Kafka integration. - * - * Requires: - * - The sample app running with `--spring.profiles.active=kafka` - * - A Kafka broker at localhost:9092 - * - The mock Sentry server at localhost:8000 - */ class KafkaOtelCoexistenceSystemTest { lateinit var testHelper: TestHelper @@ -37,9 +21,21 @@ class KafkaOtelCoexistenceSystemTest { restClient.produceKafkaMessage("otel-coexistence-test") assertEquals(200, restClient.lastKnownStatusCode) - testHelper.ensureNoTransactionReceived { transaction, _ -> - transaction.contexts.trace?.operation == "queue.process" || - transaction.spans.any { span -> span.op == "queue.publish" } + testHelper.ensureTransactionReceived { transaction, _ -> + transaction.transaction == "GET /kafka/produce" && + transaction.sdk?.integrationSet?.contains("SpringKafka") != true && + transaction.spans.any { span -> + span.op == "queue.publish" && + span.origin == "auto.opentelemetry" && + span.data?.get("messaging.system") == "kafka" + } + } + + testHelper.ensureTransactionReceived { transaction, _ -> + transaction.contexts.trace?.operation == "queue.process" && + transaction.contexts.trace?.origin == "auto.opentelemetry" && + transaction.contexts.trace?.data?.get("messaging.system") == "kafka" && + transaction.sdk?.integrationSet?.contains("SpringKafka") != true } } } diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/test/kotlin/io/sentry/systemtest/KafkaOtelCoexistenceSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/test/kotlin/io/sentry/systemtest/KafkaOtelCoexistenceSystemTest.kt index d150fe70cd..0f85e81a0a 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/test/kotlin/io/sentry/systemtest/KafkaOtelCoexistenceSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/test/kotlin/io/sentry/systemtest/KafkaOtelCoexistenceSystemTest.kt @@ -5,22 +5,6 @@ import kotlin.test.Test import kotlin.test.assertEquals import org.junit.Before -/** - * System tests for Kafka queue instrumentation on the OTel Jakarta sample. - * - * The Sentry Kafka auto-configuration (`SentryKafkaQueueConfiguration`) is intentionally suppressed - * when `io.sentry.opentelemetry.SentryAutoConfigurationCustomizerProvider` is on the classpath, so - * the Sentry `SentryKafkaProducer` and `SentryKafkaRecordInterceptor` must not be wired. - * - * These tests produce a Kafka message end-to-end and assert that Sentry-style `queue.publish` / - * `queue.process` spans/transactions are *not* emitted. Any Kafka telemetry in OTel mode must come - * from the OTel Kafka instrumentation, not from the Sentry Kafka integration. - * - * Requires: - * - The sample app running with `--spring.profiles.active=kafka` - * - A Kafka broker at localhost:9092 - * - The mock Sentry server at localhost:8000 - */ class KafkaOtelCoexistenceSystemTest { lateinit var testHelper: TestHelper @@ -37,9 +21,21 @@ class KafkaOtelCoexistenceSystemTest { restClient.produceKafkaMessage("otel-coexistence-test") assertEquals(200, restClient.lastKnownStatusCode) - testHelper.ensureNoTransactionReceived { transaction, _ -> - transaction.contexts.trace?.operation == "queue.process" || - transaction.spans.any { span -> span.op == "queue.publish" } + testHelper.ensureTransactionReceived { transaction, _ -> + transaction.transaction == "GET /kafka/produce" && + transaction.sdk?.integrationSet?.contains("SpringKafka") != true && + transaction.spans.any { span -> + span.op == "queue.publish" && + span.origin == "auto.opentelemetry" && + span.data?.get("messaging.system") == "kafka" + } + } + + testHelper.ensureTransactionReceived { transaction, _ -> + transaction.contexts.trace?.operation == "queue.process" && + transaction.contexts.trace?.origin == "auto.opentelemetry" && + transaction.contexts.trace?.data?.get("messaging.system") == "kafka" && + transaction.sdk?.integrationSet?.contains("SpringKafka") != true } } } diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java index b678abc716..e1f8b02627 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java @@ -255,7 +255,10 @@ static class SentryCacheConfiguration { "io.sentry.kafka.SentryKafkaProducer" }) @ConditionalOnProperty(name = "sentry.enable-queue-tracing", havingValue = "true") - @ConditionalOnMissingClass("io.sentry.opentelemetry.SentryAutoConfigurationCustomizerProvider") + @ConditionalOnMissingClass({ + "io.sentry.opentelemetry.SentryAutoConfigurationCustomizerProvider", + "io.sentry.opentelemetry.agent.AgentMarker" + }) @Open static class SentryKafkaQueueConfiguration { diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryKafkaAutoConfigurationTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryKafkaAutoConfigurationTest.kt index 5b010891a1..392e518475 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryKafkaAutoConfigurationTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryKafkaAutoConfigurationTest.kt @@ -2,6 +2,7 @@ package io.sentry.spring.boot.jakarta import io.sentry.kafka.SentryKafkaProducer import io.sentry.opentelemetry.SentryAutoConfigurationCustomizerProvider +import io.sentry.opentelemetry.agent.AgentMarker import io.sentry.spring.jakarta.kafka.SentryKafkaConsumerBeanPostProcessor import io.sentry.spring.jakarta.kafka.SentryKafkaProducerBeanPostProcessor import kotlin.test.Test @@ -28,20 +29,27 @@ class SentryKafkaAutoConfigurationTest { "sentry.debug=false", ) - /** Hide the OTel customizer so conditions evaluate as "no OTel present". */ private val noOtelClassLoader = + FilteredClassLoader( + SentryAutoConfigurationCustomizerProvider::class.java, + AgentMarker::class.java, + ) + + private val noOtelCustomizerClassLoader = FilteredClassLoader(SentryAutoConfigurationCustomizerProvider::class.java) private val noSentryKafkaClassLoader = FilteredClassLoader( SentryKafkaProducer::class.java, SentryAutoConfigurationCustomizerProvider::class.java, + AgentMarker::class.java, ) private val noSpringKafkaClassLoader = FilteredClassLoader( KafkaTemplate::class.java, SentryAutoConfigurationCustomizerProvider::class.java, + AgentMarker::class.java, ) @Test @@ -96,6 +104,17 @@ class SentryKafkaAutoConfigurationTest { } } + @Test + fun `does not register Kafka BPPs when OpenTelemetry agent is present`() { + contextRunner + .withClassLoader(noOtelCustomizerClassLoader) + .withPropertyValues("sentry.enable-queue-tracing=true") + .run { context -> + assertThat(context).doesNotHaveBean(SentryKafkaProducerBeanPostProcessor::class.java) + assertThat(context).doesNotHaveBean(SentryKafkaConsumerBeanPostProcessor::class.java) + } + } + @Test fun `does not register Kafka BPPs when OpenTelemetry integration is present`() { contextRunner.withPropertyValues("sentry.enable-queue-tracing=true").run { context ->