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;