diff --git a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc index b3e5b196a..436d0aab6 100644 --- a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc +++ b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc @@ -1373,6 +1373,11 @@ IMPORTANT: We don't support baggage propagation for JMS We instrument the Zuul Ribbon integration by enriching the Ribbon requests with tracing information. To disable Zuul support, set the `spring.sleuth.zuul.enabled` property to `false`. +=== Redis + +We set `tracing` property to Lettcue `ClientResources` instance to enable Brave tracing built in Lettuce . +To disable Redis support, set the `spring.sleuth.redis.enabled` property to `false`. + == Running examples You can see the running examples deployed in the https://run.pivotal.io/[Pivotal Web Services]. diff --git a/spring-cloud-sleuth-core/pom.xml b/spring-cloud-sleuth-core/pom.xml index f752dc33c..3caf750ef 100644 --- a/spring-cloud-sleuth-core/pom.xml +++ b/spring-cloud-sleuth-core/pom.xml @@ -249,6 +249,13 @@ brave-instrumentation-grpc true + + + io.lettuce + lettuce-core + true + + org.springframework.boot spring-boot-autoconfigure-processor diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/redis/OnRedisEnabled.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/redis/OnRedisEnabled.java new file mode 100644 index 000000000..1d2d96a46 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/redis/OnRedisEnabled.java @@ -0,0 +1,39 @@ +/* + * Copyright 2013-2019 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.instrument.redis; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; + +/** + * Verifies if Redis property was enabled. + * + * @author Chao Chang + * @since 2.2.0 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Documented +@ConditionalOnProperty(value = "spring.sleuth.redis.enabled", matchIfMissing = true) +@interface OnRedisEnabled { + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/redis/TraceRedisAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/redis/TraceRedisAutoConfiguration.java new file mode 100644 index 000000000..996a3e595 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/redis/TraceRedisAutoConfiguration.java @@ -0,0 +1,96 @@ +/* + * Copyright 2013-2019 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.instrument.redis; + +import brave.Tracing; +import io.lettuce.core.resource.ClientResources; +import io.lettuce.core.tracing.BraveTracing; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +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} enables Redis span information propagation. + * + * @author Chao Chang + * @since 2.2.0 + */ +@Configuration +@OnRedisEnabled +@ConditionalOnBean({ Tracing.class, ClientResources.class }) +@AutoConfigureAfter({ TraceAutoConfiguration.class }) +public class TraceRedisAutoConfiguration { + + @Configuration + static class LettuceConfig { + + @Bean + static TraceLettuceClientResourcesBeanPostProcessor traceLettuceClientResourcesBeanPostProcessor( + Tracing tracing) { + return new TraceLettuceClientResourcesBeanPostProcessor(tracing); + } + + } + +} + +class TraceLettuceClientResourcesBeanPostProcessor implements BeanPostProcessor { + + private static final Log log = LogFactory + .getLog(TraceLettuceClientResourcesBeanPostProcessor.class); + + private final Tracing tracing; + + TraceLettuceClientResourcesBeanPostProcessor(Tracing tracing) { + this.tracing = tracing; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) + throws BeansException { + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) + throws BeansException { + if (bean instanceof ClientResources) { + ClientResources cr = (ClientResources) bean; + if (!cr.tracing().isEnabled()) { + if (log.isDebugEnabled()) { + log.debug( + "Lettuce ClientResources bean is auto-configured to enable tracing."); + } + return cr.mutate().tracing(BraveTracing.create(this.tracing)).build(); + } + if (log.isDebugEnabled()) { + log.debug( + "Lettuce ClientResources bean is skipped for auto-configuration because tracing was already enabled."); + } + } + return bean; + } + +} diff --git a/spring-cloud-sleuth-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-sleuth-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 8c3f68828..d4d453bb8 100644 --- a/spring-cloud-sleuth-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-sleuth-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -53,6 +53,12 @@ "type": "java.lang.Boolean", "description": "Enable span information propagation when using GRPC.", "defaultValue": true + }, + { + "name": "spring.sleuth.redis.enabled", + "type": "java.lang.Boolean", + "description": "Enable span information propagation when using Redis.", + "defaultValue": true } ] } diff --git a/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories b/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories index db0e76446..5e0814f5f 100644 --- a/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories @@ -24,7 +24,8 @@ org.springframework.cloud.sleuth.instrument.grpc.TraceGrpcAutoConfiguration,\ 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 +org.springframework.cloud.sleuth.instrument.opentracing.OpentracingAutoConfiguration,\ +org.springframework.cloud.sleuth.instrument.redis.TraceRedisAutoConfiguration # Environment Post Processor org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.cloud.sleuth.autoconfig.TraceEnvironmentPostProcessor diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/redis/TraceRedisAutoConfigurationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/redis/TraceRedisAutoConfigurationTests.java new file mode 100644 index 000000000..2a873c706 --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/redis/TraceRedisAutoConfigurationTests.java @@ -0,0 +1,90 @@ +/* + * Copyright 2013-2019 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.instrument.redis; + +import brave.Tracing; +import io.lettuce.core.resource.ClientResources; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +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 Chao Chang + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = TraceRedisAutoConfigurationTests.Config.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) +public class TraceRedisAutoConfigurationTests { + + @Autowired + ClientResources clientResources; + + @Autowired + TestTraceLettuceClientResourcesBeanPostProcessor traceLettuceClientResourcesBeanPostProcessor; + + @Test + public void tracing_should_be_set() { + then(this.traceLettuceClientResourcesBeanPostProcessor.tracingCalled).isTrue(); + then(this.clientResources.tracing().isEnabled()).isTrue(); + } + + @Configuration + @EnableAutoConfiguration + protected static class Config { + + @Bean + ClientResources clientResources() { + ClientResources clientResources = ClientResources.create(); + then(clientResources.tracing().isEnabled()).isFalse(); + return clientResources; + } + + @Bean + TestTraceLettuceClientResourcesBeanPostProcessor testTraceLettuceClientResourcesBeanPostProcessor( + Tracing tracing) { + return new TestTraceLettuceClientResourcesBeanPostProcessor(tracing); + } + + } + +} + +class TestTraceLettuceClientResourcesBeanPostProcessor + extends TraceLettuceClientResourcesBeanPostProcessor { + + boolean tracingCalled = false; + + TestTraceLettuceClientResourcesBeanPostProcessor(Tracing tracing) { + super(tracing); + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) + throws BeansException { + this.tracingCalled = true; + return super.postProcessAfterInitialization(bean, beanName); + } + +}