diff --git a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc index f7a095efc..fe256aece 100644 --- a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc +++ b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc @@ -1028,6 +1028,7 @@ That way, you ensure that a new span is created and closed for each execution. We register a custom https://github.com/Netflix/Hystrix/wiki/Plugins#concurrencystrategy[`HystrixConcurrencyStrategy`] called `TraceCallable` that wraps all `Callable` instances in their Sleuth representative. The strategy either starts or continues a span, depending on whether tracing was already going on before the Hystrix command was called. +Optionally, you can set `spring.sleuth.hystrix.strategy.passthrough` to `true` to just propagate the trace context to the Hystrix execution thread if you don't wish to start a new span. To disable the custom Hystrix Concurrency Strategy, set the `spring.sleuth.hystrix.strategy.enabled` to `false`. ==== Manual Command setting diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixAutoConfiguration.java index d0e1fa654..d7764fcd0 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixAutoConfiguration.java @@ -23,6 +23,7 @@ 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.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; import org.springframework.context.annotation.Bean; @@ -34,8 +35,8 @@ import org.springframework.context.annotation.Configuration; * {@link com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy}. * * @author Marcin Grzejszczak - * @since 1.0.0 * @see SleuthHystrixConcurrencyStrategy + * @since 1.0.0 */ @Configuration @AutoConfigureAfter(TraceAutoConfiguration.class) @@ -43,12 +44,14 @@ import org.springframework.context.annotation.Configuration; @ConditionalOnBean(Tracing.class) @ConditionalOnProperty(value = "spring.sleuth.hystrix.strategy.enabled", matchIfMissing = true) +@EnableConfigurationProperties(SleuthHystrixConcurrencyStrategyProperties.class) public class SleuthHystrixAutoConfiguration { @Bean SleuthHystrixConcurrencyStrategy sleuthHystrixConcurrencyStrategy(Tracing tracing, - SpanNamer spanNamer) { - return new SleuthHystrixConcurrencyStrategy(tracing, spanNamer); + SpanNamer spanNamer, SleuthHystrixConcurrencyStrategyProperties properties) { + return new SleuthHystrixConcurrencyStrategy(tracing, spanNamer, + properties.isPassthrough()); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategy.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategy.java index bd990c37f..4ff9a7e38 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategy.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategy.java @@ -60,9 +60,17 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy private HystrixConcurrencyStrategy delegate; + private boolean passthrough; + public SleuthHystrixConcurrencyStrategy(Tracing tracing, SpanNamer spanNamer) { + this(tracing, spanNamer, false); + } + + public SleuthHystrixConcurrencyStrategy(Tracing tracing, SpanNamer spanNamer, + boolean passthrough) { this.tracing = tracing; this.spanNamer = spanNamer; + this.passthrough = passthrough; try { this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy(); if (this.delegate instanceof SleuthHystrixConcurrencyStrategy) { @@ -77,7 +85,7 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy .getMetricsPublisher(); HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance() .getPropertiesStrategy(); - logCurrentStateOfHysrixPlugins(eventNotifier, metricsPublisher, + logCurrentStateOfHystrixPlugins(eventNotifier, metricsPublisher, propertiesStrategy); HystrixPlugins.reset(); HystrixPlugins.getInstance().registerConcurrencyStrategy(this); @@ -92,7 +100,7 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy } } - private void logCurrentStateOfHysrixPlugins(HystrixEventNotifier eventNotifier, + private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier, HystrixMetricsPublisher metricsPublisher, HystrixPropertiesStrategy propertiesStrategy) { if (log.isDebugEnabled()) { @@ -109,13 +117,21 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy if (callable instanceof TraceCallable) { return callable; } + Callable wrappedCallable = this.delegate != null ? this.delegate.wrapCallable(callable) : callable; + if (wrappedCallable instanceof TraceCallable) { return wrappedCallable; } - return new TraceCallable<>(this.tracing, this.spanNamer, wrappedCallable, - HYSTRIX_COMPONENT); + + if (passthrough) { + return this.tracing.currentTraceContext().wrap(callable); + } + else { + return new TraceCallable<>(this.tracing, this.spanNamer, wrappedCallable, + HYSTRIX_COMPONENT); + } } @Override diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategyProperties.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategyProperties.java new file mode 100644 index 000000000..7693b907f --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategyProperties.java @@ -0,0 +1,57 @@ +/* + * 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.hystrix; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Sleuth Hystrix settings. + * + * @author Daniel Albuquerque + */ +@ConfigurationProperties("spring.sleuth.hystrix.strategy") +public class SleuthHystrixConcurrencyStrategyProperties { + + /** + * Enable custom HystrixConcurrencyStrategy that wraps all Callable instances into + * their Sleuth representative - the TraceCallable. + */ + private boolean enabled = true; + + /** + * When enabled the tracing information is passed to the Hystrix execution threads but + * spans are not created for each execution. + */ + private boolean passthrough = false; + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isPassthrough() { + return passthrough; + } + + public void setPassthrough(boolean passthrough) { + this.passthrough = passthrough; + } + +} 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 d4d453bb8..33b8369b6 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 @@ -24,12 +24,6 @@ "description": "Enable default AsyncConfigurer.", "defaultValue": true }, - { - "name": "spring.sleuth.hystrix.strategy.enabled", - "type": "java.lang.Boolean", - "description": "Enable custom HystrixConcurrencyStrategy that wraps all Callable instances into their Sleuth representative - the TraceCallable.", - "defaultValue": true - }, { "name": "spring.sleuth.feign.enabled", "type": "java.lang.Boolean", diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategyTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategyTest.java index 19b99a176..686878a44 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategyTest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategyTest.java @@ -21,8 +21,10 @@ import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import brave.Tracing; +import brave.propagation.CurrentTraceContext; import brave.propagation.StrictScopeDecorator; import brave.propagation.ThreadLocalCurrentTraceContext; +import brave.propagation.TraceContext; import com.netflix.hystrix.HystrixThreadPoolKey; import com.netflix.hystrix.HystrixThreadPoolProperties; import com.netflix.hystrix.strategy.HystrixPlugins; @@ -123,6 +125,25 @@ public class SleuthHystrixConcurrencyStrategyTest { then(this.reporter.getSpans()).hasSize(1); } + @Test + public void should_propagate_trace_context_when_passthrough_is_enabled() + throws Exception { + SleuthHystrixConcurrencyStrategy strategy = new SleuthHystrixConcurrencyStrategy( + this.tracing, new DefaultSpanNamer(), true); + + TraceContext traceContext = TraceContext.newBuilder().traceId(123L).spanId(456L) + .build(); + CurrentTraceContext.Scope scope = tracing.currentTraceContext() + .newScope(traceContext); + + Callable callable = strategy + .wrapCallable(() -> tracing.currentTraceContext().get()); + + then(callable).isNotInstanceOf(TraceCallable.class); + then(callable.call()).isEqualTo(traceContext); + scope.close(); + } + @Test public void should_delegate_work_to_custom_hystrix_concurrency_strategy() throws Exception {