From 2a012ae7fcdd8050129549e3096d92149b0d5acc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 00:56:05 +0000 Subject: [PATCH 01/50] Initial plan From 34d68ec0259effa3ec80dbd1f8351fca87ffc444 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 01:05:56 +0000 Subject: [PATCH 02/50] Initial plan for refactoring kafka interceptors Co-authored-by: trask <218610+trask@users.noreply.github.com> --- .../.kotlin/sessions/kotlin-compiler-16318826606158418015.salive | 0 .../.kotlin/sessions/kotlin-compiler-16068091306083868015.salive | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 conventions/.kotlin/sessions/kotlin-compiler-16318826606158418015.salive create mode 100644 gradle-plugins/.kotlin/sessions/kotlin-compiler-16068091306083868015.salive diff --git a/conventions/.kotlin/sessions/kotlin-compiler-16318826606158418015.salive b/conventions/.kotlin/sessions/kotlin-compiler-16318826606158418015.salive new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/gradle-plugins/.kotlin/sessions/kotlin-compiler-16068091306083868015.salive b/gradle-plugins/.kotlin/sessions/kotlin-compiler-16068091306083868015.salive new file mode 100644 index 000000000000..e69de29bb2d1 From 3b68915fba6d4a6f37e4795315be137571f2ccd0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 01:26:31 +0000 Subject: [PATCH 03/50] Refactor kafka interceptors to be configurable Co-authored-by: trask <218610+trask@users.noreply.github.com> --- ...otlin-compiler-16318826606158418015.salive | 0 ...otlin-compiler-16068091306083868015.salive | 0 .../kafkaclients/v2_6/KafkaTelemetry.java | 56 +++++++++++++++++++ .../v2_6/TracingConsumerInterceptor.java | 37 ++++++++---- .../v2_6/TracingProducerInterceptor.java | 35 ++++++++---- .../v2_6/AbstractInterceptorsTest.java | 17 ++++++ .../InterceptorsSuppressReceiveSpansTest.java | 6 ++ .../internal/KafkaTelemetrySupplier.java | 37 ++++++++++++ 8 files changed, 166 insertions(+), 22 deletions(-) delete mode 100644 conventions/.kotlin/sessions/kotlin-compiler-16318826606158418015.salive delete mode 100644 gradle-plugins/.kotlin/sessions/kotlin-compiler-16068091306083868015.salive create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-common-0.11/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaTelemetrySupplier.java diff --git a/conventions/.kotlin/sessions/kotlin-compiler-16318826606158418015.salive b/conventions/.kotlin/sessions/kotlin-compiler-16318826606158418015.salive deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/gradle-plugins/.kotlin/sessions/kotlin-compiler-16068091306083868015.salive b/gradle-plugins/.kotlin/sessions/kotlin-compiler-16068091306083868015.salive deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java index d66003f6607f..3e285da18f2d 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java @@ -21,6 +21,7 @@ import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProcessRequest; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProducerRequest; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaReceiveRequest; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaTelemetrySupplier; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaUtil; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.MetricsReporterList; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetryMetricsReporter; @@ -207,6 +208,61 @@ ConsumerRecords addTracing( return Collections.unmodifiableMap(config); } + /** + * Returns a config property key that can be used to pass this {@link KafkaTelemetry} instance to + * interceptors. + * + *

This is an internal config key used by the library instrumentation interceptors. + */ + static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = + "opentelemetry.experimental.kafka-telemetry.supplier"; + + /** + * Produces a set of kafka producer config properties to configure tracing in {@code + * TracingProducerInterceptor}. Add these resulting properties to the configuration map used to + * initialize a {@link KafkaProducer}. + * + *

Example usage: + * + *

{@code
+   * //    Map config = new HashMap<>();
+   * //    config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, ...);
+   * //    config.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName());
+   * //    config.putAll(kafkaTelemetry.producerInterceptorConfigProperties());
+   * //    try (KafkaProducer producer = new KafkaProducer<>(config)) { ... }
+   * }
+ * + * @return the kafka producer interceptor config properties + */ + public Map producerInterceptorConfigProperties() { + Map config = new HashMap<>(); + config.put(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, new KafkaTelemetrySupplier(this)); + return Collections.unmodifiableMap(config); + } + + /** + * Produces a set of kafka consumer config properties to configure tracing in {@code + * TracingConsumerInterceptor}. Add these resulting properties to the configuration map used to + * initialize a {@link KafkaConsumer}. + * + *

Example usage: + * + *

{@code
+   * //    Map config = new HashMap<>();
+   * //    config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, ...);
+   * //    config.put(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName());
+   * //    config.putAll(kafkaTelemetry.consumerInterceptorConfigProperties());
+   * //    try (KafkaConsumer consumer = new KafkaConsumer<>(config)) { ... }
+   * }
+ * + * @return the kafka consumer interceptor config properties + */ + public Map consumerInterceptorConfigProperties() { + Map config = new HashMap<>(); + config.put(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, new KafkaTelemetrySupplier(this)); + return Collections.unmodifiableMap(config); + } + /** * Build and inject span into record. * diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java index 8f60b07e6a88..48744d8a5ce7 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java @@ -16,6 +16,8 @@ import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContextUtil; import java.util.Map; import java.util.Objects; +import java.util.function.Supplier; +import javax.annotation.Nullable; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.ConsumerInterceptor; import org.apache.kafka.clients.consumer.ConsumerRecords; @@ -29,22 +31,16 @@ */ public class TracingConsumerInterceptor implements ConsumerInterceptor { - private static final KafkaTelemetry telemetry = - KafkaTelemetry.builder(GlobalOpenTelemetry.get()) - .setMessagingReceiveInstrumentationEnabled( - ConfigPropertiesUtil.getBoolean( - "otel.instrumentation.messaging.experimental.receive-telemetry.enabled", false)) - .setCapturedHeaders( - ConfigPropertiesUtil.getList( - "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) - .build(); - + @Nullable private KafkaTelemetry telemetry; private String consumerGroup; private String clientId; @Override @CanIgnoreReturnValue public ConsumerRecords onConsume(ConsumerRecords records) { + if (telemetry == null) { + return records; + } // timer should be started before fetching ConsumerRecords, but there is no callback for that Timer timer = Timer.start(); Context receiveContext = telemetry.buildAndFinishSpan(records, consumerGroup, clientId, timer); @@ -67,6 +63,25 @@ public void configure(Map configs) { consumerGroup = Objects.toString(configs.get(ConsumerConfig.GROUP_ID_CONFIG), null); clientId = Objects.toString(configs.get(ConsumerConfig.CLIENT_ID_CONFIG), null); - // TODO: support experimental attributes config + // Try to get KafkaTelemetry from config + Object telemetrySupplier = configs.get(KafkaTelemetry.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + if (telemetrySupplier instanceof Supplier) { + Object kafkaTelemetry = ((Supplier) telemetrySupplier).get(); + if (kafkaTelemetry instanceof KafkaTelemetry) { + this.telemetry = (KafkaTelemetry) kafkaTelemetry; + return; + } + } + + // Fallback to GlobalOpenTelemetry if not configured + this.telemetry = + KafkaTelemetry.builder(GlobalOpenTelemetry.get()) + .setMessagingReceiveInstrumentationEnabled( + ConfigPropertiesUtil.getBoolean( + "otel.instrumentation.messaging.experimental.receive-telemetry.enabled", false)) + .setCapturedHeaders( + ConfigPropertiesUtil.getList( + "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) + .build(); } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java index 833b0785d181..921e605ce687 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java @@ -12,6 +12,7 @@ import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; import java.util.Map; import java.util.Objects; +import java.util.function.Supplier; import javax.annotation.Nullable; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.clients.producer.ProducerInterceptor; @@ -25,19 +26,15 @@ */ public class TracingProducerInterceptor implements ProducerInterceptor { - private static final KafkaTelemetry telemetry = - KafkaTelemetry.builder(GlobalOpenTelemetry.get()) - .setCapturedHeaders( - ConfigPropertiesUtil.getList( - "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) - .build(); - + @Nullable private KafkaTelemetry telemetry; @Nullable private String clientId; @Override @CanIgnoreReturnValue public ProducerRecord onSend(ProducerRecord producerRecord) { - telemetry.buildAndInjectSpan(producerRecord, clientId); + if (telemetry != null) { + telemetry.buildAndInjectSpan(producerRecord, clientId); + } return producerRecord; } @@ -48,9 +45,25 @@ public void onAcknowledgement(RecordMetadata recordMetadata, Exception e) {} public void close() {} @Override - public void configure(Map map) { - clientId = Objects.toString(map.get(ProducerConfig.CLIENT_ID_CONFIG), null); + public void configure(Map configs) { + clientId = Objects.toString(configs.get(ProducerConfig.CLIENT_ID_CONFIG), null); + + // Try to get KafkaTelemetry from config + Object telemetrySupplier = configs.get(KafkaTelemetry.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + if (telemetrySupplier instanceof Supplier) { + Object kafkaTelemetry = ((Supplier) telemetrySupplier).get(); + if (kafkaTelemetry instanceof KafkaTelemetry) { + this.telemetry = (KafkaTelemetry) kafkaTelemetry; + return; + } + } - // TODO: support experimental attributes config + // Fallback to GlobalOpenTelemetry if not configured + this.telemetry = + KafkaTelemetry.builder(GlobalOpenTelemetry.get()) + .setCapturedHeaders( + ConfigPropertiesUtil.getList( + "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) + .build(); } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java index 108b80635425..ec966499d63b 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java @@ -28,19 +28,36 @@ abstract class AbstractInterceptorsTest extends KafkaClientBaseTest { static final String greeting = "Hello Kafka!"; + private static KafkaTelemetry kafkaTelemetry; + + protected static KafkaTelemetry createKafkaTelemetry() { + return KafkaTelemetry.builder(testing.getOpenTelemetry()) + .setCapturedHeaders(java.util.Collections.singletonList("Test-Message-Header")) + .setMessagingReceiveInstrumentationEnabled(true) + .build(); + } + @Override public Map producerProps() { + if (kafkaTelemetry == null) { + kafkaTelemetry = createKafkaTelemetry(); + } Map props = super.producerProps(); props.put( ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); + props.putAll(kafkaTelemetry.producerInterceptorConfigProperties()); return props; } @Override public Map consumerProps() { + if (kafkaTelemetry == null) { + kafkaTelemetry = createKafkaTelemetry(); + } Map props = super.consumerProps(); props.put( ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); + props.putAll(kafkaTelemetry.consumerInterceptorConfigProperties()); return props; } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsSuppressReceiveSpansTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsSuppressReceiveSpansTest.java index 049020a2d074..33c87d4456df 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsSuppressReceiveSpansTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsSuppressReceiveSpansTest.java @@ -23,6 +23,12 @@ class InterceptorsSuppressReceiveSpansTest extends AbstractInterceptorsTest { + protected static KafkaTelemetry createKafkaTelemetry() { + return KafkaTelemetry.builder(testing.getOpenTelemetry()) + .setMessagingReceiveInstrumentationEnabled(false) + .build(); + } + @SuppressWarnings("deprecation") // using deprecated semconv @Override void assertTraces() { diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-common-0.11/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaTelemetrySupplier.java b/instrumentation/kafka/kafka-clients/kafka-clients-common-0.11/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaTelemetrySupplier.java new file mode 100644 index 000000000000..38bf8bb592b7 --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-common-0.11/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaTelemetrySupplier.java @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal; + +import java.io.Serializable; +import java.util.Objects; +import java.util.function.Supplier; + +/** + * Wrapper for KafkaTelemetry that can be injected into kafka configuration without breaking + * serialization. Used by kafka interceptors to get a configured KafkaTelemetry instance. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class KafkaTelemetrySupplier implements Supplier, Serializable { + private static final long serialVersionUID = 1L; + private final transient Object kafkaTelemetry; + + public KafkaTelemetrySupplier(Object kafkaTelemetry) { + Objects.requireNonNull(kafkaTelemetry); + this.kafkaTelemetry = kafkaTelemetry; + } + + @Override + public Object get() { + return kafkaTelemetry; + } + + private Object writeReplace() { + // serialize this object to null + return null; + } +} From ce0eafc8694385efae8874e617075aedbc649279 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 01:29:31 +0000 Subject: [PATCH 04/50] Update README with interceptor configuration documentation Co-authored-by: trask <218610+trask@users.noreply.github.com> --- .../kafka-clients-2.6/library/README.md | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md index 2d4019de6fd1..33592ea49323 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md @@ -35,6 +35,49 @@ The Kafka clients API provides a way to "intercept" messages before they are sen The OpenTelemetry instrumented Kafka library provides two interceptors to be configured to add tracing information automatically. The interceptor class has to be set in the properties bag used to create the Kafka client. +##### Recommended approach: Configuring interceptors with KafkaTelemetry + +The recommended way to use interceptors is to configure them with a `KafkaTelemetry` instance. +This gives you full control over the configuration, including which `OpenTelemetry` instance to use, +whether to enable receive telemetry, and which headers to capture. + +For the producer, configure the interceptor with the KafkaTelemetry instance: + +```java +KafkaTelemetry telemetry = KafkaTelemetry.builder(openTelemetry) + .setCapturedHeaders(Arrays.asList("custom-header")) + .build(); + +Map props = new HashMap<>(); +props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); +props.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); +props.putAll(telemetry.producerInterceptorConfigProperties()); + +Producer producer = new KafkaProducer<>(props); +``` + +For the consumer, configure the interceptor with the KafkaTelemetry instance: + +```java +KafkaTelemetry telemetry = KafkaTelemetry.builder(openTelemetry) + .setMessagingReceiveInstrumentationEnabled(true) + .setCapturedHeaders(Arrays.asList("custom-header")) + .build(); + +Map props = new HashMap<>(); +props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); +props.put(ConsumerConfig.GROUP_ID_CONFIG, "my-group"); +props.put(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); +props.putAll(telemetry.consumerInterceptorConfigProperties()); + +Consumer consumer = new KafkaConsumer<>(props); +``` + +##### Alternative: Using interceptors with global OpenTelemetry + +If you don't explicitly configure the interceptors with a `KafkaTelemetry` instance, they will fall back to using +`GlobalOpenTelemetry.get()` and system properties for configuration. + Use the `TracingProducerInterceptor` for the producer in order to create a "send" span automatically, each time a message is sent. ```java @@ -47,6 +90,10 @@ Use the `TracingConsumerInterceptor` for the consumer in order to create a "rece props.setProperty(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); ``` +The interceptors will use the following system properties for configuration: +- `otel.instrumentation.messaging.experimental.receive-telemetry.enabled` - Enable receive telemetry (default: false) +- `otel.instrumentation.messaging.experimental.capture-headers` - List of headers to capture as span attributes + #### Wrapping clients The other way is by wrapping the Kafka client with a tracing enabled Kafka client. From b1877912a7c1caffd172909ab7bff9ce3135d0f4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 02:50:34 +0000 Subject: [PATCH 05/50] Follow OpenTelemetryMetricsReporter pattern for property validation Co-authored-by: trask <218610+trask@users.noreply.github.com> --- .../v2_6/TracingConsumerInterceptor.java | 46 ++++++++++++------- .../v2_6/TracingProducerInterceptor.java | 39 ++++++++++------ 2 files changed, 54 insertions(+), 31 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java index 48744d8a5ce7..e4424f5fbfdc 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java @@ -63,25 +63,37 @@ public void configure(Map configs) { consumerGroup = Objects.toString(configs.get(ConsumerConfig.GROUP_ID_CONFIG), null); clientId = Objects.toString(configs.get(ConsumerConfig.CLIENT_ID_CONFIG), null); - // Try to get KafkaTelemetry from config Object telemetrySupplier = configs.get(KafkaTelemetry.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); - if (telemetrySupplier instanceof Supplier) { - Object kafkaTelemetry = ((Supplier) telemetrySupplier).get(); - if (kafkaTelemetry instanceof KafkaTelemetry) { - this.telemetry = (KafkaTelemetry) kafkaTelemetry; - return; - } + if (telemetrySupplier == null) { + // Fallback to GlobalOpenTelemetry if not configured + this.telemetry = + KafkaTelemetry.builder(GlobalOpenTelemetry.get()) + .setMessagingReceiveInstrumentationEnabled( + ConfigPropertiesUtil.getBoolean( + "otel.instrumentation.messaging.experimental.receive-telemetry.enabled", + false)) + .setCapturedHeaders( + ConfigPropertiesUtil.getList( + "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) + .build(); + return; } - // Fallback to GlobalOpenTelemetry if not configured - this.telemetry = - KafkaTelemetry.builder(GlobalOpenTelemetry.get()) - .setMessagingReceiveInstrumentationEnabled( - ConfigPropertiesUtil.getBoolean( - "otel.instrumentation.messaging.experimental.receive-telemetry.enabled", false)) - .setCapturedHeaders( - ConfigPropertiesUtil.getList( - "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) - .build(); + if (!(telemetrySupplier instanceof Supplier)) { + throw new IllegalStateException( + "Configuration property " + + KafkaTelemetry.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + + " is not instance of Supplier"); + } + + Object kafkaTelemetry = ((Supplier) telemetrySupplier).get(); + if (!(kafkaTelemetry instanceof KafkaTelemetry)) { + throw new IllegalStateException( + "Configuration property " + + KafkaTelemetry.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + + " supplier does not return KafkaTelemetry instance"); + } + + this.telemetry = (KafkaTelemetry) kafkaTelemetry; } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java index 921e605ce687..7f2938456c1b 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java @@ -48,22 +48,33 @@ public void close() {} public void configure(Map configs) { clientId = Objects.toString(configs.get(ProducerConfig.CLIENT_ID_CONFIG), null); - // Try to get KafkaTelemetry from config Object telemetrySupplier = configs.get(KafkaTelemetry.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); - if (telemetrySupplier instanceof Supplier) { - Object kafkaTelemetry = ((Supplier) telemetrySupplier).get(); - if (kafkaTelemetry instanceof KafkaTelemetry) { - this.telemetry = (KafkaTelemetry) kafkaTelemetry; - return; - } + if (telemetrySupplier == null) { + // Fallback to GlobalOpenTelemetry if not configured + this.telemetry = + KafkaTelemetry.builder(GlobalOpenTelemetry.get()) + .setCapturedHeaders( + ConfigPropertiesUtil.getList( + "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) + .build(); + return; } - // Fallback to GlobalOpenTelemetry if not configured - this.telemetry = - KafkaTelemetry.builder(GlobalOpenTelemetry.get()) - .setCapturedHeaders( - ConfigPropertiesUtil.getList( - "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) - .build(); + if (!(telemetrySupplier instanceof Supplier)) { + throw new IllegalStateException( + "Configuration property " + + KafkaTelemetry.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + + " is not instance of Supplier"); + } + + Object kafkaTelemetry = ((Supplier) telemetrySupplier).get(); + if (!(kafkaTelemetry instanceof KafkaTelemetry)) { + throw new IllegalStateException( + "Configuration property " + + KafkaTelemetry.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + + " supplier does not return KafkaTelemetry instance"); + } + + this.telemetry = (KafkaTelemetry) kafkaTelemetry; } } From 85ec08993c8753241ca75e1bdcead6353fbb2234 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 03:23:37 +0000 Subject: [PATCH 06/50] Avoid changing KafkaTelemetry - move config keys to interceptors Co-authored-by: trask <218610+trask@users.noreply.github.com> --- .../kafka-clients-2.6/library/README.md | 6 +- .../kafkaclients/v2_6/KafkaTelemetry.java | 56 ------------------- .../v2_6/TracingConsumerInterceptor.java | 9 ++- .../v2_6/TracingProducerInterceptor.java | 9 ++- .../v2_6/AbstractInterceptorsTest.java | 12 +++- 5 files changed, 26 insertions(+), 66 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md index 33592ea49323..db0872280c72 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md @@ -51,7 +51,8 @@ KafkaTelemetry telemetry = KafkaTelemetry.builder(openTelemetry) Map props = new HashMap<>(); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); props.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); -props.putAll(telemetry.producerInterceptorConfigProperties()); +props.put(TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + new io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaTelemetrySupplier(telemetry)); Producer producer = new KafkaProducer<>(props); ``` @@ -68,7 +69,8 @@ Map props = new HashMap<>(); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); props.put(ConsumerConfig.GROUP_ID_CONFIG, "my-group"); props.put(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); -props.putAll(telemetry.consumerInterceptorConfigProperties()); +props.put(TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + new io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaTelemetrySupplier(telemetry)); Consumer consumer = new KafkaConsumer<>(props); ``` diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java index 3e285da18f2d..d66003f6607f 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java @@ -21,7 +21,6 @@ import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProcessRequest; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProducerRequest; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaReceiveRequest; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaTelemetrySupplier; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaUtil; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.MetricsReporterList; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetryMetricsReporter; @@ -208,61 +207,6 @@ ConsumerRecords addTracing( return Collections.unmodifiableMap(config); } - /** - * Returns a config property key that can be used to pass this {@link KafkaTelemetry} instance to - * interceptors. - * - *

This is an internal config key used by the library instrumentation interceptors. - */ - static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = - "opentelemetry.experimental.kafka-telemetry.supplier"; - - /** - * Produces a set of kafka producer config properties to configure tracing in {@code - * TracingProducerInterceptor}. Add these resulting properties to the configuration map used to - * initialize a {@link KafkaProducer}. - * - *

Example usage: - * - *

{@code
-   * //    Map config = new HashMap<>();
-   * //    config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, ...);
-   * //    config.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName());
-   * //    config.putAll(kafkaTelemetry.producerInterceptorConfigProperties());
-   * //    try (KafkaProducer producer = new KafkaProducer<>(config)) { ... }
-   * }
- * - * @return the kafka producer interceptor config properties - */ - public Map producerInterceptorConfigProperties() { - Map config = new HashMap<>(); - config.put(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, new KafkaTelemetrySupplier(this)); - return Collections.unmodifiableMap(config); - } - - /** - * Produces a set of kafka consumer config properties to configure tracing in {@code - * TracingConsumerInterceptor}. Add these resulting properties to the configuration map used to - * initialize a {@link KafkaConsumer}. - * - *

Example usage: - * - *

{@code
-   * //    Map config = new HashMap<>();
-   * //    config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, ...);
-   * //    config.put(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName());
-   * //    config.putAll(kafkaTelemetry.consumerInterceptorConfigProperties());
-   * //    try (KafkaConsumer consumer = new KafkaConsumer<>(config)) { ... }
-   * }
- * - * @return the kafka consumer interceptor config properties - */ - public Map consumerInterceptorConfigProperties() { - Map config = new HashMap<>(); - config.put(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, new KafkaTelemetrySupplier(this)); - return Collections.unmodifiableMap(config); - } - /** * Build and inject span into record. * diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java index e4424f5fbfdc..aa306b00f9f1 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java @@ -31,6 +31,9 @@ */ public class TracingConsumerInterceptor implements ConsumerInterceptor { + public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = + "opentelemetry.experimental.kafka-telemetry.supplier"; + @Nullable private KafkaTelemetry telemetry; private String consumerGroup; private String clientId; @@ -63,7 +66,7 @@ public void configure(Map configs) { consumerGroup = Objects.toString(configs.get(ConsumerConfig.GROUP_ID_CONFIG), null); clientId = Objects.toString(configs.get(ConsumerConfig.CLIENT_ID_CONFIG), null); - Object telemetrySupplier = configs.get(KafkaTelemetry.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); if (telemetrySupplier == null) { // Fallback to GlobalOpenTelemetry if not configured this.telemetry = @@ -82,7 +85,7 @@ public void configure(Map configs) { if (!(telemetrySupplier instanceof Supplier)) { throw new IllegalStateException( "Configuration property " - + KafkaTelemetry.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + " is not instance of Supplier"); } @@ -90,7 +93,7 @@ public void configure(Map configs) { if (!(kafkaTelemetry instanceof KafkaTelemetry)) { throw new IllegalStateException( "Configuration property " - + KafkaTelemetry.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + " supplier does not return KafkaTelemetry instance"); } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java index 7f2938456c1b..10acc76db6e2 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java @@ -26,6 +26,9 @@ */ public class TracingProducerInterceptor implements ProducerInterceptor { + public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = + "opentelemetry.experimental.kafka-telemetry.supplier"; + @Nullable private KafkaTelemetry telemetry; @Nullable private String clientId; @@ -48,7 +51,7 @@ public void close() {} public void configure(Map configs) { clientId = Objects.toString(configs.get(ProducerConfig.CLIENT_ID_CONFIG), null); - Object telemetrySupplier = configs.get(KafkaTelemetry.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); if (telemetrySupplier == null) { // Fallback to GlobalOpenTelemetry if not configured this.telemetry = @@ -63,7 +66,7 @@ public void configure(Map configs) { if (!(telemetrySupplier instanceof Supplier)) { throw new IllegalStateException( "Configuration property " - + KafkaTelemetry.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + " is not instance of Supplier"); } @@ -71,7 +74,7 @@ public void configure(Map configs) { if (!(kafkaTelemetry instanceof KafkaTelemetry)) { throw new IllegalStateException( "Configuration property " - + KafkaTelemetry.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + " supplier does not return KafkaTelemetry instance"); } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java index ec966499d63b..da11f91bbc88 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java @@ -45,7 +45,11 @@ public Map producerProps() { Map props = super.producerProps(); props.put( ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); - props.putAll(kafkaTelemetry.producerInterceptorConfigProperties()); + props.put( + TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + new io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal + .KafkaTelemetrySupplier( + kafkaTelemetry)); return props; } @@ -57,7 +61,11 @@ public Map consumerProps() { Map props = super.consumerProps(); props.put( ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); - props.putAll(kafkaTelemetry.consumerInterceptorConfigProperties()); + props.put( + TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + new io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal + .KafkaTelemetrySupplier( + kafkaTelemetry)); return props; } From 5f6294bfcbc3b154005ae77a3500ed6925d7d60d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 03:50:43 +0000 Subject: [PATCH 07/50] Use OpenTelemetrySupplier instead of KafkaTelemetrySupplier Co-authored-by: trask <218610+trask@users.noreply.github.com> --- .../kafka-clients-2.6/library/README.md | 28 +++------- .../v2_6/TracingConsumerInterceptor.java | 56 ++++++++----------- .../v2_6/TracingProducerInterceptor.java | 49 +++++++--------- .../v2_6/AbstractInterceptorsTest.java | 12 ++-- .../internal/KafkaTelemetrySupplier.java | 37 ------------ 5 files changed, 60 insertions(+), 122 deletions(-) delete mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-common-0.11/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaTelemetrySupplier.java diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md index db0872280c72..c539e6c73523 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md @@ -35,42 +35,32 @@ The Kafka clients API provides a way to "intercept" messages before they are sen The OpenTelemetry instrumented Kafka library provides two interceptors to be configured to add tracing information automatically. The interceptor class has to be set in the properties bag used to create the Kafka client. -##### Recommended approach: Configuring interceptors with KafkaTelemetry +##### Recommended approach: Configuring interceptors with OpenTelemetry -The recommended way to use interceptors is to configure them with a `KafkaTelemetry` instance. -This gives you full control over the configuration, including which `OpenTelemetry` instance to use, -whether to enable receive telemetry, and which headers to capture. +The recommended way to use interceptors is to configure them with an `OpenTelemetry` instance. +Interceptors will use system properties for additional configuration like captured headers and receive telemetry settings. -For the producer, configure the interceptor with the KafkaTelemetry instance: +For the producer: ```java -KafkaTelemetry telemetry = KafkaTelemetry.builder(openTelemetry) - .setCapturedHeaders(Arrays.asList("custom-header")) - .build(); - Map props = new HashMap<>(); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); props.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); -props.put(TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, - new io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaTelemetrySupplier(telemetry)); +props.put(TracingProducerInterceptor.CONFIG_KEY_OPENTELEMETRY_SUPPLIER, + new io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetrySupplier(openTelemetry)); Producer producer = new KafkaProducer<>(props); ``` -For the consumer, configure the interceptor with the KafkaTelemetry instance: +For the consumer: ```java -KafkaTelemetry telemetry = KafkaTelemetry.builder(openTelemetry) - .setMessagingReceiveInstrumentationEnabled(true) - .setCapturedHeaders(Arrays.asList("custom-header")) - .build(); - Map props = new HashMap<>(); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); props.put(ConsumerConfig.GROUP_ID_CONFIG, "my-group"); props.put(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); -props.put(TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, - new io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaTelemetrySupplier(telemetry)); +props.put(TracingConsumerInterceptor.CONFIG_KEY_OPENTELEMETRY_SUPPLIER, + new io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetrySupplier(openTelemetry)); Consumer consumer = new KafkaConsumer<>(props); ``` diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java index aa306b00f9f1..ba6c8b860f68 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java @@ -9,14 +9,15 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; import io.opentelemetry.instrumentation.api.internal.Timer; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContext; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContextUtil; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetrySupplier; import java.util.Map; import java.util.Objects; -import java.util.function.Supplier; import javax.annotation.Nullable; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.ConsumerInterceptor; @@ -31,8 +32,7 @@ */ public class TracingConsumerInterceptor implements ConsumerInterceptor { - public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = - "opentelemetry.experimental.kafka-telemetry.supplier"; + public static final String CONFIG_KEY_OPENTELEMETRY_SUPPLIER = "opentelemetry.supplier"; @Nullable private KafkaTelemetry telemetry; private String consumerGroup; @@ -66,37 +66,29 @@ public void configure(Map configs) { consumerGroup = Objects.toString(configs.get(ConsumerConfig.GROUP_ID_CONFIG), null); clientId = Objects.toString(configs.get(ConsumerConfig.CLIENT_ID_CONFIG), null); - Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); - if (telemetrySupplier == null) { + OpenTelemetry openTelemetry; + Object openTelemetrySupplier = configs.get(CONFIG_KEY_OPENTELEMETRY_SUPPLIER); + if (openTelemetrySupplier == null) { // Fallback to GlobalOpenTelemetry if not configured - this.telemetry = - KafkaTelemetry.builder(GlobalOpenTelemetry.get()) - .setMessagingReceiveInstrumentationEnabled( - ConfigPropertiesUtil.getBoolean( - "otel.instrumentation.messaging.experimental.receive-telemetry.enabled", - false)) - .setCapturedHeaders( - ConfigPropertiesUtil.getList( - "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) - .build(); - return; + openTelemetry = GlobalOpenTelemetry.get(); + } else { + if (!(openTelemetrySupplier instanceof OpenTelemetrySupplier)) { + throw new IllegalStateException( + "Configuration property " + + CONFIG_KEY_OPENTELEMETRY_SUPPLIER + + " is not instance of OpenTelemetrySupplier"); + } + openTelemetry = ((OpenTelemetrySupplier) openTelemetrySupplier).get(); } - if (!(telemetrySupplier instanceof Supplier)) { - throw new IllegalStateException( - "Configuration property " - + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER - + " is not instance of Supplier"); - } - - Object kafkaTelemetry = ((Supplier) telemetrySupplier).get(); - if (!(kafkaTelemetry instanceof KafkaTelemetry)) { - throw new IllegalStateException( - "Configuration property " - + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER - + " supplier does not return KafkaTelemetry instance"); - } - - this.telemetry = (KafkaTelemetry) kafkaTelemetry; + this.telemetry = + KafkaTelemetry.builder(openTelemetry) + .setMessagingReceiveInstrumentationEnabled( + ConfigPropertiesUtil.getBoolean( + "otel.instrumentation.messaging.experimental.receive-telemetry.enabled", false)) + .setCapturedHeaders( + ConfigPropertiesUtil.getList( + "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) + .build(); } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java index 10acc76db6e2..477c884db111 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java @@ -9,10 +9,11 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetrySupplier; import java.util.Map; import java.util.Objects; -import java.util.function.Supplier; import javax.annotation.Nullable; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.clients.producer.ProducerInterceptor; @@ -26,8 +27,7 @@ */ public class TracingProducerInterceptor implements ProducerInterceptor { - public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = - "opentelemetry.experimental.kafka-telemetry.supplier"; + public static final String CONFIG_KEY_OPENTELEMETRY_SUPPLIER = "opentelemetry.supplier"; @Nullable private KafkaTelemetry telemetry; @Nullable private String clientId; @@ -51,33 +51,26 @@ public void close() {} public void configure(Map configs) { clientId = Objects.toString(configs.get(ProducerConfig.CLIENT_ID_CONFIG), null); - Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); - if (telemetrySupplier == null) { + OpenTelemetry openTelemetry; + Object openTelemetrySupplier = configs.get(CONFIG_KEY_OPENTELEMETRY_SUPPLIER); + if (openTelemetrySupplier == null) { // Fallback to GlobalOpenTelemetry if not configured - this.telemetry = - KafkaTelemetry.builder(GlobalOpenTelemetry.get()) - .setCapturedHeaders( - ConfigPropertiesUtil.getList( - "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) - .build(); - return; + openTelemetry = GlobalOpenTelemetry.get(); + } else { + if (!(openTelemetrySupplier instanceof OpenTelemetrySupplier)) { + throw new IllegalStateException( + "Configuration property " + + CONFIG_KEY_OPENTELEMETRY_SUPPLIER + + " is not instance of OpenTelemetrySupplier"); + } + openTelemetry = ((OpenTelemetrySupplier) openTelemetrySupplier).get(); } - if (!(telemetrySupplier instanceof Supplier)) { - throw new IllegalStateException( - "Configuration property " - + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER - + " is not instance of Supplier"); - } - - Object kafkaTelemetry = ((Supplier) telemetrySupplier).get(); - if (!(kafkaTelemetry instanceof KafkaTelemetry)) { - throw new IllegalStateException( - "Configuration property " - + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER - + " supplier does not return KafkaTelemetry instance"); - } - - this.telemetry = (KafkaTelemetry) kafkaTelemetry; + this.telemetry = + KafkaTelemetry.builder(openTelemetry) + .setCapturedHeaders( + ConfigPropertiesUtil.getList( + "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) + .build(); } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java index da11f91bbc88..eb5532305fc0 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java @@ -46,10 +46,10 @@ public Map producerProps() { props.put( ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); props.put( - TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + TracingProducerInterceptor.CONFIG_KEY_OPENTELEMETRY_SUPPLIER, new io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal - .KafkaTelemetrySupplier( - kafkaTelemetry)); + .OpenTelemetrySupplier( + testing.getOpenTelemetry())); return props; } @@ -62,10 +62,10 @@ public Map consumerProps() { props.put( ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); props.put( - TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + TracingConsumerInterceptor.CONFIG_KEY_OPENTELEMETRY_SUPPLIER, new io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal - .KafkaTelemetrySupplier( - kafkaTelemetry)); + .OpenTelemetrySupplier( + testing.getOpenTelemetry())); return props; } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-common-0.11/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaTelemetrySupplier.java b/instrumentation/kafka/kafka-clients/kafka-clients-common-0.11/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaTelemetrySupplier.java deleted file mode 100644 index 38bf8bb592b7..000000000000 --- a/instrumentation/kafka/kafka-clients/kafka-clients-common-0.11/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaTelemetrySupplier.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal; - -import java.io.Serializable; -import java.util.Objects; -import java.util.function.Supplier; - -/** - * Wrapper for KafkaTelemetry that can be injected into kafka configuration without breaking - * serialization. Used by kafka interceptors to get a configured KafkaTelemetry instance. - * - *

This class is internal and is hence not for public use. Its APIs are unstable and can change - * at any time. - */ -public final class KafkaTelemetrySupplier implements Supplier, Serializable { - private static final long serialVersionUID = 1L; - private final transient Object kafkaTelemetry; - - public KafkaTelemetrySupplier(Object kafkaTelemetry) { - Objects.requireNonNull(kafkaTelemetry); - this.kafkaTelemetry = kafkaTelemetry; - } - - @Override - public Object get() { - return kafkaTelemetry; - } - - private Object writeReplace() { - // serialize this object to null - return null; - } -} From e5ca107b2f119e5077f4e20ae3c30368cc66f194 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 04:16:12 +0000 Subject: [PATCH 08/50] Clean up unused code and use imports instead of FQCN Co-authored-by: trask <218610+trask@users.noreply.github.com> --- .../v2_6/AbstractInterceptorsTest.java | 24 +++---------------- .../InterceptorsSuppressReceiveSpansTest.java | 6 ----- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java index eb5532305fc0..295c618567fb 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java @@ -8,6 +8,7 @@ import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaClientBaseTest; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetrySupplier; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import java.nio.charset.StandardCharsets; @@ -28,44 +29,25 @@ abstract class AbstractInterceptorsTest extends KafkaClientBaseTest { static final String greeting = "Hello Kafka!"; - private static KafkaTelemetry kafkaTelemetry; - - protected static KafkaTelemetry createKafkaTelemetry() { - return KafkaTelemetry.builder(testing.getOpenTelemetry()) - .setCapturedHeaders(java.util.Collections.singletonList("Test-Message-Header")) - .setMessagingReceiveInstrumentationEnabled(true) - .build(); - } - @Override public Map producerProps() { - if (kafkaTelemetry == null) { - kafkaTelemetry = createKafkaTelemetry(); - } Map props = super.producerProps(); props.put( ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); props.put( TracingProducerInterceptor.CONFIG_KEY_OPENTELEMETRY_SUPPLIER, - new io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal - .OpenTelemetrySupplier( - testing.getOpenTelemetry())); + new OpenTelemetrySupplier(testing.getOpenTelemetry())); return props; } @Override public Map consumerProps() { - if (kafkaTelemetry == null) { - kafkaTelemetry = createKafkaTelemetry(); - } Map props = super.consumerProps(); props.put( ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); props.put( TracingConsumerInterceptor.CONFIG_KEY_OPENTELEMETRY_SUPPLIER, - new io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal - .OpenTelemetrySupplier( - testing.getOpenTelemetry())); + new OpenTelemetrySupplier(testing.getOpenTelemetry())); return props; } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsSuppressReceiveSpansTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsSuppressReceiveSpansTest.java index 33c87d4456df..049020a2d074 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsSuppressReceiveSpansTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsSuppressReceiveSpansTest.java @@ -23,12 +23,6 @@ class InterceptorsSuppressReceiveSpansTest extends AbstractInterceptorsTest { - protected static KafkaTelemetry createKafkaTelemetry() { - return KafkaTelemetry.builder(testing.getOpenTelemetry()) - .setMessagingReceiveInstrumentationEnabled(false) - .build(); - } - @SuppressWarnings("deprecation") // using deprecated semconv @Override void assertTraces() { From e791fc8955084b308569c6b9f08b1d3d6002155c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 15:46:35 +0000 Subject: [PATCH 09/50] Add helper methods to KafkaTelemetry for interceptor config Co-authored-by: trask <218610+trask@users.noreply.github.com> --- .../kafka-clients-2.6/library/README.md | 14 ++--- .../kafkaclients/v2_6/KafkaTelemetry.java | 52 +++++++++++++++++++ .../v2_6/AbstractInterceptorsTest.java | 12 ++--- 3 files changed, 65 insertions(+), 13 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md index c539e6c73523..fa0dcf12aa5f 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md @@ -35,19 +35,20 @@ The Kafka clients API provides a way to "intercept" messages before they are sen The OpenTelemetry instrumented Kafka library provides two interceptors to be configured to add tracing information automatically. The interceptor class has to be set in the properties bag used to create the Kafka client. -##### Recommended approach: Configuring interceptors with OpenTelemetry +##### Recommended approach: Configuring interceptors with KafkaTelemetry -The recommended way to use interceptors is to configure them with an `OpenTelemetry` instance. +The recommended way to use interceptors is to configure them with a `KafkaTelemetry` instance. Interceptors will use system properties for additional configuration like captured headers and receive telemetry settings. For the producer: ```java +KafkaTelemetry telemetry = KafkaTelemetry.create(openTelemetry); + Map props = new HashMap<>(); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); props.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); -props.put(TracingProducerInterceptor.CONFIG_KEY_OPENTELEMETRY_SUPPLIER, - new io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetrySupplier(openTelemetry)); +props.putAll(telemetry.producerInterceptorConfigProperties()); Producer producer = new KafkaProducer<>(props); ``` @@ -55,12 +56,13 @@ Producer producer = new KafkaProducer<>(props); For the consumer: ```java +KafkaTelemetry telemetry = KafkaTelemetry.create(openTelemetry); + Map props = new HashMap<>(); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); props.put(ConsumerConfig.GROUP_ID_CONFIG, "my-group"); props.put(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); -props.put(TracingConsumerInterceptor.CONFIG_KEY_OPENTELEMETRY_SUPPLIER, - new io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetrySupplier(openTelemetry)); +props.putAll(telemetry.consumerInterceptorConfigProperties()); Consumer consumer = new KafkaConsumer<>(props); ``` diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java index d66003f6607f..eafde11316b3 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java @@ -207,6 +207,58 @@ ConsumerRecords addTracing( return Collections.unmodifiableMap(config); } + /** + * Returns configuration properties that can be used to enable tracing via {@code + * TracingProducerInterceptor}. Add these resulting properties to the configuration map used to + * initialize a {@link org.apache.kafka.clients.producer.KafkaProducer}. + * + *

Example usage: + * + *

{@code
+   * //    KafkaTelemetry telemetry = KafkaTelemetry.create(openTelemetry);
+   * //    Map config = new HashMap<>();
+   * //    config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, ...);
+   * //    config.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName());
+   * //    config.putAll(telemetry.producerInterceptorConfigProperties());
+   * //    try (KafkaProducer producer = new KafkaProducer<>(config)) { ... }
+   * }
+ * + * @return the kafka producer interceptor config properties + */ + public Map producerInterceptorConfigProperties() { + Map config = new HashMap<>(); + config.put( + TracingProducerInterceptor.CONFIG_KEY_OPENTELEMETRY_SUPPLIER, + new OpenTelemetrySupplier(openTelemetry)); + return Collections.unmodifiableMap(config); + } + + /** + * Returns configuration properties that can be used to enable tracing via {@code + * TracingConsumerInterceptor}. Add these resulting properties to the configuration map used to + * initialize a {@link org.apache.kafka.clients.consumer.KafkaConsumer}. + * + *

Example usage: + * + *

{@code
+   * //    KafkaTelemetry telemetry = KafkaTelemetry.create(openTelemetry);
+   * //    Map config = new HashMap<>();
+   * //    config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, ...);
+   * //    config.put(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName());
+   * //    config.putAll(telemetry.consumerInterceptorConfigProperties());
+   * //    try (KafkaConsumer consumer = new KafkaConsumer<>(config)) { ... }
+   * }
+ * + * @return the kafka consumer interceptor config properties + */ + public Map consumerInterceptorConfigProperties() { + Map config = new HashMap<>(); + config.put( + TracingConsumerInterceptor.CONFIG_KEY_OPENTELEMETRY_SUPPLIER, + new OpenTelemetrySupplier(openTelemetry)); + return Collections.unmodifiableMap(config); + } + /** * Build and inject span into record. * diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java index 295c618567fb..9fb0cfc75bcd 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java @@ -8,7 +8,6 @@ import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaClientBaseTest; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetrySupplier; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import java.nio.charset.StandardCharsets; @@ -29,14 +28,15 @@ abstract class AbstractInterceptorsTest extends KafkaClientBaseTest { static final String greeting = "Hello Kafka!"; + private static final KafkaTelemetry kafkaTelemetry = + KafkaTelemetry.create(testing.getOpenTelemetry()); + @Override public Map producerProps() { Map props = super.producerProps(); props.put( ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); - props.put( - TracingProducerInterceptor.CONFIG_KEY_OPENTELEMETRY_SUPPLIER, - new OpenTelemetrySupplier(testing.getOpenTelemetry())); + props.putAll(kafkaTelemetry.producerInterceptorConfigProperties()); return props; } @@ -45,9 +45,7 @@ public Map consumerProps() { Map props = super.consumerProps(); props.put( ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); - props.put( - TracingConsumerInterceptor.CONFIG_KEY_OPENTELEMETRY_SUPPLIER, - new OpenTelemetrySupplier(testing.getOpenTelemetry())); + props.putAll(kafkaTelemetry.consumerInterceptorConfigProperties()); return props; } From 97cbf9da22accca10b3b8e10a0ff7dbb6e05228f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 16:02:54 +0000 Subject: [PATCH 10/50] Include INTERCEPTOR_CLASSES_CONFIG in helper methods Co-authored-by: trask <218610+trask@users.noreply.github.com> --- .../kafka-clients/kafka-clients-2.6/library/README.md | 2 -- .../kafkaclients/v2_6/KafkaTelemetry.java | 10 ++++++++-- .../kafkaclients/v2_6/AbstractInterceptorsTest.java | 4 ---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md index fa0dcf12aa5f..dcca20a5bbf5 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md @@ -47,7 +47,6 @@ KafkaTelemetry telemetry = KafkaTelemetry.create(openTelemetry); Map props = new HashMap<>(); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); -props.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); props.putAll(telemetry.producerInterceptorConfigProperties()); Producer producer = new KafkaProducer<>(props); @@ -61,7 +60,6 @@ KafkaTelemetry telemetry = KafkaTelemetry.create(openTelemetry); Map props = new HashMap<>(); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); props.put(ConsumerConfig.GROUP_ID_CONFIG, "my-group"); -props.put(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); props.putAll(telemetry.consumerInterceptorConfigProperties()); Consumer consumer = new KafkaConsumer<>(props); diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java index eafde11316b3..f8d50a15cb6c 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java @@ -38,12 +38,14 @@ import java.util.logging.Logger; import org.apache.kafka.clients.CommonClientConfigs; import org.apache.kafka.clients.consumer.Consumer; +import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.clients.producer.Callback; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.clients.producer.RecordMetadata; import org.apache.kafka.common.TopicPartition; @@ -218,7 +220,6 @@ ConsumerRecords addTracing( * // KafkaTelemetry telemetry = KafkaTelemetry.create(openTelemetry); * // Map config = new HashMap<>(); * // config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, ...); - * // config.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); * // config.putAll(telemetry.producerInterceptorConfigProperties()); * // try (KafkaProducer producer = new KafkaProducer<>(config)) { ... } * } @@ -227,6 +228,9 @@ ConsumerRecords addTracing( */ public Map producerInterceptorConfigProperties() { Map config = new HashMap<>(); + config.put( + ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, + TracingProducerInterceptor.class.getName()); config.put( TracingProducerInterceptor.CONFIG_KEY_OPENTELEMETRY_SUPPLIER, new OpenTelemetrySupplier(openTelemetry)); @@ -244,7 +248,6 @@ ConsumerRecords addTracing( * // KafkaTelemetry telemetry = KafkaTelemetry.create(openTelemetry); * // Map config = new HashMap<>(); * // config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, ...); - * // config.put(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); * // config.putAll(telemetry.consumerInterceptorConfigProperties()); * // try (KafkaConsumer consumer = new KafkaConsumer<>(config)) { ... } * } @@ -253,6 +256,9 @@ ConsumerRecords addTracing( */ public Map consumerInterceptorConfigProperties() { Map config = new HashMap<>(); + config.put( + ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, + TracingConsumerInterceptor.class.getName()); config.put( TracingConsumerInterceptor.CONFIG_KEY_OPENTELEMETRY_SUPPLIER, new OpenTelemetrySupplier(openTelemetry)); diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java index 9fb0cfc75bcd..efdadd951fa9 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java @@ -34,8 +34,6 @@ abstract class AbstractInterceptorsTest extends KafkaClientBaseTest { @Override public Map producerProps() { Map props = super.producerProps(); - props.put( - ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); props.putAll(kafkaTelemetry.producerInterceptorConfigProperties()); return props; } @@ -43,8 +41,6 @@ public Map producerProps() { @Override public Map consumerProps() { Map props = super.consumerProps(); - props.put( - ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); props.putAll(kafkaTelemetry.consumerInterceptorConfigProperties()); return props; } From 4f3d30df5c6a903e6b897c587194b85aca564d53 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 16:20:43 +0000 Subject: [PATCH 11/50] Use KafkaTelemetrySupplier to pass exact KafkaTelemetry instance Co-authored-by: trask <218610+trask@users.noreply.github.com> --- .../kafkaclients/v2_6/KafkaTelemetry.java | 9 +-- .../v2_6/TracingConsumerInterceptor.java | 57 +++++++++++-------- .../v2_6/TracingProducerInterceptor.java | 50 +++++++++------- .../internal/KafkaTelemetrySupplier.java | 37 ++++++++++++ 4 files changed, 104 insertions(+), 49 deletions(-) create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-common-0.11/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaTelemetrySupplier.java diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java index f8d50a15cb6c..172ffdc6380d 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java @@ -21,6 +21,7 @@ import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProcessRequest; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProducerRequest; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaReceiveRequest; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaTelemetrySupplier; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaUtil; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.MetricsReporterList; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetryMetricsReporter; @@ -232,8 +233,8 @@ ConsumerRecords addTracing( ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); config.put( - TracingProducerInterceptor.CONFIG_KEY_OPENTELEMETRY_SUPPLIER, - new OpenTelemetrySupplier(openTelemetry)); + TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + new KafkaTelemetrySupplier(this)); return Collections.unmodifiableMap(config); } @@ -260,8 +261,8 @@ ConsumerRecords addTracing( ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); config.put( - TracingConsumerInterceptor.CONFIG_KEY_OPENTELEMETRY_SUPPLIER, - new OpenTelemetrySupplier(openTelemetry)); + TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + new KafkaTelemetrySupplier(this)); return Collections.unmodifiableMap(config); } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java index ba6c8b860f68..2775e304335e 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java @@ -9,15 +9,15 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; import io.opentelemetry.instrumentation.api.internal.Timer; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContext; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContextUtil; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetrySupplier; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaTelemetrySupplier; import java.util.Map; import java.util.Objects; +import java.util.function.Supplier; import javax.annotation.Nullable; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.ConsumerInterceptor; @@ -32,7 +32,8 @@ */ public class TracingConsumerInterceptor implements ConsumerInterceptor { - public static final String CONFIG_KEY_OPENTELEMETRY_SUPPLIER = "opentelemetry.supplier"; + public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = + "opentelemetry.experimental.kafka-telemetry.supplier"; @Nullable private KafkaTelemetry telemetry; private String consumerGroup; @@ -66,29 +67,37 @@ public void configure(Map configs) { consumerGroup = Objects.toString(configs.get(ConsumerConfig.GROUP_ID_CONFIG), null); clientId = Objects.toString(configs.get(ConsumerConfig.CLIENT_ID_CONFIG), null); - OpenTelemetry openTelemetry; - Object openTelemetrySupplier = configs.get(CONFIG_KEY_OPENTELEMETRY_SUPPLIER); - if (openTelemetrySupplier == null) { + Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + if (telemetrySupplier == null) { // Fallback to GlobalOpenTelemetry if not configured - openTelemetry = GlobalOpenTelemetry.get(); - } else { - if (!(openTelemetrySupplier instanceof OpenTelemetrySupplier)) { - throw new IllegalStateException( - "Configuration property " - + CONFIG_KEY_OPENTELEMETRY_SUPPLIER - + " is not instance of OpenTelemetrySupplier"); - } - openTelemetry = ((OpenTelemetrySupplier) openTelemetrySupplier).get(); + this.telemetry = + KafkaTelemetry.builder(GlobalOpenTelemetry.get()) + .setMessagingReceiveInstrumentationEnabled( + ConfigPropertiesUtil.getBoolean( + "otel.instrumentation.messaging.experimental.receive-telemetry.enabled", + false)) + .setCapturedHeaders( + ConfigPropertiesUtil.getList( + "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) + .build(); + return; } - this.telemetry = - KafkaTelemetry.builder(openTelemetry) - .setMessagingReceiveInstrumentationEnabled( - ConfigPropertiesUtil.getBoolean( - "otel.instrumentation.messaging.experimental.receive-telemetry.enabled", false)) - .setCapturedHeaders( - ConfigPropertiesUtil.getList( - "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) - .build(); + if (!(telemetrySupplier instanceof Supplier)) { + throw new IllegalStateException( + "Configuration property " + + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + + " is not instance of Supplier"); + } + + Object kafkaTelemetry = ((Supplier) telemetrySupplier).get(); + if (!(kafkaTelemetry instanceof KafkaTelemetry)) { + throw new IllegalStateException( + "Configuration property " + + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + + " supplier does not return KafkaTelemetry instance"); + } + + this.telemetry = (KafkaTelemetry) kafkaTelemetry; } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java index 477c884db111..96e07494788d 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java @@ -9,11 +9,11 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetrySupplier; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaTelemetrySupplier; import java.util.Map; import java.util.Objects; +import java.util.function.Supplier; import javax.annotation.Nullable; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.clients.producer.ProducerInterceptor; @@ -27,7 +27,8 @@ */ public class TracingProducerInterceptor implements ProducerInterceptor { - public static final String CONFIG_KEY_OPENTELEMETRY_SUPPLIER = "opentelemetry.supplier"; + public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = + "opentelemetry.experimental.kafka-telemetry.supplier"; @Nullable private KafkaTelemetry telemetry; @Nullable private String clientId; @@ -51,26 +52,33 @@ public void close() {} public void configure(Map configs) { clientId = Objects.toString(configs.get(ProducerConfig.CLIENT_ID_CONFIG), null); - OpenTelemetry openTelemetry; - Object openTelemetrySupplier = configs.get(CONFIG_KEY_OPENTELEMETRY_SUPPLIER); - if (openTelemetrySupplier == null) { + Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + if (telemetrySupplier == null) { // Fallback to GlobalOpenTelemetry if not configured - openTelemetry = GlobalOpenTelemetry.get(); - } else { - if (!(openTelemetrySupplier instanceof OpenTelemetrySupplier)) { - throw new IllegalStateException( - "Configuration property " - + CONFIG_KEY_OPENTELEMETRY_SUPPLIER - + " is not instance of OpenTelemetrySupplier"); - } - openTelemetry = ((OpenTelemetrySupplier) openTelemetrySupplier).get(); + this.telemetry = + KafkaTelemetry.builder(GlobalOpenTelemetry.get()) + .setCapturedHeaders( + ConfigPropertiesUtil.getList( + "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) + .build(); + return; } - this.telemetry = - KafkaTelemetry.builder(openTelemetry) - .setCapturedHeaders( - ConfigPropertiesUtil.getList( - "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) - .build(); + if (!(telemetrySupplier instanceof Supplier)) { + throw new IllegalStateException( + "Configuration property " + + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + + " is not instance of Supplier"); + } + + Object kafkaTelemetry = ((Supplier) telemetrySupplier).get(); + if (!(kafkaTelemetry instanceof KafkaTelemetry)) { + throw new IllegalStateException( + "Configuration property " + + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + + " supplier does not return KafkaTelemetry instance"); + } + + this.telemetry = (KafkaTelemetry) kafkaTelemetry; } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-common-0.11/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaTelemetrySupplier.java b/instrumentation/kafka/kafka-clients/kafka-clients-common-0.11/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaTelemetrySupplier.java new file mode 100644 index 000000000000..28ba44cae8ae --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-common-0.11/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaTelemetrySupplier.java @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal; + +import java.io.Serializable; +import java.util.Objects; +import java.util.function.Supplier; + +/** + * Wrapper for KafkaTelemetry that can be injected into kafka configuration without breaking + * serialization. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class KafkaTelemetrySupplier implements Supplier, Serializable { + private static final long serialVersionUID = 1L; + private final transient Object kafkaTelemetry; + + public KafkaTelemetrySupplier(Object kafkaTelemetry) { + Objects.requireNonNull(kafkaTelemetry); + this.kafkaTelemetry = kafkaTelemetry; + } + + @Override + public Object get() { + return kafkaTelemetry; + } + + private Object writeReplace() { + // serialize this object to null + return null; + } +} From a31210c170db5441c05aa8f37b378bf800f28c64 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 16:39:08 +0000 Subject: [PATCH 12/50] Remove experimental from config key, simplify fallback, add test Co-authored-by: trask <218610+trask@users.noreply.github.com> --- ...kotlin-compiler-3745061290931361383.salive | 0 ...kotlin-compiler-7174543796092179295.salive | 0 .../v2_6/TracingConsumerInterceptor.java | 16 +- .../v2_6/TracingProducerInterceptor.java | 12 +- .../v2_6/KafkaTelemetryInterceptorTest.java | 172 ++++++++++++++++++ 5 files changed, 176 insertions(+), 24 deletions(-) create mode 100644 conventions/.kotlin/sessions/kotlin-compiler-3745061290931361383.salive create mode 100644 gradle-plugins/.kotlin/sessions/kotlin-compiler-7174543796092179295.salive create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java diff --git a/conventions/.kotlin/sessions/kotlin-compiler-3745061290931361383.salive b/conventions/.kotlin/sessions/kotlin-compiler-3745061290931361383.salive new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/gradle-plugins/.kotlin/sessions/kotlin-compiler-7174543796092179295.salive b/gradle-plugins/.kotlin/sessions/kotlin-compiler-7174543796092179295.salive new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java index 2775e304335e..640e01285f66 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java @@ -5,12 +5,9 @@ package io.opentelemetry.instrumentation.kafkaclients.v2_6; -import static java.util.Collections.emptyList; - import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; import io.opentelemetry.instrumentation.api.internal.Timer; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContext; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContextUtil; @@ -33,7 +30,7 @@ public class TracingConsumerInterceptor implements ConsumerInterceptor { public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = - "opentelemetry.experimental.kafka-telemetry.supplier"; + "opentelemetry.kafka-telemetry.supplier"; @Nullable private KafkaTelemetry telemetry; private String consumerGroup; @@ -70,16 +67,7 @@ public void configure(Map configs) { Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); if (telemetrySupplier == null) { // Fallback to GlobalOpenTelemetry if not configured - this.telemetry = - KafkaTelemetry.builder(GlobalOpenTelemetry.get()) - .setMessagingReceiveInstrumentationEnabled( - ConfigPropertiesUtil.getBoolean( - "otel.instrumentation.messaging.experimental.receive-telemetry.enabled", - false)) - .setCapturedHeaders( - ConfigPropertiesUtil.getList( - "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) - .build(); + this.telemetry = KafkaTelemetry.create(GlobalOpenTelemetry.get()); return; } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java index 96e07494788d..1a3a00070a48 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java @@ -5,11 +5,8 @@ package io.opentelemetry.instrumentation.kafkaclients.v2_6; -import static java.util.Collections.emptyList; - import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaTelemetrySupplier; import java.util.Map; import java.util.Objects; @@ -28,7 +25,7 @@ public class TracingProducerInterceptor implements ProducerInterceptor { public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = - "opentelemetry.experimental.kafka-telemetry.supplier"; + "opentelemetry.kafka-telemetry.supplier"; @Nullable private KafkaTelemetry telemetry; @Nullable private String clientId; @@ -55,12 +52,7 @@ public void configure(Map configs) { Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); if (telemetrySupplier == null) { // Fallback to GlobalOpenTelemetry if not configured - this.telemetry = - KafkaTelemetry.builder(GlobalOpenTelemetry.get()) - .setCapturedHeaders( - ConfigPropertiesUtil.getList( - "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) - .build(); + this.telemetry = KafkaTelemetry.create(GlobalOpenTelemetry.get()); return; } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java new file mode 100644 index 000000000000..8395e022b34e --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java @@ -0,0 +1,172 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaTelemetrySupplier; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.util.HashMap; +import java.util.Map; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class KafkaTelemetryInterceptorTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + private static Map producerConfig() { + Map config = new HashMap<>(); + config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); + config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + config.putAll( + KafkaTelemetry.create(testing.getOpenTelemetry()).producerInterceptorConfigProperties()); + return config; + } + + private static Map consumerConfig() { + Map config = new HashMap<>(); + config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); + config.put(ConsumerConfig.GROUP_ID_CONFIG, "test"); + config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + config.putAll( + KafkaTelemetry.create(testing.getOpenTelemetry()).consumerInterceptorConfigProperties()); + return config; + } + + @Test + void badProducerConfig() { + Assumptions.assumeFalse(Boolean.getBoolean("testLatestDeps")); + + // Bad config - wrong type for supplier + assertThatThrownBy( + () -> { + Map producerConfig = producerConfig(); + producerConfig.put( + TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, "foo"); + new KafkaProducer<>(producerConfig).close(); + }) + .hasRootCauseInstanceOf(IllegalStateException.class) + .hasRootCauseMessage( + "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of Supplier"); + + // Bad config - supplier returns wrong type + assertThatThrownBy( + () -> { + Map producerConfig = producerConfig(); + producerConfig.put( + TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + (java.util.function.Supplier) () -> "not a KafkaTelemetry"); + new KafkaProducer<>(producerConfig).close(); + }) + .hasRootCauseInstanceOf(IllegalStateException.class) + .hasRootCauseMessage( + "Configuration property opentelemetry.kafka-telemetry.supplier supplier does not return KafkaTelemetry instance"); + } + + @Test + void badConsumerConfig() { + Assumptions.assumeFalse(Boolean.getBoolean("testLatestDeps")); + + // Bad config - wrong type for supplier + assertThatThrownBy( + () -> { + Map consumerConfig = consumerConfig(); + consumerConfig.put( + TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, "foo"); + new KafkaConsumer<>(consumerConfig).close(); + }) + .hasRootCauseInstanceOf(IllegalStateException.class) + .hasRootCauseMessage( + "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of Supplier"); + + // Bad config - supplier returns wrong type + assertThatThrownBy( + () -> { + Map consumerConfig = consumerConfig(); + consumerConfig.put( + TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + (java.util.function.Supplier) () -> "not a KafkaTelemetry"); + new KafkaConsumer<>(consumerConfig).close(); + }) + .hasRootCauseInstanceOf(IllegalStateException.class) + .hasRootCauseMessage( + "Configuration property opentelemetry.kafka-telemetry.supplier supplier does not return KafkaTelemetry instance"); + } + + @Test + void serializableConfig() throws IOException, ClassNotFoundException { + testSerialize(producerConfig()); + testSerialize(consumerConfig()); + } + + @SuppressWarnings("unchecked") + private static void testSerialize(Map map) + throws IOException, ClassNotFoundException { + // Check that producer config has the supplier + Object producerSupplier = map.get(TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + Object consumerSupplier = map.get(TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + + KafkaTelemetrySupplier supplier = null; + if (producerSupplier instanceof KafkaTelemetrySupplier) { + supplier = (KafkaTelemetrySupplier) producerSupplier; + } else if (consumerSupplier instanceof KafkaTelemetrySupplier) { + supplier = (KafkaTelemetrySupplier) consumerSupplier; + } + + assertThat(supplier).isNotNull(); + assertThat(supplier.get()).isNotNull(); + + ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); + try (ObjectOutputStream outputStream = new ObjectOutputStream(byteOutputStream)) { + outputStream.writeObject(map); + } + + class CustomObjectInputStream extends ObjectInputStream { + CustomObjectInputStream(InputStream inputStream) throws IOException { + super(inputStream); + } + + @Override + protected Class resolveClass(ObjectStreamClass desc) + throws IOException, ClassNotFoundException { + if (desc.getName().startsWith("io.opentelemetry.")) { + throw new IllegalStateException( + "Serial form contains opentelemetry class " + desc.getName()); + } + return super.resolveClass(desc); + } + } + + try (ObjectInputStream inputStream = + new CustomObjectInputStream(new ByteArrayInputStream(byteOutputStream.toByteArray()))) { + Map result = (Map) inputStream.readObject(); + assertThat(result.get(TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER)) + .isNull(); + assertThat(result.get(TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER)) + .isNull(); + } + } +} From 84e69ad0543634152748265bb93990142b7d2ac6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 16:40:45 +0000 Subject: [PATCH 13/50] Refactor kafka-clients interceptors to be configurable Co-authored-by: trask <218610+trask@users.noreply.github.com> --- .../.kotlin/sessions/kotlin-compiler-3745061290931361383.salive | 0 .../.kotlin/sessions/kotlin-compiler-7174543796092179295.salive | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 conventions/.kotlin/sessions/kotlin-compiler-3745061290931361383.salive delete mode 100644 gradle-plugins/.kotlin/sessions/kotlin-compiler-7174543796092179295.salive diff --git a/conventions/.kotlin/sessions/kotlin-compiler-3745061290931361383.salive b/conventions/.kotlin/sessions/kotlin-compiler-3745061290931361383.salive deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/gradle-plugins/.kotlin/sessions/kotlin-compiler-7174543796092179295.salive b/gradle-plugins/.kotlin/sessions/kotlin-compiler-7174543796092179295.salive deleted file mode 100644 index e69de29bb2d1..000000000000 From 5419f0360dd794bfe7f286eaa449ce42ef6e985b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 17:18:46 +0000 Subject: [PATCH 14/50] Restore system property checks in fallback with TODO comment Co-authored-by: trask <218610+trask@users.noreply.github.com> --- .../v2_6/TracingConsumerInterceptor.java | 15 ++++++++++++++- .../v2_6/TracingProducerInterceptor.java | 11 ++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java index 640e01285f66..b97ea86beb6a 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java @@ -5,9 +5,12 @@ package io.opentelemetry.instrumentation.kafkaclients.v2_6; +import static java.util.Collections.emptyList; + import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; import io.opentelemetry.instrumentation.api.internal.Timer; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContext; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContextUtil; @@ -67,7 +70,17 @@ public void configure(Map configs) { Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); if (telemetrySupplier == null) { // Fallback to GlobalOpenTelemetry if not configured - this.telemetry = KafkaTelemetry.create(GlobalOpenTelemetry.get()); + // TODO: remove these config properties checks once declarative configuration is available + this.telemetry = + KafkaTelemetry.builder(GlobalOpenTelemetry.get()) + .setMessagingReceiveInstrumentationEnabled( + ConfigPropertiesUtil.getBoolean( + "otel.instrumentation.messaging.experimental.receive-telemetry.enabled", + false)) + .setCapturedHeaders( + ConfigPropertiesUtil.getList( + "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) + .build(); return; } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java index 1a3a00070a48..4edee1ba6d86 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java @@ -5,8 +5,11 @@ package io.opentelemetry.instrumentation.kafkaclients.v2_6; +import static java.util.Collections.emptyList; + import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaTelemetrySupplier; import java.util.Map; import java.util.Objects; @@ -52,7 +55,13 @@ public void configure(Map configs) { Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); if (telemetrySupplier == null) { // Fallback to GlobalOpenTelemetry if not configured - this.telemetry = KafkaTelemetry.create(GlobalOpenTelemetry.get()); + // TODO: remove these config properties checks once declarative configuration is available + this.telemetry = + KafkaTelemetry.builder(GlobalOpenTelemetry.get()) + .setCapturedHeaders( + ConfigPropertiesUtil.getList( + "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) + .build(); return; } From f4e568afb261e8576d66bd58edb9b405392b7712 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 17:23:46 +0000 Subject: [PATCH 15/50] Use import instead of FQCN for Supplier in test Co-authored-by: trask <218610+trask@users.noreply.github.com> --- .../kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java index 8395e022b34e..21b92d72827e 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java @@ -20,6 +20,7 @@ import java.io.ObjectStreamClass; import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.clients.producer.KafkaProducer; @@ -78,7 +79,7 @@ void badProducerConfig() { Map producerConfig = producerConfig(); producerConfig.put( TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, - (java.util.function.Supplier) () -> "not a KafkaTelemetry"); + (Supplier) () -> "not a KafkaTelemetry"); new KafkaProducer<>(producerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) @@ -108,7 +109,7 @@ void badConsumerConfig() { Map consumerConfig = consumerConfig(); consumerConfig.put( TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, - (java.util.function.Supplier) () -> "not a KafkaTelemetry"); + (Supplier) () -> "not a KafkaTelemetry"); new KafkaConsumer<>(consumerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) From 82248c4c5c8a1c9a71924c99ab92b0442966e76e Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 3 Oct 2025 10:42:43 -0700 Subject: [PATCH 16/50] Apply suggestions from code review --- .../kafkaclients/v2_6/TracingConsumerInterceptor.java | 2 +- .../kafkaclients/v2_6/TracingProducerInterceptor.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java index b97ea86beb6a..807d543f7fb0 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java @@ -70,9 +70,9 @@ public void configure(Map configs) { Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); if (telemetrySupplier == null) { // Fallback to GlobalOpenTelemetry if not configured - // TODO: remove these config properties checks once declarative configuration is available this.telemetry = KafkaTelemetry.builder(GlobalOpenTelemetry.get()) + // TODO: remove now that programmatic configuration is available .setMessagingReceiveInstrumentationEnabled( ConfigPropertiesUtil.getBoolean( "otel.instrumentation.messaging.experimental.receive-telemetry.enabled", diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java index 4edee1ba6d86..c32dbef0ebcf 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java @@ -55,9 +55,9 @@ public void configure(Map configs) { Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); if (telemetrySupplier == null) { // Fallback to GlobalOpenTelemetry if not configured - // TODO: remove these config properties checks once declarative configuration is available this.telemetry = KafkaTelemetry.builder(GlobalOpenTelemetry.get()) + // TODO: remove now that programmatic configuration is available .setCapturedHeaders( ConfigPropertiesUtil.getList( "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) From f1e7ddeaf36d529ac5ddeae4e5fcec56ef1fc707 Mon Sep 17 00:00:00 2001 From: otelbot <197425009+otelbot@users.noreply.github.com> Date: Fri, 3 Oct 2025 17:52:14 +0000 Subject: [PATCH 17/50] ./gradlew spotlessApply --- .../kafkaclients/v2_6/KafkaTelemetry.java | 6 ++---- .../v2_6/TracingConsumerInterceptor.java | 1 - .../v2_6/TracingProducerInterceptor.java | 1 - .../kafkaclients/v2_6/AbstractInterceptorsTest.java | 2 -- .../v2_6/KafkaTelemetryInterceptorTest.java | 12 +++++++----- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java index 172ffdc6380d..a58e5a42ffd5 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java @@ -230,8 +230,7 @@ ConsumerRecords addTracing( public Map producerInterceptorConfigProperties() { Map config = new HashMap<>(); config.put( - ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, - TracingProducerInterceptor.class.getName()); + ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); config.put( TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, new KafkaTelemetrySupplier(this)); @@ -258,8 +257,7 @@ ConsumerRecords addTracing( public Map consumerInterceptorConfigProperties() { Map config = new HashMap<>(); config.put( - ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, - TracingConsumerInterceptor.class.getName()); + ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); config.put( TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, new KafkaTelemetrySupplier(this)); diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java index 807d543f7fb0..124a2467c744 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java @@ -14,7 +14,6 @@ import io.opentelemetry.instrumentation.api.internal.Timer; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContext; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContextUtil; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaTelemetrySupplier; import java.util.Map; import java.util.Objects; import java.util.function.Supplier; diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java index c32dbef0ebcf..006943ed5306 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java @@ -10,7 +10,6 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaTelemetrySupplier; import java.util.Map; import java.util.Objects; import java.util.function.Supplier; diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java index efdadd951fa9..2c3f87b2acd1 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java @@ -13,10 +13,8 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.Map; -import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; -import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.clients.producer.ProducerRecord; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java index 21b92d72827e..d870a9e133ea 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java @@ -127,19 +127,21 @@ void serializableConfig() throws IOException, ClassNotFoundException { private static void testSerialize(Map map) throws IOException, ClassNotFoundException { // Check that producer config has the supplier - Object producerSupplier = map.get(TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); - Object consumerSupplier = map.get(TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); - + Object producerSupplier = + map.get(TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + Object consumerSupplier = + map.get(TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + KafkaTelemetrySupplier supplier = null; if (producerSupplier instanceof KafkaTelemetrySupplier) { supplier = (KafkaTelemetrySupplier) producerSupplier; } else if (consumerSupplier instanceof KafkaTelemetrySupplier) { supplier = (KafkaTelemetrySupplier) consumerSupplier; } - + assertThat(supplier).isNotNull(); assertThat(supplier.get()).isNotNull(); - + ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); try (ObjectOutputStream outputStream = new ObjectOutputStream(byteOutputStream)) { outputStream.writeObject(map); From 7703b1d2704109b94e0b12afcabcffe473f1377a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Oct 2025 03:50:27 +0000 Subject: [PATCH 18/50] Create new OpenTelemetry interceptors, keep old Tracing ones for backwards compatibility, move KafkaTelemetrySupplier Co-authored-by: trask <218610+trask@users.noreply.github.com> --- .../kafka-clients-2.6/library/README.md | 10 +- .../kafkaclients/v2_6/KafkaTelemetry.java | 23 ++--- .../v2_6}/KafkaTelemetrySupplier.java | 6 +- .../OpenTelemetryConsumerInterceptor.java | 92 +++++++++++++++++++ .../OpenTelemetryProducerInterceptor.java | 77 ++++++++++++++++ .../v2_6/TracingConsumerInterceptor.java | 51 ++-------- .../v2_6/TracingProducerInterceptor.java | 48 ++-------- .../v2_6/KafkaTelemetryInterceptorTest.java | 18 ++-- 8 files changed, 219 insertions(+), 106 deletions(-) rename instrumentation/kafka/kafka-clients/{kafka-clients-common-0.11/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal => kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6}/KafkaTelemetrySupplier.java (77%) create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/OpenTelemetryConsumerInterceptor.java create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/OpenTelemetryProducerInterceptor.java diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md index dcca20a5bbf5..22480c034082 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md @@ -70,18 +70,20 @@ Consumer consumer = new KafkaConsumer<>(props); If you don't explicitly configure the interceptors with a `KafkaTelemetry` instance, they will fall back to using `GlobalOpenTelemetry.get()` and system properties for configuration. -Use the `TracingProducerInterceptor` for the producer in order to create a "send" span automatically, each time a message is sent. +Use the `OpenTelemetryProducerInterceptor` for the producer in order to create a "send" span automatically, each time a message is sent. ```java -props.setProperty(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); +props.setProperty(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, OpenTelemetryProducerInterceptor.class.getName()); ``` -Use the `TracingConsumerInterceptor` for the consumer in order to create a "receive" span automatically, each time a message is received. +Use the `OpenTelemetryConsumerInterceptor` for the consumer in order to create a "receive" span automatically, each time a message is received. ```java -props.setProperty(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); +props.setProperty(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, OpenTelemetryConsumerInterceptor.class.getName()); ``` +Note: The `TracingProducerInterceptor` and `TracingConsumerInterceptor` classes are still available for backwards compatibility, but new code should use the `OpenTelemetry*` variants. + The interceptors will use the following system properties for configuration: - `otel.instrumentation.messaging.experimental.receive-telemetry.enabled` - Enable receive telemetry (default: false) - `otel.instrumentation.messaging.experimental.capture-headers` - List of headers to capture as span attributes diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java index a58e5a42ffd5..77f91e110afa 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java @@ -21,7 +21,6 @@ import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProcessRequest; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProducerRequest; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaReceiveRequest; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaTelemetrySupplier; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaUtil; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.MetricsReporterList; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetryMetricsReporter; @@ -211,9 +210,9 @@ ConsumerRecords addTracing( } /** - * Returns configuration properties that can be used to enable tracing via {@code - * TracingProducerInterceptor}. Add these resulting properties to the configuration map used to - * initialize a {@link org.apache.kafka.clients.producer.KafkaProducer}. + * Returns configuration properties that can be used to enable OpenTelemetry instrumentation via + * {@code OpenTelemetryProducerInterceptor}. Add these resulting properties to the configuration + * map used to initialize a {@link org.apache.kafka.clients.producer.KafkaProducer}. * *

Example usage: * @@ -230,17 +229,18 @@ ConsumerRecords addTracing( public Map producerInterceptorConfigProperties() { Map config = new HashMap<>(); config.put( - ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); + ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, + OpenTelemetryProducerInterceptor.class.getName()); config.put( - TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, new KafkaTelemetrySupplier(this)); return Collections.unmodifiableMap(config); } /** - * Returns configuration properties that can be used to enable tracing via {@code - * TracingConsumerInterceptor}. Add these resulting properties to the configuration map used to - * initialize a {@link org.apache.kafka.clients.consumer.KafkaConsumer}. + * Returns configuration properties that can be used to enable OpenTelemetry instrumentation via + * {@code OpenTelemetryConsumerInterceptor}. Add these resulting properties to the configuration + * map used to initialize a {@link org.apache.kafka.clients.consumer.KafkaConsumer}. * *

Example usage: * @@ -257,9 +257,10 @@ ConsumerRecords addTracing( public Map consumerInterceptorConfigProperties() { Map config = new HashMap<>(); config.put( - ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); + ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, + OpenTelemetryConsumerInterceptor.class.getName()); config.put( - TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, new KafkaTelemetrySupplier(this)); return Collections.unmodifiableMap(config); } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-common-0.11/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaTelemetrySupplier.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetrySupplier.java similarity index 77% rename from instrumentation/kafka/kafka-clients/kafka-clients-common-0.11/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaTelemetrySupplier.java rename to instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetrySupplier.java index 28ba44cae8ae..9d87a3b1af21 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-common-0.11/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/common/v0_11/internal/KafkaTelemetrySupplier.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetrySupplier.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal; +package io.opentelemetry.instrumentation.kafkaclients.v2_6; import java.io.Serializable; import java.util.Objects; @@ -16,11 +16,11 @@ *

This class is internal and is hence not for public use. Its APIs are unstable and can change * at any time. */ -public final class KafkaTelemetrySupplier implements Supplier, Serializable { +final class KafkaTelemetrySupplier implements Supplier, Serializable { private static final long serialVersionUID = 1L; private final transient Object kafkaTelemetry; - public KafkaTelemetrySupplier(Object kafkaTelemetry) { + KafkaTelemetrySupplier(Object kafkaTelemetry) { Objects.requireNonNull(kafkaTelemetry); this.kafkaTelemetry = kafkaTelemetry; } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/OpenTelemetryConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/OpenTelemetryConsumerInterceptor.java new file mode 100644 index 000000000000..5a98f18f71d7 --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/OpenTelemetryConsumerInterceptor.java @@ -0,0 +1,92 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.internal.Timer; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContext; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContextUtil; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; +import javax.annotation.Nullable; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.ConsumerInterceptor; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.OffsetAndMetadata; +import org.apache.kafka.common.TopicPartition; + +/** + * A ConsumerInterceptor that adds OpenTelemetry instrumentation. Add this interceptor's class name + * or class via ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG property to your Consumer's properties to + * get it instantiated and used. See more details on ConsumerInterceptor usage in its Javadoc. + * + *

To configure the interceptor, use {@link KafkaTelemetry#consumerInterceptorConfigProperties} + * to obtain the configuration properties and add them to your consumer configuration. + * + * @see KafkaTelemetry#consumerInterceptorConfigProperties() + */ +public class OpenTelemetryConsumerInterceptor implements ConsumerInterceptor { + + public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = + "opentelemetry.kafka-telemetry.supplier"; + + @Nullable private KafkaTelemetry telemetry; + private String consumerGroup; + private String clientId; + + @Override + @CanIgnoreReturnValue + public ConsumerRecords onConsume(ConsumerRecords records) { + if (telemetry == null) { + return records; + } + // timer should be started before fetching ConsumerRecords, but there is no callback for that + Timer timer = Timer.start(); + Context receiveContext = telemetry.buildAndFinishSpan(records, consumerGroup, clientId, timer); + if (receiveContext == null) { + receiveContext = Context.current(); + } + KafkaConsumerContext consumerContext = + KafkaConsumerContextUtil.create(receiveContext, consumerGroup, clientId); + return telemetry.addTracing(records, consumerContext); + } + + @Override + public void onCommit(Map offsets) {} + + @Override + public void close() {} + + @Override + public void configure(Map configs) { + consumerGroup = Objects.toString(configs.get(ConsumerConfig.GROUP_ID_CONFIG), null); + clientId = Objects.toString(configs.get(ConsumerConfig.CLIENT_ID_CONFIG), null); + + Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + if (telemetrySupplier == null) { + return; + } + + if (!(telemetrySupplier instanceof Supplier)) { + throw new IllegalStateException( + "Configuration property " + + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + + " is not instance of Supplier"); + } + + Object kafkaTelemetry = ((Supplier) telemetrySupplier).get(); + if (!(kafkaTelemetry instanceof KafkaTelemetry)) { + throw new IllegalStateException( + "Configuration property " + + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + + " supplier does not return KafkaTelemetry instance"); + } + + this.telemetry = (KafkaTelemetry) kafkaTelemetry; + } +} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/OpenTelemetryProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/OpenTelemetryProducerInterceptor.java new file mode 100644 index 000000000000..242a10eb5693 --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/OpenTelemetryProducerInterceptor.java @@ -0,0 +1,77 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; +import javax.annotation.Nullable; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerInterceptor; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; + +/** + * A ProducerInterceptor that adds OpenTelemetry instrumentation. Add this interceptor's class name + * or class via ProducerConfig.INTERCEPTOR_CLASSES_CONFIG property to your Producer's properties to + * get it instantiated and used. See more details on ProducerInterceptor usage in its Javadoc. + * + *

To configure the interceptor, use {@link KafkaTelemetry#producerInterceptorConfigProperties} + * to obtain the configuration properties and add them to your producer configuration. + * + * @see KafkaTelemetry#producerInterceptorConfigProperties() + */ +public class OpenTelemetryProducerInterceptor implements ProducerInterceptor { + + public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = + "opentelemetry.kafka-telemetry.supplier"; + + @Nullable private KafkaTelemetry telemetry; + @Nullable private String clientId; + + @Override + @CanIgnoreReturnValue + public ProducerRecord onSend(ProducerRecord producerRecord) { + if (telemetry != null) { + telemetry.buildAndInjectSpan(producerRecord, clientId); + } + return producerRecord; + } + + @Override + public void onAcknowledgement(RecordMetadata recordMetadata, Exception e) {} + + @Override + public void close() {} + + @Override + public void configure(Map configs) { + clientId = Objects.toString(configs.get(ProducerConfig.CLIENT_ID_CONFIG), null); + + Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + if (telemetrySupplier == null) { + return; + } + + if (!(telemetrySupplier instanceof Supplier)) { + throw new IllegalStateException( + "Configuration property " + + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + + " is not instance of Supplier"); + } + + Object kafkaTelemetry = ((Supplier) telemetrySupplier).get(); + if (!(kafkaTelemetry instanceof KafkaTelemetry)) { + throw new IllegalStateException( + "Configuration property " + + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER + + " supplier does not return KafkaTelemetry instance"); + } + + this.telemetry = (KafkaTelemetry) kafkaTelemetry; + } +} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java index 124a2467c744..8f60b07e6a88 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java @@ -16,8 +16,6 @@ import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContextUtil; import java.util.Map; import java.util.Objects; -import java.util.function.Supplier; -import javax.annotation.Nullable; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.ConsumerInterceptor; import org.apache.kafka.clients.consumer.ConsumerRecords; @@ -31,19 +29,22 @@ */ public class TracingConsumerInterceptor implements ConsumerInterceptor { - public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = - "opentelemetry.kafka-telemetry.supplier"; + private static final KafkaTelemetry telemetry = + KafkaTelemetry.builder(GlobalOpenTelemetry.get()) + .setMessagingReceiveInstrumentationEnabled( + ConfigPropertiesUtil.getBoolean( + "otel.instrumentation.messaging.experimental.receive-telemetry.enabled", false)) + .setCapturedHeaders( + ConfigPropertiesUtil.getList( + "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) + .build(); - @Nullable private KafkaTelemetry telemetry; private String consumerGroup; private String clientId; @Override @CanIgnoreReturnValue public ConsumerRecords onConsume(ConsumerRecords records) { - if (telemetry == null) { - return records; - } // timer should be started before fetching ConsumerRecords, but there is no callback for that Timer timer = Timer.start(); Context receiveContext = telemetry.buildAndFinishSpan(records, consumerGroup, clientId, timer); @@ -66,38 +67,6 @@ public void configure(Map configs) { consumerGroup = Objects.toString(configs.get(ConsumerConfig.GROUP_ID_CONFIG), null); clientId = Objects.toString(configs.get(ConsumerConfig.CLIENT_ID_CONFIG), null); - Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); - if (telemetrySupplier == null) { - // Fallback to GlobalOpenTelemetry if not configured - this.telemetry = - KafkaTelemetry.builder(GlobalOpenTelemetry.get()) - // TODO: remove now that programmatic configuration is available - .setMessagingReceiveInstrumentationEnabled( - ConfigPropertiesUtil.getBoolean( - "otel.instrumentation.messaging.experimental.receive-telemetry.enabled", - false)) - .setCapturedHeaders( - ConfigPropertiesUtil.getList( - "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) - .build(); - return; - } - - if (!(telemetrySupplier instanceof Supplier)) { - throw new IllegalStateException( - "Configuration property " - + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER - + " is not instance of Supplier"); - } - - Object kafkaTelemetry = ((Supplier) telemetrySupplier).get(); - if (!(kafkaTelemetry instanceof KafkaTelemetry)) { - throw new IllegalStateException( - "Configuration property " - + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER - + " supplier does not return KafkaTelemetry instance"); - } - - this.telemetry = (KafkaTelemetry) kafkaTelemetry; + // TODO: support experimental attributes config } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java index 006943ed5306..833b0785d181 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java @@ -12,7 +12,6 @@ import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; import java.util.Map; import java.util.Objects; -import java.util.function.Supplier; import javax.annotation.Nullable; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.clients.producer.ProducerInterceptor; @@ -26,18 +25,19 @@ */ public class TracingProducerInterceptor implements ProducerInterceptor { - public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = - "opentelemetry.kafka-telemetry.supplier"; + private static final KafkaTelemetry telemetry = + KafkaTelemetry.builder(GlobalOpenTelemetry.get()) + .setCapturedHeaders( + ConfigPropertiesUtil.getList( + "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) + .build(); - @Nullable private KafkaTelemetry telemetry; @Nullable private String clientId; @Override @CanIgnoreReturnValue public ProducerRecord onSend(ProducerRecord producerRecord) { - if (telemetry != null) { - telemetry.buildAndInjectSpan(producerRecord, clientId); - } + telemetry.buildAndInjectSpan(producerRecord, clientId); return producerRecord; } @@ -48,37 +48,9 @@ public void onAcknowledgement(RecordMetadata recordMetadata, Exception e) {} public void close() {} @Override - public void configure(Map configs) { - clientId = Objects.toString(configs.get(ProducerConfig.CLIENT_ID_CONFIG), null); + public void configure(Map map) { + clientId = Objects.toString(map.get(ProducerConfig.CLIENT_ID_CONFIG), null); - Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); - if (telemetrySupplier == null) { - // Fallback to GlobalOpenTelemetry if not configured - this.telemetry = - KafkaTelemetry.builder(GlobalOpenTelemetry.get()) - // TODO: remove now that programmatic configuration is available - .setCapturedHeaders( - ConfigPropertiesUtil.getList( - "otel.instrumentation.messaging.experimental.capture-headers", emptyList())) - .build(); - return; - } - - if (!(telemetrySupplier instanceof Supplier)) { - throw new IllegalStateException( - "Configuration property " - + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER - + " is not instance of Supplier"); - } - - Object kafkaTelemetry = ((Supplier) telemetrySupplier).get(); - if (!(kafkaTelemetry instanceof KafkaTelemetry)) { - throw new IllegalStateException( - "Configuration property " - + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER - + " supplier does not return KafkaTelemetry instance"); - } - - this.telemetry = (KafkaTelemetry) kafkaTelemetry; + // TODO: support experimental attributes config } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java index d870a9e133ea..82c4292bf70d 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java @@ -8,7 +8,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaTelemetrySupplier; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaTelemetrySupplier; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import java.io.ByteArrayInputStream; @@ -66,7 +66,7 @@ void badProducerConfig() { () -> { Map producerConfig = producerConfig(); producerConfig.put( - TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, "foo"); + OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, "foo"); new KafkaProducer<>(producerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) @@ -78,7 +78,7 @@ void badProducerConfig() { () -> { Map producerConfig = producerConfig(); producerConfig.put( - TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, (Supplier) () -> "not a KafkaTelemetry"); new KafkaProducer<>(producerConfig).close(); }) @@ -96,7 +96,7 @@ void badConsumerConfig() { () -> { Map consumerConfig = consumerConfig(); consumerConfig.put( - TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, "foo"); + OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, "foo"); new KafkaConsumer<>(consumerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) @@ -108,7 +108,7 @@ void badConsumerConfig() { () -> { Map consumerConfig = consumerConfig(); consumerConfig.put( - TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, (Supplier) () -> "not a KafkaTelemetry"); new KafkaConsumer<>(consumerConfig).close(); }) @@ -128,9 +128,9 @@ private static void testSerialize(Map map) throws IOException, ClassNotFoundException { // Check that producer config has the supplier Object producerSupplier = - map.get(TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + map.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); Object consumerSupplier = - map.get(TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + map.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); KafkaTelemetrySupplier supplier = null; if (producerSupplier instanceof KafkaTelemetrySupplier) { @@ -166,9 +166,9 @@ protected Class resolveClass(ObjectStreamClass desc) try (ObjectInputStream inputStream = new CustomObjectInputStream(new ByteArrayInputStream(byteOutputStream.toByteArray()))) { Map result = (Map) inputStream.readObject(); - assertThat(result.get(TracingProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER)) + assertThat(result.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER)) .isNull(); - assertThat(result.get(TracingConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER)) + assertThat(result.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER)) .isNull(); } } From 95d1723d1ca5d824adf6c1af0a1a178e7a552077 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Oct 2025 14:33:42 +0000 Subject: [PATCH 19/50] Mark TracingConsumerInterceptor and TracingProducerInterceptor as deprecated Co-authored-by: trask <218610+trask@users.noreply.github.com> --- .../kafkaclients/v2_6/TracingConsumerInterceptor.java | 5 +++++ .../kafkaclients/v2_6/TracingProducerInterceptor.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java index 8f60b07e6a88..82595c090eba 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java @@ -26,7 +26,12 @@ * A ConsumerInterceptor that adds tracing capability. Add this interceptor's class name or class * via ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG property to your Consumer's properties to get it * instantiated and used. See more details on ConsumerInterceptor usage in its Javadoc. + * + * @deprecated Use {@link OpenTelemetryConsumerInterceptor} instead. This class uses {@link + * GlobalOpenTelemetry} and is not configurable. The new class allows you to pass a configured + * {@link KafkaTelemetry} instance. */ +@Deprecated public class TracingConsumerInterceptor implements ConsumerInterceptor { private static final KafkaTelemetry telemetry = diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java index 833b0785d181..f0cbe4e8b5a1 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java @@ -22,7 +22,12 @@ * A ProducerInterceptor that adds tracing capability. Add this interceptor's class name or class * via ProducerConfig.INTERCEPTOR_CLASSES_CONFIG property to your Producer's properties to get it * instantiated and used. See more details on ProducerInterceptor usage in its Javadoc. + * + * @deprecated Use {@link OpenTelemetryProducerInterceptor} instead. This class uses {@link + * GlobalOpenTelemetry} and is not configurable. The new class allows you to pass a configured + * {@link KafkaTelemetry} instance. */ +@Deprecated public class TracingProducerInterceptor implements ProducerInterceptor { private static final KafkaTelemetry telemetry = From 3fdd463f965cf74a5c928698dc0c933469d7000e Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 8 Oct 2025 16:34:47 -0700 Subject: [PATCH 20/50] Update instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java --- .../kafkaclients/v2_6/TracingConsumerInterceptor.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java index 82595c090eba..8f9d060ff968 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java @@ -27,9 +27,7 @@ * via ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG property to your Consumer's properties to get it * instantiated and used. See more details on ConsumerInterceptor usage in its Javadoc. * - * @deprecated Use {@link OpenTelemetryConsumerInterceptor} instead. This class uses {@link - * GlobalOpenTelemetry} and is not configurable. The new class allows you to pass a configured - * {@link KafkaTelemetry} instance. + * @deprecated Use {@link OpenTelemetryConsumerInterceptor} instead. */ @Deprecated public class TracingConsumerInterceptor implements ConsumerInterceptor { From f72ae47dc36e90880ded048a128746f576eac5ab Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 8 Oct 2025 16:35:07 -0700 Subject: [PATCH 21/50] Update instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java --- .../kafkaclients/v2_6/TracingProducerInterceptor.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java index f0cbe4e8b5a1..ea631f78054d 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java @@ -23,9 +23,7 @@ * via ProducerConfig.INTERCEPTOR_CLASSES_CONFIG property to your Producer's properties to get it * instantiated and used. See more details on ProducerInterceptor usage in its Javadoc. * - * @deprecated Use {@link OpenTelemetryProducerInterceptor} instead. This class uses {@link - * GlobalOpenTelemetry} and is not configurable. The new class allows you to pass a configured - * {@link KafkaTelemetry} instance. + * @deprecated Use {@link OpenTelemetryProducerInterceptor} instead. */ @Deprecated public class TracingProducerInterceptor implements ProducerInterceptor { From 2bc5492934e9ca21cb257bb7314f155b528fa4be Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 8 Oct 2025 20:48:24 -0700 Subject: [PATCH 22/50] Test deprecated classes until they're removed --- .../AbstractDeprecatedInterceptorsTest.java | 95 ++++++++++++++ ...dInterceptorsSuppressReceiveSpansTest.java | 81 ++++++++++++ .../v2_6/DeprecatedInterceptorsTest.java | 118 ++++++++++++++++++ 3 files changed, 294 insertions(+) create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/DeprecatedInterceptorsSuppressReceiveSpansTest.java create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/DeprecatedInterceptorsTest.java diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java new file mode 100644 index 000000000000..a2a161917d64 --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java @@ -0,0 +1,95 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaClientBaseTest; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.Map; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +abstract class AbstractDeprecatedInterceptorsTest extends KafkaClientBaseTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + static final String greeting = "Hello Kafka!"; + + private static final KafkaTelemetry kafkaTelemetry = + KafkaTelemetry.create(testing.getOpenTelemetry()); + + @Override + public Map producerProps() { + Map props = super.producerProps(); + props.put( + ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); + props.putAll(kafkaTelemetry.producerInterceptorConfigProperties()); + return props; + } + + @Override + public Map consumerProps() { + Map props = super.consumerProps(); + props.put( + ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); + props.putAll(kafkaTelemetry.consumerInterceptorConfigProperties()); + return props; + } + + @Test + void testInterceptors() throws InterruptedException { + testing.runWithSpan( + "parent", + () -> { + ProducerRecord producerRecord = + new ProducerRecord<>(SHARED_TOPIC, greeting); + producerRecord + .headers() + // add header to test capturing header value as span attribute + .add("Test-Message-Header", "test".getBytes(StandardCharsets.UTF_8)) + // adding baggage header in w3c baggage format + .add( + "baggage", + "test-baggage-key-1=test-baggage-value-1".getBytes(StandardCharsets.UTF_8)) + .add( + "baggage", + "test-baggage-key-2=test-baggage-value-2".getBytes(StandardCharsets.UTF_8)); + producer.send( + producerRecord, + (meta, ex) -> { + if (ex == null) { + testing.runWithSpan("producer callback", () -> {}); + } else { + testing.runWithSpan("producer exception: " + ex, () -> {}); + } + }); + }); + + awaitUntilConsumerIsReady(); + // check that the message was received + ConsumerRecords records = consumer.poll(Duration.ofSeconds(5)); + assertThat(records.count()).isEqualTo(1); + for (ConsumerRecord record : records) { + assertThat(record.value()).isEqualTo(greeting); + assertThat(record.key()).isNull(); + testing.runWithSpan("process child", () -> {}); + } + + assertTraces(); + } + + abstract void assertTraces(); +} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/DeprecatedInterceptorsSuppressReceiveSpansTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/DeprecatedInterceptorsSuppressReceiveSpansTest.java new file mode 100644 index 000000000000..93554d5c7f5d --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/DeprecatedInterceptorsSuppressReceiveSpansTest.java @@ -0,0 +1,81 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_PARTITION_ID; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_KAFKA_CONSUMER_GROUP; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_KAFKA_MESSAGE_OFFSET; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.SpanKind; +import java.nio.charset.StandardCharsets; +import org.assertj.core.api.AbstractLongAssert; +import org.assertj.core.api.AbstractStringAssert; + +class DeprecatedInterceptorsSuppressReceiveSpansTest extends AbstractDeprecatedInterceptorsTest { + + @SuppressWarnings("deprecation") // using deprecated semconv + @Override + void assertTraces() { + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> + span.hasName(SHARED_TOPIC + " publish") + .hasKind(SpanKind.PRODUCER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo(MESSAGING_SYSTEM, "kafka"), + equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), + equalTo(MESSAGING_OPERATION, "publish"), + satisfies( + MESSAGING_CLIENT_ID, + stringAssert -> stringAssert.startsWith("producer"))), + span -> + span.hasName(SHARED_TOPIC + " process") + .hasKind(SpanKind.CONSUMER) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly( + equalTo(MESSAGING_SYSTEM, "kafka"), + equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), + equalTo(MESSAGING_OPERATION, "process"), + equalTo( + MESSAGING_MESSAGE_BODY_SIZE, + greeting.getBytes(StandardCharsets.UTF_8).length), + satisfies( + MESSAGING_DESTINATION_PARTITION_ID, + AbstractStringAssert::isNotEmpty), + satisfies( + MESSAGING_KAFKA_MESSAGE_OFFSET, AbstractLongAssert::isNotNegative), + equalTo(MESSAGING_KAFKA_CONSUMER_GROUP, "test"), + satisfies( + MESSAGING_CLIENT_ID, + stringAssert -> stringAssert.startsWith("consumer")), + equalTo( + AttributeKey.stringKey("test-baggage-key-1"), + "test-baggage-value-1"), + equalTo( + AttributeKey.stringKey("test-baggage-key-2"), + "test-baggage-value-2")), + span -> + span.hasName("process child") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(2))), + // ideally we'd want producer callback to be part of the main trace, we just aren't able to + // instrument that + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("producer callback").hasKind(SpanKind.INTERNAL).hasNoParent())); + } +} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/DeprecatedInterceptorsTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/DeprecatedInterceptorsTest.java new file mode 100644 index 000000000000..803a61f5b23d --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/DeprecatedInterceptorsTest.java @@ -0,0 +1,118 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6; + +import static io.opentelemetry.api.common.AttributeKey.stringArrayKey; +import static io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil.orderByRootSpanName; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_BATCH_MESSAGE_COUNT; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_PARTITION_ID; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_KAFKA_CONSUMER_GROUP; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_KAFKA_MESSAGE_OFFSET; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.sdk.trace.data.LinkData; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicReference; +import org.assertj.core.api.AbstractLongAssert; +import org.assertj.core.api.AbstractStringAssert; + +class DeprecatedInterceptorsTest extends AbstractDeprecatedInterceptorsTest { + + @SuppressWarnings("deprecation") // using deprecated semconv + @Override + void assertTraces() { + AtomicReference producerSpanContext = new AtomicReference<>(); + testing.waitAndAssertSortedTraces( + orderByRootSpanName("parent", SHARED_TOPIC + " receive", "producer callback"), + trace -> { + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> + span.hasName(SHARED_TOPIC + " publish") + .hasKind(SpanKind.PRODUCER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo( + stringArrayKey("messaging.header.Test_Message_Header"), + singletonList("test")), + equalTo(MESSAGING_SYSTEM, "kafka"), + equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), + equalTo(MESSAGING_OPERATION, "publish"), + satisfies( + MESSAGING_CLIENT_ID, + stringAssert -> stringAssert.startsWith("producer")))); + SpanContext spanContext = trace.getSpan(1).getSpanContext(); + producerSpanContext.set( + SpanContext.createFromRemoteParent( + spanContext.getTraceId(), + spanContext.getSpanId(), + spanContext.getTraceFlags(), + spanContext.getTraceState())); + }, + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName(SHARED_TOPIC + " receive") + .hasKind(SpanKind.CONSUMER) + .hasNoParent() + .hasLinksSatisfying(links -> assertThat(links).isEmpty()) + .hasAttributesSatisfyingExactly( + equalTo( + stringArrayKey("messaging.header.Test_Message_Header"), + singletonList("test")), + equalTo(MESSAGING_SYSTEM, "kafka"), + equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), + equalTo(MESSAGING_OPERATION, "receive"), + equalTo(MESSAGING_KAFKA_CONSUMER_GROUP, "test"), + satisfies( + MESSAGING_CLIENT_ID, + stringAssert -> stringAssert.startsWith("consumer")), + equalTo(MESSAGING_BATCH_MESSAGE_COUNT, 1)), + span -> + span.hasName(SHARED_TOPIC + " process") + .hasKind(SpanKind.CONSUMER) + .hasParent(trace.getSpan(0)) + .hasLinks(LinkData.create(producerSpanContext.get())) + .hasAttributesSatisfyingExactly( + equalTo( + stringArrayKey("messaging.header.Test_Message_Header"), + singletonList("test")), + equalTo(MESSAGING_SYSTEM, "kafka"), + equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), + equalTo(MESSAGING_OPERATION, "process"), + equalTo( + MESSAGING_MESSAGE_BODY_SIZE, + greeting.getBytes(StandardCharsets.UTF_8).length), + satisfies( + MESSAGING_DESTINATION_PARTITION_ID, + AbstractStringAssert::isNotEmpty), + satisfies( + MESSAGING_KAFKA_MESSAGE_OFFSET, AbstractLongAssert::isNotNegative), + equalTo(MESSAGING_KAFKA_CONSUMER_GROUP, "test"), + satisfies( + MESSAGING_CLIENT_ID, + stringAssert -> stringAssert.startsWith("consumer"))), + span -> + span.hasName("process child") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1))), + // ideally we'd want producer callback to be part of the main trace, we just aren't able to + // instrument that + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("producer callback").hasKind(SpanKind.INTERNAL).hasNoParent())); + } +} From f24a0a4aa7a9d5522631b9cbe6096681afc9019e Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Thu, 9 Oct 2025 10:24:49 -0700 Subject: [PATCH 23/50] fix --- .../kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java index 82c4292bf70d..48722b78543c 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java @@ -8,7 +8,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaTelemetrySupplier; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import java.io.ByteArrayInputStream; From 45c75100cb8a3c1932ca203d52cdd70a9d09d4ef Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Thu, 9 Oct 2025 10:27:59 -0700 Subject: [PATCH 24/50] fix --- .../kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java index a2a161917d64..4439e2fc8ccd 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java @@ -31,6 +31,7 @@ abstract class AbstractDeprecatedInterceptorsTest extends KafkaClientBaseTest { private static final KafkaTelemetry kafkaTelemetry = KafkaTelemetry.create(testing.getOpenTelemetry()); + @SuppressWarnings("deprecation") // testing deprecated interceptors @Override public Map producerProps() { Map props = super.producerProps(); @@ -40,6 +41,7 @@ public Map producerProps() { return props; } + @SuppressWarnings("deprecation") // testing deprecated interceptors @Override public Map consumerProps() { Map props = super.consumerProps(); From 85aaaaa9458f1413ed5afc46a7e19aa836af8b9d Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Thu, 9 Oct 2025 12:00:45 -0700 Subject: [PATCH 25/50] fix --- .../library/build.gradle.kts | 20 +++++++++---------- .../AbstractDeprecatedInterceptorsTest.java | 6 +----- .../v2_6/AbstractInterceptorsTest.java | 4 +++- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/build.gradle.kts b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/build.gradle.kts index b23528d55646..03a7afa4f5a5 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/build.gradle.kts +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/build.gradle.kts @@ -22,27 +22,25 @@ tasks { systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) } - val testReceiveSpansDisabled by registering(Test::class) { - testClassesDirs = sourceSets.test.get().output.classesDirs - classpath = sourceSets.test.get().runtimeClasspath + test { filter { - includeTestsMatching("InterceptorsSuppressReceiveSpansTest") - includeTestsMatching("WrapperSuppressReceiveSpansTest") + excludeTestsMatching("*DeprecatedInterceptorsTest") } - include("**/InterceptorsSuppressReceiveSpansTest.*", "**/WrapperSuppressReceiveSpansTest.*") + systemProperty("otel.instrumentation.messaging.experimental.capture-headers", "Test-Message-Header") } - test { + val testDeprecated by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath filter { - excludeTestsMatching("InterceptorsSuppressReceiveSpansTest") - excludeTestsMatching("WrapperSuppressReceiveSpansTest") + includeTestsMatching("*DeprecatedInterceptorsTest") } - jvmArgs("-Dotel.instrumentation.messaging.experimental.receive-telemetry.enabled=true") + systemProperty("otel.instrumentation.messaging.experimental.receive-telemetry.enabled", "true") systemProperty("otel.instrumentation.messaging.experimental.capture-headers", "Test-Message-Header") } check { - dependsOn(testReceiveSpansDisabled) + dependsOn(testDeprecated) } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java index 4439e2fc8ccd..92ea47459e60 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java @@ -28,16 +28,12 @@ abstract class AbstractDeprecatedInterceptorsTest extends KafkaClientBaseTest { static final String greeting = "Hello Kafka!"; - private static final KafkaTelemetry kafkaTelemetry = - KafkaTelemetry.create(testing.getOpenTelemetry()); - @SuppressWarnings("deprecation") // testing deprecated interceptors @Override public Map producerProps() { Map props = super.producerProps(); props.put( ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); - props.putAll(kafkaTelemetry.producerInterceptorConfigProperties()); return props; } @@ -47,7 +43,6 @@ public Map consumerProps() { Map props = super.consumerProps(); props.put( ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); - props.putAll(kafkaTelemetry.consumerInterceptorConfigProperties()); return props; } @@ -95,3 +90,4 @@ void testInterceptors() throws InterruptedException { abstract void assertTraces(); } + diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java index 2c3f87b2acd1..c746dd7806a9 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java @@ -27,7 +27,9 @@ abstract class AbstractInterceptorsTest extends KafkaClientBaseTest { static final String greeting = "Hello Kafka!"; private static final KafkaTelemetry kafkaTelemetry = - KafkaTelemetry.create(testing.getOpenTelemetry()); + KafkaTelemetry.builder(testing.getOpenTelemetry()) + .setMessagingReceiveInstrumentationEnabled(true) + .build(); @Override public Map producerProps() { From 9b9b0757b81ab825bf5263cc8770e296e3339299 Mon Sep 17 00:00:00 2001 From: otelbot <197425009+otelbot@users.noreply.github.com> Date: Thu, 9 Oct 2025 19:09:56 +0000 Subject: [PATCH 26/50] ./gradlew spotlessApply --- .../kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java index 92ea47459e60..81a161ab6541 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractDeprecatedInterceptorsTest.java @@ -90,4 +90,3 @@ void testInterceptors() throws InterruptedException { abstract void assertTraces(); } - From 6aa63066b3992161200c5a92c05ca7e2779dc351 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Thu, 9 Oct 2025 14:01:43 -0700 Subject: [PATCH 27/50] fix --- .../kafka-clients-2.6/library/build.gradle.kts | 13 ++++++++++--- .../kafkaclients/v2_6/AbstractInterceptorsTest.java | 9 +++------ .../v2_6/InterceptorsSuppressReceiveSpansTest.java | 8 ++++++++ .../kafkaclients/v2_6/InterceptorsTest.java | 11 +++++++++++ 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/build.gradle.kts b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/build.gradle.kts index 03a7afa4f5a5..b449cfb467a7 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/build.gradle.kts +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/build.gradle.kts @@ -24,9 +24,8 @@ tasks { test { filter { - excludeTestsMatching("*DeprecatedInterceptorsTest") + excludeTestsMatching("*Deprecated*") } - systemProperty("otel.instrumentation.messaging.experimental.capture-headers", "Test-Message-Header") } val testDeprecated by registering(Test::class) { @@ -39,8 +38,16 @@ tasks { systemProperty("otel.instrumentation.messaging.experimental.capture-headers", "Test-Message-Header") } + val testDeprecatedSuppressReceiveSpans by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + filter { + includeTestsMatching("*DeprecatedInterceptorsSuppressReceiveSpansTest") + } + } + check { - dependsOn(testDeprecated) + dependsOn(testDeprecated, testDeprecatedSuppressReceiveSpans) } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java index c746dd7806a9..402fada6c6ca 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractInterceptorsTest.java @@ -26,22 +26,19 @@ abstract class AbstractInterceptorsTest extends KafkaClientBaseTest { static final String greeting = "Hello Kafka!"; - private static final KafkaTelemetry kafkaTelemetry = - KafkaTelemetry.builder(testing.getOpenTelemetry()) - .setMessagingReceiveInstrumentationEnabled(true) - .build(); + protected abstract KafkaTelemetry kafkaTelemetry(); @Override public Map producerProps() { Map props = super.producerProps(); - props.putAll(kafkaTelemetry.producerInterceptorConfigProperties()); + props.putAll(kafkaTelemetry().producerInterceptorConfigProperties()); return props; } @Override public Map consumerProps() { Map props = super.consumerProps(); - props.putAll(kafkaTelemetry.consumerInterceptorConfigProperties()); + props.putAll(kafkaTelemetry().consumerInterceptorConfigProperties()); return props; } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsSuppressReceiveSpansTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsSuppressReceiveSpansTest.java index 049020a2d074..44d1603743b1 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsSuppressReceiveSpansTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsSuppressReceiveSpansTest.java @@ -23,6 +23,14 @@ class InterceptorsSuppressReceiveSpansTest extends AbstractInterceptorsTest { + private static final KafkaTelemetry kafkaTelemetry = + KafkaTelemetry.create(testing.getOpenTelemetry()); + + @Override + protected KafkaTelemetry kafkaTelemetry() { + return kafkaTelemetry; + } + @SuppressWarnings("deprecation") // using deprecated semconv @Override void assertTraces() { diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsTest.java index f8003095032d..3e42dce07622 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/InterceptorsTest.java @@ -30,6 +30,17 @@ class InterceptorsTest extends AbstractInterceptorsTest { + private static final KafkaTelemetry kafkaTelemetry = + KafkaTelemetry.builder(testing.getOpenTelemetry()) + .setMessagingReceiveInstrumentationEnabled(true) + .setCapturedHeaders(singletonList("Test-Message-Header")) + .build(); + + @Override + protected KafkaTelemetry kafkaTelemetry() { + return kafkaTelemetry; + } + @SuppressWarnings("deprecation") // using deprecated semconv @Override void assertTraces() { From a541ee6db079e2bac096d5ed07023c3628cc4c2e Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Thu, 9 Oct 2025 14:10:33 -0700 Subject: [PATCH 28/50] fix --- .../kafka-clients/kafka-clients-2.6/library/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/build.gradle.kts b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/build.gradle.kts index b449cfb467a7..5fe805b69792 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/build.gradle.kts +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/build.gradle.kts @@ -34,6 +34,7 @@ tasks { filter { includeTestsMatching("*DeprecatedInterceptorsTest") } + forkEvery = 1 // to avoid system properties polluting other tests systemProperty("otel.instrumentation.messaging.experimental.receive-telemetry.enabled", "true") systemProperty("otel.instrumentation.messaging.experimental.capture-headers", "Test-Message-Header") } From 06184d16e881a2a0ed8ace6af26de256b6b26889 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Thu, 9 Oct 2025 16:15:14 -0700 Subject: [PATCH 29/50] docs --- .../kafka-clients-2.6/library/README.md | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md index 22480c034082..ad883e01b1d5 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md @@ -35,11 +35,6 @@ The Kafka clients API provides a way to "intercept" messages before they are sen The OpenTelemetry instrumented Kafka library provides two interceptors to be configured to add tracing information automatically. The interceptor class has to be set in the properties bag used to create the Kafka client. -##### Recommended approach: Configuring interceptors with KafkaTelemetry - -The recommended way to use interceptors is to configure them with a `KafkaTelemetry` instance. -Interceptors will use system properties for additional configuration like captured headers and receive telemetry settings. - For the producer: ```java @@ -65,29 +60,6 @@ props.putAll(telemetry.consumerInterceptorConfigProperties()); Consumer consumer = new KafkaConsumer<>(props); ``` -##### Alternative: Using interceptors with global OpenTelemetry - -If you don't explicitly configure the interceptors with a `KafkaTelemetry` instance, they will fall back to using -`GlobalOpenTelemetry.get()` and system properties for configuration. - -Use the `OpenTelemetryProducerInterceptor` for the producer in order to create a "send" span automatically, each time a message is sent. - -```java -props.setProperty(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, OpenTelemetryProducerInterceptor.class.getName()); -``` - -Use the `OpenTelemetryConsumerInterceptor` for the consumer in order to create a "receive" span automatically, each time a message is received. - -```java -props.setProperty(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, OpenTelemetryConsumerInterceptor.class.getName()); -``` - -Note: The `TracingProducerInterceptor` and `TracingConsumerInterceptor` classes are still available for backwards compatibility, but new code should use the `OpenTelemetry*` variants. - -The interceptors will use the following system properties for configuration: -- `otel.instrumentation.messaging.experimental.receive-telemetry.enabled` - Enable receive telemetry (default: false) -- `otel.instrumentation.messaging.experimental.capture-headers` - List of headers to capture as span attributes - #### Wrapping clients The other way is by wrapping the Kafka client with a tracing enabled Kafka client. From 30bc6dd6b6edf75afdccc2414b99b7d2aca28361 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Thu, 9 Oct 2025 20:04:27 -0700 Subject: [PATCH 30/50] internal --- .../kafka-clients-2.6/library/README.md | 8 +++----- .../kafkaclients/v2_6/KafkaTelemetry.java | 2 ++ .../OpenTelemetryConsumerInterceptor.java | 13 +++++-------- .../OpenTelemetryProducerInterceptor.java | 13 +++++-------- .../v2_6/KafkaTelemetryInterceptorTest.java | 2 ++ 5 files changed, 17 insertions(+), 21 deletions(-) rename instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/{ => internal}/OpenTelemetryConsumerInterceptor.java (84%) rename instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/{ => internal}/OpenTelemetryProducerInterceptor.java (79%) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md index ad883e01b1d5..b22e6d9fcf85 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/README.md @@ -31,11 +31,9 @@ There are two options for capturing traces, either using interceptors or wrappin #### Using interceptors -The Kafka clients API provides a way to "intercept" messages before they are sent to the brokers as well as messages received from the broker before being passed to the application. -The OpenTelemetry instrumented Kafka library provides two interceptors to be configured to add tracing information automatically. -The interceptor class has to be set in the properties bag used to create the Kafka client. +The Kafka clients API provides a way to intercept messages before they are sent to the brokers as well as messages received from the broker before being passed to the application. -For the producer: +To intercept messages and emit telemetry for a `KafkaProducer`: ```java KafkaTelemetry telemetry = KafkaTelemetry.create(openTelemetry); @@ -47,7 +45,7 @@ props.putAll(telemetry.producerInterceptorConfigProperties()); Producer producer = new KafkaProducer<>(props); ``` -For the consumer: +To intercept messages and emit telemetry for a `KafkaConsumer`: ```java KafkaTelemetry telemetry = KafkaTelemetry.create(openTelemetry); diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java index 77f91e110afa..28e3a414f7fd 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java @@ -26,6 +26,8 @@ import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetryMetricsReporter; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetrySupplier; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.TracingList; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryConsumerInterceptor; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryProducerInterceptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; import java.util.Collections; diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/OpenTelemetryConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java similarity index 84% rename from instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/OpenTelemetryConsumerInterceptor.java rename to instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java index 5a98f18f71d7..009255bc71b7 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/OpenTelemetryConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java @@ -3,13 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.kafkaclients.v2_6; +package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.internal.Timer; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContext; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContextUtil; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry; import java.util.Map; import java.util.Objects; import java.util.function.Supplier; @@ -21,14 +22,10 @@ import org.apache.kafka.common.TopicPartition; /** - * A ConsumerInterceptor that adds OpenTelemetry instrumentation. Add this interceptor's class name - * or class via ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG property to your Consumer's properties to - * get it instantiated and used. See more details on ConsumerInterceptor usage in its Javadoc. + * A ConsumerInterceptor that adds OpenTelemetry instrumentation. * - *

To configure the interceptor, use {@link KafkaTelemetry#consumerInterceptorConfigProperties} - * to obtain the configuration properties and add them to your consumer configuration. - * - * @see KafkaTelemetry#consumerInterceptorConfigProperties() + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. */ public class OpenTelemetryConsumerInterceptor implements ConsumerInterceptor { diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/OpenTelemetryProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java similarity index 79% rename from instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/OpenTelemetryProducerInterceptor.java rename to instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java index 242a10eb5693..6bc5fc10b8a1 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/OpenTelemetryProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java @@ -3,9 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.kafkaclients.v2_6; +package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry; import java.util.Map; import java.util.Objects; import java.util.function.Supplier; @@ -16,14 +17,10 @@ import org.apache.kafka.clients.producer.RecordMetadata; /** - * A ProducerInterceptor that adds OpenTelemetry instrumentation. Add this interceptor's class name - * or class via ProducerConfig.INTERCEPTOR_CLASSES_CONFIG property to your Producer's properties to - * get it instantiated and used. See more details on ProducerInterceptor usage in its Javadoc. + * A ProducerInterceptor that adds OpenTelemetry instrumentation. * - *

To configure the interceptor, use {@link KafkaTelemetry#producerInterceptorConfigProperties} - * to obtain the configuration properties and add them to your producer configuration. - * - * @see KafkaTelemetry#producerInterceptorConfigProperties() + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. */ public class OpenTelemetryProducerInterceptor implements ProducerInterceptor { diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java index 48722b78543c..3ad48f0ce5d6 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java @@ -8,6 +8,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryConsumerInterceptor; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryProducerInterceptor; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import java.io.ByteArrayInputStream; From b207b258f20ffc6d3271e4074bf5ec00f481d6dc Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Thu, 9 Oct 2025 20:34:08 -0700 Subject: [PATCH 31/50] simplify --- .../kafkaclients/v2_6/KafkaTelemetry.java | 1 + .../KafkaTelemetrySupplier.java | 11 ++++---- .../OpenTelemetryConsumerInterceptor.java | 27 +++++++++---------- .../OpenTelemetryProducerInterceptor.java | 27 +++++++++---------- .../v2_6/KafkaTelemetryInterceptorTest.java | 1 + 5 files changed, 34 insertions(+), 33 deletions(-) rename instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/{ => internal}/KafkaTelemetrySupplier.java (64%) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java index 28e3a414f7fd..3f098edfa8e9 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java @@ -26,6 +26,7 @@ import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetryMetricsReporter; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetrySupplier; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.TracingList; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaTelemetrySupplier; import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryConsumerInterceptor; import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryProducerInterceptor; import java.lang.reflect.InvocationTargetException; diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetrySupplier.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaTelemetrySupplier.java similarity index 64% rename from instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetrySupplier.java rename to instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaTelemetrySupplier.java index 9d87a3b1af21..29fde5a98262 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetrySupplier.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaTelemetrySupplier.java @@ -3,8 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.kafkaclients.v2_6; +package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry; import java.io.Serializable; import java.util.Objects; import java.util.function.Supplier; @@ -16,17 +17,17 @@ *

This class is internal and is hence not for public use. Its APIs are unstable and can change * at any time. */ -final class KafkaTelemetrySupplier implements Supplier, Serializable { +public final class KafkaTelemetrySupplier implements Supplier, Serializable { private static final long serialVersionUID = 1L; - private final transient Object kafkaTelemetry; + private final transient KafkaTelemetry kafkaTelemetry; - KafkaTelemetrySupplier(Object kafkaTelemetry) { + public KafkaTelemetrySupplier(KafkaTelemetry kafkaTelemetry) { Objects.requireNonNull(kafkaTelemetry); this.kafkaTelemetry = kafkaTelemetry; } @Override - public Object get() { + public KafkaTelemetry get() { return kafkaTelemetry; } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java index 009255bc71b7..98b3e91aa32f 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java @@ -13,7 +13,6 @@ import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry; import java.util.Map; import java.util.Objects; -import java.util.function.Supplier; import javax.annotation.Nullable; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.ConsumerInterceptor; @@ -69,21 +68,21 @@ public void configure(Map configs) { return; } - if (!(telemetrySupplier instanceof Supplier)) { - throw new IllegalStateException( - "Configuration property " - + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER - + " is not instance of Supplier"); - } + KafkaTelemetrySupplier supplier = + getProperty(configs, CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, KafkaTelemetrySupplier.class); + this.telemetry = supplier.get(); + } - Object kafkaTelemetry = ((Supplier) telemetrySupplier).get(); - if (!(kafkaTelemetry instanceof KafkaTelemetry)) { + @SuppressWarnings("unchecked") + private static T getProperty(Map configs, String key, Class requiredType) { + Object value = configs.get(key); + if (value == null) { + throw new IllegalStateException("Missing required configuration property: " + key); + } + if (!requiredType.isInstance(value)) { throw new IllegalStateException( - "Configuration property " - + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER - + " supplier does not return KafkaTelemetry instance"); + "Configuration property " + key + " is not instance of " + requiredType.getSimpleName()); } - - this.telemetry = (KafkaTelemetry) kafkaTelemetry; + return (T) value; } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java index 6bc5fc10b8a1..d3cae56fe684 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java @@ -9,7 +9,6 @@ import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry; import java.util.Map; import java.util.Objects; -import java.util.function.Supplier; import javax.annotation.Nullable; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.clients.producer.ProducerInterceptor; @@ -54,21 +53,21 @@ public void configure(Map configs) { return; } - if (!(telemetrySupplier instanceof Supplier)) { - throw new IllegalStateException( - "Configuration property " - + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER - + " is not instance of Supplier"); - } + KafkaTelemetrySupplier supplier = + getProperty(configs, CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, KafkaTelemetrySupplier.class); + this.telemetry = supplier.get(); + } - Object kafkaTelemetry = ((Supplier) telemetrySupplier).get(); - if (!(kafkaTelemetry instanceof KafkaTelemetry)) { + @SuppressWarnings("unchecked") + private static T getProperty(Map configs, String key, Class requiredType) { + Object value = configs.get(key); + if (value == null) { + throw new IllegalStateException("Missing required configuration property: " + key); + } + if (!requiredType.isInstance(value)) { throw new IllegalStateException( - "Configuration property " - + CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER - + " supplier does not return KafkaTelemetry instance"); + "Configuration property " + key + " is not instance of " + requiredType.getSimpleName()); } - - this.telemetry = (KafkaTelemetry) kafkaTelemetry; + return (T) value; } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java index 3ad48f0ce5d6..d470fcf69b4f 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java @@ -8,6 +8,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaTelemetrySupplier; import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryConsumerInterceptor; import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryProducerInterceptor; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; From a2b44cc81add2c3a245143a48a45f3681125d772 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 11:13:51 -0700 Subject: [PATCH 32/50] Apply suggestions from code review --- .../v2_6/internal/OpenTelemetryConsumerInterceptor.java | 5 ----- .../v2_6/internal/OpenTelemetryProducerInterceptor.java | 5 ----- 2 files changed, 10 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java index 98b3e91aa32f..77110b3cd100 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java @@ -63,11 +63,6 @@ public void configure(Map configs) { consumerGroup = Objects.toString(configs.get(ConsumerConfig.GROUP_ID_CONFIG), null); clientId = Objects.toString(configs.get(ConsumerConfig.CLIENT_ID_CONFIG), null); - Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); - if (telemetrySupplier == null) { - return; - } - KafkaTelemetrySupplier supplier = getProperty(configs, CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, KafkaTelemetrySupplier.class); this.telemetry = supplier.get(); diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java index d3cae56fe684..5a3c812b1035 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java @@ -48,11 +48,6 @@ public void close() {} public void configure(Map configs) { clientId = Objects.toString(configs.get(ProducerConfig.CLIENT_ID_CONFIG), null); - Object telemetrySupplier = configs.get(CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); - if (telemetrySupplier == null) { - return; - } - KafkaTelemetrySupplier supplier = getProperty(configs, CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, KafkaTelemetrySupplier.class); this.telemetry = supplier.get(); From d861b8814c92cb9c3261b7a6260aea84333d3084 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 13:14:34 -0700 Subject: [PATCH 33/50] KafkaHelper --- .../kafkaclients/v2_6/KafkaHelper.java | 125 +++++++++ .../kafkaclients/v2_6/KafkaTelemetry.java | 254 ++---------------- .../v2_6/TracingConsumerInterceptor.java | 5 +- .../v2_6/TracingProducerInterceptor.java | 2 +- .../v2_6/internal/KafkaHelperSupplier.java | 38 +++ .../OpenTelemetryConsumerInterceptor.java | 20 +- .../OpenTelemetryProducerInterceptor.java | 18 +- 7 files changed, 204 insertions(+), 258 deletions(-) create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaHelper.java create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaHelperSupplier.java diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaHelper.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaHelper.java new file mode 100644 index 000000000000..efaaf9d4f84a --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaHelper.java @@ -0,0 +1,125 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6; + +import static java.util.logging.Level.WARNING; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.context.propagation.TextMapSetter; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.internal.InstrumenterUtil; +import io.opentelemetry.instrumentation.api.internal.Timer; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContext; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaHeadersSetter; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProcessRequest; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProducerRequest; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaReceiveRequest; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.TracingList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; +import org.apache.kafka.common.TopicPartition; +import org.apache.kafka.common.header.Headers; + +public final class KafkaHelper { + private static final Logger logger = Logger.getLogger(KafkaHelper.class.getName()); + + private static final TextMapSetter SETTER = KafkaHeadersSetter.INSTANCE; + + private final TextMapPropagator propagator; + private final Instrumenter producerInstrumenter; + private final Instrumenter consumerReceiveInstrumenter; + private final Instrumenter consumerProcessInstrumenter; + private final boolean producerPropagationEnabled; + + KafkaHelper( + TextMapPropagator propagator, + Instrumenter producerInstrumenter, + Instrumenter consumerReceiveInstrumenter, + Instrumenter consumerProcessInstrumenter, + boolean producerPropagationEnabled) { + this.propagator = propagator; + this.producerInstrumenter = producerInstrumenter; + this.consumerReceiveInstrumenter = consumerReceiveInstrumenter; + this.consumerProcessInstrumenter = consumerProcessInstrumenter; + this.producerPropagationEnabled = producerPropagationEnabled; + } + + /** + * Build and inject span into record. + * + * @param record the producer record to inject span info. + */ + public void buildAndInjectSpan(ProducerRecord record, String clientId) { + Context parentContext = Context.current(); + + KafkaProducerRequest request = KafkaProducerRequest.create(record, clientId); + if (!producerInstrumenter.shouldStart(parentContext, request)) { + return; + } + + Context context = producerInstrumenter.start(parentContext, request); + if (producerPropagationEnabled) { + try { + propagator.inject(context, record.headers(), SETTER); + } catch (Throwable t) { + // it can happen if headers are read only (when record is sent second time) + logger.log(WARNING, "failed to inject span context. sending record second time?", t); + } + } + producerInstrumenter.end(context, request, null, null); + } + + public Context buildAndFinishSpan( + ConsumerRecords records, String consumerGroup, String clientId, Timer timer) { + if (records.isEmpty()) { + return null; + } + Context parentContext = Context.current(); + KafkaReceiveRequest request = KafkaReceiveRequest.create(records, consumerGroup, clientId); + Context context = null; + if (consumerReceiveInstrumenter.shouldStart(parentContext, request)) { + context = + InstrumenterUtil.startAndEnd( + consumerReceiveInstrumenter, + parentContext, + request, + null, + null, + timer.startTime(), + timer.now()); + } + + // we're returning the context of the receive span so that process spans can use it as + // parent context even though the span has ended + // this is the suggested behavior according to the spec batch receive scenario: + // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/messaging/messaging-spans.md#batch-receiving + return context; + } + + public ConsumerRecords addTracing( + ConsumerRecords consumerRecords, KafkaConsumerContext consumerContext) { + if (consumerRecords.isEmpty()) { + return consumerRecords; + } + + Map>> records = new LinkedHashMap<>(); + for (TopicPartition partition : consumerRecords.partitions()) { + List> list = consumerRecords.records(partition); + if (list != null && !list.isEmpty()) { + list = TracingList.wrap(list, consumerProcessInstrumenter, () -> true, consumerContext); + } + records.put(partition, list); + } + return new ConsumerRecords<>(records); + } +} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java index 3f098edfa8e9..5bf11315d54e 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java @@ -5,66 +5,32 @@ package io.opentelemetry.instrumentation.kafkaclients.v2_6; -import static java.util.logging.Level.WARNING; - import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; -import io.opentelemetry.context.propagation.TextMapPropagator; -import io.opentelemetry.context.propagation.TextMapSetter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.internal.InstrumenterUtil; -import io.opentelemetry.instrumentation.api.internal.Timer; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContext; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContextUtil; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaHeadersSetter; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProcessRequest; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProducerRequest; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaReceiveRequest; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaUtil; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.MetricsReporterList; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetryMetricsReporter; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetrySupplier; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.TracingList; -import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaTelemetrySupplier; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaHelperSupplier; import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryConsumerInterceptor; import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryProducerInterceptor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Proxy; import java.util.Collections; import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; -import java.util.concurrent.Future; -import java.util.function.BiFunction; -import java.util.logging.Logger; import org.apache.kafka.clients.CommonClientConfigs; -import org.apache.kafka.clients.consumer.Consumer; import org.apache.kafka.clients.consumer.ConsumerConfig; -import org.apache.kafka.clients.consumer.ConsumerRecord; -import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; -import org.apache.kafka.clients.producer.Callback; import org.apache.kafka.clients.producer.KafkaProducer; -import org.apache.kafka.clients.producer.Producer; import org.apache.kafka.clients.producer.ProducerConfig; -import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.clients.producer.RecordMetadata; -import org.apache.kafka.common.TopicPartition; -import org.apache.kafka.common.header.Headers; import org.apache.kafka.common.metrics.MetricsReporter; public final class KafkaTelemetry { - private static final Logger logger = Logger.getLogger(KafkaTelemetry.class.getName()); - - private static final TextMapSetter SETTER = KafkaHeadersSetter.INSTANCE; private final OpenTelemetry openTelemetry; - private final Instrumenter producerInstrumenter; - private final Instrumenter consumerReceiveInstrumenter; - private final Instrumenter consumerProcessInstrumenter; - private final boolean producerPropagationEnabled; + private final KafkaHelper helper; KafkaTelemetry( OpenTelemetry openTelemetry, @@ -73,10 +39,18 @@ public final class KafkaTelemetry { Instrumenter consumerProcessInstrumenter, boolean producerPropagationEnabled) { this.openTelemetry = openTelemetry; - this.producerInstrumenter = producerInstrumenter; - this.consumerReceiveInstrumenter = consumerReceiveInstrumenter; - this.consumerProcessInstrumenter = consumerProcessInstrumenter; - this.producerPropagationEnabled = producerPropagationEnabled; + this.helper = + new KafkaHelper( + openTelemetry.getPropagators().getTextMapPropagator(), + producerInstrumenter, + consumerReceiveInstrumenter, + consumerProcessInstrumenter, + producerPropagationEnabled); + } + + @Deprecated + KafkaHelper getHelper() { + return helper; } /** Returns a new {@link KafkaTelemetry} configured with the given {@link OpenTelemetry}. */ @@ -91,87 +65,6 @@ public static KafkaTelemetryBuilder builder(OpenTelemetry openTelemetry) { return new KafkaTelemetryBuilder(openTelemetry); } - private TextMapPropagator propagator() { - return openTelemetry.getPropagators().getTextMapPropagator(); - } - - /** Returns a decorated {@link Producer} that emits spans for each sent message. */ - @SuppressWarnings("unchecked") - public Producer wrap(Producer producer) { - return (Producer) - Proxy.newProxyInstance( - KafkaTelemetry.class.getClassLoader(), - new Class[] {Producer.class}, - (proxy, method, args) -> { - // Future send(ProducerRecord record) - // Future send(ProducerRecord record, Callback callback) - if ("send".equals(method.getName()) - && method.getParameterCount() >= 1 - && method.getParameterTypes()[0] == ProducerRecord.class) { - ProducerRecord record = (ProducerRecord) args[0]; - Callback callback = - method.getParameterCount() >= 2 - && method.getParameterTypes()[1] == Callback.class - ? (Callback) args[1] - : null; - return buildAndInjectSpan(record, producer, callback, producer::send); - } - try { - return method.invoke(producer, args); - } catch (InvocationTargetException exception) { - throw exception.getCause(); - } - }); - } - - /** Returns a decorated {@link Consumer} that consumes spans for each received message. */ - @SuppressWarnings("unchecked") - public Consumer wrap(Consumer consumer) { - return (Consumer) - Proxy.newProxyInstance( - KafkaTelemetry.class.getClassLoader(), - new Class[] {Consumer.class}, - (proxy, method, args) -> { - Object result; - Timer timer = "poll".equals(method.getName()) ? Timer.start() : null; - try { - result = method.invoke(consumer, args); - } catch (InvocationTargetException exception) { - throw exception.getCause(); - } - // ConsumerRecords poll(long timeout) - // ConsumerRecords poll(Duration duration) - if ("poll".equals(method.getName()) && result instanceof ConsumerRecords) { - ConsumerRecords consumerRecords = (ConsumerRecords) result; - Context receiveContext = buildAndFinishSpan(consumerRecords, consumer, timer); - if (receiveContext == null) { - receiveContext = Context.current(); - } - KafkaConsumerContext consumerContext = - KafkaConsumerContextUtil.create(receiveContext, consumer); - result = addTracing(consumerRecords, consumerContext); - } - return result; - }); - } - - ConsumerRecords addTracing( - ConsumerRecords consumerRecords, KafkaConsumerContext consumerContext) { - if (consumerRecords.isEmpty()) { - return consumerRecords; - } - - Map>> records = new LinkedHashMap<>(); - for (TopicPartition partition : consumerRecords.partitions()) { - List> list = consumerRecords.records(partition); - if (list != null && !list.isEmpty()) { - list = TracingList.wrap(list, consumerProcessInstrumenter, () -> true, consumerContext); - } - records.put(partition, list); - } - return new ConsumerRecords<>(records); - } - /** * Produces a set of kafka client config properties (consumer or producer) to register a {@link * MetricsReporter} that records metrics to an {@code openTelemetry} instance. Add these resulting @@ -235,8 +128,8 @@ ConsumerRecords addTracing( ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, OpenTelemetryProducerInterceptor.class.getName()); config.put( - OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, - new KafkaTelemetrySupplier(this)); + OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER, + new KafkaHelperSupplier(helper)); return Collections.unmodifiableMap(config); } @@ -263,119 +156,8 @@ ConsumerRecords addTracing( ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, OpenTelemetryConsumerInterceptor.class.getName()); config.put( - OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, - new KafkaTelemetrySupplier(this)); + OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER, + new KafkaHelperSupplier(helper)); return Collections.unmodifiableMap(config); } - - /** - * Build and inject span into record. - * - * @param record the producer record to inject span info. - */ - void buildAndInjectSpan(ProducerRecord record, String clientId) { - Context parentContext = Context.current(); - - KafkaProducerRequest request = KafkaProducerRequest.create(record, clientId); - if (!producerInstrumenter.shouldStart(parentContext, request)) { - return; - } - - Context context = producerInstrumenter.start(parentContext, request); - if (producerPropagationEnabled) { - try { - propagator().inject(context, record.headers(), SETTER); - } catch (Throwable t) { - // it can happen if headers are read only (when record is sent second time) - logger.log(WARNING, "failed to inject span context. sending record second time?", t); - } - } - producerInstrumenter.end(context, request, null, null); - } - - /** - * Build and inject span into record. - * - * @param record the producer record to inject span info. - * @param callback the producer send callback - * @return send function's result - */ - Future buildAndInjectSpan( - ProducerRecord record, - Producer producer, - Callback callback, - BiFunction, Callback, Future> sendFn) { - Context parentContext = Context.current(); - - KafkaProducerRequest request = KafkaProducerRequest.create(record, producer); - if (!producerInstrumenter.shouldStart(parentContext, request)) { - return sendFn.apply(record, callback); - } - - Context context = producerInstrumenter.start(parentContext, request); - propagator().inject(context, record.headers(), SETTER); - - try (Scope ignored = context.makeCurrent()) { - return sendFn.apply(record, new ProducerCallback(callback, parentContext, context, request)); - } - } - - private Context buildAndFinishSpan( - ConsumerRecords records, Consumer consumer, Timer timer) { - return buildAndFinishSpan( - records, KafkaUtil.getConsumerGroup(consumer), KafkaUtil.getClientId(consumer), timer); - } - - Context buildAndFinishSpan( - ConsumerRecords records, String consumerGroup, String clientId, Timer timer) { - if (records.isEmpty()) { - return null; - } - Context parentContext = Context.current(); - KafkaReceiveRequest request = KafkaReceiveRequest.create(records, consumerGroup, clientId); - Context context = null; - if (consumerReceiveInstrumenter.shouldStart(parentContext, request)) { - context = - InstrumenterUtil.startAndEnd( - consumerReceiveInstrumenter, - parentContext, - request, - null, - null, - timer.startTime(), - timer.now()); - } - - // we're returning the context of the receive span so that process spans can use it as - // parent context even though the span has ended - // this is the suggested behavior according to the spec batch receive scenario: - // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/messaging/messaging-spans.md#batch-receiving - return context; - } - - private class ProducerCallback implements Callback { - private final Callback callback; - private final Context parentContext; - private final Context context; - private final KafkaProducerRequest request; - - ProducerCallback( - Callback callback, Context parentContext, Context context, KafkaProducerRequest request) { - this.callback = callback; - this.parentContext = parentContext; - this.context = context; - this.request = request; - } - - @Override - public void onCompletion(RecordMetadata metadata, Exception exception) { - producerInstrumenter.end(context, request, metadata, exception); - - if (callback != null) { - try (Scope ignored = parentContext.makeCurrent()) { - callback.onCompletion(metadata, exception); - } - } - } - } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java index 8f9d060ff968..9ccaaac38260 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java @@ -50,13 +50,14 @@ public class TracingConsumerInterceptor implements ConsumerInterceptor onConsume(ConsumerRecords records) { // timer should be started before fetching ConsumerRecords, but there is no callback for that Timer timer = Timer.start(); - Context receiveContext = telemetry.buildAndFinishSpan(records, consumerGroup, clientId, timer); + Context receiveContext = + telemetry.getHelper().buildAndFinishSpan(records, consumerGroup, clientId, timer); if (receiveContext == null) { receiveContext = Context.current(); } KafkaConsumerContext consumerContext = KafkaConsumerContextUtil.create(receiveContext, consumerGroup, clientId); - return telemetry.addTracing(records, consumerContext); + return telemetry.getHelper().addTracing(records, consumerContext); } @Override diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java index ea631f78054d..50d5ead4f9e1 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java @@ -40,7 +40,7 @@ public class TracingProducerInterceptor implements ProducerInterceptor onSend(ProducerRecord producerRecord) { - telemetry.buildAndInjectSpan(producerRecord, clientId); + telemetry.getHelper().buildAndInjectSpan(producerRecord, clientId); return producerRecord; } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaHelperSupplier.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaHelperSupplier.java new file mode 100644 index 000000000000..90584e024507 --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaHelperSupplier.java @@ -0,0 +1,38 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; + +import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaHelper; +import java.io.Serializable; +import java.util.Objects; +import java.util.function.Supplier; + +/** + * Wrapper for KafkaHelper that can be injected into kafka configuration without breaking + * serialization. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class KafkaHelperSupplier implements Supplier, Serializable { + private static final long serialVersionUID = 1L; + private final transient KafkaHelper kafkaHelper; + + public KafkaHelperSupplier(KafkaHelper kafkaHelper) { + Objects.requireNonNull(kafkaHelper); + this.kafkaHelper = kafkaHelper; + } + + @Override + public KafkaHelper get() { + return kafkaHelper; + } + + private Object writeReplace() { + // serialize this object to null + return null; + } +} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java index 77110b3cd100..f108737d5a2e 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java @@ -10,7 +10,7 @@ import io.opentelemetry.instrumentation.api.internal.Timer; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContext; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContextUtil; -import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaHelper; import java.util.Map; import java.util.Objects; import javax.annotation.Nullable; @@ -28,28 +28,28 @@ */ public class OpenTelemetryConsumerInterceptor implements ConsumerInterceptor { - public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = - "opentelemetry.kafka-telemetry.supplier"; + public static final String CONFIG_KEY_KAFKA_HELPER_SUPPLIER = + "opentelemetry.kafka-helper.supplier"; - @Nullable private KafkaTelemetry telemetry; + @Nullable private KafkaHelper helper; private String consumerGroup; private String clientId; @Override @CanIgnoreReturnValue public ConsumerRecords onConsume(ConsumerRecords records) { - if (telemetry == null) { + if (helper == null) { return records; } // timer should be started before fetching ConsumerRecords, but there is no callback for that Timer timer = Timer.start(); - Context receiveContext = telemetry.buildAndFinishSpan(records, consumerGroup, clientId, timer); + Context receiveContext = helper.buildAndFinishSpan(records, consumerGroup, clientId, timer); if (receiveContext == null) { receiveContext = Context.current(); } KafkaConsumerContext consumerContext = KafkaConsumerContextUtil.create(receiveContext, consumerGroup, clientId); - return telemetry.addTracing(records, consumerContext); + return helper.addTracing(records, consumerContext); } @Override @@ -63,9 +63,9 @@ public void configure(Map configs) { consumerGroup = Objects.toString(configs.get(ConsumerConfig.GROUP_ID_CONFIG), null); clientId = Objects.toString(configs.get(ConsumerConfig.CLIENT_ID_CONFIG), null); - KafkaTelemetrySupplier supplier = - getProperty(configs, CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, KafkaTelemetrySupplier.class); - this.telemetry = supplier.get(); + KafkaHelperSupplier supplier = + getProperty(configs, CONFIG_KEY_KAFKA_HELPER_SUPPLIER, KafkaHelperSupplier.class); + this.helper = supplier.get(); } @SuppressWarnings("unchecked") diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java index 5a3c812b1035..25219a840873 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java @@ -6,7 +6,7 @@ package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaHelper; import java.util.Map; import java.util.Objects; import javax.annotation.Nullable; @@ -23,17 +23,17 @@ */ public class OpenTelemetryProducerInterceptor implements ProducerInterceptor { - public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = - "opentelemetry.kafka-telemetry.supplier"; + public static final String CONFIG_KEY_KAFKA_HELPER_SUPPLIER = + "opentelemetry.kafka-helper.supplier"; - @Nullable private KafkaTelemetry telemetry; + @Nullable private KafkaHelper helper; @Nullable private String clientId; @Override @CanIgnoreReturnValue public ProducerRecord onSend(ProducerRecord producerRecord) { - if (telemetry != null) { - telemetry.buildAndInjectSpan(producerRecord, clientId); + if (helper != null) { + helper.buildAndInjectSpan(producerRecord, clientId); } return producerRecord; } @@ -48,9 +48,9 @@ public void close() {} public void configure(Map configs) { clientId = Objects.toString(configs.get(ProducerConfig.CLIENT_ID_CONFIG), null); - KafkaTelemetrySupplier supplier = - getProperty(configs, CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, KafkaTelemetrySupplier.class); - this.telemetry = supplier.get(); + KafkaHelperSupplier supplier = + getProperty(configs, CONFIG_KEY_KAFKA_HELPER_SUPPLIER, KafkaHelperSupplier.class); + this.helper = supplier.get(); } @SuppressWarnings("unchecked") From 3c4f97f9559ad91892c01207a5107e44639b5f7a Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 13:45:07 -0700 Subject: [PATCH 34/50] fixes --- .../OpenTelemetryConsumerInterceptor.java | 2 +- .../OpenTelemetryProducerInterceptor.java | 2 +- .../v2_6/AbstractWrapperTest.java | 82 ---------- .../v2_6/ExceptionHandlingTest.java | 60 ------- .../v2_6/KafkaTelemetryInterceptorTest.java | 40 ++--- .../v2_6/WrapperSuppressReceiveSpansTest.java | 108 ------------- .../kafkaclients/v2_6/WrapperTest.java | 151 ------------------ 7 files changed, 22 insertions(+), 423 deletions(-) delete mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractWrapperTest.java delete mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/ExceptionHandlingTest.java delete mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java delete mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperTest.java diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java index f108737d5a2e..84a1e2443f5b 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java @@ -29,7 +29,7 @@ public class OpenTelemetryConsumerInterceptor implements ConsumerInterceptor { public static final String CONFIG_KEY_KAFKA_HELPER_SUPPLIER = - "opentelemetry.kafka-helper.supplier"; + "opentelemetry.kafka-telemetry.supplier"; @Nullable private KafkaHelper helper; private String consumerGroup; diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java index 25219a840873..98911eef0fe1 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java @@ -24,7 +24,7 @@ public class OpenTelemetryProducerInterceptor implements ProducerInterceptor { public static final String CONFIG_KEY_KAFKA_HELPER_SUPPLIER = - "opentelemetry.kafka-helper.supplier"; + "opentelemetry.kafka-telemetry.supplier"; @Nullable private KafkaHelper helper; @Nullable private String clientId; diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractWrapperTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractWrapperTest.java deleted file mode 100644 index 4dc84990ef94..000000000000 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractWrapperTest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.kafkaclients.v2_6; - -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; - -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaClientBaseTest; -import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import org.apache.kafka.clients.consumer.Consumer; -import org.apache.kafka.clients.consumer.ConsumerRecord; -import org.apache.kafka.clients.consumer.ConsumerRecords; -import org.apache.kafka.clients.producer.Producer; -import org.apache.kafka.clients.producer.ProducerRecord; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -abstract class AbstractWrapperTest extends KafkaClientBaseTest { - - @RegisterExtension - static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); - - static final String greeting = "Hello Kafka!"; - - @ParameterizedTest - @ValueSource(booleans = {true, false}) - void testWrappers(boolean testHeaders) throws InterruptedException { - KafkaTelemetryBuilder telemetryBuilder = - KafkaTelemetry.builder(testing.getOpenTelemetry()) - .setCapturedHeaders(singletonList("Test-Message-Header")) - // TODO run tests both with and without experimental span attributes - .setCaptureExperimentalSpanAttributes(true); - configure(telemetryBuilder); - KafkaTelemetry telemetry = telemetryBuilder.build(); - - Producer wrappedProducer = telemetry.wrap(producer); - - testing.runWithSpan( - "parent", - () -> { - ProducerRecord producerRecord = - new ProducerRecord<>(SHARED_TOPIC, greeting); - if (testHeaders) { - producerRecord - .headers() - .add("Test-Message-Header", "test".getBytes(StandardCharsets.UTF_8)); - } - wrappedProducer.send( - producerRecord, - (meta, ex) -> { - if (ex == null) { - testing.runWithSpan("producer callback", () -> {}); - } else { - testing.runWithSpan("producer exception: " + ex, () -> {}); - } - }); - }); - - awaitUntilConsumerIsReady(); - Consumer wrappedConsumer = telemetry.wrap(consumer); - ConsumerRecords records = wrappedConsumer.poll(Duration.ofSeconds(10)); - assertThat(records.count()).isEqualTo(1); - for (ConsumerRecord record : records) { - assertThat(record.value()).isEqualTo(greeting); - assertThat(record.key()).isNull(); - testing.runWithSpan("process child", () -> {}); - } - - assertTraces(testHeaders); - } - - abstract void configure(KafkaTelemetryBuilder builder); - - abstract void assertTraces(boolean testHeaders); -} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/ExceptionHandlingTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/ExceptionHandlingTest.java deleted file mode 100644 index ca9b376a7322..000000000000 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/ExceptionHandlingTest.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.kafkaclients.v2_6; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; -import java.lang.reflect.Proxy; -import java.time.Duration; -import org.apache.kafka.clients.consumer.Consumer; -import org.apache.kafka.clients.producer.Producer; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -class ExceptionHandlingTest { - - @RegisterExtension - static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); - - @Test - void testConsumerExceptionPropagatesToCaller() { - Consumer consumer = - (Consumer) - Proxy.newProxyInstance( - ExceptionHandlingTest.class.getClassLoader(), - new Class[] {Consumer.class}, - (proxy, method, args) -> { - throw new IllegalStateException("can't invoke"); - }); - - KafkaTelemetry telemetry = KafkaTelemetry.builder(testing.getOpenTelemetry()).build(); - Consumer wrappedConsumer = telemetry.wrap(consumer); - - assertThatThrownBy(() -> wrappedConsumer.poll(Duration.ofMillis(1))) - .isInstanceOf(IllegalStateException.class) - .hasMessage("can't invoke"); - } - - @Test - void testProducerExceptionPropagatesToCaller() { - Producer producer = - (Producer) - Proxy.newProxyInstance( - ExceptionHandlingTest.class.getClassLoader(), - new Class[] {Producer.class}, - (proxy, method, args) -> { - throw new IllegalStateException("can't invoke"); - }); - - KafkaTelemetry telemetry = KafkaTelemetry.builder(testing.getOpenTelemetry()).build(); - Producer wrappedProducer = telemetry.wrap(producer); - assertThatThrownBy(wrappedProducer::flush) - .isInstanceOf(IllegalStateException.class) - .hasMessage("can't invoke"); - } -} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java index d470fcf69b4f..b4520519d3c6 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java @@ -8,7 +8,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaTelemetrySupplier; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaHelperSupplier; import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryConsumerInterceptor; import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryProducerInterceptor; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; @@ -68,25 +68,25 @@ void badProducerConfig() { () -> { Map producerConfig = producerConfig(); producerConfig.put( - OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, "foo"); + OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER, "foo"); new KafkaProducer<>(producerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) .hasRootCauseMessage( - "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of Supplier"); + "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of KafkaHelperSupplier"); // Bad config - supplier returns wrong type assertThatThrownBy( () -> { Map producerConfig = producerConfig(); producerConfig.put( - OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, - (Supplier) () -> "not a KafkaTelemetry"); + OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER, + (Supplier) () -> "not a KafkaHelper"); new KafkaProducer<>(producerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) .hasRootCauseMessage( - "Configuration property opentelemetry.kafka-telemetry.supplier supplier does not return KafkaTelemetry instance"); + "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of KafkaHelperSupplier"); } @Test @@ -98,25 +98,25 @@ void badConsumerConfig() { () -> { Map consumerConfig = consumerConfig(); consumerConfig.put( - OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, "foo"); + OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER, "foo"); new KafkaConsumer<>(consumerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) .hasRootCauseMessage( - "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of Supplier"); + "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of KafkaHelperSupplier"); // Bad config - supplier returns wrong type assertThatThrownBy( () -> { Map consumerConfig = consumerConfig(); consumerConfig.put( - OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, - (Supplier) () -> "not a KafkaTelemetry"); + OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER, + (Supplier) () -> "not a KafkaHelper"); new KafkaConsumer<>(consumerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) .hasRootCauseMessage( - "Configuration property opentelemetry.kafka-telemetry.supplier supplier does not return KafkaTelemetry instance"); + "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of KafkaHelperSupplier"); } @Test @@ -130,15 +130,15 @@ private static void testSerialize(Map map) throws IOException, ClassNotFoundException { // Check that producer config has the supplier Object producerSupplier = - map.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + map.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER); Object consumerSupplier = - map.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + map.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER); - KafkaTelemetrySupplier supplier = null; - if (producerSupplier instanceof KafkaTelemetrySupplier) { - supplier = (KafkaTelemetrySupplier) producerSupplier; - } else if (consumerSupplier instanceof KafkaTelemetrySupplier) { - supplier = (KafkaTelemetrySupplier) consumerSupplier; + KafkaHelperSupplier supplier = null; + if (producerSupplier instanceof KafkaHelperSupplier) { + supplier = (KafkaHelperSupplier) producerSupplier; + } else if (consumerSupplier instanceof KafkaHelperSupplier) { + supplier = (KafkaHelperSupplier) consumerSupplier; } assertThat(supplier).isNotNull(); @@ -168,9 +168,9 @@ protected Class resolveClass(ObjectStreamClass desc) try (ObjectInputStream inputStream = new CustomObjectInputStream(new ByteArrayInputStream(byteOutputStream.toByteArray()))) { Map result = (Map) inputStream.readObject(); - assertThat(result.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER)) + assertThat(result.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER)) .isNull(); - assertThat(result.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER)) + assertThat(result.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER)) .isNull(); } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java deleted file mode 100644 index b3cd095d09e4..000000000000 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.kafkaclients.v2_6; - -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; -import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME; -import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_PARTITION_ID; -import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_KAFKA_CONSUMER_GROUP; -import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_KAFKA_MESSAGE_OFFSET; -import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE; -import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; -import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; - -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import org.assertj.core.api.AbstractLongAssert; -import org.assertj.core.api.AbstractStringAssert; - -class WrapperSuppressReceiveSpansTest extends AbstractWrapperTest { - - @Override - void configure(KafkaTelemetryBuilder builder) { - builder.setMessagingReceiveInstrumentationEnabled(false); - } - - @Override - void assertTraces(boolean testHeaders) { - testing.waitAndAssertTraces( - trace -> - trace.hasSpansSatisfyingExactly( - span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), - span -> - span.hasName(SHARED_TOPIC + " publish") - .hasKind(SpanKind.PRODUCER) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly(sendAttributes(testHeaders)), - span -> - span.hasName(SHARED_TOPIC + " process") - .hasKind(SpanKind.CONSUMER) - .hasParent(trace.getSpan(1)) - .hasAttributesSatisfyingExactly(processAttributes(greeting, testHeaders)), - span -> - span.hasName("process child") - .hasKind(SpanKind.INTERNAL) - .hasParent(trace.getSpan(2)), - span -> - span.hasName("producer callback") - .hasKind(SpanKind.INTERNAL) - .hasParent(trace.getSpan(0)))); - } - - @SuppressWarnings("deprecation") // using deprecated semconv - protected static List sendAttributes(boolean testHeaders) { - List assertions = - new ArrayList<>( - Arrays.asList( - equalTo(MESSAGING_SYSTEM, "kafka"), - equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), - equalTo(MESSAGING_OPERATION, "publish"), - satisfies(MESSAGING_CLIENT_ID, stringAssert -> stringAssert.startsWith("producer")), - satisfies(MESSAGING_DESTINATION_PARTITION_ID, AbstractStringAssert::isNotEmpty), - satisfies(MESSAGING_KAFKA_MESSAGE_OFFSET, AbstractLongAssert::isNotNegative))); - if (testHeaders) { - assertions.add( - equalTo( - AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), - Collections.singletonList("test"))); - } - return assertions; - } - - @SuppressWarnings("deprecation") // using deprecated semconv - private static List processAttributes(String greeting, boolean testHeaders) { - List assertions = - new ArrayList<>( - Arrays.asList( - equalTo(MESSAGING_SYSTEM, "kafka"), - equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), - equalTo(MESSAGING_OPERATION, "process"), - equalTo( - MESSAGING_MESSAGE_BODY_SIZE, greeting.getBytes(StandardCharsets.UTF_8).length), - satisfies(MESSAGING_DESTINATION_PARTITION_ID, AbstractStringAssert::isNotEmpty), - satisfies(MESSAGING_KAFKA_MESSAGE_OFFSET, AbstractLongAssert::isNotNegative), - satisfies( - AttributeKey.longKey("kafka.record.queue_time_ms"), - AbstractLongAssert::isNotNegative), - equalTo(MESSAGING_KAFKA_CONSUMER_GROUP, "test"), - satisfies( - MESSAGING_CLIENT_ID, stringAssert -> stringAssert.startsWith("consumer")))); - if (testHeaders) { - assertions.add( - equalTo( - AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), - Collections.singletonList("test"))); - } - return assertions; - } -} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperTest.java deleted file mode 100644 index 85ddd5aa4cad..000000000000 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperTest.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.kafkaclients.v2_6; - -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; -import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_BATCH_MESSAGE_COUNT; -import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME; -import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_PARTITION_ID; -import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_KAFKA_CONSUMER_GROUP; -import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_KAFKA_MESSAGE_OFFSET; -import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE; -import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; -import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; -import static org.assertj.core.api.Assertions.assertThat; - -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.trace.SpanContext; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; -import io.opentelemetry.sdk.trace.data.LinkData; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; -import org.assertj.core.api.AbstractLongAssert; -import org.assertj.core.api.AbstractStringAssert; - -class WrapperTest extends AbstractWrapperTest { - - @Override - void configure(KafkaTelemetryBuilder builder) { - builder.setMessagingReceiveInstrumentationEnabled(true); - } - - @Override - void assertTraces(boolean testHeaders) { - AtomicReference producerSpanContext = new AtomicReference<>(); - testing.waitAndAssertTraces( - trace -> { - trace.hasSpansSatisfyingExactly( - span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), - span -> - span.hasName(SHARED_TOPIC + " publish") - .hasKind(SpanKind.PRODUCER) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly(sendAttributes(testHeaders)), - span -> - span.hasName("producer callback") - .hasKind(SpanKind.INTERNAL) - .hasParent(trace.getSpan(0))); - SpanContext spanContext = trace.getSpan(1).getSpanContext(); - producerSpanContext.set( - SpanContext.createFromRemoteParent( - spanContext.getTraceId(), - spanContext.getSpanId(), - spanContext.getTraceFlags(), - spanContext.getTraceState())); - }, - trace -> - trace.hasSpansSatisfyingExactly( - span -> - span.hasName(SHARED_TOPIC + " receive") - .hasKind(SpanKind.CONSUMER) - .hasNoParent() - .hasLinksSatisfying(links -> assertThat(links).isEmpty()) - .hasAttributesSatisfyingExactly(receiveAttributes(testHeaders)), - span -> - span.hasName(SHARED_TOPIC + " process") - .hasKind(SpanKind.CONSUMER) - .hasParent(trace.getSpan(0)) - .hasLinks(LinkData.create(producerSpanContext.get())) - .hasAttributesSatisfyingExactly(processAttributes(greeting, testHeaders)), - span -> - span.hasName("process child") - .hasKind(SpanKind.INTERNAL) - .hasParent(trace.getSpan(1)))); - } - - @SuppressWarnings("deprecation") // using deprecated semconv - protected static List sendAttributes(boolean testHeaders) { - List assertions = - new ArrayList<>( - Arrays.asList( - equalTo(MESSAGING_SYSTEM, "kafka"), - equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), - equalTo(MESSAGING_OPERATION, "publish"), - satisfies(MESSAGING_CLIENT_ID, stringAssert -> stringAssert.startsWith("producer")), - satisfies(MESSAGING_DESTINATION_PARTITION_ID, AbstractStringAssert::isNotEmpty), - satisfies(MESSAGING_KAFKA_MESSAGE_OFFSET, AbstractLongAssert::isNotNegative))); - if (testHeaders) { - assertions.add( - equalTo( - AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), - Collections.singletonList("test"))); - } - return assertions; - } - - @SuppressWarnings("deprecation") // using deprecated semconv - private static List processAttributes(String greeting, boolean testHeaders) { - List assertions = - new ArrayList<>( - Arrays.asList( - equalTo(MESSAGING_SYSTEM, "kafka"), - equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), - equalTo(MESSAGING_OPERATION, "process"), - equalTo( - MESSAGING_MESSAGE_BODY_SIZE, greeting.getBytes(StandardCharsets.UTF_8).length), - satisfies(MESSAGING_DESTINATION_PARTITION_ID, AbstractStringAssert::isNotEmpty), - satisfies(MESSAGING_KAFKA_MESSAGE_OFFSET, AbstractLongAssert::isNotNegative), - satisfies( - AttributeKey.longKey("kafka.record.queue_time_ms"), - AbstractLongAssert::isNotNegative), - equalTo(MESSAGING_KAFKA_CONSUMER_GROUP, "test"), - satisfies( - MESSAGING_CLIENT_ID, stringAssert -> stringAssert.startsWith("consumer")))); - if (testHeaders) { - assertions.add( - equalTo( - AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), - Collections.singletonList("test"))); - } - return assertions; - } - - @SuppressWarnings("deprecation") // using deprecated semconv - protected static List receiveAttributes(boolean testHeaders) { - List assertions = - new ArrayList<>( - Arrays.asList( - equalTo(MESSAGING_SYSTEM, "kafka"), - equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), - equalTo(MESSAGING_OPERATION, "receive"), - equalTo(MESSAGING_KAFKA_CONSUMER_GROUP, "test"), - satisfies(MESSAGING_CLIENT_ID, stringAssert -> stringAssert.startsWith("consumer")), - equalTo(MESSAGING_BATCH_MESSAGE_COUNT, 1))); - if (testHeaders) { - assertions.add( - equalTo( - AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), - Collections.singletonList("test"))); - } - return assertions; - } -} From ff96054a0fc541c6375cc6aa24a9e7223f034aaa Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 13:55:19 -0700 Subject: [PATCH 35/50] split --- .../kafkaclients/v2_6/KafkaHelper.java | 125 ------------------ .../kafkaclients/v2_6/KafkaTelemetry.java | 33 +++-- .../v2_6/TracingConsumerInterceptor.java | 4 +- .../v2_6/TracingProducerInterceptor.java | 2 +- .../v2_6/internal/KafkaHelperSupplier.java | 38 ------ .../v2_6/internal/KafkaTelemetrySupplier.java | 38 ------ .../OpenTelemetryConsumerInterceptor.java | 19 +-- .../OpenTelemetryProducerInterceptor.java | 16 +-- .../v2_6/KafkaTelemetryInterceptorTest.java | 41 +++--- 9 files changed, 63 insertions(+), 253 deletions(-) delete mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaHelper.java delete mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaHelperSupplier.java delete mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaTelemetrySupplier.java diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaHelper.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaHelper.java deleted file mode 100644 index efaaf9d4f84a..000000000000 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaHelper.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.kafkaclients.v2_6; - -import static java.util.logging.Level.WARNING; - -import io.opentelemetry.context.Context; -import io.opentelemetry.context.propagation.TextMapPropagator; -import io.opentelemetry.context.propagation.TextMapSetter; -import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.internal.InstrumenterUtil; -import io.opentelemetry.instrumentation.api.internal.Timer; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContext; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaHeadersSetter; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProcessRequest; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProducerRequest; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaReceiveRequest; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.TracingList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Logger; -import org.apache.kafka.clients.consumer.ConsumerRecord; -import org.apache.kafka.clients.consumer.ConsumerRecords; -import org.apache.kafka.clients.producer.ProducerRecord; -import org.apache.kafka.clients.producer.RecordMetadata; -import org.apache.kafka.common.TopicPartition; -import org.apache.kafka.common.header.Headers; - -public final class KafkaHelper { - private static final Logger logger = Logger.getLogger(KafkaHelper.class.getName()); - - private static final TextMapSetter SETTER = KafkaHeadersSetter.INSTANCE; - - private final TextMapPropagator propagator; - private final Instrumenter producerInstrumenter; - private final Instrumenter consumerReceiveInstrumenter; - private final Instrumenter consumerProcessInstrumenter; - private final boolean producerPropagationEnabled; - - KafkaHelper( - TextMapPropagator propagator, - Instrumenter producerInstrumenter, - Instrumenter consumerReceiveInstrumenter, - Instrumenter consumerProcessInstrumenter, - boolean producerPropagationEnabled) { - this.propagator = propagator; - this.producerInstrumenter = producerInstrumenter; - this.consumerReceiveInstrumenter = consumerReceiveInstrumenter; - this.consumerProcessInstrumenter = consumerProcessInstrumenter; - this.producerPropagationEnabled = producerPropagationEnabled; - } - - /** - * Build and inject span into record. - * - * @param record the producer record to inject span info. - */ - public void buildAndInjectSpan(ProducerRecord record, String clientId) { - Context parentContext = Context.current(); - - KafkaProducerRequest request = KafkaProducerRequest.create(record, clientId); - if (!producerInstrumenter.shouldStart(parentContext, request)) { - return; - } - - Context context = producerInstrumenter.start(parentContext, request); - if (producerPropagationEnabled) { - try { - propagator.inject(context, record.headers(), SETTER); - } catch (Throwable t) { - // it can happen if headers are read only (when record is sent second time) - logger.log(WARNING, "failed to inject span context. sending record second time?", t); - } - } - producerInstrumenter.end(context, request, null, null); - } - - public Context buildAndFinishSpan( - ConsumerRecords records, String consumerGroup, String clientId, Timer timer) { - if (records.isEmpty()) { - return null; - } - Context parentContext = Context.current(); - KafkaReceiveRequest request = KafkaReceiveRequest.create(records, consumerGroup, clientId); - Context context = null; - if (consumerReceiveInstrumenter.shouldStart(parentContext, request)) { - context = - InstrumenterUtil.startAndEnd( - consumerReceiveInstrumenter, - parentContext, - request, - null, - null, - timer.startTime(), - timer.now()); - } - - // we're returning the context of the receive span so that process spans can use it as - // parent context even though the span has ended - // this is the suggested behavior according to the spec batch receive scenario: - // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/messaging/messaging-spans.md#batch-receiving - return context; - } - - public ConsumerRecords addTracing( - ConsumerRecords consumerRecords, KafkaConsumerContext consumerContext) { - if (consumerRecords.isEmpty()) { - return consumerRecords; - } - - Map>> records = new LinkedHashMap<>(); - for (TopicPartition partition : consumerRecords.partitions()) { - List> list = consumerRecords.records(partition); - if (list != null && !list.isEmpty()) { - list = TracingList.wrap(list, consumerProcessInstrumenter, () -> true, consumerContext); - } - records.put(partition, list); - } - return new ConsumerRecords<>(records); - } -} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java index 5bf11315d54e..a145d4d1b807 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java @@ -13,7 +13,10 @@ import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.MetricsReporterList; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetryMetricsReporter; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetrySupplier; -import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaHelperSupplier; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaConsumerTelemetry; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaConsumerTelemetrySupplier; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaProducerTelemetry; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaProducerTelemetrySupplier; import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryConsumerInterceptor; import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryProducerInterceptor; import java.util.Collections; @@ -30,7 +33,8 @@ public final class KafkaTelemetry { private final OpenTelemetry openTelemetry; - private final KafkaHelper helper; + private final KafkaProducerTelemetry producerTelemetry; + private final KafkaConsumerTelemetry consumerTelemetry; KafkaTelemetry( OpenTelemetry openTelemetry, @@ -39,18 +43,23 @@ public final class KafkaTelemetry { Instrumenter consumerProcessInstrumenter, boolean producerPropagationEnabled) { this.openTelemetry = openTelemetry; - this.helper = - new KafkaHelper( + this.producerTelemetry = + new KafkaProducerTelemetry( openTelemetry.getPropagators().getTextMapPropagator(), producerInstrumenter, - consumerReceiveInstrumenter, - consumerProcessInstrumenter, producerPropagationEnabled); + this.consumerTelemetry = + new KafkaConsumerTelemetry(consumerReceiveInstrumenter, consumerProcessInstrumenter); } @Deprecated - KafkaHelper getHelper() { - return helper; + KafkaProducerTelemetry getProducerTelemetry() { + return producerTelemetry; + } + + @Deprecated + KafkaConsumerTelemetry getConsumerTelemetry() { + return consumerTelemetry; } /** Returns a new {@link KafkaTelemetry} configured with the given {@link OpenTelemetry}. */ @@ -128,8 +137,8 @@ public static KafkaTelemetryBuilder builder(OpenTelemetry openTelemetry) { ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, OpenTelemetryProducerInterceptor.class.getName()); config.put( - OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER, - new KafkaHelperSupplier(helper)); + OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + new KafkaProducerTelemetrySupplier(producerTelemetry)); return Collections.unmodifiableMap(config); } @@ -156,8 +165,8 @@ public static KafkaTelemetryBuilder builder(OpenTelemetry openTelemetry) { ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, OpenTelemetryConsumerInterceptor.class.getName()); config.put( - OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER, - new KafkaHelperSupplier(helper)); + OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + new KafkaConsumerTelemetrySupplier(consumerTelemetry)); return Collections.unmodifiableMap(config); } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java index 9ccaaac38260..ca3b23d7d15b 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java @@ -51,13 +51,13 @@ public ConsumerRecords onConsume(ConsumerRecords records) { // timer should be started before fetching ConsumerRecords, but there is no callback for that Timer timer = Timer.start(); Context receiveContext = - telemetry.getHelper().buildAndFinishSpan(records, consumerGroup, clientId, timer); + telemetry.getConsumerTelemetry().buildAndFinishSpan(records, consumerGroup, clientId, timer); if (receiveContext == null) { receiveContext = Context.current(); } KafkaConsumerContext consumerContext = KafkaConsumerContextUtil.create(receiveContext, consumerGroup, clientId); - return telemetry.getHelper().addTracing(records, consumerContext); + return telemetry.getConsumerTelemetry().addTracing(records, consumerContext); } @Override diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java index 50d5ead4f9e1..97e982028a26 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java @@ -40,7 +40,7 @@ public class TracingProducerInterceptor implements ProducerInterceptor onSend(ProducerRecord producerRecord) { - telemetry.getHelper().buildAndInjectSpan(producerRecord, clientId); + telemetry.getProducerTelemetry().buildAndInjectSpan(producerRecord, clientId); return producerRecord; } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaHelperSupplier.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaHelperSupplier.java deleted file mode 100644 index 90584e024507..000000000000 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaHelperSupplier.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; - -import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaHelper; -import java.io.Serializable; -import java.util.Objects; -import java.util.function.Supplier; - -/** - * Wrapper for KafkaHelper that can be injected into kafka configuration without breaking - * serialization. - * - *

This class is internal and is hence not for public use. Its APIs are unstable and can change - * at any time. - */ -public final class KafkaHelperSupplier implements Supplier, Serializable { - private static final long serialVersionUID = 1L; - private final transient KafkaHelper kafkaHelper; - - public KafkaHelperSupplier(KafkaHelper kafkaHelper) { - Objects.requireNonNull(kafkaHelper); - this.kafkaHelper = kafkaHelper; - } - - @Override - public KafkaHelper get() { - return kafkaHelper; - } - - private Object writeReplace() { - // serialize this object to null - return null; - } -} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaTelemetrySupplier.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaTelemetrySupplier.java deleted file mode 100644 index 29fde5a98262..000000000000 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaTelemetrySupplier.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; - -import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry; -import java.io.Serializable; -import java.util.Objects; -import java.util.function.Supplier; - -/** - * Wrapper for KafkaTelemetry that can be injected into kafka configuration without breaking - * serialization. - * - *

This class is internal and is hence not for public use. Its APIs are unstable and can change - * at any time. - */ -public final class KafkaTelemetrySupplier implements Supplier, Serializable { - private static final long serialVersionUID = 1L; - private final transient KafkaTelemetry kafkaTelemetry; - - public KafkaTelemetrySupplier(KafkaTelemetry kafkaTelemetry) { - Objects.requireNonNull(kafkaTelemetry); - this.kafkaTelemetry = kafkaTelemetry; - } - - @Override - public KafkaTelemetry get() { - return kafkaTelemetry; - } - - private Object writeReplace() { - // serialize this object to null - return null; - } -} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java index 84a1e2443f5b..e2d58360bce2 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java @@ -10,7 +10,6 @@ import io.opentelemetry.instrumentation.api.internal.Timer; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContext; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContextUtil; -import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaHelper; import java.util.Map; import java.util.Objects; import javax.annotation.Nullable; @@ -28,28 +27,29 @@ */ public class OpenTelemetryConsumerInterceptor implements ConsumerInterceptor { - public static final String CONFIG_KEY_KAFKA_HELPER_SUPPLIER = + public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = "opentelemetry.kafka-telemetry.supplier"; - @Nullable private KafkaHelper helper; + @Nullable private KafkaConsumerTelemetry consumerTelemetry; private String consumerGroup; private String clientId; @Override @CanIgnoreReturnValue public ConsumerRecords onConsume(ConsumerRecords records) { - if (helper == null) { + if (consumerTelemetry == null) { return records; } // timer should be started before fetching ConsumerRecords, but there is no callback for that Timer timer = Timer.start(); - Context receiveContext = helper.buildAndFinishSpan(records, consumerGroup, clientId, timer); + Context receiveContext = + consumerTelemetry.buildAndFinishSpan(records, consumerGroup, clientId, timer); if (receiveContext == null) { receiveContext = Context.current(); } KafkaConsumerContext consumerContext = KafkaConsumerContextUtil.create(receiveContext, consumerGroup, clientId); - return helper.addTracing(records, consumerContext); + return consumerTelemetry.addTracing(records, consumerContext); } @Override @@ -63,9 +63,10 @@ public void configure(Map configs) { consumerGroup = Objects.toString(configs.get(ConsumerConfig.GROUP_ID_CONFIG), null); clientId = Objects.toString(configs.get(ConsumerConfig.CLIENT_ID_CONFIG), null); - KafkaHelperSupplier supplier = - getProperty(configs, CONFIG_KEY_KAFKA_HELPER_SUPPLIER, KafkaHelperSupplier.class); - this.helper = supplier.get(); + KafkaConsumerTelemetrySupplier supplier = + getProperty( + configs, CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, KafkaConsumerTelemetrySupplier.class); + this.consumerTelemetry = supplier.get(); } @SuppressWarnings("unchecked") diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java index 98911eef0fe1..0dd93a529000 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java @@ -6,7 +6,6 @@ package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaHelper; import java.util.Map; import java.util.Objects; import javax.annotation.Nullable; @@ -23,17 +22,17 @@ */ public class OpenTelemetryProducerInterceptor implements ProducerInterceptor { - public static final String CONFIG_KEY_KAFKA_HELPER_SUPPLIER = + public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = "opentelemetry.kafka-telemetry.supplier"; - @Nullable private KafkaHelper helper; + @Nullable private KafkaProducerTelemetry producerTelemetry; @Nullable private String clientId; @Override @CanIgnoreReturnValue public ProducerRecord onSend(ProducerRecord producerRecord) { - if (helper != null) { - helper.buildAndInjectSpan(producerRecord, clientId); + if (producerTelemetry != null) { + producerTelemetry.buildAndInjectSpan(producerRecord, clientId); } return producerRecord; } @@ -48,9 +47,10 @@ public void close() {} public void configure(Map configs) { clientId = Objects.toString(configs.get(ProducerConfig.CLIENT_ID_CONFIG), null); - KafkaHelperSupplier supplier = - getProperty(configs, CONFIG_KEY_KAFKA_HELPER_SUPPLIER, KafkaHelperSupplier.class); - this.helper = supplier.get(); + KafkaProducerTelemetrySupplier supplier = + getProperty( + configs, CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, KafkaProducerTelemetrySupplier.class); + this.producerTelemetry = supplier.get(); } @SuppressWarnings("unchecked") diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java index b4520519d3c6..c968eb357501 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java @@ -8,7 +8,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaHelperSupplier; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaConsumerTelemetrySupplier; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaProducerTelemetrySupplier; import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryConsumerInterceptor; import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryProducerInterceptor; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; @@ -68,25 +69,25 @@ void badProducerConfig() { () -> { Map producerConfig = producerConfig(); producerConfig.put( - OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER, "foo"); + OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, "foo"); new KafkaProducer<>(producerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) .hasRootCauseMessage( - "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of KafkaHelperSupplier"); + "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of KafkaProducerTelemetrySupplier"); // Bad config - supplier returns wrong type assertThatThrownBy( () -> { Map producerConfig = producerConfig(); producerConfig.put( - OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER, - (Supplier) () -> "not a KafkaHelper"); + OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + (Supplier) () -> "not a KafkaProducerTelemetry"); new KafkaProducer<>(producerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) .hasRootCauseMessage( - "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of KafkaHelperSupplier"); + "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of KafkaProducerTelemetrySupplier"); } @Test @@ -98,25 +99,25 @@ void badConsumerConfig() { () -> { Map consumerConfig = consumerConfig(); consumerConfig.put( - OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER, "foo"); + OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, "foo"); new KafkaConsumer<>(consumerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) .hasRootCauseMessage( - "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of KafkaHelperSupplier"); + "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of KafkaConsumerTelemetrySupplier"); // Bad config - supplier returns wrong type assertThatThrownBy( () -> { Map consumerConfig = consumerConfig(); consumerConfig.put( - OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER, - (Supplier) () -> "not a KafkaHelper"); + OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + (Supplier) () -> "not a KafkaConsumerTelemetry"); new KafkaConsumer<>(consumerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) .hasRootCauseMessage( - "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of KafkaHelperSupplier"); + "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of KafkaConsumerTelemetrySupplier"); } @Test @@ -130,15 +131,15 @@ private static void testSerialize(Map map) throws IOException, ClassNotFoundException { // Check that producer config has the supplier Object producerSupplier = - map.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER); + map.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); Object consumerSupplier = - map.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER); + map.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); - KafkaHelperSupplier supplier = null; - if (producerSupplier instanceof KafkaHelperSupplier) { - supplier = (KafkaHelperSupplier) producerSupplier; - } else if (consumerSupplier instanceof KafkaHelperSupplier) { - supplier = (KafkaHelperSupplier) consumerSupplier; + Supplier supplier = null; + if (producerSupplier instanceof KafkaProducerTelemetrySupplier) { + supplier = (KafkaProducerTelemetrySupplier) producerSupplier; + } else if (consumerSupplier instanceof KafkaConsumerTelemetrySupplier) { + supplier = (KafkaConsumerTelemetrySupplier) consumerSupplier; } assertThat(supplier).isNotNull(); @@ -168,9 +169,9 @@ protected Class resolveClass(ObjectStreamClass desc) try (ObjectInputStream inputStream = new CustomObjectInputStream(new ByteArrayInputStream(byteOutputStream.toByteArray()))) { Map result = (Map) inputStream.readObject(); - assertThat(result.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER)) + assertThat(result.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER)) .isNull(); - assertThat(result.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_HELPER_SUPPLIER)) + assertThat(result.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER)) .isNull(); } } From 61813b559fbbb077b16bb58cdc0eac8792558134 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 13:58:19 -0700 Subject: [PATCH 36/50] split2 --- .../kafkaclients/v2_6/KafkaTelemetry.java | 4 ++-- .../OpenTelemetryConsumerInterceptor.java | 6 ++--- .../OpenTelemetryProducerInterceptor.java | 6 ++--- .../v2_6/KafkaTelemetryInterceptorTest.java | 24 +++++++++---------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java index a145d4d1b807..6bea398a59bd 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java @@ -137,7 +137,7 @@ public static KafkaTelemetryBuilder builder(OpenTelemetry openTelemetry) { ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, OpenTelemetryProducerInterceptor.class.getName()); config.put( - OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER, new KafkaProducerTelemetrySupplier(producerTelemetry)); return Collections.unmodifiableMap(config); } @@ -165,7 +165,7 @@ public static KafkaTelemetryBuilder builder(OpenTelemetry openTelemetry) { ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, OpenTelemetryConsumerInterceptor.class.getName()); config.put( - OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_CONSUMER_TELEMETRY_SUPPLIER, new KafkaConsumerTelemetrySupplier(consumerTelemetry)); return Collections.unmodifiableMap(config); } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java index e2d58360bce2..c0d9b9703f3d 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java @@ -27,8 +27,8 @@ */ public class OpenTelemetryConsumerInterceptor implements ConsumerInterceptor { - public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = - "opentelemetry.kafka-telemetry.supplier"; + public static final String CONFIG_KEY_KAFKA_CONSUMER_TELEMETRY_SUPPLIER = + "opentelemetry.kafka-consumer-telemetry.supplier"; @Nullable private KafkaConsumerTelemetry consumerTelemetry; private String consumerGroup; @@ -65,7 +65,7 @@ public void configure(Map configs) { KafkaConsumerTelemetrySupplier supplier = getProperty( - configs, CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, KafkaConsumerTelemetrySupplier.class); + configs, CONFIG_KEY_KAFKA_CONSUMER_TELEMETRY_SUPPLIER, KafkaConsumerTelemetrySupplier.class); this.consumerTelemetry = supplier.get(); } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java index 0dd93a529000..baba11a90ae5 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java @@ -22,8 +22,8 @@ */ public class OpenTelemetryProducerInterceptor implements ProducerInterceptor { - public static final String CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER = - "opentelemetry.kafka-telemetry.supplier"; + public static final String CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER = + "opentelemetry.kafka-producer-telemetry.supplier"; @Nullable private KafkaProducerTelemetry producerTelemetry; @Nullable private String clientId; @@ -49,7 +49,7 @@ public void configure(Map configs) { KafkaProducerTelemetrySupplier supplier = getProperty( - configs, CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, KafkaProducerTelemetrySupplier.class); + configs, CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER, KafkaProducerTelemetrySupplier.class); this.producerTelemetry = supplier.get(); } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java index c968eb357501..f9a05abcd5a0 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java @@ -69,25 +69,25 @@ void badProducerConfig() { () -> { Map producerConfig = producerConfig(); producerConfig.put( - OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, "foo"); + OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER, "foo"); new KafkaProducer<>(producerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) .hasRootCauseMessage( - "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of KafkaProducerTelemetrySupplier"); + "Configuration property opentelemetry.kafka-producer-telemetry.supplier is not instance of KafkaProducerTelemetrySupplier"); // Bad config - supplier returns wrong type assertThatThrownBy( () -> { Map producerConfig = producerConfig(); producerConfig.put( - OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER, (Supplier) () -> "not a KafkaProducerTelemetry"); new KafkaProducer<>(producerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) .hasRootCauseMessage( - "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of KafkaProducerTelemetrySupplier"); + "Configuration property opentelemetry.kafka-producer-telemetry.supplier is not instance of KafkaProducerTelemetrySupplier"); } @Test @@ -99,25 +99,25 @@ void badConsumerConfig() { () -> { Map consumerConfig = consumerConfig(); consumerConfig.put( - OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, "foo"); + OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_CONSUMER_TELEMETRY_SUPPLIER, "foo"); new KafkaConsumer<>(consumerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) .hasRootCauseMessage( - "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of KafkaConsumerTelemetrySupplier"); + "Configuration property opentelemetry.kafka-consumer-telemetry.supplier is not instance of KafkaConsumerTelemetrySupplier"); // Bad config - supplier returns wrong type assertThatThrownBy( () -> { Map consumerConfig = consumerConfig(); consumerConfig.put( - OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER, + OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_CONSUMER_TELEMETRY_SUPPLIER, (Supplier) () -> "not a KafkaConsumerTelemetry"); new KafkaConsumer<>(consumerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) .hasRootCauseMessage( - "Configuration property opentelemetry.kafka-telemetry.supplier is not instance of KafkaConsumerTelemetrySupplier"); + "Configuration property opentelemetry.kafka-consumer-telemetry.supplier is not instance of KafkaConsumerTelemetrySupplier"); } @Test @@ -131,9 +131,9 @@ private static void testSerialize(Map map) throws IOException, ClassNotFoundException { // Check that producer config has the supplier Object producerSupplier = - map.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + map.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER); Object consumerSupplier = - map.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER); + map.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_CONSUMER_TELEMETRY_SUPPLIER); Supplier supplier = null; if (producerSupplier instanceof KafkaProducerTelemetrySupplier) { @@ -169,9 +169,9 @@ protected Class resolveClass(ObjectStreamClass desc) try (ObjectInputStream inputStream = new CustomObjectInputStream(new ByteArrayInputStream(byteOutputStream.toByteArray()))) { Map result = (Map) inputStream.readObject(); - assertThat(result.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER)) + assertThat(result.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER)) .isNull(); - assertThat(result.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_TELEMETRY_SUPPLIER)) + assertThat(result.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_CONSUMER_TELEMETRY_SUPPLIER)) .isNull(); } } From 41c767cd51f1c272ca6cb44ad203659e978bfe4d Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 14:10:51 -0700 Subject: [PATCH 37/50] split test --- .../v2_6/internal/KafkaConsumerTelemetry.java | 84 ++++++++++++ .../KafkaConsumerTelemetrySupplier.java | 40 ++++++ .../v2_6/internal/KafkaProducerTelemetry.java | 69 ++++++++++ .../KafkaProducerTelemetrySupplier.java | 40 ++++++ ...OpenTelemetryConsumerInterceptorTest.java} | 71 +---------- .../OpenTelemetryMetricsReporterTest.java | 3 - .../OpenTelemetryProducerInterceptorTest.java | 120 ++++++++++++++++++ 7 files changed, 360 insertions(+), 67 deletions(-) create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetry.java create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetrySupplier.java create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetry.java create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetrySupplier.java rename instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/{KafkaTelemetryInterceptorTest.java => internal/OpenTelemetryConsumerInterceptorTest.java} (58%) create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptorTest.java diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetry.java new file mode 100644 index 000000000000..0bc941a31254 --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetry.java @@ -0,0 +1,84 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; + +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.internal.InstrumenterUtil; +import io.opentelemetry.instrumentation.api.internal.Timer; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContext; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProcessRequest; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaReceiveRequest; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.TracingList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.common.TopicPartition; + +/** + * Helper for consumer-side instrumentation. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public class KafkaConsumerTelemetry { + + private final Instrumenter consumerReceiveInstrumenter; + private final Instrumenter consumerProcessInstrumenter; + + public KafkaConsumerTelemetry( + Instrumenter consumerReceiveInstrumenter, + Instrumenter consumerProcessInstrumenter) { + this.consumerReceiveInstrumenter = consumerReceiveInstrumenter; + this.consumerProcessInstrumenter = consumerProcessInstrumenter; + } + + public Context buildAndFinishSpan( + ConsumerRecords records, String consumerGroup, String clientId, Timer timer) { + if (records.isEmpty()) { + return null; + } + Context parentContext = Context.current(); + KafkaReceiveRequest request = KafkaReceiveRequest.create(records, consumerGroup, clientId); + Context context = null; + if (consumerReceiveInstrumenter.shouldStart(parentContext, request)) { + context = + InstrumenterUtil.startAndEnd( + consumerReceiveInstrumenter, + parentContext, + request, + null, + null, + timer.startTime(), + timer.now()); + } + + // we're returning the context of the receive span so that process spans can use it as + // parent context even though the span has ended + // this is the suggested behavior according to the spec batch receive scenario: + // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/messaging/messaging-spans.md#batch-receiving + return context; + } + + public ConsumerRecords addTracing( + ConsumerRecords consumerRecords, KafkaConsumerContext consumerContext) { + if (consumerRecords.isEmpty()) { + return consumerRecords; + } + + Map>> records = new LinkedHashMap<>(); + for (TopicPartition partition : consumerRecords.partitions()) { + List> list = consumerRecords.records(partition); + if (list != null && !list.isEmpty()) { + list = TracingList.wrap(list, consumerProcessInstrumenter, () -> true, consumerContext); + } + records.put(partition, list); + } + return new ConsumerRecords<>(records); + } +} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetrySupplier.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetrySupplier.java new file mode 100644 index 000000000000..fa0d878461cd --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetrySupplier.java @@ -0,0 +1,40 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; + +import java.io.Serializable; +import java.util.Objects; +import java.util.function.Supplier; + +/** + * Wrapper for KafkaConsumerTelemetry that can be injected into kafka configuration without breaking + * serialization. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public class KafkaConsumerTelemetrySupplier + implements Supplier, Serializable { + + private final KafkaConsumerTelemetry consumerTelemetry; + + public KafkaConsumerTelemetrySupplier(KafkaConsumerTelemetry consumerTelemetry) { + this.consumerTelemetry = Objects.requireNonNull(consumerTelemetry); + } + + @Override + public KafkaConsumerTelemetry get() { + return consumerTelemetry; + } + + /** + * Replaces this object with null in the serialization stream. This ensures the supplier does not + * get serialized. + */ + private Object writeReplace() { + return null; + } +} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetry.java new file mode 100644 index 000000000000..4b4bc99484e1 --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetry.java @@ -0,0 +1,69 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; + +import static java.util.logging.Level.WARNING; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.context.propagation.TextMapSetter; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaHeadersSetter; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProducerRequest; +import java.util.logging.Logger; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; +import org.apache.kafka.common.header.Headers; + +/** + * Helper for producer-side instrumentation. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public class KafkaProducerTelemetry { + private static final Logger logger = Logger.getLogger(KafkaProducerTelemetry.class.getName()); + + private static final TextMapSetter SETTER = KafkaHeadersSetter.INSTANCE; + + private final TextMapPropagator propagator; + private final Instrumenter producerInstrumenter; + private final boolean producerPropagationEnabled; + + public KafkaProducerTelemetry( + TextMapPropagator propagator, + Instrumenter producerInstrumenter, + boolean producerPropagationEnabled) { + this.propagator = propagator; + this.producerInstrumenter = producerInstrumenter; + this.producerPropagationEnabled = producerPropagationEnabled; + } + + /** + * Build and inject span into record. + * + * @param record the producer record to inject span info. + */ + public void buildAndInjectSpan(ProducerRecord record, String clientId) { + Context parentContext = Context.current(); + + KafkaProducerRequest request = KafkaProducerRequest.create(record, clientId); + if (!producerInstrumenter.shouldStart(parentContext, request)) { + return; + } + + Context context = producerInstrumenter.start(parentContext, request); + if (producerPropagationEnabled) { + try { + propagator.inject(context, record.headers(), SETTER); + } catch (Throwable t) { + // it can happen if headers are read only (when record is sent second time) + logger.log(WARNING, "failed to inject span context. sending record second time?", t); + } + } + producerInstrumenter.end(context, request, null, null); + } +} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetrySupplier.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetrySupplier.java new file mode 100644 index 000000000000..6853ebd87b33 --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetrySupplier.java @@ -0,0 +1,40 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; + +import java.io.Serializable; +import java.util.Objects; +import java.util.function.Supplier; + +/** + * Wrapper for KafkaProducerTelemetry that can be injected into kafka configuration without breaking + * serialization. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public class KafkaProducerTelemetrySupplier + implements Supplier, Serializable { + + private final KafkaProducerTelemetry producerTelemetry; + + public KafkaProducerTelemetrySupplier(KafkaProducerTelemetry producerTelemetry) { + this.producerTelemetry = Objects.requireNonNull(producerTelemetry); + } + + @Override + public KafkaProducerTelemetry get() { + return producerTelemetry; + } + + /** + * Replaces this object with null in the serialization stream. This ensures the supplier does not + * get serialized. + */ + private Object writeReplace() { + return null; + } +} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptorTest.java similarity index 58% rename from instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java rename to instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptorTest.java index f9a05abcd5a0..8a92c684c2a0 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetryInterceptorTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptorTest.java @@ -3,15 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.kafkaclients.v2_6; +package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaConsumerTelemetrySupplier; -import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaProducerTelemetrySupplier; -import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryConsumerInterceptor; -import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryProducerInterceptor; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import java.io.ByteArrayInputStream; @@ -26,29 +23,16 @@ import java.util.function.Supplier; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.KafkaConsumer; -import org.apache.kafka.clients.producer.KafkaProducer; -import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.serialization.StringDeserializer; -import org.apache.kafka.common.serialization.StringSerializer; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -class KafkaTelemetryInterceptorTest { +class OpenTelemetryConsumerInterceptorTest { @RegisterExtension static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); - private static Map producerConfig() { - Map config = new HashMap<>(); - config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); - config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); - config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); - config.putAll( - KafkaTelemetry.create(testing.getOpenTelemetry()).producerInterceptorConfigProperties()); - return config; - } - private static Map consumerConfig() { Map config = new HashMap<>(); config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); @@ -61,37 +45,7 @@ private static Map consumerConfig() { } @Test - void badProducerConfig() { - Assumptions.assumeFalse(Boolean.getBoolean("testLatestDeps")); - - // Bad config - wrong type for supplier - assertThatThrownBy( - () -> { - Map producerConfig = producerConfig(); - producerConfig.put( - OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER, "foo"); - new KafkaProducer<>(producerConfig).close(); - }) - .hasRootCauseInstanceOf(IllegalStateException.class) - .hasRootCauseMessage( - "Configuration property opentelemetry.kafka-producer-telemetry.supplier is not instance of KafkaProducerTelemetrySupplier"); - - // Bad config - supplier returns wrong type - assertThatThrownBy( - () -> { - Map producerConfig = producerConfig(); - producerConfig.put( - OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER, - (Supplier) () -> "not a KafkaProducerTelemetry"); - new KafkaProducer<>(producerConfig).close(); - }) - .hasRootCauseInstanceOf(IllegalStateException.class) - .hasRootCauseMessage( - "Configuration property opentelemetry.kafka-producer-telemetry.supplier is not instance of KafkaProducerTelemetrySupplier"); - } - - @Test - void badConsumerConfig() { + void badConfig() { Assumptions.assumeFalse(Boolean.getBoolean("testLatestDeps")); // Bad config - wrong type for supplier @@ -122,27 +76,18 @@ void badConsumerConfig() { @Test void serializableConfig() throws IOException, ClassNotFoundException { - testSerialize(producerConfig()); testSerialize(consumerConfig()); } @SuppressWarnings("unchecked") private static void testSerialize(Map map) throws IOException, ClassNotFoundException { - // Check that producer config has the supplier - Object producerSupplier = - map.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER); + // Check that consumer config has the supplier Object consumerSupplier = map.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_CONSUMER_TELEMETRY_SUPPLIER); - Supplier supplier = null; - if (producerSupplier instanceof KafkaProducerTelemetrySupplier) { - supplier = (KafkaProducerTelemetrySupplier) producerSupplier; - } else if (consumerSupplier instanceof KafkaConsumerTelemetrySupplier) { - supplier = (KafkaConsumerTelemetrySupplier) consumerSupplier; - } - - assertThat(supplier).isNotNull(); + assertThat(consumerSupplier).isInstanceOf(KafkaConsumerTelemetrySupplier.class); + KafkaConsumerTelemetrySupplier supplier = (KafkaConsumerTelemetrySupplier) consumerSupplier; assertThat(supplier.get()).isNotNull(); ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); @@ -169,8 +114,6 @@ protected Class resolveClass(ObjectStreamClass desc) try (ObjectInputStream inputStream = new CustomObjectInputStream(new ByteArrayInputStream(byteOutputStream.toByteArray()))) { Map result = (Map) inputStream.readObject(); - assertThat(result.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER)) - .isNull(); assertThat(result.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_CONSUMER_TELEMETRY_SUPPLIER)) .isNull(); } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryMetricsReporterTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryMetricsReporterTest.java index 68403de9b92b..86f71a0084d7 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryMetricsReporterTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryMetricsReporterTest.java @@ -24,7 +24,6 @@ import java.util.Map; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.clients.producer.KafkaProducer; -import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -45,8 +44,6 @@ protected InstrumentationExtension testing() { @Test void badConfig() { - Assumptions.assumeFalse(Boolean.getBoolean("testLatestDeps")); - // Bad producer config assertThatThrownBy( () -> { diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptorTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptorTest.java new file mode 100644 index 000000000000..8cc6c68b4759 --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptorTest.java @@ -0,0 +1,120 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.serialization.StringSerializer; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class OpenTelemetryProducerInterceptorTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + private static Map producerConfig() { + Map config = new HashMap<>(); + config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); + config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + config.putAll( + KafkaTelemetry.create(testing.getOpenTelemetry()).producerInterceptorConfigProperties()); + return config; + } + + @Test + void badConfig() { + Assumptions.assumeFalse(Boolean.getBoolean("testLatestDeps")); + + // Bad config - wrong type for supplier + assertThatThrownBy( + () -> { + Map producerConfig = producerConfig(); + producerConfig.put( + OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER, "foo"); + new KafkaProducer<>(producerConfig).close(); + }) + .hasRootCauseInstanceOf(IllegalStateException.class) + .hasRootCauseMessage( + "Configuration property opentelemetry.kafka-producer-telemetry.supplier is not instance of KafkaProducerTelemetrySupplier"); + + // Bad config - supplier returns wrong type + assertThatThrownBy( + () -> { + Map producerConfig = producerConfig(); + producerConfig.put( + OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER, + (Supplier) () -> "not a KafkaProducerTelemetry"); + new KafkaProducer<>(producerConfig).close(); + }) + .hasRootCauseInstanceOf(IllegalStateException.class) + .hasRootCauseMessage( + "Configuration property opentelemetry.kafka-producer-telemetry.supplier is not instance of KafkaProducerTelemetrySupplier"); + } + + @Test + void serializableConfig() throws IOException, ClassNotFoundException { + testSerialize(producerConfig()); + } + + @SuppressWarnings("unchecked") + private static void testSerialize(Map map) + throws IOException, ClassNotFoundException { + // Check that producer config has the supplier + Object producerSupplier = + map.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER); + + assertThat(producerSupplier).isInstanceOf(KafkaProducerTelemetrySupplier.class); + KafkaProducerTelemetrySupplier supplier = (KafkaProducerTelemetrySupplier) producerSupplier; + assertThat(supplier.get()).isNotNull(); + + ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); + try (ObjectOutputStream outputStream = new ObjectOutputStream(byteOutputStream)) { + outputStream.writeObject(map); + } + + class CustomObjectInputStream extends ObjectInputStream { + CustomObjectInputStream(InputStream inputStream) throws IOException { + super(inputStream); + } + + @Override + protected Class resolveClass(ObjectStreamClass desc) + throws IOException, ClassNotFoundException { + if (desc.getName().startsWith("io.opentelemetry.")) { + throw new IllegalStateException( + "Serial form contains opentelemetry class " + desc.getName()); + } + return super.resolveClass(desc); + } + } + + try (ObjectInputStream inputStream = + new CustomObjectInputStream(new ByteArrayInputStream(byteOutputStream.toByteArray()))) { + Map result = (Map) inputStream.readObject(); + assertThat(result.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER)) + .isNull(); + } + } +} From 810c22258766e6f124b4b2747a8b14386f034e9d Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 14:32:27 -0700 Subject: [PATCH 38/50] more --- .../v2_6/TracingConsumerInterceptor.java | 4 +- .../OpenTelemetryConsumerInterceptor.java | 4 +- .../OpenTelemetryProducerInterceptor.java | 4 +- .../OpenTelemetryConsumerInterceptorTest.java | 57 ++--------------- .../OpenTelemetryMetricsReporterTest.java | 51 ++------------- .../OpenTelemetryProducerInterceptorTest.java | 57 ++--------------- .../v2_6/internal/SerializationTestUtil.java | 64 +++++++++++++++++++ 7 files changed, 87 insertions(+), 154 deletions(-) create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/SerializationTestUtil.java diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java index ca3b23d7d15b..07f87fa85095 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java @@ -51,7 +51,9 @@ public ConsumerRecords onConsume(ConsumerRecords records) { // timer should be started before fetching ConsumerRecords, but there is no callback for that Timer timer = Timer.start(); Context receiveContext = - telemetry.getConsumerTelemetry().buildAndFinishSpan(records, consumerGroup, clientId, timer); + telemetry + .getConsumerTelemetry() + .buildAndFinishSpan(records, consumerGroup, clientId, timer); if (receiveContext == null) { receiveContext = Context.current(); } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java index c0d9b9703f3d..21b5d3ef8999 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptor.java @@ -65,7 +65,9 @@ public void configure(Map configs) { KafkaConsumerTelemetrySupplier supplier = getProperty( - configs, CONFIG_KEY_KAFKA_CONSUMER_TELEMETRY_SUPPLIER, KafkaConsumerTelemetrySupplier.class); + configs, + CONFIG_KEY_KAFKA_CONSUMER_TELEMETRY_SUPPLIER, + KafkaConsumerTelemetrySupplier.class); this.consumerTelemetry = supplier.get(); } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java index baba11a90ae5..4cb65adf70a3 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptor.java @@ -49,7 +49,9 @@ public void configure(Map configs) { KafkaProducerTelemetrySupplier supplier = getProperty( - configs, CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER, KafkaProducerTelemetrySupplier.class); + configs, + CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER, + KafkaProducerTelemetrySupplier.class); this.producerTelemetry = supplier.get(); } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptorTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptorTest.java index 8a92c684c2a0..ce5e9014c773 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptorTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryConsumerInterceptorTest.java @@ -5,26 +5,18 @@ package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; -import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.ObjectStreamClass; import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.common.serialization.StringDeserializer; -import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -46,14 +38,13 @@ private static Map consumerConfig() { @Test void badConfig() { - Assumptions.assumeFalse(Boolean.getBoolean("testLatestDeps")); - // Bad config - wrong type for supplier assertThatThrownBy( () -> { Map consumerConfig = consumerConfig(); consumerConfig.put( - OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_CONSUMER_TELEMETRY_SUPPLIER, "foo"); + OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_CONSUMER_TELEMETRY_SUPPLIER, + "foo"); new KafkaConsumer<>(consumerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) @@ -76,46 +67,8 @@ void badConfig() { @Test void serializableConfig() throws IOException, ClassNotFoundException { - testSerialize(consumerConfig()); - } - - @SuppressWarnings("unchecked") - private static void testSerialize(Map map) - throws IOException, ClassNotFoundException { - // Check that consumer config has the supplier - Object consumerSupplier = - map.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_CONSUMER_TELEMETRY_SUPPLIER); - - assertThat(consumerSupplier).isInstanceOf(KafkaConsumerTelemetrySupplier.class); - KafkaConsumerTelemetrySupplier supplier = (KafkaConsumerTelemetrySupplier) consumerSupplier; - assertThat(supplier.get()).isNotNull(); - - ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); - try (ObjectOutputStream outputStream = new ObjectOutputStream(byteOutputStream)) { - outputStream.writeObject(map); - } - - class CustomObjectInputStream extends ObjectInputStream { - CustomObjectInputStream(InputStream inputStream) throws IOException { - super(inputStream); - } - - @Override - protected Class resolveClass(ObjectStreamClass desc) - throws IOException, ClassNotFoundException { - if (desc.getName().startsWith("io.opentelemetry.")) { - throw new IllegalStateException( - "Serial form contains opentelemetry class " + desc.getName()); - } - return super.resolveClass(desc); - } - } - - try (ObjectInputStream inputStream = - new CustomObjectInputStream(new ByteArrayInputStream(byteOutputStream.toByteArray()))) { - Map result = (Map) inputStream.readObject(); - assertThat(result.get(OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_CONSUMER_TELEMETRY_SUPPLIER)) - .isNull(); - } + SerializationTestUtil.testSerialize( + consumerConfig(), + OpenTelemetryConsumerInterceptor.CONFIG_KEY_KAFKA_CONSUMER_TELEMETRY_SUPPLIER); } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryMetricsReporterTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryMetricsReporterTest.java index 86f71a0084d7..e92666d90755 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryMetricsReporterTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryMetricsReporterTest.java @@ -5,22 +5,14 @@ package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; -import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.AbstractOpenTelemetryMetricsReporterTest; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetryMetricsReporter; -import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.OpenTelemetrySupplier; import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.ObjectStreamClass; import java.util.Map; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.clients.producer.KafkaProducer; @@ -127,44 +119,9 @@ void badConfig() { @Test void serializableConfig() throws IOException, ClassNotFoundException { - testSerialize(producerConfig()); - testSerialize(consumerConfig()); - } - - @SuppressWarnings("unchecked") - private static void testSerialize(Map map) - throws IOException, ClassNotFoundException { - OpenTelemetrySupplier supplier = - (OpenTelemetrySupplier) - map.get(OpenTelemetryMetricsReporter.CONFIG_KEY_OPENTELEMETRY_SUPPLIER); - assertThat(supplier).isNotNull(); - assertThat(supplier.get()).isNotNull(); - ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); - try (ObjectOutputStream outputStream = new ObjectOutputStream(byteOutputStream)) { - outputStream.writeObject(map); - } - - class CustomObjectInputStream extends ObjectInputStream { - CustomObjectInputStream(InputStream inputStream) throws IOException { - super(inputStream); - } - - @Override - protected Class resolveClass(ObjectStreamClass desc) - throws IOException, ClassNotFoundException { - if (desc.getName().startsWith("io.opentelemetry.")) { - throw new IllegalStateException( - "Serial form contains opentelemetry class " + desc.getName()); - } - return super.resolveClass(desc); - } - } - - try (ObjectInputStream inputStream = - new CustomObjectInputStream(new ByteArrayInputStream(byteOutputStream.toByteArray()))) { - Map result = (Map) inputStream.readObject(); - assertThat(result.get(OpenTelemetryMetricsReporter.CONFIG_KEY_OPENTELEMETRY_SUPPLIER)) - .isNull(); - } + SerializationTestUtil.testSerialize( + producerConfig(), OpenTelemetryMetricsReporter.CONFIG_KEY_OPENTELEMETRY_SUPPLIER); + SerializationTestUtil.testSerialize( + consumerConfig(), OpenTelemetryMetricsReporter.CONFIG_KEY_OPENTELEMETRY_SUPPLIER); } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptorTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptorTest.java index 8cc6c68b4759..860007c9cad5 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptorTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/OpenTelemetryProducerInterceptorTest.java @@ -5,26 +5,18 @@ package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; -import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.ObjectStreamClass; import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.serialization.StringSerializer; -import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -45,14 +37,13 @@ private static Map producerConfig() { @Test void badConfig() { - Assumptions.assumeFalse(Boolean.getBoolean("testLatestDeps")); - // Bad config - wrong type for supplier assertThatThrownBy( () -> { Map producerConfig = producerConfig(); producerConfig.put( - OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER, "foo"); + OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER, + "foo"); new KafkaProducer<>(producerConfig).close(); }) .hasRootCauseInstanceOf(IllegalStateException.class) @@ -75,46 +66,8 @@ void badConfig() { @Test void serializableConfig() throws IOException, ClassNotFoundException { - testSerialize(producerConfig()); - } - - @SuppressWarnings("unchecked") - private static void testSerialize(Map map) - throws IOException, ClassNotFoundException { - // Check that producer config has the supplier - Object producerSupplier = - map.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER); - - assertThat(producerSupplier).isInstanceOf(KafkaProducerTelemetrySupplier.class); - KafkaProducerTelemetrySupplier supplier = (KafkaProducerTelemetrySupplier) producerSupplier; - assertThat(supplier.get()).isNotNull(); - - ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); - try (ObjectOutputStream outputStream = new ObjectOutputStream(byteOutputStream)) { - outputStream.writeObject(map); - } - - class CustomObjectInputStream extends ObjectInputStream { - CustomObjectInputStream(InputStream inputStream) throws IOException { - super(inputStream); - } - - @Override - protected Class resolveClass(ObjectStreamClass desc) - throws IOException, ClassNotFoundException { - if (desc.getName().startsWith("io.opentelemetry.")) { - throw new IllegalStateException( - "Serial form contains opentelemetry class " + desc.getName()); - } - return super.resolveClass(desc); - } - } - - try (ObjectInputStream inputStream = - new CustomObjectInputStream(new ByteArrayInputStream(byteOutputStream.toByteArray()))) { - Map result = (Map) inputStream.readObject(); - assertThat(result.get(OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER)) - .isNull(); - } + SerializationTestUtil.testSerialize( + producerConfig(), + OpenTelemetryProducerInterceptor.CONFIG_KEY_KAFKA_PRODUCER_TELEMETRY_SUPPLIER); } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/SerializationTestUtil.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/SerializationTestUtil.java new file mode 100644 index 000000000000..2b948a9b73ea --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/SerializationTestUtil.java @@ -0,0 +1,64 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6.internal; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.util.Map; + +final class SerializationTestUtil { + + /** + * Tests that a configuration map can be serialized and that OpenTelemetry classes are replaced + * with null during serialization (via writeReplace()). + * + * @param map the configuration map to serialize + * @param configKey the key to check for in the deserialized map + */ + static void testSerialize(Map map, String configKey) + throws IOException, ClassNotFoundException { + Object supplierValue = map.get(configKey); + assertThat(supplierValue).isNotNull(); + + ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); + try (ObjectOutputStream outputStream = new ObjectOutputStream(byteOutputStream)) { + outputStream.writeObject(map); + } + + class CustomObjectInputStream extends ObjectInputStream { + CustomObjectInputStream(InputStream inputStream) throws IOException { + super(inputStream); + } + + @Override + protected Class resolveClass(ObjectStreamClass desc) + throws IOException, ClassNotFoundException { + if (desc.getName().startsWith("io.opentelemetry.")) { + throw new IllegalStateException( + "Serial form contains opentelemetry class " + desc.getName()); + } + return super.resolveClass(desc); + } + } + + try (ObjectInputStream inputStream = + new CustomObjectInputStream(new ByteArrayInputStream(byteOutputStream.toByteArray()))) { + @SuppressWarnings("unchecked") + Map result = (Map) inputStream.readObject(); + // After deserialization, the supplier should be null (replaced via writeReplace()) + assertThat(result.get(configKey)).isNull(); + } + } + + private SerializationTestUtil() {} +} From a2286d825ae23beeb1c899735aa39994ce57c03e Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 14:45:01 -0700 Subject: [PATCH 39/50] up --- .../v2_6/internal/KafkaConsumerTelemetrySupplier.java | 7 +++---- .../v2_6/internal/KafkaProducerTelemetrySupplier.java | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetrySupplier.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetrySupplier.java index fa0d878461cd..4613e202ad43 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetrySupplier.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetrySupplier.java @@ -19,6 +19,8 @@ public class KafkaConsumerTelemetrySupplier implements Supplier, Serializable { + private static final long serialVersionUID = 1L; + private final KafkaConsumerTelemetry consumerTelemetry; public KafkaConsumerTelemetrySupplier(KafkaConsumerTelemetry consumerTelemetry) { @@ -30,11 +32,8 @@ public KafkaConsumerTelemetry get() { return consumerTelemetry; } - /** - * Replaces this object with null in the serialization stream. This ensures the supplier does not - * get serialized. - */ private Object writeReplace() { + // serialize this object to null return null; } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetrySupplier.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetrySupplier.java index 6853ebd87b33..87afe340c9a9 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetrySupplier.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetrySupplier.java @@ -19,6 +19,8 @@ public class KafkaProducerTelemetrySupplier implements Supplier, Serializable { + private static final long serialVersionUID = 1L; + private final KafkaProducerTelemetry producerTelemetry; public KafkaProducerTelemetrySupplier(KafkaProducerTelemetry producerTelemetry) { @@ -30,11 +32,8 @@ public KafkaProducerTelemetry get() { return producerTelemetry; } - /** - * Replaces this object with null in the serialization stream. This ensures the supplier does not - * get serialized. - */ private Object writeReplace() { + // serialize this object to null return null; } } From 536c1cd6cee8950b07f72d27682dddf102ad9038 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 15:35:37 -0700 Subject: [PATCH 40/50] up --- .../kafkaclients/v2_6/internal/SerializationTestUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/SerializationTestUtil.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/SerializationTestUtil.java index 2b948a9b73ea..911629701596 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/SerializationTestUtil.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/SerializationTestUtil.java @@ -16,7 +16,7 @@ import java.io.ObjectStreamClass; import java.util.Map; -final class SerializationTestUtil { +class SerializationTestUtil { /** * Tests that a configuration map can be serialized and that OpenTelemetry classes are replaced From bbd21cbaa49e51931fb562da582327d3beed53f1 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 16:23:20 -0700 Subject: [PATCH 41/50] fix --- .../kafkaclients/v2_6/KafkaTelemetry.java | 76 ++++++++++++++++++- .../v2_6/internal/KafkaConsumerTelemetry.java | 14 ++++ .../v2_6/internal/KafkaProducerTelemetry.java | 72 ++++++++++++++++++ .../v2_6/internal/SerializationTestUtil.java | 14 ++-- 4 files changed, 166 insertions(+), 10 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java index 6bea398a59bd..b11608db4c05 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java @@ -6,7 +6,11 @@ package io.opentelemetry.instrumentation.kafkaclients.v2_6; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.internal.Timer; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContext; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContextUtil; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProcessRequest; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProducerRequest; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaReceiveRequest; @@ -19,14 +23,21 @@ import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.KafkaProducerTelemetrySupplier; import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryConsumerInterceptor; import io.opentelemetry.instrumentation.kafkaclients.v2_6.internal.OpenTelemetryProducerInterceptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Proxy; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.apache.kafka.clients.CommonClientConfigs; +import org.apache.kafka.clients.consumer.Consumer; import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.Callback; import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.Producer; import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.clients.producer.RecordMetadata; import org.apache.kafka.common.metrics.MetricsReporter; @@ -52,16 +63,77 @@ public final class KafkaTelemetry { new KafkaConsumerTelemetry(consumerReceiveInstrumenter, consumerProcessInstrumenter); } - @Deprecated + // this method can be removed when the deprecated TracingProducerInterceptor is removed KafkaProducerTelemetry getProducerTelemetry() { return producerTelemetry; } - @Deprecated + // this method can be removed when the deprecated TracingProducerInterceptor is removed KafkaConsumerTelemetry getConsumerTelemetry() { return consumerTelemetry; } + // TODO consider if this is needed in public API + @SuppressWarnings("unchecked") + public Producer wrap(Producer producer) { + return (Producer) + Proxy.newProxyInstance( + KafkaTelemetry.class.getClassLoader(), + new Class[] {Producer.class}, + (proxy, method, args) -> { + // Future send(ProducerRecord record) + // Future send(ProducerRecord record, Callback callback) + if ("send".equals(method.getName()) + && method.getParameterCount() >= 1 + && method.getParameterTypes()[0] == ProducerRecord.class) { + ProducerRecord record = (ProducerRecord) args[0]; + Callback callback = + method.getParameterCount() >= 2 + && method.getParameterTypes()[1] == Callback.class + ? (Callback) args[1] + : null; + return producerTelemetry.buildAndInjectSpan(record, producer, callback, producer::send); + } + try { + return method.invoke(producer, args); + } catch (InvocationTargetException exception) { + throw exception.getCause(); + } + }); + } + + // TODO consider if this is needed in public API + @SuppressWarnings("unchecked") + public Consumer wrap(Consumer consumer) { + return (Consumer) + Proxy.newProxyInstance( + KafkaTelemetry.class.getClassLoader(), + new Class[] {Consumer.class}, + (proxy, method, args) -> { + Object result; + Timer timer = "poll".equals(method.getName()) ? Timer.start() : null; + try { + result = method.invoke(consumer, args); + } catch (InvocationTargetException exception) { + throw exception.getCause(); + } + // ConsumerRecords poll(long timeout) + // ConsumerRecords poll(Duration duration) + if ("poll".equals(method.getName()) && result instanceof ConsumerRecords) { + ConsumerRecords consumerRecords = (ConsumerRecords) result; + Context receiveContext = + consumerTelemetry.buildAndFinishSpan(consumerRecords, consumer, timer); + if (receiveContext == null) { + receiveContext = Context.current(); + } + KafkaConsumerContext consumerContext = + KafkaConsumerContextUtil.create(receiveContext, consumer); + result = consumerTelemetry.addTracing(consumerRecords, consumerContext); + } + return result; + }); + } + /** Returns a new {@link KafkaTelemetry} configured with the given {@link OpenTelemetry}. */ public static KafkaTelemetry create(OpenTelemetry openTelemetry) { return builder(openTelemetry).build(); diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetry.java index 0bc941a31254..4fec7f291668 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetry.java @@ -12,10 +12,12 @@ import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaConsumerContext; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProcessRequest; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaReceiveRequest; +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaUtil; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.TracingList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import org.apache.kafka.clients.consumer.Consumer; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.common.TopicPartition; @@ -38,6 +40,18 @@ public KafkaConsumerTelemetry( this.consumerProcessInstrumenter = consumerProcessInstrumenter; } + // this getter is needed for the deprecated wrap() methods in KafkaTelemetry + public Instrumenter getConsumerProcessInstrumenter() { + return consumerProcessInstrumenter; + } + + // this overload is needed for the deprecated wrap() methods in KafkaTelemetry + public Context buildAndFinishSpan( + ConsumerRecords records, Consumer consumer, Timer timer) { + return buildAndFinishSpan( + records, KafkaUtil.getConsumerGroup(consumer), KafkaUtil.getClientId(consumer), timer); + } + public Context buildAndFinishSpan( ConsumerRecords records, String consumerGroup, String clientId, Timer timer) { if (records.isEmpty()) { diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetry.java index 4b4bc99484e1..d86559d37da2 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetry.java @@ -8,12 +8,17 @@ import static java.util.logging.Level.WARNING; import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.context.propagation.TextMapSetter; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaHeadersSetter; import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaProducerRequest; +import java.util.concurrent.Future; +import java.util.function.BiFunction; import java.util.logging.Logger; +import org.apache.kafka.clients.producer.Callback; +import org.apache.kafka.clients.producer.Producer; import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.clients.producer.RecordMetadata; import org.apache.kafka.common.header.Headers; @@ -42,6 +47,19 @@ public KafkaProducerTelemetry( this.producerPropagationEnabled = producerPropagationEnabled; } + // these getters are needed for the deprecated wrap() methods in KafkaTelemetry + public TextMapPropagator getPropagator() { + return propagator; + } + + public Instrumenter getProducerInstrumenter() { + return producerInstrumenter; + } + + public TextMapSetter getSetter() { + return SETTER; + } + /** * Build and inject span into record. * @@ -66,4 +84,58 @@ public void buildAndInjectSpan(ProducerRecord record, String client } producerInstrumenter.end(context, request, null, null); } + + /** + * Build and inject span into record. + * + * @param record the producer record to inject span info. + * @param callback the producer send callback + * @return send function's result + */ + @SuppressWarnings("FutureReturnValueIgnored") + public Future buildAndInjectSpan( + ProducerRecord record, + Producer producer, + Callback callback, + BiFunction, Callback, Future> sendFn) { + Context parentContext = Context.current(); + + KafkaProducerRequest request = KafkaProducerRequest.create(record, producer); + if (!producerInstrumenter.shouldStart(parentContext, request)) { + return sendFn.apply(record, callback); + } + + Context context = producerInstrumenter.start(parentContext, request); + propagator.inject(context, record.headers(), SETTER); + + try (Scope ignored = context.makeCurrent()) { + return sendFn.apply(record, new ProducerCallback(callback, parentContext, context, request)); + } + } + + private class ProducerCallback implements Callback { + private final Callback callback; + private final Context parentContext; + private final Context context; + private final KafkaProducerRequest request; + + ProducerCallback( + Callback callback, Context parentContext, Context context, KafkaProducerRequest request) { + this.callback = callback; + this.parentContext = parentContext; + this.context = context; + this.request = request; + } + + @Override + public void onCompletion(RecordMetadata metadata, Exception exception) { + producerInstrumenter.end(context, request, metadata, exception); + + if (callback != null) { + try (Scope ignored = parentContext.makeCurrent()) { + callback.onCompletion(metadata, exception); + } + } + } + } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/SerializationTestUtil.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/SerializationTestUtil.java index 911629701596..f3d5dee71ab9 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/SerializationTestUtil.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/SerializationTestUtil.java @@ -19,15 +19,13 @@ class SerializationTestUtil { /** - * Tests that a configuration map can be serialized and that OpenTelemetry classes are replaced - * with null during serialization (via writeReplace()). - * - * @param map the configuration map to serialize - * @param configKey the key to check for in the deserialized map + * Tests that a configuration map can be serialized and that supplier instance is replaced with + * null during serialization (via writeReplace()). */ - static void testSerialize(Map map, String configKey) + static void testSerialize(Map map, String supplierKey) throws IOException, ClassNotFoundException { - Object supplierValue = map.get(configKey); + + Object supplierValue = map.get(supplierKey); assertThat(supplierValue).isNotNull(); ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); @@ -56,7 +54,7 @@ protected Class resolveClass(ObjectStreamClass desc) @SuppressWarnings("unchecked") Map result = (Map) inputStream.readObject(); // After deserialization, the supplier should be null (replaced via writeReplace()) - assertThat(result.get(configKey)).isNull(); + assertThat(result.get(supplierKey)).isNull(); } } From 385bc151be89be27086df5f8b4405e27d8e342a0 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 16:31:30 -0700 Subject: [PATCH 42/50] up --- .../instrumentation/kafkaclients/v2_6/KafkaTelemetry.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java index b11608db4c05..a6f30b27bf76 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java @@ -73,7 +73,6 @@ KafkaConsumerTelemetry getConsumerTelemetry() { return consumerTelemetry; } - // TODO consider if this is needed in public API @SuppressWarnings("unchecked") public Producer wrap(Producer producer) { return (Producer) @@ -102,7 +101,6 @@ public Producer wrap(Producer producer) { }); } - // TODO consider if this is needed in public API @SuppressWarnings("unchecked") public Consumer wrap(Consumer consumer) { return (Consumer) From 84c92121e5dbf2debfe47c3d36b2e969576c336a Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 16:35:05 -0700 Subject: [PATCH 43/50] up --- .../kafkaclients/v2_6/KafkaTelemetry.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java index a6f30b27bf76..265e48b5c809 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java @@ -63,6 +63,18 @@ public final class KafkaTelemetry { new KafkaConsumerTelemetry(consumerReceiveInstrumenter, consumerProcessInstrumenter); } + /** Returns a new {@link KafkaTelemetry} configured with the given {@link OpenTelemetry}. */ + public static KafkaTelemetry create(OpenTelemetry openTelemetry) { + return builder(openTelemetry).build(); + } + + /** + * Returns a new {@link KafkaTelemetryBuilder} configured with the given {@link OpenTelemetry}. + */ + public static KafkaTelemetryBuilder builder(OpenTelemetry openTelemetry) { + return new KafkaTelemetryBuilder(openTelemetry); + } + // this method can be removed when the deprecated TracingProducerInterceptor is removed KafkaProducerTelemetry getProducerTelemetry() { return producerTelemetry; @@ -132,17 +144,6 @@ public Consumer wrap(Consumer consumer) { }); } - /** Returns a new {@link KafkaTelemetry} configured with the given {@link OpenTelemetry}. */ - public static KafkaTelemetry create(OpenTelemetry openTelemetry) { - return builder(openTelemetry).build(); - } - - /** - * Returns a new {@link KafkaTelemetryBuilder} configured with the given {@link OpenTelemetry}. - */ - public static KafkaTelemetryBuilder builder(OpenTelemetry openTelemetry) { - return new KafkaTelemetryBuilder(openTelemetry); - } /** * Produces a set of kafka client config properties (consumer or producer) to register a {@link From 686de0cb94c8fd97e2e0237eb42cef0ba836ef8d Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 16:42:48 -0700 Subject: [PATCH 44/50] up --- .../instrumentation/kafkaclients/v2_6/KafkaTelemetry.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java index 265e48b5c809..d8973026f48b 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java @@ -80,11 +80,12 @@ KafkaProducerTelemetry getProducerTelemetry() { return producerTelemetry; } - // this method can be removed when the deprecated TracingProducerInterceptor is removed + // this method can be removed when the deprecated TracingProducerInterceptor is removed KafkaConsumerTelemetry getConsumerTelemetry() { return consumerTelemetry; } + /** Returns a decorated {@link Producer} that emits spans for each sent message. */ @SuppressWarnings("unchecked") public Producer wrap(Producer producer) { return (Producer) @@ -103,7 +104,8 @@ public Producer wrap(Producer producer) { && method.getParameterTypes()[1] == Callback.class ? (Callback) args[1] : null; - return producerTelemetry.buildAndInjectSpan(record, producer, callback, producer::send); + return producerTelemetry.buildAndInjectSpan( + record, producer, callback, producer::send); } try { return method.invoke(producer, args); @@ -113,6 +115,7 @@ public Producer wrap(Producer producer) { }); } + /** Returns a decorated {@link Consumer} that consumes spans for each received message. */ @SuppressWarnings("unchecked") public Consumer wrap(Consumer consumer) { return (Consumer) @@ -144,7 +147,6 @@ public Consumer wrap(Consumer consumer) { }); } - /** * Produces a set of kafka client config properties (consumer or producer) to register a {@link * MetricsReporter} that records metrics to an {@code openTelemetry} instance. Add these resulting From eb7ecf9204bbcae87553b70ea2a9d2c4ebc0828a Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 16:44:24 -0700 Subject: [PATCH 45/50] up --- .../instrumentation/kafkaclients/v2_6/KafkaTelemetry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java index d8973026f48b..8378c4616ffa 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/KafkaTelemetry.java @@ -80,7 +80,7 @@ KafkaProducerTelemetry getProducerTelemetry() { return producerTelemetry; } - // this method can be removed when the deprecated TracingProducerInterceptor is removed + // this method can be removed when the deprecated TracingProducerInterceptor is removed KafkaConsumerTelemetry getConsumerTelemetry() { return consumerTelemetry; } From 3fe741b00d76f6fa07df4fe1b4fac586cdf4efe9 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 16:51:15 -0700 Subject: [PATCH 46/50] up --- .../v2_6/internal/KafkaConsumerTelemetry.java | 36 ++++++++----------- .../v2_6/internal/KafkaProducerTelemetry.java | 13 ------- 2 files changed, 15 insertions(+), 34 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetry.java index 4fec7f291668..97debc510193 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaConsumerTelemetry.java @@ -40,12 +40,23 @@ public KafkaConsumerTelemetry( this.consumerProcessInstrumenter = consumerProcessInstrumenter; } - // this getter is needed for the deprecated wrap() methods in KafkaTelemetry - public Instrumenter getConsumerProcessInstrumenter() { - return consumerProcessInstrumenter; + public ConsumerRecords addTracing( + ConsumerRecords consumerRecords, KafkaConsumerContext consumerContext) { + if (consumerRecords.isEmpty()) { + return consumerRecords; + } + + Map>> records = new LinkedHashMap<>(); + for (TopicPartition partition : consumerRecords.partitions()) { + List> list = consumerRecords.records(partition); + if (list != null && !list.isEmpty()) { + list = TracingList.wrap(list, consumerProcessInstrumenter, () -> true, consumerContext); + } + records.put(partition, list); + } + return new ConsumerRecords<>(records); } - // this overload is needed for the deprecated wrap() methods in KafkaTelemetry public Context buildAndFinishSpan( ConsumerRecords records, Consumer consumer, Timer timer) { return buildAndFinishSpan( @@ -78,21 +89,4 @@ public Context buildAndFinishSpan( // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/messaging/messaging-spans.md#batch-receiving return context; } - - public ConsumerRecords addTracing( - ConsumerRecords consumerRecords, KafkaConsumerContext consumerContext) { - if (consumerRecords.isEmpty()) { - return consumerRecords; - } - - Map>> records = new LinkedHashMap<>(); - for (TopicPartition partition : consumerRecords.partitions()) { - List> list = consumerRecords.records(partition); - if (list != null && !list.isEmpty()) { - list = TracingList.wrap(list, consumerProcessInstrumenter, () -> true, consumerContext); - } - records.put(partition, list); - } - return new ConsumerRecords<>(records); - } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetry.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetry.java index d86559d37da2..acd7099125f0 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetry.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/internal/KafkaProducerTelemetry.java @@ -47,19 +47,6 @@ public KafkaProducerTelemetry( this.producerPropagationEnabled = producerPropagationEnabled; } - // these getters are needed for the deprecated wrap() methods in KafkaTelemetry - public TextMapPropagator getPropagator() { - return propagator; - } - - public Instrumenter getProducerInstrumenter() { - return producerInstrumenter; - } - - public TextMapSetter getSetter() { - return SETTER; - } - /** * Build and inject span into record. * From 4f58c8dda5220800cad75a2097cd748dbcea6365 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 17:06:48 -0700 Subject: [PATCH 47/50] revert --- .../v2_6/AbstractWrapperTest.java | 82 ++++++++++ .../v2_6/ExceptionHandlingTest.java | 58 +++++++ .../v2_6/WrapperSuppressReceiveSpansTest.java | 110 +++++++++++++ .../kafkaclients/v2_6/WrapperTest.java | 151 ++++++++++++++++++ 4 files changed, 401 insertions(+) create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractWrapperTest.java create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/ExceptionHandlingTest.java create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java create mode 100644 instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperTest.java diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractWrapperTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractWrapperTest.java new file mode 100644 index 000000000000..4dc84990ef94 --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/AbstractWrapperTest.java @@ -0,0 +1,82 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6; + +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.instrumentation.kafkaclients.common.v0_11.internal.KafkaClientBaseTest; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import org.apache.kafka.clients.consumer.Consumer; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +abstract class AbstractWrapperTest extends KafkaClientBaseTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + static final String greeting = "Hello Kafka!"; + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testWrappers(boolean testHeaders) throws InterruptedException { + KafkaTelemetryBuilder telemetryBuilder = + KafkaTelemetry.builder(testing.getOpenTelemetry()) + .setCapturedHeaders(singletonList("Test-Message-Header")) + // TODO run tests both with and without experimental span attributes + .setCaptureExperimentalSpanAttributes(true); + configure(telemetryBuilder); + KafkaTelemetry telemetry = telemetryBuilder.build(); + + Producer wrappedProducer = telemetry.wrap(producer); + + testing.runWithSpan( + "parent", + () -> { + ProducerRecord producerRecord = + new ProducerRecord<>(SHARED_TOPIC, greeting); + if (testHeaders) { + producerRecord + .headers() + .add("Test-Message-Header", "test".getBytes(StandardCharsets.UTF_8)); + } + wrappedProducer.send( + producerRecord, + (meta, ex) -> { + if (ex == null) { + testing.runWithSpan("producer callback", () -> {}); + } else { + testing.runWithSpan("producer exception: " + ex, () -> {}); + } + }); + }); + + awaitUntilConsumerIsReady(); + Consumer wrappedConsumer = telemetry.wrap(consumer); + ConsumerRecords records = wrappedConsumer.poll(Duration.ofSeconds(10)); + assertThat(records.count()).isEqualTo(1); + for (ConsumerRecord record : records) { + assertThat(record.value()).isEqualTo(greeting); + assertThat(record.key()).isNull(); + testing.runWithSpan("process child", () -> {}); + } + + assertTraces(testHeaders); + } + + abstract void configure(KafkaTelemetryBuilder builder); + + abstract void assertTraces(boolean testHeaders); +} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/ExceptionHandlingTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/ExceptionHandlingTest.java new file mode 100644 index 000000000000..cdc7ca77e7ec --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/ExceptionHandlingTest.java @@ -0,0 +1,58 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import org.apache.kafka.clients.consumer.Consumer; +import org.apache.kafka.clients.producer.Producer; +import org.junit.jupiter.api.Test; + +class ExceptionHandlingTest extends KafkaClientBaseTest { + + @Test + void testConsumerPropagatesException() { + Consumer throwingConsumer = + (Consumer) + Proxy.newProxyInstance( + getClass().getClassLoader(), + new Class[] {Consumer.class}, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) { + throw new IllegalStateException("Test exception"); + } + }); + Consumer wrappedConsumer = + KafkaTelemetry.create(testing.getOpenTelemetry()).wrap(throwingConsumer); + assertThatThrownBy(() -> wrappedConsumer.poll(null)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Test exception"); + } + + @Test + void testProducerPropagatesException() { + Producer throwingProducer = + (Producer) + Proxy.newProxyInstance( + getClass().getClassLoader(), + new Class[] {Producer.class}, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) { + throw new IllegalStateException("Test exception"); + } + }); + Producer wrappedProducer = + KafkaTelemetry.create(testing.getOpenTelemetry()).wrap(throwingProducer); + assertThatThrownBy(() -> wrappedProducer.send(null)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Test exception"); + } +} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java new file mode 100644 index 000000000000..32a10cb5c0fc --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java @@ -0,0 +1,110 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_PARTITION_ID; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_KAFKA_CONSUMER_GROUP; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_KAFKA_MESSAGE_OFFSET; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.assertj.core.api.AbstractLongAssert; +import org.assertj.core.api.AbstractStringAssert; + +class WrapperSuppressReceiveSpansTest extends AbstractWrapperTest { + + @Override + void configure(KafkaTelemetryBuilder builder) { + builder.setMessagingReceiveInstrumentationEnabled(false); + } + + @Override + void assertTraces(boolean testHeaders) { + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> + span.hasName(SHARED_TOPIC + " publish") + .hasKind(SpanKind.PRODUCER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(sendAttributes(testHeaders)), + span -> + span.hasName("producer callback") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)), + span -> + span.hasName(SHARED_TOPIC + " process") + .hasKind(SpanKind.CONSUMER) + .hasParent(trace.getSpan(1)) + .hasLinksSatisfying(links -> assertThat(links).isEmpty()) + .hasAttributesSatisfyingExactly(processAttributes(greeting, testHeaders)), + span -> + span.hasName("process child") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(3)))); + } + + @SuppressWarnings("deprecation") // using deprecated semconv + protected static List sendAttributes(boolean testHeaders) { + List assertions = + new ArrayList<>( + Arrays.asList( + equalTo(MESSAGING_SYSTEM, "kafka"), + equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), + equalTo(MESSAGING_OPERATION, "publish"), + satisfies(MESSAGING_CLIENT_ID, stringAssert -> stringAssert.startsWith("producer")), + satisfies(MESSAGING_DESTINATION_PARTITION_ID, AbstractStringAssert::isNotEmpty), + satisfies(MESSAGING_KAFKA_MESSAGE_OFFSET, AbstractLongAssert::isNotNegative))); + if (testHeaders) { + assertions.add( + equalTo( + AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), + Collections.singletonList("test"))); + } + return assertions; + } + + @SuppressWarnings("deprecation") // using deprecated semconv + private static List processAttributes(String greeting, boolean testHeaders) { + List assertions = + new ArrayList<>( + Arrays.asList( + equalTo(MESSAGING_SYSTEM, "kafka"), + equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), + equalTo(MESSAGING_OPERATION, "process"), + equalTo( + MESSAGING_MESSAGE_BODY_SIZE, greeting.getBytes(StandardCharsets.UTF_8).length), + satisfies(MESSAGING_DESTINATION_PARTITION_ID, AbstractStringAssert::isNotEmpty), + satisfies(MESSAGING_KAFKA_MESSAGE_OFFSET, AbstractLongAssert::isNotNegative), + satisfies( + AttributeKey.longKey("kafka.record.queue_time_ms"), + AbstractLongAssert::isNotNegative), + equalTo(MESSAGING_KAFKA_CONSUMER_GROUP, "test"), + satisfies( + MESSAGING_CLIENT_ID, stringAssert -> stringAssert.startsWith("consumer")))); + if (testHeaders) { + assertions.add( + equalTo( + AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), + Collections.singletonList("test"))); + } + return assertions; + } +} diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperTest.java new file mode 100644 index 000000000000..85ddd5aa4cad --- /dev/null +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperTest.java @@ -0,0 +1,151 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.kafkaclients.v2_6; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_BATCH_MESSAGE_COUNT; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_PARTITION_ID; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_KAFKA_CONSUMER_GROUP; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_KAFKA_MESSAGE_OFFSET; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import io.opentelemetry.sdk.trace.data.LinkData; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import org.assertj.core.api.AbstractLongAssert; +import org.assertj.core.api.AbstractStringAssert; + +class WrapperTest extends AbstractWrapperTest { + + @Override + void configure(KafkaTelemetryBuilder builder) { + builder.setMessagingReceiveInstrumentationEnabled(true); + } + + @Override + void assertTraces(boolean testHeaders) { + AtomicReference producerSpanContext = new AtomicReference<>(); + testing.waitAndAssertTraces( + trace -> { + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> + span.hasName(SHARED_TOPIC + " publish") + .hasKind(SpanKind.PRODUCER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(sendAttributes(testHeaders)), + span -> + span.hasName("producer callback") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0))); + SpanContext spanContext = trace.getSpan(1).getSpanContext(); + producerSpanContext.set( + SpanContext.createFromRemoteParent( + spanContext.getTraceId(), + spanContext.getSpanId(), + spanContext.getTraceFlags(), + spanContext.getTraceState())); + }, + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName(SHARED_TOPIC + " receive") + .hasKind(SpanKind.CONSUMER) + .hasNoParent() + .hasLinksSatisfying(links -> assertThat(links).isEmpty()) + .hasAttributesSatisfyingExactly(receiveAttributes(testHeaders)), + span -> + span.hasName(SHARED_TOPIC + " process") + .hasKind(SpanKind.CONSUMER) + .hasParent(trace.getSpan(0)) + .hasLinks(LinkData.create(producerSpanContext.get())) + .hasAttributesSatisfyingExactly(processAttributes(greeting, testHeaders)), + span -> + span.hasName("process child") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1)))); + } + + @SuppressWarnings("deprecation") // using deprecated semconv + protected static List sendAttributes(boolean testHeaders) { + List assertions = + new ArrayList<>( + Arrays.asList( + equalTo(MESSAGING_SYSTEM, "kafka"), + equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), + equalTo(MESSAGING_OPERATION, "publish"), + satisfies(MESSAGING_CLIENT_ID, stringAssert -> stringAssert.startsWith("producer")), + satisfies(MESSAGING_DESTINATION_PARTITION_ID, AbstractStringAssert::isNotEmpty), + satisfies(MESSAGING_KAFKA_MESSAGE_OFFSET, AbstractLongAssert::isNotNegative))); + if (testHeaders) { + assertions.add( + equalTo( + AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), + Collections.singletonList("test"))); + } + return assertions; + } + + @SuppressWarnings("deprecation") // using deprecated semconv + private static List processAttributes(String greeting, boolean testHeaders) { + List assertions = + new ArrayList<>( + Arrays.asList( + equalTo(MESSAGING_SYSTEM, "kafka"), + equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), + equalTo(MESSAGING_OPERATION, "process"), + equalTo( + MESSAGING_MESSAGE_BODY_SIZE, greeting.getBytes(StandardCharsets.UTF_8).length), + satisfies(MESSAGING_DESTINATION_PARTITION_ID, AbstractStringAssert::isNotEmpty), + satisfies(MESSAGING_KAFKA_MESSAGE_OFFSET, AbstractLongAssert::isNotNegative), + satisfies( + AttributeKey.longKey("kafka.record.queue_time_ms"), + AbstractLongAssert::isNotNegative), + equalTo(MESSAGING_KAFKA_CONSUMER_GROUP, "test"), + satisfies( + MESSAGING_CLIENT_ID, stringAssert -> stringAssert.startsWith("consumer")))); + if (testHeaders) { + assertions.add( + equalTo( + AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), + Collections.singletonList("test"))); + } + return assertions; + } + + @SuppressWarnings("deprecation") // using deprecated semconv + protected static List receiveAttributes(boolean testHeaders) { + List assertions = + new ArrayList<>( + Arrays.asList( + equalTo(MESSAGING_SYSTEM, "kafka"), + equalTo(MESSAGING_DESTINATION_NAME, SHARED_TOPIC), + equalTo(MESSAGING_OPERATION, "receive"), + equalTo(MESSAGING_KAFKA_CONSUMER_GROUP, "test"), + satisfies(MESSAGING_CLIENT_ID, stringAssert -> stringAssert.startsWith("consumer")), + equalTo(MESSAGING_BATCH_MESSAGE_COUNT, 1))); + if (testHeaders) { + assertions.add( + equalTo( + AttributeKey.stringArrayKey("messaging.header.Test_Message_Header"), + Collections.singletonList("test"))); + } + return assertions; + } +} From 45c3e11371614acbe04525e8d9aeed4e8f46421c Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 17:10:22 -0700 Subject: [PATCH 48/50] more --- .../v2_6/ExceptionHandlingTest.java | 56 ++++++++++--------- .../v2_6/WrapperSuppressReceiveSpansTest.java | 11 ++-- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/ExceptionHandlingTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/ExceptionHandlingTest.java index cdc7ca77e7ec..ca9b376a7322 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/ExceptionHandlingTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/ExceptionHandlingTest.java @@ -7,52 +7,54 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import java.lang.reflect.Proxy; +import java.time.Duration; import org.apache.kafka.clients.consumer.Consumer; import org.apache.kafka.clients.producer.Producer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; -class ExceptionHandlingTest extends KafkaClientBaseTest { +class ExceptionHandlingTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); @Test - void testConsumerPropagatesException() { - Consumer throwingConsumer = + void testConsumerExceptionPropagatesToCaller() { + Consumer consumer = (Consumer) Proxy.newProxyInstance( - getClass().getClassLoader(), + ExceptionHandlingTest.class.getClassLoader(), new Class[] {Consumer.class}, - new InvocationHandler() { - @Override - public Object invoke(Object proxy, Method method, Object[] args) { - throw new IllegalStateException("Test exception"); - } + (proxy, method, args) -> { + throw new IllegalStateException("can't invoke"); }); - Consumer wrappedConsumer = - KafkaTelemetry.create(testing.getOpenTelemetry()).wrap(throwingConsumer); - assertThatThrownBy(() -> wrappedConsumer.poll(null)) + + KafkaTelemetry telemetry = KafkaTelemetry.builder(testing.getOpenTelemetry()).build(); + Consumer wrappedConsumer = telemetry.wrap(consumer); + + assertThatThrownBy(() -> wrappedConsumer.poll(Duration.ofMillis(1))) .isInstanceOf(IllegalStateException.class) - .hasMessage("Test exception"); + .hasMessage("can't invoke"); } @Test - void testProducerPropagatesException() { - Producer throwingProducer = + void testProducerExceptionPropagatesToCaller() { + Producer producer = (Producer) Proxy.newProxyInstance( - getClass().getClassLoader(), + ExceptionHandlingTest.class.getClassLoader(), new Class[] {Producer.class}, - new InvocationHandler() { - @Override - public Object invoke(Object proxy, Method method, Object[] args) { - throw new IllegalStateException("Test exception"); - } + (proxy, method, args) -> { + throw new IllegalStateException("can't invoke"); }); - Producer wrappedProducer = - KafkaTelemetry.create(testing.getOpenTelemetry()).wrap(throwingProducer); - assertThatThrownBy(() -> wrappedProducer.send(null)) + + KafkaTelemetry telemetry = KafkaTelemetry.builder(testing.getOpenTelemetry()).build(); + Producer wrappedProducer = telemetry.wrap(producer); + assertThatThrownBy(wrappedProducer::flush) .isInstanceOf(IllegalStateException.class) - .hasMessage("Test exception"); + .hasMessage("can't invoke"); } } diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java index 32a10cb5c0fc..ba39a340af53 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java @@ -45,20 +45,19 @@ void assertTraces(boolean testHeaders) { .hasKind(SpanKind.PRODUCER) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly(sendAttributes(testHeaders)), - span -> - span.hasName("producer callback") - .hasKind(SpanKind.INTERNAL) - .hasParent(trace.getSpan(0)), span -> span.hasName(SHARED_TOPIC + " process") .hasKind(SpanKind.CONSUMER) .hasParent(trace.getSpan(1)) - .hasLinksSatisfying(links -> assertThat(links).isEmpty()) .hasAttributesSatisfyingExactly(processAttributes(greeting, testHeaders)), span -> span.hasName("process child") .hasKind(SpanKind.INTERNAL) - .hasParent(trace.getSpan(3)))); + .hasParent(trace.getSpan(2)), + span -> + span.hasName("producer callback") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)))); } @SuppressWarnings("deprecation") // using deprecated semconv From 96bc1c912e0a2e42949400fe613c5dbb03aaec4c Mon Sep 17 00:00:00 2001 From: otelbot <197425009+otelbot@users.noreply.github.com> Date: Sat, 11 Oct 2025 00:21:04 +0000 Subject: [PATCH 49/50] ./gradlew spotlessApply --- .../kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java index ba39a340af53..b3cd095d09e4 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/test/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/WrapperSuppressReceiveSpansTest.java @@ -14,7 +14,6 @@ import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; -import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.SpanKind; From 44975a66ab0360ad681f219e9fb16853b09cc9c8 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Fri, 10 Oct 2025 18:12:04 -0700 Subject: [PATCH 50/50] javadoc --- .../kafkaclients/v2_6/TracingConsumerInterceptor.java | 2 +- .../kafkaclients/v2_6/TracingProducerInterceptor.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java index 07f87fa85095..96c040074088 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingConsumerInterceptor.java @@ -27,7 +27,7 @@ * via ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG property to your Consumer's properties to get it * instantiated and used. See more details on ConsumerInterceptor usage in its Javadoc. * - * @deprecated Use {@link OpenTelemetryConsumerInterceptor} instead. + * @deprecated Use {@link KafkaTelemetry#consumerInterceptorConfigProperties()} instead. */ @Deprecated public class TracingConsumerInterceptor implements ConsumerInterceptor { diff --git a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java index 97e982028a26..007e1df51d4b 100644 --- a/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java +++ b/instrumentation/kafka/kafka-clients/kafka-clients-2.6/library/src/main/java/io/opentelemetry/instrumentation/kafkaclients/v2_6/TracingProducerInterceptor.java @@ -23,7 +23,7 @@ * via ProducerConfig.INTERCEPTOR_CLASSES_CONFIG property to your Producer's properties to get it * instantiated and used. See more details on ProducerInterceptor usage in its Javadoc. * - * @deprecated Use {@link OpenTelemetryProducerInterceptor} instead. + * @deprecated Use {@link KafkaTelemetry#producerInterceptorConfigProperties()} instead. */ @Deprecated public class TracingProducerInterceptor implements ProducerInterceptor {