Added spring-kafka support; fixes gh-896

This commit is contained in:
Marcin Grzejszczak
2018-03-09 10:16:26 +01:00
parent 0474478f59
commit 73f9ec61f6
4 changed files with 115 additions and 1 deletions

View File

@@ -1125,6 +1125,8 @@ include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/
=== Messaging
==== Spring Integration and Spring Cloud Stream
Spring Cloud Sleuth integrates with http://projects.spring.io/spring-integration/[Spring Integration].
It creates spans for publish and subscribe events.
To disable Spring Integration instrumentation, set `spring.sleuth.integration.enabled` to `false`.
@@ -1137,11 +1139,19 @@ Decorating the Spring Integration Executor Channel with `TraceableExecutorServic
==== Spring RabbitMq
We instrument the `RabbiTemplate` so that tracing headers get injected
We instrument the `RabbitTemplate` so that tracing headers get injected
into the message.
To block this feature, set `spring.sleuth.messaging.enabled` to `false`.
==== Spring Kafka
We instrument the Spring Kafka's `ProducerFactory` and `ConsumerFactory`
so that tracing headers get injected into the created Spring Kafka's
`Producer` and `Consumer`.
To block this feature, set `spring.sleuth.messaging.enabled` to `false`.
=== Zuul
We instrument the Zuul Ribbon integration by enriching the Ribbon requests with tracing information.

View File

@@ -96,6 +96,11 @@
<artifactId>spring-rabbit</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
@@ -173,6 +178,10 @@
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-spring-rabbit</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-kafka-clients</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-httpclient</artifactId>

View File

@@ -17,7 +17,14 @@
package org.springframework.cloud.sleuth.instrument.messaging;
import brave.Tracing;
import brave.kafka.clients.KafkaTracing;
import brave.spring.rabbit.SpringRabbitTracing;
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.producer.Producer;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeansException;
@@ -32,6 +39,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.core.ProducerFactory;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
@@ -67,6 +75,24 @@ public class TraceMessagingAutoConfiguration {
return new SleuthRabbitBeanPostProcessor(beanFactory);
}
}
@Configuration
@ConditionalOnClass(ProducerFactory.class)
protected static class SleuthKafkaConfiguration {
@Bean
@ConditionalOnMissingBean
KafkaTracing kafkaTracing(Tracing tracing) {
return KafkaTracing.create(tracing);
}
@Bean
// for tests
@ConditionalOnMissingBean
SleuthKafkaAspect sleuthKafkaAspect(KafkaTracing kafkaTracing) {
return new SleuthKafkaAspect(kafkaTracing);
}
}
}
class SleuthRabbitBeanPostProcessor implements BeanPostProcessor {
@@ -96,4 +122,32 @@ class SleuthRabbitBeanPostProcessor implements BeanPostProcessor {
}
return this.tracing;
}
}
@Aspect
class SleuthKafkaAspect {
private final KafkaTracing kafkaTracing;
SleuthKafkaAspect(KafkaTracing kafkaTracing) {
this.kafkaTracing = kafkaTracing;
}
@Pointcut("execution(public * org.springframework.kafka.core.ProducerFactory.createProducer(..))")
private void anyProducerFactory() { } // NOSONAR
@Pointcut("execution(public * org.springframework.kafka.core.ConsumerFactory.createConsumer(..))")
private void anyConsumerFactory() { } // NOSONAR
@Around("anyProducerFactory()")
public Object wrapProducerFactory(ProceedingJoinPoint pjp) throws Throwable {
Producer producer = (Producer) pjp.proceed();
return this.kafkaTracing.producer(producer);
}
@Around("anyConsumerFactory()")
public Object wrapConsumerFactory(ProceedingJoinPoint pjp) throws Throwable {
Consumer consumer = (Consumer) pjp.proceed();
return this.kafkaTracing.consumer(consumer);
}
}

View File

@@ -16,9 +16,11 @@
package org.springframework.cloud.sleuth.instrument.messaging;
import brave.kafka.clients.KafkaTracing;
import brave.sampler.Sampler;
import brave.spring.rabbit.SpringRabbitTracing;
import com.rabbitmq.client.Channel;
import org.aspectj.lang.ProceedingJoinPoint;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -41,6 +43,8 @@ import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.ProducerFactory;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.BDDAssertions.then;
@@ -56,6 +60,9 @@ public class TraceMessagingAutoConfigurationTests {
@Autowired RabbitTemplate rabbitTemplate;
@Autowired ArrayListSpanReporter reporter;
@Autowired TestSleuthRabbitBeanPostProcessor postProcessor;
@Autowired MySleuthKafkaAspect mySleuthKafkaAspect;
@Autowired ProducerFactory producerFactory;
@Autowired ConsumerFactory consumerFactory;
@Test
public void should_wrap_rabbit_template() {
@@ -63,6 +70,15 @@ public class TraceMessagingAutoConfigurationTests {
then(this.postProcessor.rabbitTracingCalled).isTrue();
}
@Test
public void should_wrap_kafka() {
this.producerFactory.createProducer();
then(this.mySleuthKafkaAspect.producerWrapped).isTrue();
this.consumerFactory.createConsumer();
then(this.mySleuthKafkaAspect.consumerWrapped).isTrue();
}
@Configuration
@EnableAutoConfiguration
protected static class Config {
@@ -77,6 +93,9 @@ public class TraceMessagingAutoConfigurationTests {
@Bean SleuthRabbitBeanPostProcessor postProcessor(BeanFactory beanFactory) {
return new TestSleuthRabbitBeanPostProcessor(beanFactory);
}
@Bean SleuthKafkaAspect sleuthKafkaAspect(KafkaTracing kafkaTracing) {
return new MySleuthKafkaAspect(kafkaTracing);
}
}
}
@@ -92,4 +111,26 @@ class TestSleuthRabbitBeanPostProcessor extends SleuthRabbitBeanPostProcessor {
this.rabbitTracingCalled = true;
return super.rabbitTracing();
}
}
class MySleuthKafkaAspect extends SleuthKafkaAspect {
boolean producerWrapped;
boolean consumerWrapped;
MySleuthKafkaAspect(KafkaTracing kafkaTracing) {
super(kafkaTracing);
}
@Override public Object wrapProducerFactory(ProceedingJoinPoint pjp)
throws Throwable {
this.producerWrapped = true;
return super.wrapProducerFactory(pjp);
}
@Override public Object wrapConsumerFactory(ProceedingJoinPoint pjp)
throws Throwable {
this.consumerWrapped = true;
return super.wrapConsumerFactory(pjp);
}
}