diff --git a/docs/src/main/asciidoc/_configprops.adoc b/docs/src/main/asciidoc/_configprops.adoc index bc9c8b87b..3425dd37b 100644 --- a/docs/src/main/asciidoc/_configprops.adoc +++ b/docs/src/main/asciidoc/_configprops.adoc @@ -75,6 +75,7 @@ |spring.sleuth.sampler.refresh.enabled | `true` | Enable refresh scope for sampler. |spring.sleuth.scheduled.enabled | `true` | Enable tracing for {@link org.springframework.scheduling.annotation.Scheduled}. |spring.sleuth.scheduled.skip-pattern | | Pattern for the fully qualified name of a class that should be skipped. +|spring.sleuth.session.enabled | `true` | Enable Spring Session instrumentation. |spring.sleuth.span-filter.additional-span-name-patterns-to-ignore | | Additional list of span names to ignore. Will be appended to {@link #spanNamePatternsToSkip}. |spring.sleuth.span-filter.enabled | `false` | Will turn on the default Sleuth handler mechanism. Might ignore exporting of certain spans; |spring.sleuth.span-filter.span-name-patterns-to-skip | `^catalogWatchTaskScheduler$` | List of span names to ignore. They will not be sent to external systems. diff --git a/docs/src/main/asciidoc/integrations.adoc b/docs/src/main/asciidoc/integrations.adoc index e57563d3a..1d43c2604 100644 --- a/docs/src/main/asciidoc/integrations.adoc +++ b/docs/src/main/asciidoc/integrations.adoc @@ -434,7 +434,21 @@ include::{project-root}/benchmarks/src/main/java/org/springframework/cloud/sleut This feature is available for Brave tracer implementation. -We set `tracing` property to Lettuce `ClientResources` instance to enable Brave tracing built in Lettuce . +We set `tracing` property to Lettuce `ClientResources` instance to enable Brave tracing built in Lettuce. + +Spring Cloud Sleuth will provide a traced version of the `ClientResources` bean. If you have your own implementation of that bean, remember to customize the `ClientResources.Builder` with a stream of `ClientResourcesBuilderCustomizer`s like presented below: + +[source,java,indent=0] +---- + @Bean(destroyMethod = "shutdown") + DefaultClientResources myLettuceClientResources(ObjectProvider customizer) { + DefaultClientResources.Builder builder = DefaultClientResources.builder(); + // setting up the builder manually + customizer.stream().forEach(c -> c.customize(builder)); + return builder.build(); + } +---- + To disable Redis support, set the `spring.sleuth.redis.enabled` property to `false`. [[sleuth-runnablecallable-integration]] diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/redis/BraveRedisAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/redis/BraveRedisAutoConfiguration.java index 22cbb4049..6e0596d2d 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/redis/BraveRedisAutoConfiguration.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/redis/BraveRedisAutoConfiguration.java @@ -17,17 +17,23 @@ package org.springframework.cloud.sleuth.autoconfig.brave.instrument.redis; import brave.Tracing; +import brave.sampler.Sampler; import io.lettuce.core.resource.ClientResources; +import io.lettuce.core.resource.DefaultClientResources; import io.lettuce.core.tracing.BraveTracing; -import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; 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.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration; -import org.springframework.cloud.sleuth.autoconfig.instrument.redis.TraceLettuceClientResourcesBeanPostProcessor; +import org.springframework.cloud.sleuth.brave.instrument.redis.ClientResourcesBuilderCustomizer; +import org.springframework.cloud.sleuth.brave.instrument.redis.TraceLettuceClientResourcesBuilderCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -40,16 +46,37 @@ import org.springframework.context.annotation.Configuration; */ @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(value = "spring.sleuth.redis.enabled", matchIfMissing = true) -@ConditionalOnBean({ Tracing.class, ClientResources.class }) +@ConditionalOnBean(Tracing.class) @AutoConfigureAfter({ BraveAutoConfiguration.class }) +@AutoConfigureBefore({ RedisAutoConfiguration.class }) @EnableConfigurationProperties(TraceRedisProperties.class) @ConditionalOnClass(BraveTracing.class) public class BraveRedisAutoConfiguration { + // TODO: The customization auto configuration should come from Spring Boot + @Bean(destroyMethod = "shutdown") + @ConditionalOnMissingBean(ClientResources.class) + DefaultClientResources traceLettuceClientResources(ObjectProvider customizer) { + DefaultClientResources.Builder builder = DefaultClientResources.builder(); + customizer.stream().forEach(c -> c.customize(builder)); + return builder.build(); + } + @Bean - static TraceLettuceClientResourcesBeanPostProcessor traceLettuceClientResourcesBeanPostProcessor( - BeanFactory beanFactory) { - return new TraceLettuceClientResourcesBeanPostProcessor(beanFactory); + TraceLettuceClientResourcesBuilderCustomizer traceLettuceClientResourcesBuilderCustomizer(Tracing tracing, + TraceRedisProperties traceRedisProperties, Sampler sampler) { + eagerlyInitializePotentiallyRefreshScopeSampler(sampler); + return new TraceLettuceClientResourcesBuilderCustomizer(tracing, traceRedisProperties.getRemoteServiceName()); + } + + /** + * We need to do the eager method invocation. Since this might be @RefreshScope, a + * proxy is being created. Trying to resolve the proxy from a different thread than + * main can lead to cross thread locking. + * @param sampler potentially refresh scope sampler + */ + private void eagerlyInitializePotentiallyRefreshScopeSampler(Sampler sampler) { + sampler.isSampled(0L); } } diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/redis/TraceLettuceClientResourcesBeanPostProcessor.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/redis/TraceLettuceClientResourcesBeanPostProcessor.java index 26b31f472..a53d44af8 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/redis/TraceLettuceClientResourcesBeanPostProcessor.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/redis/TraceLettuceClientResourcesBeanPostProcessor.java @@ -33,6 +33,7 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.cloud.sleuth.autoconfig.brave.instrument.redis.TraceRedisProperties; +import org.springframework.cloud.sleuth.brave.instrument.redis.TraceLettuceClientResourcesBuilderCustomizer; import org.springframework.cloud.sleuth.internal.ContextUtil; /** @@ -40,6 +41,7 @@ import org.springframework.cloud.sleuth.internal.ContextUtil; * * @author Marcin Grzejszczak * @since 3.0.0 + * @deprecated please use {@link TraceLettuceClientResourcesBuilderCustomizer}. */ public class TraceLettuceClientResourcesBeanPostProcessor implements BeanPostProcessor { @@ -90,26 +92,27 @@ class LazyTracing implements io.lettuce.core.tracing.Tracing { @Override public TracerProvider getTracerProvider() { - if (ContextUtil.isContextUnusable(this.beanFactory)) { - return this.noOpTracing.getTracerProvider(); - } - return braveTracing().getTracerProvider(); + return () -> { + if (ContextUtil.isContextUnusable(beanFactory)) { + return noOpTracing.getTracerProvider().getTracer(); + } + return braveTracing().getTracerProvider().getTracer(); + }; } @Override public TraceContextProvider initialTraceContextProvider() { - if (ContextUtil.isContextUnusable(this.beanFactory)) { - return this.noOpTracing.initialTraceContextProvider(); - } - return braveTracing().initialTraceContextProvider(); + return () -> { + if (ContextUtil.isContextUnusable(beanFactory)) { + return noOpTracing.initialTraceContextProvider().getTraceContext(); + } + return braveTracing().initialTraceContextProvider().getTraceContext(); + }; } @Override public boolean isEnabled() { - if (ContextUtil.isContextUnusable(this.beanFactory)) { - return this.noOpTracing.isEnabled(); - } - return braveTracing().isEnabled(); + return true; } @Override diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/redis/ClientResourcesBuilderCustomizer.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/redis/ClientResourcesBuilderCustomizer.java new file mode 100644 index 000000000..bde0e1ccb --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/redis/ClientResourcesBuilderCustomizer.java @@ -0,0 +1,35 @@ +/* + * 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.brave.instrument.redis; + +import io.lettuce.core.resource.ClientResources; + +/** + * Customizer for {@link ClientResources.Builder}. + * + * @author Marcin Grzejszczak + * @since 3.0.4 + */ +public interface ClientResourcesBuilderCustomizer { + + /** + * Customizes the builder. + * @param builder builder to customize + */ + void customize(ClientResources.Builder builder); + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/redis/TraceLettuceClientResourcesBuilderCustomizer.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/redis/TraceLettuceClientResourcesBuilderCustomizer.java new file mode 100644 index 000000000..1b45845c4 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/redis/TraceLettuceClientResourcesBuilderCustomizer.java @@ -0,0 +1,45 @@ +/* + * 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.brave.instrument.redis; + +import brave.Tracing; +import io.lettuce.core.resource.ClientResources; +import io.lettuce.core.tracing.BraveTracing; + +import org.springframework.beans.factory.config.BeanPostProcessor; + +/** + * {@link BeanPostProcessor} for wrapping Lettuce components in a tracing representation. + * + * @author Marcin Grzejszczak + * @since 3.0.4 + */ +public class TraceLettuceClientResourcesBuilderCustomizer implements ClientResourcesBuilderCustomizer { + + private final BraveTracing tracing; + + public TraceLettuceClientResourcesBuilderCustomizer(Tracing tracing, String serviceName) { + this.tracing = BraveTracing.builder().tracing(tracing).excludeCommandArgsFromSpanTags().serviceName(serviceName) + .build(); + } + + @Override + public void customize(ClientResources.Builder builder) { + builder.tracing(this.tracing); + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-lettuce-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/redis/BraveRedisAutoConfigurationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-lettuce-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/redis/BraveRedisAutoConfigurationTests.java index 306ba7a2a..88e857192 100644 --- a/tests/brave/spring-cloud-sleuth-instrumentation-lettuce-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/redis/BraveRedisAutoConfigurationTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-lettuce-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/redis/BraveRedisAutoConfigurationTests.java @@ -19,13 +19,9 @@ package org.springframework.cloud.sleuth.brave.instrument.redis; import io.lettuce.core.resource.ClientResources; import org.junit.jupiter.api.Test; -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.cloud.sleuth.autoconfig.instrument.redis.TraceLettuceClientResourcesBeanPostProcessor; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import static org.assertj.core.api.BDDAssertions.then; @@ -42,47 +38,18 @@ public class BraveRedisAutoConfigurationTests { ClientResources clientResources; @Autowired - TestTraceLettuceClientResourcesBeanPostProcessor traceLettuceClientResourcesBeanPostProcessor; + TraceLettuceClientResourcesBuilderCustomizer customizer; @Test public void tracing_should_be_set() { - then(this.traceLettuceClientResourcesBeanPostProcessor.tracingCalled).isTrue(); then(this.clientResources.tracing().isEnabled()).isTrue(); + then(this.customizer).isNotNull(); } @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration protected static class Config { - @Bean - ClientResources clientResources() { - ClientResources clientResources = ClientResources.create(); - then(clientResources.tracing().isEnabled()).isFalse(); - return clientResources; - } - - @Bean - static TestTraceLettuceClientResourcesBeanPostProcessor testTraceLettuceClientResourcesBeanPostProcessor( - BeanFactory beanFactory) { - return new TestTraceLettuceClientResourcesBeanPostProcessor(beanFactory); - } - - } - -} - -class TestTraceLettuceClientResourcesBeanPostProcessor extends TraceLettuceClientResourcesBeanPostProcessor { - - boolean tracingCalled = false; - - TestTraceLettuceClientResourcesBeanPostProcessor(BeanFactory beanFactory) { - super(beanFactory); - } - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - this.tracingCalled = true; - return super.postProcessAfterInitialization(bean, beanName); } }