Remove remaining public accesses of SpanContextHolder
Hystrix is complex and the stacks get very deep. It turns out that it is also rather stateful, so order of tests affects the outcomes. Long story short: if you set the concurrency strategy it infects other tests (like the TraecCommandTests), which then have to assert slightly more carefully.
This commit is contained in:
@@ -42,7 +42,6 @@ public class TraceCallable<V> extends TraceDelegate<Callable<V>> implements Call
|
||||
|
||||
@Override
|
||||
public V call() throws Exception {
|
||||
ensureThatThreadIsNotPollutedByPreviousTraces();
|
||||
Span span = startSpan();
|
||||
try {
|
||||
return this.getDelegate().call();
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.cloud.sleuth.instrument;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.trace.SpanContextHolder;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@@ -56,7 +55,4 @@ public abstract class TraceDelegate<T> {
|
||||
return this.name == null ? Thread.currentThread().getName() : this.name;
|
||||
}
|
||||
|
||||
protected void ensureThatThreadIsNotPollutedByPreviousTraces() {
|
||||
SpanContextHolder.removeCurrentSpan();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ public class TraceRunnable extends TraceDelegate<Runnable> implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ensureThatThreadIsNotPollutedByPreviousTraces();
|
||||
Span span = startSpan();
|
||||
try {
|
||||
this.getDelegate().run();
|
||||
|
||||
@@ -13,7 +13,8 @@ import com.netflix.hystrix.HystrixCommand;
|
||||
@ConditionalOnProperty(value = "spring.sleuth.hystrix.strategy.enabled", matchIfMissing = true)
|
||||
public class SleuthHystrixAutoConfiguration {
|
||||
|
||||
@Bean SleuthHystrixConcurrencyStrategy sleuthHystrixConcurrencyStrategy(Tracer tracer) {
|
||||
@Bean
|
||||
SleuthHystrixConcurrencyStrategy sleuthHystrixConcurrencyStrategy(Tracer tracer) {
|
||||
return new SleuthHystrixConcurrencyStrategy(tracer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package org.springframework.cloud.sleuth.instrument.hystrix;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
import com.netflix.hystrix.strategy.HystrixPlugins;
|
||||
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.TraceCallable;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
|
||||
@@ -17,14 +21,62 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy
|
||||
this.tracer = tracer;
|
||||
try {
|
||||
HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
|
||||
} catch (Exception e) {
|
||||
HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
|
||||
log.debug("Failed to register Sleuth Hystrix Concurrency Strategy. Will use the current one which is [" + concurrencyStrategy + "]", e);
|
||||
}
|
||||
catch (Exception e) {
|
||||
HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance()
|
||||
.getConcurrencyStrategy();
|
||||
log.debug(
|
||||
"Failed to register Sleuth Hystrix Concurrency Strategy. Will use the current one which is ["
|
||||
+ concurrencyStrategy + "]",
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void close() {
|
||||
HystrixPlugins.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Callable<T> wrapCallable(Callable<T> callable) {
|
||||
return new TraceCallable<>(this.tracer, callable);
|
||||
return new HystrixTraceCallable<T>(this.tracer, callable);
|
||||
}
|
||||
|
||||
private static class HystrixTraceCallable<S> implements Callable<S> {
|
||||
|
||||
private Tracer tracer;
|
||||
private Callable<S> callable;
|
||||
private Span parent;
|
||||
|
||||
public HystrixTraceCallable(Tracer tracer, Callable<S> callable) {
|
||||
this.tracer = tracer;
|
||||
this.callable = callable;
|
||||
this.parent = tracer.getCurrentSpan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public S call() throws Exception {
|
||||
Span span = this.parent;
|
||||
boolean created = false;
|
||||
if (span != null) {
|
||||
span = this.tracer.continueSpan(span);
|
||||
}
|
||||
else {
|
||||
span = this.tracer.startTrace(Thread.currentThread().getName());
|
||||
created = true;
|
||||
}
|
||||
try {
|
||||
return this.callable.call();
|
||||
}
|
||||
finally {
|
||||
if (created) {
|
||||
this.tracer.close(span);
|
||||
}
|
||||
else {
|
||||
this.tracer.detach(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.cloud.sleuth.instrument.hystrix;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.trace.SpanContextHolder;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommand;
|
||||
|
||||
@@ -45,7 +44,6 @@ public abstract class TraceCommand<R> extends HystrixCommand<R> {
|
||||
|
||||
@Override
|
||||
protected R run() throws Exception {
|
||||
enforceThatHystrixThreadIsNotPollutedByPreviousTraces();
|
||||
Span span = this.tracer.joinTrace(getCommandKey().name(), this.parentSpan);
|
||||
try {
|
||||
return doRun();
|
||||
@@ -55,10 +53,5 @@ public abstract class TraceCommand<R> extends HystrixCommand<R> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Do more analysis why this is not removed properly
|
||||
private void enforceThatHystrixThreadIsNotPollutedByPreviousTraces() {
|
||||
SpanContextHolder.removeCurrentSpan();
|
||||
}
|
||||
|
||||
public abstract R doRun() throws Exception;
|
||||
}
|
||||
|
||||
@@ -157,6 +157,8 @@ public class DefaultTracer implements Tracer {
|
||||
public Span continueSpan(Span span) {
|
||||
if (span != null) {
|
||||
this.publisher.publishEvent(new SpanContinuedEvent(this, span));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
Span newSpan = createSpan(span, SpanContextHolder.getCurrentSpan());
|
||||
SpanContextHolder.setCurrentSpan(newSpan);
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.junit.Ignore;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
import org.springframework.cloud.sleuth.instrument.hystrix.SpanPassingForHystrixViaAnnotationsIntegrationTests;
|
||||
import org.springframework.cloud.sleuth.instrument.hystrix.HystrixAnnotationsIntegrationTests;
|
||||
import org.springframework.cloud.sleuth.instrument.hystrix.TraceCommandTests;
|
||||
|
||||
/**
|
||||
@@ -29,7 +29,7 @@ import org.springframework.cloud.sleuth.instrument.hystrix.TraceCommandTests;
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses({ SpanPassingForHystrixViaAnnotationsIntegrationTests.class,
|
||||
@SuiteClasses({ HystrixAnnotationsIntegrationTests.class,
|
||||
TraceCommandTests.class })
|
||||
@Ignore
|
||||
public class AdhocTestSuite {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package org.springframework.cloud.sleuth.assertions;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.assertj.core.api.AbstractAssert;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
|
||||
import java.util.Objects;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class SpanAssert extends AbstractAssert<SpanAssert, Span> {
|
||||
@@ -19,8 +20,8 @@ public class SpanAssert extends AbstractAssert<SpanAssert, Span> {
|
||||
|
||||
public SpanAssert hasTraceIdEqualTo(long traceId) {
|
||||
isNotNull();
|
||||
if (!Objects.equals(actual.getTraceId(), traceId)) {
|
||||
String message = String.format("Expected span's traceId to be <%s> but was <%s>", traceId, actual.getTraceId());
|
||||
if (!Objects.equals(this.actual.getTraceId(), traceId)) {
|
||||
String message = String.format("Expected span's traceId to be <%s> but was <%s>", traceId, this.actual.getTraceId());
|
||||
log.error(message);
|
||||
failWithMessage(message);
|
||||
}
|
||||
@@ -29,8 +30,18 @@ public class SpanAssert extends AbstractAssert<SpanAssert, Span> {
|
||||
|
||||
public SpanAssert hasNameNotEqualTo(String name) {
|
||||
isNotNull();
|
||||
if (Objects.equals(actual.getName(), name)) {
|
||||
String message = String.format("Expected span's name not to be <%s> but was <%s>", name, actual.getName());
|
||||
if (Objects.equals(this.actual.getName(), name)) {
|
||||
String message = String.format("Expected span's name not to be <%s> but was <%s>", name, this.actual.getName());
|
||||
log.error(message);
|
||||
failWithMessage(message);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpanAssert hasNameEqualTo(String name) {
|
||||
isNotNull();
|
||||
if (!Objects.equals(this.actual.getName(), name)) {
|
||||
String message = String.format("Expected span's name to be <%s> but it was <%s>", name, this.actual.getName());
|
||||
log.error(message);
|
||||
failWithMessage(message);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import java.lang.annotation.Target;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.instrument.integration.TraceSpringIntegrationAutoConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
@@ -17,7 +16,7 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@EnableAutoConfiguration(exclude = { LoadBalancerAutoConfiguration.class,
|
||||
JmxAutoConfiguration.class, TraceSpringIntegrationAutoConfiguration.class,
|
||||
ArchaiusAutoConfiguration.class, LoadBalancerAutoConfiguration.class })
|
||||
LoadBalancerAutoConfiguration.class })
|
||||
@EnableAspectJAutoProxy(proxyTargetClass = true)
|
||||
@Configuration
|
||||
public @interface DefaultTestAutoConfiguration {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.springframework.cloud.sleuth.instrument.hystrix;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
@@ -16,7 +17,7 @@ import org.springframework.cloud.sleuth.instrument.DefaultTestAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.trace.SpanContextHolder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import com.jayway.awaitility.Awaitility;
|
||||
@@ -24,16 +25,17 @@ import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = {
|
||||
SpanPassingForHystrixViaAnnotationsIntegrationTests.TestConfig.class })
|
||||
@TestPropertySource(properties="hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000000")
|
||||
public class SpanPassingForHystrixViaAnnotationsIntegrationTests {
|
||||
HystrixAnnotationsIntegrationTests.TestConfig.class })
|
||||
@DirtiesContext
|
||||
public class HystrixAnnotationsIntegrationTests {
|
||||
|
||||
@Autowired HystrixCommandInvocationSpanCatcher hystrixCommandInvocationSpanCatcher;
|
||||
@Autowired
|
||||
HystrixCommandInvocationSpanCatcher catcher;
|
||||
@Autowired
|
||||
Tracer tracer;
|
||||
|
||||
@After
|
||||
public void clean() {
|
||||
public void cleanTrace() {
|
||||
SpanContextHolder.removeCurrentSpan();
|
||||
}
|
||||
|
||||
@@ -53,31 +55,31 @@ public class SpanPassingForHystrixViaAnnotationsIntegrationTests {
|
||||
}
|
||||
|
||||
private void whenHystrixCommandAnnotatedMethodGetsExecuted() {
|
||||
this.hystrixCommandInvocationSpanCatcher.invokeLogicWrappedInHystrixCommand();
|
||||
this.catcher.invokeLogicWrappedInHystrixCommand();
|
||||
}
|
||||
|
||||
private void thenTraceIdIsPassedFromTheCurrentThreadToTheHystrixOne(final Span span) {
|
||||
then(span).isNotNull();
|
||||
Awaitility.await().until(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
then(HystrixAnnotationsIntegrationTests.this.catcher).isNotNull();
|
||||
then(span)
|
||||
.hasTraceIdEqualTo(SpanPassingForHystrixViaAnnotationsIntegrationTests.this.hystrixCommandInvocationSpanCatcher.getTraceId())
|
||||
.hasNameNotEqualTo(SpanPassingForHystrixViaAnnotationsIntegrationTests.this.hystrixCommandInvocationSpanCatcher.getSpanName());
|
||||
.hasTraceIdEqualTo(HystrixAnnotationsIntegrationTests.this.catcher
|
||||
.getTraceId())
|
||||
.hasNameEqualTo(HystrixAnnotationsIntegrationTests.this.catcher
|
||||
.getSpanName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanTrace() {
|
||||
SpanContextHolder.removeCurrentSpan();
|
||||
}
|
||||
|
||||
@DefaultTestAutoConfiguration
|
||||
@EnableHystrix
|
||||
@Configuration
|
||||
static class TestConfig {
|
||||
|
||||
@Bean HystrixCommandInvocationSpanCatcher spanCatcher() {
|
||||
@Bean
|
||||
HystrixCommandInvocationSpanCatcher spanCatcher() {
|
||||
return new HystrixCommandInvocationSpanCatcher();
|
||||
}
|
||||
|
||||
@@ -89,21 +91,23 @@ public class SpanPassingForHystrixViaAnnotationsIntegrationTests {
|
||||
|
||||
@HystrixCommand
|
||||
public void invokeLogicWrappedInHystrixCommand() {
|
||||
this.spanCaughtFromHystrixThread = new AtomicReference<>(SpanContextHolder.getCurrentSpan());
|
||||
this.spanCaughtFromHystrixThread = new AtomicReference<>(
|
||||
SpanContextHolder.getCurrentSpan());
|
||||
}
|
||||
|
||||
public Long getTraceId() {
|
||||
if (this.spanCaughtFromHystrixThread == null ||
|
||||
this.spanCaughtFromHystrixThread.get() == null) {
|
||||
if (this.spanCaughtFromHystrixThread == null
|
||||
|| this.spanCaughtFromHystrixThread.get() == null) {
|
||||
return null;
|
||||
}
|
||||
return this.spanCaughtFromHystrixThread.get().getTraceId();
|
||||
}
|
||||
|
||||
public String getSpanName() {
|
||||
if (this.spanCaughtFromHystrixThread == null ||
|
||||
(this.spanCaughtFromHystrixThread.get() != null &&
|
||||
this.spanCaughtFromHystrixThread.get().getName() == null)) {
|
||||
if (this.spanCaughtFromHystrixThread == null
|
||||
|| (this.spanCaughtFromHystrixThread.get() != null
|
||||
&& this.spanCaughtFromHystrixThread.get()
|
||||
.getName() == null)) {
|
||||
return null;
|
||||
}
|
||||
return this.spanCaughtFromHystrixThread.get().getName();
|
||||
@@ -60,11 +60,6 @@ public class TraceCommandTests {
|
||||
then(spanFromCommand.getTraceId()).isEqualTo(EXPECTED_TRACE_ID);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUpTrace() {
|
||||
SpanContextHolder.removeCurrentSpan();
|
||||
}
|
||||
|
||||
private Span givenATraceIsPresentInTheCurrentThread() {
|
||||
return this.tracer.joinTrace("test",
|
||||
Span.builder().traceId(EXPECTED_TRACE_ID).build());
|
||||
|
||||
Reference in New Issue
Block a user