Kafka instrumentation enhancements (#1936)

* Bump javadocs since version to 3.1.0

* Extend integration tests to cover all Kafka clients

* Add autoconfiguration module for kafka instrumentation

* Refactor instrumentation for reactive Kafka Receiver

* Refactor instrumentation for reactive Kafka Receiver

* Add docs for Kafka instrumentation

* Revert "Refactor instrumentation for reactive Kafka Receiver"

This reverts commit 58c8f2fa

* Revert "Revert "Refactor instrumentation for reactive Kafka Receiver""

This reverts commit 450a9f8c

* Remove empty test

* Resolve comments from PR 1936

* Revert whitespaces

* Revert whitespaces in common tests pom.xml

* Fix autoconfig to consider generics when registering beans.
Only register reactor-kafka beans if the dependency is on the classpath.

* Split autoconfig for kafka and reactor-kafka
This commit is contained in:
Flaviu Mureșan
2021-05-07 22:20:37 +02:00
committed by GitHub
parent 507e918c39
commit 59c170d147
32 changed files with 1266 additions and 232 deletions

View File

@@ -275,6 +275,11 @@
<artifactId>spring-jms</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor.kafka</groupId>
<artifactId>reactor-kafka</artifactId>
<optional>true</optional>
</dependency>
<!-- GRPC Optional Dependencies -->
<dependency>
<groupId>io.github.lognet</groupId>

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.kafka;
import org.apache.kafka.clients.KafkaClient;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.kafka.TracingKafkaPropagatorGetter;
import org.springframework.cloud.sleuth.instrument.kafka.TracingKafkaPropagatorSetter;
import org.springframework.cloud.sleuth.propagation.Propagator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that registers instrumentation for Kafka.
*
* @author Anders Clausen
* @author Flaviu Muresan
* @since 3.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(KafkaClient.class)
@ConditionalOnBean(Tracer.class)
@AutoConfigureAfter(BraveAutoConfiguration.class)
@ConditionalOnProperty(value = "spring.sleuth.kafka.enabled", matchIfMissing = true)
public class TracingKafkaAutoConfiguration {
@Bean
@ConditionalOnMissingBean(value = ProducerRecord.class, parameterizedContainer = Propagator.Setter.class)
Propagator.Setter<ProducerRecord<?, ?>> tracingKafkaPropagationSetter() {
return new TracingKafkaPropagatorSetter();
}
@Bean
@ConditionalOnMissingBean(value = ConsumerRecord.class, parameterizedContainer = Propagator.Getter.class)
Propagator.Getter<ConsumerRecord<?, ?>> tracingKafkaPropagationGetter() {
return new TracingKafkaPropagatorGetter();
}
@Bean
static TracingKafkaProducerBeanPostProcessor tracingKafkaProducerBeanPostProcessor(BeanFactory beanFactory) {
return new TracingKafkaProducerBeanPostProcessor(beanFactory);
}
@Bean
static TracingKafkaConsumerBeanPostProcessor tracingKafkaConsumerBeanPostProcessor(BeanFactory beanFactory) {
return new TracingKafkaConsumerBeanPostProcessor(beanFactory);
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.kafka;
import org.apache.kafka.clients.consumer.Consumer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.sleuth.instrument.kafka.TracingKafkaConsumer;
/**
* Bean post processor for {@link org.apache.kafka.clients.consumer.Consumer}.
*
* @author Anders Clausen
* @author Flaviu Muresan
* @since 3.1.0
*/
public class TracingKafkaConsumerBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
public TracingKafkaConsumerBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Consumer && !(bean instanceof TracingKafkaConsumer)) {
return new TracingKafkaConsumer<>((Consumer) bean, beanFactory);
}
return bean;
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.kafka;
import org.apache.kafka.clients.producer.Producer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.sleuth.instrument.kafka.TracingKafkaProducer;
/**
* Bean post processor for {@link org.apache.kafka.clients.producer.Producer}.
*
* @author Anders Clausen
* @author Flaviu Muresan
* @since 3.1.0
*/
public class TracingKafkaProducerBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
public TracingKafkaProducerBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Producer && !(bean instanceof TracingKafkaProducer)) {
return new TracingKafkaProducer<>((Producer) bean, beanFactory);
}
return bean;
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.kafka;
import reactor.kafka.receiver.KafkaReceiver;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.kafka.TracingKafkaConsumerFactory;
import org.springframework.cloud.sleuth.instrument.kafka.TracingKafkaProducerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that registers instrumentation for Reactor Kafka.
*
* @author Anders Clausen
* @author Flaviu Muresan
* @since 3.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(KafkaReceiver.class)
@ConditionalOnBean(Tracer.class)
@AutoConfigureAfter(BraveAutoConfiguration.class)
@ConditionalOnProperty(value = "spring.sleuth.kafka.enabled", matchIfMissing = true)
public class TracingReactorKafkaAutoConfiguration {
@Bean
@ConditionalOnMissingBean
TracingKafkaProducerFactory tracingKafkaProducerFactory(BeanFactory beanFactory) {
return new TracingKafkaProducerFactory(beanFactory);
}
@Bean
@ConditionalOnMissingBean
TracingKafkaConsumerFactory tracingKafkaConsumerFactory(BeanFactory beanFactory) {
return new TracingKafkaConsumerFactory(beanFactory);
}
}

View File

@@ -45,13 +45,13 @@ class TraceSpringMessagingAutoConfiguration {
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnMissingBean(value = MessageHeaderAccessor.class, parameterizedContainer = Propagator.Setter.class)
Propagator.Setter<MessageHeaderAccessor> traceMessagePropagationSetter() {
return new MessageHeaderPropagatorSetter();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnMissingBean(value = MessageHeaderAccessor.class, parameterizedContainer = Propagator.Getter.class)
Propagator.Getter<MessageHeaderAccessor> traceMessagePropagationGetter() {
return new MessageHeaderPropagatorGetter();
}

View File

@@ -41,6 +41,12 @@
"description": "Enable tracing for WebSockets.",
"defaultValue": true
},
{
"name": "spring.sleuth.kafka.enabled",
"type": "java.lang.Boolean",
"description": "Enable instrumenting of Apache Kafka clients.",
"defaultValue": true
},
{
"name": "spring.sleuth.async.enabled",
"type": "java.lang.Boolean",

View File

@@ -1,5 +1,6 @@
# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.sleuth.autoconfig.instrument.kafka.TracingKafkaAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.async.TraceAsyncAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.async.TraceAsyncCustomAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.async.TraceAsyncDefaultAutoConfiguration,\