Added instrumentation of spring amqp components
with this change we're reusing Brave to add instrumentation to any existing RabbitTemplate
fixes gh-883
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -272,7 +272,7 @@
|
||||
<spring-cloud-stream.version>Elmhurst.BUILD-SNAPSHOT</spring-cloud-stream.version>
|
||||
<spring-cloud-netflix.version>2.0.0.BUILD-SNAPSHOT</spring-cloud-netflix.version>
|
||||
<spring-cloud-openfeign.version>2.0.0.BUILD-SNAPSHOT</spring-cloud-openfeign.version>
|
||||
<brave.version>4.17.1</brave.version>
|
||||
<brave.version>4.17.2</brave.version>
|
||||
<!-- Version set until zipkin-junit gets defined in Brave BOM -->
|
||||
<zipkin.version>2.5.1</zipkin.version>
|
||||
<spring-security-boot-autoconfigure.version>2.0.0.RELEASE</spring-security-boot-autoconfigure.version>
|
||||
|
||||
@@ -91,6 +91,11 @@
|
||||
<artifactId>spring-integration-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.amqp</groupId>
|
||||
<artifactId>spring-rabbit</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security.oauth</groupId>
|
||||
<artifactId>spring-security-oauth2</artifactId>
|
||||
@@ -164,6 +169,10 @@
|
||||
<groupId>io.zipkin.brave</groupId>
|
||||
<artifactId>brave-instrumentation-spring-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.brave</groupId>
|
||||
<artifactId>brave-instrumentation-spring-rabbit</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.brave</groupId>
|
||||
<artifactId>brave-instrumentation-httpclient</artifactId>
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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
|
||||
*
|
||||
* http://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.instrument.messaging;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@ConfigurationProperties("spring.sleuth")
|
||||
public class SleuthMessagingProperties {
|
||||
|
||||
private Integration integration = new Integration();
|
||||
|
||||
private Messaging messaging = new Messaging();
|
||||
|
||||
public Integration getIntegration() {
|
||||
return this.integration;
|
||||
}
|
||||
|
||||
public void setIntegration(Integration integration) {
|
||||
this.integration = integration;
|
||||
}
|
||||
|
||||
public Messaging getMessaging() {
|
||||
return this.messaging;
|
||||
}
|
||||
|
||||
public void setMessaging(Messaging messaging) {
|
||||
this.messaging = messaging;
|
||||
}
|
||||
|
||||
public static class Integration {
|
||||
private boolean enabled;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Messaging {
|
||||
private boolean enabled;
|
||||
|
||||
private String remoteServiceName = "mq-service";
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getRemoteServiceName() {
|
||||
return this.remoteServiceName;
|
||||
}
|
||||
|
||||
public void setRemoteServiceName(String remoteServiceName) {
|
||||
this.remoteServiceName = remoteServiceName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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
|
||||
*
|
||||
* http://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.instrument.messaging;
|
||||
|
||||
import brave.Tracing;
|
||||
import brave.spring.rabbit.SpringRabbitTracing;
|
||||
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
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.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
* Auto-configuration} that registers a tracing instrumentation of
|
||||
* messaging components.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnBean(Tracing.class)
|
||||
@AutoConfigureAfter({ TraceAutoConfiguration.class })
|
||||
@ConditionalOnProperty(value = "spring.sleuth.messaging.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(SleuthMessagingProperties.class)
|
||||
public class TraceMessagingAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(RabbitTemplate.class)
|
||||
protected static class SleuthRabbitConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
SpringRabbitTracing springRabbitTracing(Tracing tracing,
|
||||
SleuthMessagingProperties properties) {
|
||||
return SpringRabbitTracing.newBuilder(tracing)
|
||||
.remoteServiceName(properties.getMessaging()
|
||||
.getRemoteServiceName())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
// for tests
|
||||
@ConditionalOnMissingBean
|
||||
SleuthRabbitBeanPostProcessor sleuthRabbitBeanPostProcessor(BeanFactory beanFactory) {
|
||||
return new SleuthRabbitBeanPostProcessor(beanFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SleuthRabbitBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
private SpringRabbitTracing tracing;
|
||||
|
||||
SleuthRabbitBeanPostProcessor(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (bean instanceof RabbitTemplate) {
|
||||
return rabbitTracing()
|
||||
.decorateRabbitTemplate((RabbitTemplate) bean);
|
||||
} else if (bean instanceof SimpleRabbitListenerContainerFactory) {
|
||||
return rabbitTracing()
|
||||
.decorateSimpleRabbitListenerContainerFactory((SimpleRabbitListenerContainerFactory) bean);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
SpringRabbitTracing rabbitTracing() {
|
||||
if (this.tracing == null) {
|
||||
this.tracing = this.beanFactory.getBean(SpringRabbitTracing.class);
|
||||
}
|
||||
return this.tracing;
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -43,7 +42,7 @@ import org.springframework.integration.config.GlobalChannelInterceptor;
|
||||
@ConditionalOnBean(Tracing.class)
|
||||
@AutoConfigureAfter({ TraceAutoConfiguration.class })
|
||||
@ConditionalOnProperty(value = "spring.sleuth.integration.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(TraceKeys.class)
|
||||
@EnableConfigurationProperties(SleuthMessagingProperties.class)
|
||||
public class TraceSpringIntegrationAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -17,6 +17,7 @@ org.springframework.cloud.sleuth.instrument.rxjava.RxJavaAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.reactor.TraceReactorAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.web.TraceWebFluxAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.zuul.TraceZuulAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.messaging.TraceMessagingAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.messaging.TraceSpringIntegrationAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.messaging.websocket.TraceWebSocketAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.opentracing.OpentracingAutoConfiguration
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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
|
||||
*
|
||||
* http://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.instrument.messaging;
|
||||
|
||||
import brave.sampler.Sampler;
|
||||
import brave.spring.rabbit.SpringRabbitTracing;
|
||||
import com.rabbitmq.client.Channel;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.BDDMockito;
|
||||
import org.mockito.Mock;
|
||||
import org.springframework.amqp.AmqpException;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.connection.Connection;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionListener;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
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.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = TraceMessagingAutoConfigurationTests.Config.class,
|
||||
webEnvironment = SpringBootTest.WebEnvironment.NONE)
|
||||
public class TraceMessagingAutoConfigurationTests {
|
||||
|
||||
@Autowired RabbitTemplate rabbitTemplate;
|
||||
@Autowired ArrayListSpanReporter reporter;
|
||||
@Autowired TestSleuthRabbitBeanPostProcessor postProcessor;
|
||||
|
||||
@Test
|
||||
public void should_wrap_rabbit_template() {
|
||||
then(this.rabbitTemplate).isNotNull();
|
||||
then(this.postProcessor.rabbitTracingCalled).isTrue();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
protected static class Config {
|
||||
@Bean Sampler sampler() {
|
||||
return Sampler.ALWAYS_SAMPLE;
|
||||
}
|
||||
|
||||
@Bean ArrayListSpanReporter reporter() {
|
||||
return new ArrayListSpanReporter();
|
||||
}
|
||||
|
||||
@Bean SleuthRabbitBeanPostProcessor postProcessor(BeanFactory beanFactory) {
|
||||
return new TestSleuthRabbitBeanPostProcessor(beanFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestSleuthRabbitBeanPostProcessor extends SleuthRabbitBeanPostProcessor {
|
||||
|
||||
boolean rabbitTracingCalled = false;
|
||||
|
||||
TestSleuthRabbitBeanPostProcessor(BeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
@Override SpringRabbitTracing rabbitTracing() {
|
||||
this.rabbitTracingCalled = true;
|
||||
return super.rabbitTracing();
|
||||
}
|
||||
}
|
||||
@@ -106,8 +106,7 @@ public class WebClientExceptionTests {
|
||||
|
||||
then(this.tracer.tracer().currentSpan()).isNull();
|
||||
then(this.reporter.getSpans()).isNotEmpty();
|
||||
then(this.reporter.getSpans().get(0).tags().get("error"))
|
||||
.contains("invalid.host.to.break.tests");
|
||||
then(this.reporter.getSpans().get(0).tags()).containsKey("error");
|
||||
}
|
||||
|
||||
Object[] parametersForShouldCloseSpanUponException() {
|
||||
|
||||
Reference in New Issue
Block a user