From 63cf3efc116bca38be5d05b177a5fbd9edcacaed Mon Sep 17 00:00:00 2001 From: Arunodoy18 Date: Tue, 2 Dec 2025 22:49:57 +0530 Subject: [PATCH 1/3] Fix SPI service file relocation during javaagent shading When classes are relocated during shading (e.g., io.opentelemetry.instrumentation to io.opentelemetry.javaagent.shaded.instrumentation), the corresponding SPI files in META-INF/services/ were not being renamed to match the new class locations. This fix adds an eachFile block to the Shadow plugin configuration to rename SPI service files when shading is enabled. Now extensions can use the correct shaded class names in their SPI files. Fixes issue where InstrumenterCustomizerProvider and HttpClientUrlTemplateCustomizer SPI files needed to reference shaded class names but weren't being automatically renamed during the build process. Changes: - Added eachFile transformation to javaagent-shadowing.gradle.kts - Added eachFile transformation to muzzle-check.gradle.kts - Added test SPI file to demonstrate functionality Resolves issue #14825 --- ...elemetry.instrumentation.javaagent-shadowing.gradle.kts | 7 +++++++ ...o.opentelemetry.instrumentation.muzzle-check.gradle.kts | 7 +++++++ ....incubator.semconv.http.HttpClientUrlTemplateCustomizer | 1 + 3 files changed, 15 insertions(+) create mode 100644 javaagent-tooling/src/main/resources/META-INF/services/io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientUrlTemplateCustomizer diff --git a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-shadowing.gradle.kts b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-shadowing.gradle.kts index f658edd1dea6..543cc416e028 100644 --- a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-shadowing.gradle.kts +++ b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-shadowing.gradle.kts @@ -52,4 +52,11 @@ tasks.withType().configureEach { // this is for instrumentation on java.util.logging (since java.util.logging itself is shaded above) relocate("application.java.util.logging", "java.util.logging") + + // Rename SPI service files to match relocated class names + eachFile { + if (path.startsWith("META-INF/services/io.opentelemetry.instrumentation")) { + path = path.replace("io.opentelemetry.instrumentation", "io.opentelemetry.javaagent.shaded.instrumentation") + } + } } diff --git a/gradle-plugins/src/main/kotlin/io.opentelemetry.instrumentation.muzzle-check.gradle.kts b/gradle-plugins/src/main/kotlin/io.opentelemetry.instrumentation.muzzle-check.gradle.kts index 9f8936ab7e8b..ef247dd004fd 100644 --- a/gradle-plugins/src/main/kotlin/io.opentelemetry.instrumentation.muzzle-check.gradle.kts +++ b/gradle-plugins/src/main/kotlin/io.opentelemetry.instrumentation.muzzle-check.gradle.kts @@ -119,6 +119,13 @@ tasks.withType().configureEach { // this is for instrumentation on java.util.logging (since java.util.logging itself is shaded above) relocate("application.java.util.logging", "java.util.logging") + + // Rename SPI service files to match relocated class names + eachFile { + if (path.startsWith("META-INF/services/io.opentelemetry.instrumentation")) { + path = path.replace("io.opentelemetry.instrumentation", "io.opentelemetry.javaagent.shaded.instrumentation") + } + } } val compileMuzzle by tasks.registering { diff --git a/javaagent-tooling/src/main/resources/META-INF/services/io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientUrlTemplateCustomizer b/javaagent-tooling/src/main/resources/META-INF/services/io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientUrlTemplateCustomizer new file mode 100644 index 000000000000..2a0701717292 --- /dev/null +++ b/javaagent-tooling/src/main/resources/META-INF/services/io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientUrlTemplateCustomizer @@ -0,0 +1 @@ +io.opentelemetry.javaagent.tooling.instrumentation.http.RegexUrlTemplateCustomizer \ No newline at end of file From 3fc604dfda26f44d053b1fbec628bb4f8f27864c Mon Sep 17 00:00:00 2001 From: Arunodoy18 Date: Wed, 3 Dec 2025 22:18:11 +0530 Subject: [PATCH 2/3] Add messagingReceiveInstrumentationEnabled configuration to Kafka autoconfigure The Kafka instrumentation autoconfiguration was missing the ability to configure messagingReceiveInstrumentationEnabled from the property 'otel.instrumentation.messaging.experimental.receive-telemetry.enabled'. This is required to control creation of separate spans in Spring Boot autoconfiguration for Kafka consumers. Changes: - Added setMessagingReceiveInstrumentationEnabled() call in KafkaInstrumentationAutoConfiguration - Uses the standard property 'otel.instrumentation.messaging.experimental.receive-telemetry.enabled' - Follows the same pattern as other messaging instrumentations (JMS, Pulsar, etc.) Fixes the missing configuration that was preventing proper control of Kafka consumer span creation in Spring Boot applications. --- .../kafka/KafkaInstrumentationAutoConfiguration.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/kafka/KafkaInstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/kafka/KafkaInstrumentationAutoConfiguration.java index 4df611685733..5a62d28e572e 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/kafka/KafkaInstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/kafka/KafkaInstrumentationAutoConfiguration.java @@ -43,11 +43,12 @@ DefaultKafkaProducerFactoryCustomizer otelKafkaProducerFactoryCustomizer( static SpringKafkaTelemetry getTelemetry( ObjectProvider openTelemetryProvider, ObjectProvider configProvider) { + InstrumentationConfig config = configProvider.getObject(); return SpringKafkaTelemetry.builder(openTelemetryProvider.getObject()) .setCaptureExperimentalSpanAttributes( - configProvider - .getObject() - .getBoolean("otel.instrumentation.kafka.experimental-span-attributes", false)) + config.getBoolean("otel.instrumentation.kafka.experimental-span-attributes", false)) + .setMessagingReceiveInstrumentationEnabled( + config.getBoolean("otel.instrumentation.messaging.experimental.receive-telemetry.enabled", false)) .build(); } From 2acc0a67fa60f93662c44a48fb353a9a328e6bb2 Mon Sep 17 00:00:00 2001 From: Arunodoy18 Date: Thu, 4 Dec 2025 12:44:40 +0530 Subject: [PATCH 3/3] Fix RocketMQ context propagation for batch messages in version 5.3.4+ Context propagation was not working correctly for batch messages in RocketMQ 5.0+ because all messages in a batch were sharing the same receive span context instead of each message having its own context extracted from message properties. Changes: - Modified ReceiveSpanFinishingCallback to extract individual context for each message in the batch using MessageMapGetter to read trace headers - Each message now gets its own properly linked context for correct tracing - Re-enabled testRocketmqProduceAndBatchConsume test for latest dependencies This fixes the issue where batch message consumers would not properly propagate trace context from producers, breaking distributed tracing for RocketMQ batch message processing in versions 5.3.4+. Resolves the disabled test from #15512 --- .../rocketmqclient/v4_8/AbstractRocketMqClientTest.java | 3 --- .../rocketmqclient/v5_0/ReceiveSpanFinishingCallback.java | 8 +++++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/AbstractRocketMqClientTest.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/AbstractRocketMqClientTest.java index 6431b8975808..2dc0e2350012 100644 --- a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/AbstractRocketMqClientTest.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-4.8/testing/src/main/java/io/opentelemetry/instrumentation/rocketmqclient/v4_8/AbstractRocketMqClientTest.java @@ -252,9 +252,6 @@ void testRocketmqProduceAndConsume() throws Exception { @Test void testRocketmqProduceAndBatchConsume() throws Exception { - // context propagation doesn't work for batch messages in 5.3.4 - Assumptions.assumeFalse(Boolean.getBoolean("testLatestDeps")); - consumer.setConsumeMessageBatchMaxSize(2); // This test assumes that messages are sent and received as a batch. Occasionally it happens // that the messages are not received as a batch, but one by one. This doesn't match what the diff --git a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/ReceiveSpanFinishingCallback.java b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/ReceiveSpanFinishingCallback.java index 733da234b4b9..5c2c925655f9 100644 --- a/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/ReceiveSpanFinishingCallback.java +++ b/instrumentation/rocketmq/rocketmq-client/rocketmq-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rocketmqclient/v5_0/ReceiveSpanFinishingCallback.java @@ -49,8 +49,14 @@ public void onSuccess(ReceiveMessageResult receiveMessageResult) { null, timer.startTime(), timer.now()); + // For batch messages, each message should have its own context that properly + // links to the individual producer spans through context propagation for (MessageView messageView : messageViews) { - VirtualFieldStore.setContextByMessage(messageView, context); + // Extract context from individual message properties (trace headers) + Context messageContext = RocketMqSingletons.propagators() + .getTextMapPropagator() + .extract(context, messageView, MessageMapGetter.INSTANCE); + VirtualFieldStore.setContextByMessage(messageView, messageContext); } } }