From cb03ca0f8640cf372dccef688fbc1c4211866eeb Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 1 Dec 2020 14:26:23 +0100 Subject: [PATCH 1/4] Ensures that all executor service based tracing is done when context is ready; fixes gh-1128 --- .../LazyTraceScheduledThreadPoolExecutor.java | 54 ++++++++------ .../LazyTraceThreadPoolTaskScheduler.java | 72 ++++++++++--------- ...TraceWebServletAutoConfigurationTests.java | 8 ++- 3 files changed, 77 insertions(+), 57 deletions(-) diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutor.java index 2a72377b2..17e87dac2 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutor.java @@ -228,13 +228,27 @@ class LazyTraceScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor { makeAccessibleIfNotNull(this.newTaskForCallable); } + private Runnable traceRunnableWhenContextReady(Runnable delegate) { + if (ContextUtil.isContextUnusable(this.beanFactory)) { + return delegate; + } + return new TraceRunnable(tracing(), spanNamer(), delegate); + } + + private Callable traceCallableWhenContextReady(Callable delegate) { + if (ContextUtil.isContextUnusable(this.beanFactory)) { + return delegate; + } + return new TraceCallable<>(tracing(), spanNamer(), delegate); + } + @Override @SuppressWarnings("unchecked") public RunnableScheduledFuture decorateTask(Runnable runnable, RunnableScheduledFuture task) { return (RunnableScheduledFuture) ReflectionUtils.invokeMethod( this.decorateTaskRunnable, this.delegate, - new TraceRunnable(tracing(), spanNamer(), runnable), task); + traceRunnableWhenContextReady(runnable), task); } @Override @@ -243,57 +257,54 @@ class LazyTraceScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor { RunnableScheduledFuture task) { return (RunnableScheduledFuture) ReflectionUtils.invokeMethod( this.decorateTaskCallable, this.delegate, - new TraceCallable<>(tracing(), spanNamer(), callable), task); + traceCallableWhenContextReady(callable), task); } @Override public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { - return this.delegate.schedule(new TraceRunnable(tracing(), spanNamer(), command), - delay, unit); + return this.delegate.schedule(traceRunnableWhenContextReady(command), delay, + unit); } @Override public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { - return this.delegate.schedule( - new TraceCallable<>(tracing(), spanNamer(), callable), delay, unit); + return this.delegate.schedule(traceCallableWhenContextReady(callable), delay, + unit); } @Override public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { - return this.delegate.scheduleAtFixedRate( - new TraceRunnable(tracing(), spanNamer(), command), initialDelay, period, - unit); + return this.delegate.scheduleAtFixedRate(traceRunnableWhenContextReady(command), + initialDelay, period, unit); } @Override public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { return this.delegate.scheduleWithFixedDelay( - new TraceRunnable(tracing(), spanNamer(), command), initialDelay, delay, - unit); + traceRunnableWhenContextReady(command), initialDelay, delay, unit); } @Override public void execute(Runnable command) { - this.delegate.execute(new TraceRunnable(tracing(), spanNamer(), command)); + this.delegate.execute(traceRunnableWhenContextReady(command)); } @Override public Future submit(Runnable task) { - return this.delegate.submit(new TraceRunnable(tracing(), spanNamer(), task)); + return this.delegate.submit(traceRunnableWhenContextReady(task)); } @Override public Future submit(Runnable task, T result) { - return this.delegate.submit(new TraceRunnable(tracing(), spanNamer(), task), - result); + return this.delegate.submit(traceRunnableWhenContextReady(task), result); } @Override public Future submit(Callable task) { - return this.delegate.submit(new TraceCallable<>(tracing(), spanNamer(), task)); + return this.delegate.submit(traceCallableWhenContextReady(task)); } @Override @@ -479,13 +490,13 @@ class LazyTraceScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor { @Override public void beforeExecute(Thread t, Runnable r) { ReflectionUtils.invokeMethod(this.beforeExecute, this.delegate, t, - new TraceRunnable(tracing(), spanNamer(), r)); + traceRunnableWhenContextReady(r)); } @Override public void afterExecute(Runnable r, Throwable t) { ReflectionUtils.invokeMethod(this.afterExecute, this.delegate, - new TraceRunnable(tracing(), spanNamer(), r), t); + traceRunnableWhenContextReady(r), t); } @Override @@ -497,15 +508,14 @@ class LazyTraceScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor { @SuppressWarnings("unchecked") public RunnableFuture newTaskFor(Runnable runnable, T value) { return (RunnableFuture) ReflectionUtils.invokeMethod(this.newTaskForRunnable, - this.delegate, new TraceRunnable(tracing(), spanNamer(), runnable), - value); + this.delegate, traceRunnableWhenContextReady(runnable), value); } @Override @SuppressWarnings("unchecked") public RunnableFuture newTaskFor(Callable callable) { return (RunnableFuture) ReflectionUtils.invokeMethod(this.newTaskForCallable, - this.delegate, new TraceCallable<>(tracing(), spanNamer(), callable)); + this.delegate, traceCallableWhenContextReady(callable)); } @Override @@ -519,7 +529,7 @@ class LazyTraceScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor { List> ts = new ArrayList<>(); for (Callable task : tasks) { if (!(task instanceof TraceCallable)) { - ts.add(new TraceCallable<>(tracing(), spanNamer(), task)); + ts.add(traceCallableWhenContextReady(task)); } } return ts; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskScheduler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskScheduler.java index bb69c6823..9c0e6426b 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskScheduler.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskScheduler.java @@ -103,6 +103,20 @@ class LazyTraceThreadPoolTaskScheduler extends ThreadPoolTaskScheduler { } } + private Runnable traceRunnableWhenContextReady(Runnable delegate) { + if (ContextUtil.isContextUnusable(this.beanFactory)) { + return delegate; + } + return new TraceRunnable(tracing(), spanNamer(), delegate); + } + + private Callable traceCallableWhenContextReady(Callable delegate) { + if (ContextUtil.isContextUnusable(this.beanFactory)) { + return delegate; + } + return new TraceCallable<>(tracing(), spanNamer(), delegate); + } + @Override public void setPoolSize(int poolSize) { this.delegate.setPoolSize(poolSize); @@ -184,41 +198,38 @@ class LazyTraceThreadPoolTaskScheduler extends ThreadPoolTaskScheduler { @Override public void execute(Runnable task) { - this.delegate.execute(new TraceRunnable(tracing(), spanNamer(), task)); + this.delegate.execute(traceRunnableWhenContextReady(task)); } @Override public void execute(Runnable task, long startTimeout) { - this.delegate.execute(new TraceRunnable(tracing(), spanNamer(), task), - startTimeout); + this.delegate.execute(traceRunnableWhenContextReady(task), startTimeout); } @Override public Future submit(Runnable task) { - return this.delegate.submit(new TraceRunnable(tracing(), spanNamer(), task)); + return this.delegate.submit(traceRunnableWhenContextReady(task)); } @Override public Future submit(Callable task) { - return this.delegate.submit(new TraceCallable<>(tracing(), spanNamer(), task)); + return this.delegate.submit(traceCallableWhenContextReady(task)); } @Override public ListenableFuture submitListenable(Runnable task) { - return this.delegate - .submitListenable(new TraceRunnable(tracing(), spanNamer(), task)); + return this.delegate.submitListenable(traceRunnableWhenContextReady(task)); } @Override public ListenableFuture submitListenable(Callable task) { - return this.delegate - .submitListenable(new TraceCallable<>(tracing(), spanNamer(), task)); + return this.delegate.submitListenable(traceCallableWhenContextReady(task)); } @Override public void cancelRemainingTask(Runnable task) { ReflectionUtils.invokeMethod(this.cancelRemainingTask, this.delegate, - new TraceRunnable(tracing(), spanNamer(), task)); + traceRunnableWhenContextReady(task)); } @Override @@ -229,40 +240,38 @@ class LazyTraceThreadPoolTaskScheduler extends ThreadPoolTaskScheduler { @Override @Nullable public ScheduledFuture schedule(Runnable task, Trigger trigger) { - return this.delegate.schedule(new TraceRunnable(tracing(), spanNamer(), task), - trigger); + return this.delegate.schedule(traceRunnableWhenContextReady(task), trigger); } @Override public ScheduledFuture schedule(Runnable task, Date startTime) { - return this.delegate.schedule(new TraceRunnable(tracing(), spanNamer(), task), - startTime); + return this.delegate.schedule(traceRunnableWhenContextReady(task), startTime); } @Override public ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period) { - return this.delegate.scheduleAtFixedRate( - new TraceRunnable(tracing(), spanNamer(), task), startTime, period); + return this.delegate.scheduleAtFixedRate(traceRunnableWhenContextReady(task), + startTime, period); } @Override public ScheduledFuture scheduleAtFixedRate(Runnable task, long period) { - return this.delegate.scheduleAtFixedRate( - new TraceRunnable(tracing(), spanNamer(), task), period); + return this.delegate.scheduleAtFixedRate(traceRunnableWhenContextReady(task), + period); } @Override public ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay) { - return this.delegate.scheduleWithFixedDelay( - new TraceRunnable(tracing(), spanNamer(), task), startTime, delay); + return this.delegate.scheduleWithFixedDelay(traceRunnableWhenContextReady(task), + startTime, delay); } @Override public ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay) { - return this.delegate.scheduleWithFixedDelay( - new TraceRunnable(tracing(), spanNamer(), task), delay); + return this.delegate.scheduleWithFixedDelay(traceRunnableWhenContextReady(task), + delay); } @Override @@ -385,34 +394,33 @@ class LazyTraceThreadPoolTaskScheduler extends ThreadPoolTaskScheduler { @Override public ScheduledFuture schedule(Runnable task, Instant startTime) { - return this.delegate.schedule(new TraceRunnable(tracing(), spanNamer(), task), - startTime); + return this.delegate.schedule(traceRunnableWhenContextReady(task), startTime); } @Override public ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period) { - return this.delegate.scheduleAtFixedRate( - new TraceRunnable(tracing(), spanNamer(), task), startTime, period); + return this.delegate.scheduleAtFixedRate(traceRunnableWhenContextReady(task), + startTime, period); } @Override public ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period) { - return this.delegate.scheduleAtFixedRate( - new TraceRunnable(tracing(), spanNamer(), task), period); + return this.delegate.scheduleAtFixedRate(traceRunnableWhenContextReady(task), + period); } @Override public ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay) { - return this.delegate.scheduleWithFixedDelay( - new TraceRunnable(tracing(), spanNamer(), task), startTime, delay); + return this.delegate.scheduleWithFixedDelay(traceRunnableWhenContextReady(task), + startTime, delay); } @Override public ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay) { - return this.delegate.scheduleWithFixedDelay( - new TraceRunnable(tracing(), spanNamer(), task), delay); + return this.delegate.scheduleWithFixedDelay(traceRunnableWhenContextReady(task), + delay); } private Tracing tracing() { diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceWebServletAutoConfigurationTests.java b/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceWebServletAutoConfigurationTests.java index 8a25f550a..20e39b19b 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceWebServletAutoConfigurationTests.java +++ b/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceWebServletAutoConfigurationTests.java @@ -40,9 +40,11 @@ public class TraceWebServletAutoConfigurationTests { @Test public void shouldNotCreateTracedWebBeansWhenServletClassMissing() { - this.contextRunner.withClassLoader(new FilteredClassLoader(HandlerInterceptorAdapter.class)).run((context) -> { - assertThat(context).doesNotHaveBean(TraceWebAspect.class); - }); + this.contextRunner + .withClassLoader(new FilteredClassLoader(HandlerInterceptorAdapter.class)) + .run((context) -> { + assertThat(context).doesNotHaveBean(TraceWebAspect.class); + }); } @Test From 55440c641e294440a776f02ae11d109afdd809f0 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 1 Dec 2020 15:13:58 +0100 Subject: [PATCH 2/4] Ensures that all Lazy components are resolving any beans when context is running fixes gh-1750 --- .../autoconfig/TraceAutoConfiguration.java | 6 ++ .../async/AsyncAutoConfiguration.java | 6 -- .../async/LazyTraceAsyncTaskExecutor.java | 1 + .../instrument/async/LazyTraceExecutor.java | 1 + .../LazyTraceScheduledThreadPoolExecutor.java | 1 + .../LazyTraceThreadPoolTaskExecutor.java | 1 + .../LazyTraceThreadPoolTaskScheduler.java | 1 + .../async/TraceableExecutorService.java | 1 + .../TraceableScheduledExecutorService.java | 1 + .../TraceWebClientAutoConfiguration.java | 10 ++- .../async => internal}/ContextUtil.java | 12 ++- .../SleuthContextListener.java | 25 ++++-- ...LazyTraceThreadPoolTaskSchedulerTests.java | 1 + .../async/TraceableExecutorServiceTests.java | 1 + ...TraceableScheduledExecutorServiceTest.java | 1 + ...cingClientHttpRequestInterceptorTests.java | 76 +++++++++++++++++++ .../SleuthContextListenerAccessor.java | 2 +- 17 files changed, 130 insertions(+), 17 deletions(-) rename spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/{instrument/async => internal}/ContextUtil.java (80%) rename spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/{instrument/async => internal}/SleuthContextListener.java (82%) create mode 100644 spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/LazyTracingClientHttpRequestInterceptorTests.java rename spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/{instrument/async => internal}/SleuthContextListenerAccessor.java (94%) diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java index 035535946..ef4393918 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java @@ -52,6 +52,7 @@ import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.LocalServiceName; import org.springframework.cloud.sleuth.SpanAdjuster; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.internal.SleuthContextListener; import org.springframework.cloud.sleuth.log.SleuthLogAutoConfiguration; import org.springframework.cloud.sleuth.sampler.SamplerAutoConfiguration; import org.springframework.context.annotation.Bean; @@ -104,6 +105,11 @@ public class TraceAutoConfiguration { return 0; }; + @Bean + SleuthContextListener traceContextClosedListener() { + return new SleuthContextListener(); + } + @Bean @ConditionalOnMissingBean // NOTE: stable bean name as might be used outside sleuth diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncAutoConfiguration.java index c1f9cd875..fc26848b7 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncAutoConfiguration.java @@ -19,7 +19,6 @@ package org.springframework.cloud.sleuth.instrument.async; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.sleuth.instrument.scheduling.SleuthSchedulingProperties; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** @@ -38,9 +37,4 @@ import org.springframework.context.annotation.Configuration; SleuthSchedulingProperties.class }) public class AsyncAutoConfiguration { - @Bean - SleuthContextListener traceContextClosedListener() { - return new SleuthContextListener(); - } - } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceAsyncTaskExecutor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceAsyncTaskExecutor.java index de5e32bc0..5d5c97049 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceAsyncTaskExecutor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceAsyncTaskExecutor.java @@ -27,6 +27,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.internal.ContextUtil; import org.springframework.core.task.AsyncTaskExecutor; /** diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceExecutor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceExecutor.java index c68c67e6e..18178e109 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceExecutor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceExecutor.java @@ -26,6 +26,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.internal.ContextUtil; /** * {@link Executor} that wraps {@link Runnable} in a trace representation. diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutor.java index 17e87dac2..a83717012 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutor.java @@ -41,6 +41,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.internal.ContextUtil; import org.springframework.util.ReflectionUtils; /** diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskExecutor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskExecutor.java index 34ca5ea15..9a086d879 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskExecutor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskExecutor.java @@ -30,6 +30,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.internal.ContextUtil; import org.springframework.core.task.TaskDecorator; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.util.concurrent.ListenableFuture; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskScheduler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskScheduler.java index 9c0e6426b..d03c0482f 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskScheduler.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskScheduler.java @@ -37,6 +37,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.internal.ContextUtil; import org.springframework.lang.Nullable; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorService.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorService.java index 6ca48073f..a2bce14cd 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorService.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorService.java @@ -30,6 +30,7 @@ import brave.Tracing; import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.internal.ContextUtil; /** * A decorator class for {@link ExecutorService} to support tracing in Executors. diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorService.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorService.java index 0de1b4d60..71b83d0db 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorService.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorService.java @@ -23,6 +23,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.springframework.beans.factory.BeanFactory; +import org.springframework.cloud.sleuth.internal.ContextUtil; /** * A decorator class for {@link ScheduledExecutorService} to support tracing in Executors. diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java index f0f34a8cc..c7971222c 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java @@ -43,6 +43,7 @@ import org.springframework.boot.web.client.RestTemplateCustomizer; import org.springframework.cloud.commons.httpclient.HttpClientConfiguration; import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter; import org.springframework.cloud.sleuth.instrument.web.TraceHttpAutoConfiguration; +import org.springframework.cloud.sleuth.internal.ContextUtil; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -309,10 +310,17 @@ class LazyTracingClientHttpRequestInterceptor implements ClientHttpRequestInterc @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { + if (isContextUnusable()) { + return execution.execute(request, body); + } return interceptor().intercept(request, body, execution); } - private TracingClientHttpRequestInterceptor interceptor() { + boolean isContextUnusable() { + return ContextUtil.isContextUnusable(this.beanFactory); + } + + ClientHttpRequestInterceptor interceptor() { if (this.interceptor == null) { this.interceptor = this.beanFactory .getBean(TracingClientHttpRequestInterceptor.class); diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/ContextUtil.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/internal/ContextUtil.java similarity index 80% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/ContextUtil.java rename to spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/internal/ContextUtil.java index 2e6304b2b..944dd2d26 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/ContextUtil.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/internal/ContextUtil.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.async; +package org.springframework.cloud.sleuth.internal; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -22,12 +22,12 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; /** - * Utility class that verifies that context is in creation. + * Utility class that verifies that context is in creation. Do not use. * * @author Marcin Grzejszczak * @since 2.1.0 */ -final class ContextUtil { +public final class ContextUtil { private ContextUtil() { throw new IllegalStateException("Can't instantiate a utility class"); @@ -35,7 +35,11 @@ final class ContextUtil { private static final Log log = LogFactory.getLog(ContextUtil.class); - static boolean isContextUnusable(BeanFactory beanFactory) { + /** + * @param beanFactory bean facotry + * @return {@code true} when context is not ready to be used + */ + public static boolean isContextUnusable(BeanFactory beanFactory) { SleuthContextListener listener = SleuthContextListener.getBean(beanFactory); boolean contextUnusable = listener.isUnusable(); if (contextUnusable && log.isDebugEnabled()) { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListener.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/internal/SleuthContextListener.java similarity index 82% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListener.java rename to spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/internal/SleuthContextListener.java index 450bc1254..2c689d31f 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListener.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/internal/SleuthContextListener.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.async; +package org.springframework.cloud.sleuth.internal; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -32,7 +32,13 @@ import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.SmartApplicationListener; -class SleuthContextListener implements SmartApplicationListener { +/** + * Internal tool used by Sleuth. Do not use. + * + * @author Marcin Grzejszczak + * @since 2.2.5 + */ +public class SleuthContextListener implements SmartApplicationListener { static final Map CACHE = new ConcurrentHashMap<>(); @@ -42,7 +48,7 @@ class SleuthContextListener implements SmartApplicationListener { final AtomicBoolean closed; - SleuthContextListener() { + public SleuthContextListener() { this.refreshed = new AtomicBoolean(); this.closed = new AtomicBoolean(); } @@ -52,7 +58,13 @@ class SleuthContextListener implements SmartApplicationListener { this.closed = closed; } - static SleuthContextListener getBean(BeanFactory beanFactory) { + /** + * Returns an instance of the {@link SleuthContextListener} that might have already + * been initialized. + * @param beanFactory bean factory + * @return instance of {@link SleuthContextListener} + */ + public static SleuthContextListener getBean(BeanFactory beanFactory) { return CACHE.getOrDefault(beanFactory, new SleuthContextListener()); } @@ -83,7 +95,10 @@ class SleuthContextListener implements SmartApplicationListener { } } - boolean isUnusable() { + /** + * @return @{code true} when Spring Context has NOT yet been started + */ + public boolean isUnusable() { return !this.refreshed.get() || this.closed.get(); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java index 6313083d8..e1de3430e 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java @@ -36,6 +36,7 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.internal.SleuthContextListenerAccessor; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.util.ErrorHandler; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java index 4a2d280cb..482e783c7 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java @@ -48,6 +48,7 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.internal.SleuthContextListenerAccessor; import static java.util.stream.Collectors.toList; import static org.assertj.core.api.BDDAssertions.then; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorServiceTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorServiceTest.java index 422bdc581..409b923de 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorServiceTest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorServiceTest.java @@ -36,6 +36,7 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.internal.SleuthContextListenerAccessor; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/LazyTracingClientHttpRequestInterceptorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/LazyTracingClientHttpRequestInterceptorTests.java new file mode 100644 index 000000000..4d4f56c56 --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/LazyTracingClientHttpRequestInterceptorTests.java @@ -0,0 +1,76 @@ +/* + * 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.web.client; + +import java.io.IOException; + +import brave.spring.web.TracingClientHttpRequestInterceptor; +import org.junit.jupiter.api.Test; +import org.mockito.BDDMockito; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpRequestInterceptor; + +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; + +class LazyTracingClientHttpRequestInterceptorTests { + + @Test + void should_not_retrieve_bean_from_context_when_context_is_not_ready() + throws IOException { + BeanFactory beanFactory = mock(BeanFactory.class); + LazyTracingClientHttpRequestInterceptor interceptor = new LazyTracingClientHttpRequestInterceptor( + beanFactory); + + interceptor.intercept(mock(HttpRequest.class), new byte[0], + mock(ClientHttpRequestExecution.class)); + + then(beanFactory).should(never()) + .getBean(TracingClientHttpRequestInterceptor.class); + } + + @Test + void should_retrieve_bean_from_context_when_context_is_ready() throws IOException { + BeanFactory beanFactory = mock(BeanFactory.class); + ClientHttpRequestInterceptor requestInterceptor = mock( + ClientHttpRequestInterceptor.class); + LazyTracingClientHttpRequestInterceptor interceptor = new LazyTracingClientHttpRequestInterceptor( + beanFactory) { + @Override + ClientHttpRequestInterceptor interceptor() { + return requestInterceptor; + } + + @Override + boolean isContextUnusable() { + return false; + } + }; + + interceptor.intercept(mock(HttpRequest.class), new byte[0], + mock(ClientHttpRequestExecution.class)); + + then(requestInterceptor).should().intercept(BDDMockito.any(HttpRequest.class), + BDDMockito.any(byte[].class), + BDDMockito.any(ClientHttpRequestExecution.class)); + } + +} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListenerAccessor.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/internal/SleuthContextListenerAccessor.java similarity index 94% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListenerAccessor.java rename to spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/internal/SleuthContextListenerAccessor.java index 444244128..f2ea51e55 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListenerAccessor.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/internal/SleuthContextListenerAccessor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.async; +package org.springframework.cloud.sleuth.internal; import java.util.concurrent.atomic.AtomicBoolean; From f40cbf85a059e487548f5ec80de59bf94dfe78ab Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 1 Dec 2020 16:05:48 +0100 Subject: [PATCH 3/4] Not doing assertions for ReactorSleuth; fixes gh-1712 --- .../cloud/sleuth/instrument/reactor/ReactorSleuth.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/ReactorSleuth.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/ReactorSleuth.java index 5fd092220..968f9532a 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/ReactorSleuth.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/ReactorSleuth.java @@ -84,14 +84,11 @@ public abstract class ReactorSleuth { } if (!springContext.isActive()) { - boolean assertOn = false; - assert assertOn = true; // gives a message in unit test failures - if (log.isTraceEnabled() || assertOn) { + if (log.isTraceEnabled()) { String message = "Spring Context [" + springContext + "] is not yet refreshed. This is unexpected. Reactor Context is [" + sub.currentContext() + "] and name is [" + name(sub) + "]"; log.trace(message); - assert false : message; // should never happen, but don't break. } return sub; } From f50fb221a7a0763c9c18633e9b33a1e3506c2b18 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 1 Dec 2020 19:54:17 +0100 Subject: [PATCH 4/4] Added broker information basing on the binders in spring cloud stream fixes gh-1711 --- spring-cloud-sleuth-core/pom.xml | 5 +++ .../messaging/TracingChannelInterceptor.java | 39 ++++++++++++++++++- .../cloud/sleuth/SpanAdjusterTests.java | 4 +- .../cloud/sleuth/SpanHandlerTests.java | 4 +- 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/spring-cloud-sleuth-core/pom.xml b/spring-cloud-sleuth-core/pom.xml index 9f0b269f4..dd5daa76e 100644 --- a/spring-cloud-sleuth-core/pom.xml +++ b/spring-cloud-sleuth-core/pom.xml @@ -85,6 +85,11 @@ org.springframework.cloud spring-cloud-commons + + org.springframework.cloud + spring-cloud-stream + true + org.springframework.cloud spring-cloud-aws-messaging diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptor.java index 3f95197b0..2eecfa9ea 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptor.java @@ -16,6 +16,9 @@ package org.springframework.cloud.sleuth.instrument.messaging; +import java.util.Iterator; +import java.util.Map; + import brave.Span; import brave.SpanCustomizer; import brave.Tracer; @@ -28,8 +31,13 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.aop.support.AopUtils; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.sleuth.util.SpanNameUtil; +import org.springframework.cloud.stream.binder.BinderType; +import org.springframework.cloud.stream.binder.BinderTypeRegistry; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.integration.channel.AbstractMessageChannel; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.context.IntegrationObjectSupport; @@ -63,7 +71,7 @@ import org.springframework.util.ClassUtils; */ @Deprecated public final class TracingChannelInterceptor extends ChannelInterceptorAdapter - implements ExecutorChannelInterceptor { + implements ExecutorChannelInterceptor, ApplicationContextAware { /** * Name of the class in Spring Cloud Stream that is a direct channel. @@ -109,9 +117,13 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter private final boolean hasDirectChannelClass; + private final boolean hasBinderTypeRegistry; + // special case of a Stream private final Class directWithAttributesChannelClass; + private ApplicationContext applicationContext; + @Autowired TracingChannelInterceptor(Tracing tracing, SleuthMessagingProperties properties) { this(tracing, properties, MessageHeaderPropagation.INSTANCE, @@ -131,6 +143,8 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter "org.springframework.integration.context.IntegrationObjectSupport", null); this.hasDirectChannelClass = ClassUtils .isPresent("org.springframework.integration.channel.DirectChannel", null); + this.hasBinderTypeRegistry = ClassUtils.isPresent( + "org.springframework.cloud.stream.binder.BinderTypeRegistry", null); this.directWithAttributesChannelClass = ClassUtils .isPresent(STREAM_DIRECT_CHANNEL, null) ? ClassUtils.resolveClassName(STREAM_DIRECT_CHANNEL, null) : null; @@ -204,6 +218,23 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter return this.properties.getMessaging().getRabbit().getRemoteServiceName(); } } + if (this.hasBinderTypeRegistry && this.applicationContext != null) { + BinderTypeRegistry typeRegistry = this.applicationContext + .getBean(BinderTypeRegistry.class); + Iterator> iterator = typeRegistry.getAll() + .entrySet().iterator(); + if (iterator.hasNext()) { + String binderName = iterator.next().getKey(); + if (binderName.equals("kafka")) { + return this.properties.getMessaging().getKafka() + .getRemoteServiceName(); + } + else if (binderName.equals("rabbit")) { + return this.properties.getMessaging().getRabbit() + .getRemoteServiceName(); + } + } + } return REMOTE_SERVICE_NAME; } @@ -435,4 +466,10 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter return message == null; } + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = applicationContext; + } + } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanAdjusterTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanAdjusterTests.java index 282f85dba..9cce9ca84 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanAdjusterTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanAdjusterTests.java @@ -29,6 +29,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.sleuth.util.ArrayListSpanReporter; +import org.springframework.cloud.stream.function.FunctionConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.junit4.SpringRunner; @@ -58,7 +59,8 @@ public class SpanAdjusterTests { } @Configuration - @EnableAutoConfiguration(exclude = IntegrationAutoConfiguration.class) + @EnableAutoConfiguration( + exclude = { IntegrationAutoConfiguration.class, FunctionConfiguration.class }) static class SpanAdjusterAspectTestsConfig { @Bean diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanHandlerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanHandlerTests.java index 7818dd3e3..51ecaeece 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanHandlerTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanHandlerTests.java @@ -31,6 +31,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.stream.function.FunctionConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.junit4.SpringRunner; @@ -62,7 +63,8 @@ public class SpanHandlerTests { } @Configuration - @EnableAutoConfiguration(exclude = IntegrationAutoConfiguration.class) + @EnableAutoConfiguration( + exclude = { IntegrationAutoConfiguration.class, FunctionConfiguration.class }) static class SpanHandlerAspectTestsConfig { @Bean