Skip to content

Commit c2c5d80

Browse files
authored
Add an option to disable automatic kafka interceptor configuration in spring starter (#12833)
1 parent 81c7713 commit c2c5d80

File tree

3 files changed

+57
-21
lines changed

3 files changed

+57
-21
lines changed

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/kafka/ConcurrentKafkaListenerContainerFactoryPostProcessor.java

+32-20
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,55 @@
55

66
package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.kafka;
77

8-
import io.opentelemetry.api.OpenTelemetry;
98
import io.opentelemetry.instrumentation.spring.kafka.v2_7.SpringKafkaTelemetry;
10-
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
11-
import org.springframework.beans.factory.ObjectProvider;
9+
import java.lang.reflect.Field;
10+
import java.util.function.Supplier;
1211
import org.springframework.beans.factory.config.BeanPostProcessor;
12+
import org.springframework.kafka.config.AbstractKafkaListenerContainerFactory;
1313
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
14+
import org.springframework.kafka.listener.BatchInterceptor;
15+
import org.springframework.kafka.listener.RecordInterceptor;
1416

1517
class ConcurrentKafkaListenerContainerFactoryPostProcessor implements BeanPostProcessor {
1618

17-
private final ObjectProvider<OpenTelemetry> openTelemetryProvider;
18-
private final ObjectProvider<ConfigProperties> configPropertiesProvider;
19+
private final Supplier<SpringKafkaTelemetry> springKafkaTelemetry;
1920

2021
ConcurrentKafkaListenerContainerFactoryPostProcessor(
21-
ObjectProvider<OpenTelemetry> openTelemetryProvider,
22-
ObjectProvider<ConfigProperties> configPropertiesProvider) {
23-
this.openTelemetryProvider = openTelemetryProvider;
24-
this.configPropertiesProvider = configPropertiesProvider;
22+
Supplier<SpringKafkaTelemetry> springKafkaTelemetry) {
23+
this.springKafkaTelemetry = springKafkaTelemetry;
2524
}
2625

26+
@SuppressWarnings("unchecked")
2727
@Override
2828
public Object postProcessAfterInitialization(Object bean, String beanName) {
2929
if (!(bean instanceof ConcurrentKafkaListenerContainerFactory)) {
3030
return bean;
3131
}
3232

33-
ConcurrentKafkaListenerContainerFactory<?, ?> listenerContainerFactory =
34-
(ConcurrentKafkaListenerContainerFactory<?, ?>) bean;
35-
SpringKafkaTelemetry springKafkaTelemetry =
36-
SpringKafkaTelemetry.builder(openTelemetryProvider.getObject())
37-
.setCaptureExperimentalSpanAttributes(
38-
configPropertiesProvider
39-
.getObject()
40-
.getBoolean("otel.instrumentation.kafka.experimental-span-attributes", false))
41-
.build();
42-
listenerContainerFactory.setBatchInterceptor(springKafkaTelemetry.createBatchInterceptor());
43-
listenerContainerFactory.setRecordInterceptor(springKafkaTelemetry.createRecordInterceptor());
33+
ConcurrentKafkaListenerContainerFactory<Object, Object> listenerContainerFactory =
34+
(ConcurrentKafkaListenerContainerFactory<Object, Object>) bean;
35+
SpringKafkaTelemetry springKafkaTelemetry = this.springKafkaTelemetry.get();
36+
37+
// use reflection to read existing values to avoid overwriting user configured interceptors
38+
BatchInterceptor<Object, Object> batchInterceptor =
39+
readField(listenerContainerFactory, "batchInterceptor", BatchInterceptor.class);
40+
RecordInterceptor<Object, Object> recordInterceptor =
41+
readField(listenerContainerFactory, "recordInterceptor", RecordInterceptor.class);
42+
listenerContainerFactory.setBatchInterceptor(
43+
springKafkaTelemetry.createBatchInterceptor(batchInterceptor));
44+
listenerContainerFactory.setRecordInterceptor(
45+
springKafkaTelemetry.createRecordInterceptor(recordInterceptor));
4446

4547
return listenerContainerFactory;
4648
}
49+
50+
private static <T> T readField(Object container, String filedName, Class<T> fieldType) {
51+
try {
52+
Field field = AbstractKafkaListenerContainerFactory.class.getDeclaredField(filedName);
53+
field.setAccessible(true);
54+
return fieldType.cast(field.get(container));
55+
} catch (Exception exception) {
56+
return null;
57+
}
58+
}
4759
}

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/kafka/KafkaInstrumentationAutoConfiguration.java

+19-1
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
import io.opentelemetry.api.OpenTelemetry;
99
import io.opentelemetry.instrumentation.kafkaclients.v2_6.KafkaTelemetry;
1010
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation;
11+
import io.opentelemetry.instrumentation.spring.kafka.v2_7.SpringKafkaTelemetry;
1112
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
1213
import org.springframework.beans.factory.ObjectProvider;
1314
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
15+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
1416
import org.springframework.boot.autoconfigure.kafka.DefaultKafkaProducerFactoryCustomizer;
1517
import org.springframework.context.annotation.Bean;
1618
import org.springframework.context.annotation.Configuration;
@@ -33,13 +35,29 @@ DefaultKafkaProducerFactoryCustomizer otelKafkaProducerFactoryCustomizer(
3335
return producerFactory -> producerFactory.addPostProcessor(kafkaTelemetry::wrap);
3436
}
3537

38+
@Bean
39+
static SpringKafkaTelemetry getTelemetry(
40+
ObjectProvider<OpenTelemetry> openTelemetryProvider,
41+
ObjectProvider<ConfigProperties> configPropertiesProvider) {
42+
return SpringKafkaTelemetry.builder(openTelemetryProvider.getObject())
43+
.setCaptureExperimentalSpanAttributes(
44+
configPropertiesProvider
45+
.getObject()
46+
.getBoolean("otel.instrumentation.kafka.experimental-span-attributes", false))
47+
.build();
48+
}
49+
3650
// static to avoid "is not eligible for getting processed by all BeanPostProcessors" warning
3751
@Bean
52+
@ConditionalOnProperty(
53+
name = "otel.instrumentation.kafka.autoconfigure-interceptor",
54+
havingValue = "true",
55+
matchIfMissing = true)
3856
static ConcurrentKafkaListenerContainerFactoryPostProcessor
3957
otelKafkaListenerContainerFactoryBeanPostProcessor(
4058
ObjectProvider<OpenTelemetry> openTelemetryProvider,
4159
ObjectProvider<ConfigProperties> configPropertiesProvider) {
4260
return new ConcurrentKafkaListenerContainerFactoryPostProcessor(
43-
openTelemetryProvider, configPropertiesProvider);
61+
() -> getTelemetry(openTelemetryProvider, configPropertiesProvider));
4462
}
4563
}

instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json

+6
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,12 @@
357357
"description": "Enable the capture of experimental Kafka span attributes.",
358358
"defaultValue": false
359359
},
360+
{
361+
"name": "otel.instrumentation.kafka.autoconfigure-interceptor",
362+
"type": "java.lang.Boolean",
363+
"description": "Enable automatic configuration of tracing interceptors on <code>ConcurrentKafkaListenerContainerFactory</code> using a <code>BeanPostProcessor</code>. You may disable this if you wish to manually configure these interceptors.",
364+
"defaultValue": true
365+
},
360366
{
361367
"name": "otel.instrumentation.mongo.enabled",
362368
"type": "java.lang.Boolean",

0 commit comments

Comments
 (0)