diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/BraveAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/BraveAutoConfiguration.java index 285f4aafc..7b27b25d6 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/BraveAutoConfiguration.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/BraveAutoConfiguration.java @@ -43,6 +43,7 @@ import org.springframework.cloud.sleuth.autoconfig.TraceConfiguration; import org.springframework.cloud.sleuth.autoconfig.brave.instrument.web.BraveHttpConfiguration; import org.springframework.cloud.sleuth.brave.LocalServiceName; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; +import org.springframework.cloud.sleuth.internal.SleuthContextListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -153,4 +154,9 @@ public class BraveAutoConfiguration { return CurrentSpanCustomizer.create(tracing); } + @Bean + SleuthContextListener sleuthContextListener() { + return new SleuthContextListener(); + } + } diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/messaging/BraveMessagingAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/messaging/BraveMessagingAutoConfiguration.java index 43b25a9c8..bbae983cc 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/messaging/BraveMessagingAutoConfiguration.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/messaging/BraveMessagingAutoConfiguration.java @@ -36,6 +36,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.sleuth.autoconfig.instrument.messaging.SleuthMessagingProperties; import org.springframework.cloud.sleuth.brave.instrument.messaging.ConditionalOnMessagingEnabled; import org.springframework.cloud.sleuth.brave.instrument.messaging.ConsumerSampler; import org.springframework.cloud.sleuth.brave.instrument.messaging.KafkaFactoryBeanPostProcessor; diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/async/TraceAsyncAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/async/TraceAsyncAutoConfiguration.java index fb93c31b4..643a49ce3 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/async/TraceAsyncAutoConfiguration.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/async/TraceAsyncAutoConfiguration.java @@ -23,8 +23,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration; import org.springframework.cloud.sleuth.autoconfig.otel.OtelAutoConfiguration; -import org.springframework.cloud.sleuth.instrument.async.SleuthContextListener; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** @@ -41,9 +39,4 @@ import org.springframework.context.annotation.Configuration; @AutoConfigureAfter({ BraveAutoConfiguration.class, OtelAutoConfiguration.class }) public class TraceAsyncAutoConfiguration { - @Bean - SleuthContextListener traceContextClosedListener() { - return new SleuthContextListener(); - } - } diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/messaging/SleuthMessagingProperties.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/messaging/SleuthMessagingProperties.java similarity index 97% rename from spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/messaging/SleuthMessagingProperties.java rename to spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/messaging/SleuthMessagingProperties.java index 2e0cfaa1c..aaaeade62 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/messaging/SleuthMessagingProperties.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/messaging/SleuthMessagingProperties.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.autoconfig.brave.instrument.messaging; +package org.springframework.cloud.sleuth.autoconfig.instrument.messaging; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/messaging/TraceSpringIntegrationAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/messaging/TraceSpringIntegrationAutoConfiguration.java index cf38f7fed..88f40a177 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/messaging/TraceSpringIntegrationAutoConfiguration.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/messaging/TraceSpringIntegrationAutoConfiguration.java @@ -16,6 +16,8 @@ package org.springframework.cloud.sleuth.autoconfig.instrument.messaging; +import java.util.function.Function; + import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -39,6 +41,7 @@ import org.springframework.integration.channel.interceptor.GlobalChannelIntercep import org.springframework.integration.config.GlobalChannelInterceptor; import org.springframework.messaging.support.MessageHeaderAccessor; import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; /** * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration @@ -70,9 +73,25 @@ public class TraceSpringIntegrationAutoConfiguration { @Bean TracingChannelInterceptor traceChannelInterceptor(Tracer tracer, Propagator propagator, Propagator.Setter traceMessagePropagationSetter, - Propagator.Getter traceMessagePropagationGetter) { + Propagator.Getter traceMessagePropagationGetter, + SleuthMessagingProperties properties) { return new TracingChannelInterceptor(tracer, propagator, traceMessagePropagationSetter, - traceMessagePropagationGetter); + traceMessagePropagationGetter, remoteServiceNameMapper(properties)); + } + + static Function remoteServiceNameMapper(SleuthMessagingProperties properties) { + return s -> { + if (!StringUtils.hasText(s)) { + return null; + } + if (s.startsWith("amqp") || s.startsWith("rabbit")) { + return properties.getMessaging().getRabbit().getRemoteServiceName(); + } + else if (s.startsWith("kafka")) { + return properties.getMessaging().getKafka().getRemoteServiceName(); + } + return null; + }; } } diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/messaging/TraceSpringMessagingAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/messaging/TraceSpringMessagingAutoConfiguration.java index b2fb30f7f..1a6c83e3b 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/messaging/TraceSpringMessagingAutoConfiguration.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/messaging/TraceSpringMessagingAutoConfiguration.java @@ -29,7 +29,7 @@ import org.springframework.messaging.support.MessageHeaderAccessor; @Configuration(proxyBeanMethods = false) @ConditionalOnClass(MessageHeaderAccessor.class) @ConditionalOnProperty(value = "spring.sleuth.messaging.enabled", matchIfMissing = true) -@EnableConfigurationProperties(SleuthIntegrationMessagingProperties.class) +@EnableConfigurationProperties({ SleuthIntegrationMessagingProperties.class, SleuthMessagingProperties.class }) class TraceSpringMessagingAutoConfiguration { @Bean diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/messaging/TraceWebSocketAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/messaging/TraceWebSocketAutoConfiguration.java index d9e995369..ddd8e3025 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/messaging/TraceWebSocketAutoConfiguration.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/messaging/TraceWebSocketAutoConfiguration.java @@ -61,6 +61,9 @@ class TraceWebSocketAutoConfiguration extends AbstractWebSocketMessageBrokerConf @Autowired Propagator.Getter getter; + @Autowired + SleuthMessagingProperties sleuthMessagingProperties; + @Override public void registerStompEndpoints(StompEndpointRegistry registry) { // The user must register their own endpoints @@ -68,20 +71,23 @@ class TraceWebSocketAutoConfiguration extends AbstractWebSocketMessageBrokerConf @Override public void configureMessageBroker(MessageBrokerRegistry registry) { - registry.configureBrokerChannel() - .setInterceptors(new TracingChannelInterceptor(this.tracer, this.propagator, this.setter, this.getter)); + registry.configureBrokerChannel().setInterceptors(new TracingChannelInterceptor(this.tracer, this.propagator, + this.setter, this.getter, + TraceSpringIntegrationAutoConfiguration.remoteServiceNameMapper(this.sleuthMessagingProperties))); } @Override public void configureClientOutboundChannel(ChannelRegistration registration) { - registration - .setInterceptors(new TracingChannelInterceptor(this.tracer, this.propagator, this.setter, this.getter)); + registration.setInterceptors(new TracingChannelInterceptor(this.tracer, this.propagator, this.setter, + this.getter, + TraceSpringIntegrationAutoConfiguration.remoteServiceNameMapper(this.sleuthMessagingProperties))); } @Override public void configureClientInboundChannel(ChannelRegistration registration) { - registration - .setInterceptors(new TracingChannelInterceptor(this.tracer, this.propagator, this.setter, this.getter)); + registration.setInterceptors(new TracingChannelInterceptor(this.tracer, this.propagator, this.setter, + this.getter, + TraceSpringIntegrationAutoConfiguration.remoteServiceNameMapper(this.sleuthMessagingProperties))); } } diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/OtelAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/OtelAutoConfiguration.java index 89337e981..b047291a3 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/OtelAutoConfiguration.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/otel/OtelAutoConfiguration.java @@ -45,6 +45,7 @@ import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties; import org.springframework.cloud.sleuth.autoconfig.SleuthSpanFilterProperties; import org.springframework.cloud.sleuth.autoconfig.SleuthTracerProperties; import org.springframework.cloud.sleuth.autoconfig.TraceConfiguration; +import org.springframework.cloud.sleuth.internal.SleuthContextListener; import org.springframework.cloud.sleuth.otel.bridge.OtelOpenTelemetry; import org.springframework.cloud.sleuth.otel.bridge.SpanExporterCustomizer; import org.springframework.context.annotation.Bean; @@ -137,4 +138,9 @@ public class OtelAutoConfiguration { return Sampler.traceIdRatioBased(otelProperties.getTraceIdRatioBased()); } + @Bean + SleuthContextListener sleuthContextListener() { + return new SleuthContextListener(); + } + } diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/TraceWebServletConfigurationTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/TraceWebServletConfigurationTests.java index 4f1e0ba01..37c3367d3 100644 --- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/TraceWebServletConfigurationTests.java +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/TraceWebServletConfigurationTests.java @@ -40,7 +40,8 @@ import static org.assertj.core.api.Assertions.assertThat; public class TraceWebServletConfigurationTests { private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(TraceWebAutoConfiguration.class)).withUserConfiguration(TestConfig.class); + .withConfiguration(AutoConfigurations.of(TraceWebAutoConfiguration.class)) + .withUserConfiguration(TestConfig.class); @Test public void shouldNotCreateTracedWebBeansWhenServletClassMissing() { @@ -78,6 +79,7 @@ public class TraceWebServletConfigurationTests { HttpServerHandler httpServerHandler() { return BDDMockito.mock(HttpServerHandler.class); } + } } 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..06e5c6e30 --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/LazyTracingClientHttpRequestInterceptorTests.java @@ -0,0 +1,68 @@ +/* + * 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-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceAsyncTaskExecutor.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceAsyncTaskExecutor.java index 849ed7a60..e18e527ef 100644 --- a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceAsyncTaskExecutor.java +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceAsyncTaskExecutor.java @@ -26,6 +26,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.internal.ContextUtil; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; import org.springframework.core.task.AsyncTaskExecutor; diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceExecutor.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceExecutor.java index 08483e1a6..db822c899 100644 --- a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceExecutor.java +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceExecutor.java @@ -25,6 +25,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.internal.ContextUtil; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; /** diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutor.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutor.java index 91cff6b17..c75331036 100644 --- a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutor.java +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutor.java @@ -40,6 +40,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.internal.ContextUtil; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; import org.springframework.util.ReflectionUtils; @@ -144,61 +145,76 @@ class LazyTraceScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor { makeAccessibleIfNotNull(this.newTaskForCallable); } + private Runnable traceRunnableWhenContextReady(Runnable delegate) { + if (isContextUnusable()) { + return delegate; + } + return new TraceRunnable(tracing(), spanNamer(), delegate, this.beanName); + } + + boolean isContextUnusable() { + return ContextUtil.isContextUnusable(this.beanFactory); + } + + private Callable traceCallableWhenContextReady(Callable delegate) { + if (isContextUnusable()) { + return delegate; + } + return new TraceCallable<>(tracing(), spanNamer(), delegate, this.beanName); + } + @Override @SuppressWarnings("unchecked") public RunnableScheduledFuture decorateTask(Runnable runnable, RunnableScheduledFuture task) { return (RunnableScheduledFuture) ReflectionUtils.invokeMethod(this.decorateTaskRunnable, this.delegate, - new TraceRunnable(tracing(), spanNamer(), runnable, this.beanName), task); + traceRunnableWhenContextReady(runnable), task); } @Override @SuppressWarnings("unchecked") public RunnableScheduledFuture decorateTask(Callable callable, RunnableScheduledFuture task) { return (RunnableScheduledFuture) ReflectionUtils.invokeMethod(this.decorateTaskCallable, this.delegate, - new TraceCallable<>(tracing(), spanNamer(), callable, this.beanName), task); + traceCallableWhenContextReady(callable), task); } @Override public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { - return this.delegate.schedule(new TraceRunnable(tracing(), spanNamer(), command, this.beanName), 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, this.beanName), 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, this.beanName), - 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, this.beanName), - initialDelay, delay, unit); + return this.delegate.scheduleWithFixedDelay(traceRunnableWhenContextReady(command), initialDelay, delay, unit); } @Override public void execute(Runnable command) { - this.delegate.execute(new TraceRunnable(tracing(), spanNamer(), command, this.beanName)); + this.delegate.execute(traceRunnableWhenContextReady(command)); } @Override public Future submit(Runnable task) { - return this.delegate.submit(new TraceRunnable(tracing(), spanNamer(), task, this.beanName)); + return this.delegate.submit(traceRunnableWhenContextReady(task)); } @Override public Future submit(Runnable task, T result) { - return this.delegate.submit(new TraceRunnable(tracing(), spanNamer(), task, this.beanName), result); + return this.delegate.submit(traceRunnableWhenContextReady(task), result); } @Override public Future submit(Callable task) { - return this.delegate.submit(new TraceCallable<>(tracing(), spanNamer(), task, this.beanName)); + return this.delegate.submit(traceCallableWhenContextReady(task)); } @Override @@ -382,14 +398,12 @@ class LazyTraceScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor { @Override public void beforeExecute(Thread t, Runnable r) { - ReflectionUtils.invokeMethod(this.beforeExecute, this.delegate, t, - new TraceRunnable(tracing(), spanNamer(), r, this.beanName)); + ReflectionUtils.invokeMethod(this.beforeExecute, this.delegate, t, traceRunnableWhenContextReady(r)); } @Override public void afterExecute(Runnable r, Throwable t) { - ReflectionUtils.invokeMethod(this.afterExecute, this.delegate, - new TraceRunnable(tracing(), spanNamer(), r, this.beanName), t); + ReflectionUtils.invokeMethod(this.afterExecute, this.delegate, traceRunnableWhenContextReady(r), t); } @Override @@ -401,14 +415,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, this.beanName), value); + 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.beanName)); + traceCallableWhenContextReady(callable)); } @Override @@ -420,7 +434,7 @@ class LazyTraceScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor { List> ts = new ArrayList<>(); for (Callable task : tasks) { if (!(task instanceof TraceCallable)) { - ts.add(new TraceCallable<>(tracing(), spanNamer(), task, this.beanName)); + ts.add(traceCallableWhenContextReady(task)); } } return ts; diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskExecutor.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskExecutor.java index 8aed835f0..20f2ee431 100644 --- a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskExecutor.java +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskExecutor.java @@ -29,6 +29,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.internal.ContextUtil; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; import org.springframework.core.task.TaskDecorator; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskScheduler.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskScheduler.java index 883ff9b98..e9f435a54 100644 --- a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskScheduler.java +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskScheduler.java @@ -36,6 +36,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.internal.ContextUtil; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; import org.springframework.lang.Nullable; import org.springframework.scheduling.Trigger; @@ -101,6 +102,20 @@ class LazyTraceThreadPoolTaskScheduler extends ThreadPoolTaskScheduler { } } + private Runnable traceRunnableWhenContextReady(Runnable delegate) { + if (ContextUtil.isContextUnusable(this.beanFactory)) { + return delegate; + } + return new TraceRunnable(tracing(), spanNamer(), delegate, this.beanName); + } + + private Callable traceCallableWhenContextReady(Callable delegate) { + if (ContextUtil.isContextUnusable(this.beanFactory)) { + return delegate; + } + return new TraceCallable<>(tracing(), spanNamer(), delegate, this.beanName); + } + @Override public void setPoolSize(int poolSize) { this.delegate.setPoolSize(poolSize); @@ -177,38 +192,37 @@ class LazyTraceThreadPoolTaskScheduler extends ThreadPoolTaskScheduler { @Override public void execute(Runnable task) { - this.delegate.execute(new TraceRunnable(tracing(), spanNamer(), task, this.beanName)); + this.delegate.execute(traceRunnableWhenContextReady(task)); } @Override public void execute(Runnable task, long startTimeout) { - this.delegate.execute(new TraceRunnable(tracing(), spanNamer(), task, this.beanName), startTimeout); + this.delegate.execute(traceRunnableWhenContextReady(task), startTimeout); } @Override public Future submit(Runnable task) { - return this.delegate.submit(new TraceRunnable(tracing(), spanNamer(), task, this.beanName)); + return this.delegate.submit(traceRunnableWhenContextReady(task)); } @Override public Future submit(Callable task) { - return this.delegate.submit(new TraceCallable<>(tracing(), spanNamer(), task, this.beanName)); + return this.delegate.submit(traceCallableWhenContextReady(task)); } @Override public ListenableFuture submitListenable(Runnable task) { - return this.delegate.submitListenable(new TraceRunnable(tracing(), spanNamer(), task, this.beanName)); + return this.delegate.submitListenable(traceRunnableWhenContextReady(task)); } @Override public ListenableFuture submitListenable(Callable task) { - return this.delegate.submitListenable(new TraceCallable<>(tracing(), spanNamer(), task, this.beanName)); + return this.delegate.submitListenable(traceCallableWhenContextReady(task)); } @Override public void cancelRemainingTask(Runnable task) { - ReflectionUtils.invokeMethod(this.cancelRemainingTask, this.delegate, - new TraceRunnable(tracing(), spanNamer(), task, this.beanName)); + ReflectionUtils.invokeMethod(this.cancelRemainingTask, this.delegate, traceRunnableWhenContextReady(task)); } @Override @@ -219,36 +233,32 @@ class LazyTraceThreadPoolTaskScheduler extends ThreadPoolTaskScheduler { @Override @Nullable public ScheduledFuture schedule(Runnable task, Trigger trigger) { - return this.delegate.schedule(new TraceRunnable(tracing(), spanNamer(), task, this.beanName), 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, this.beanName), 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, this.beanName), - 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, this.beanName), - 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, this.beanName), - 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, this.beanName), - delay); + return this.delegate.scheduleWithFixedDelay(traceRunnableWhenContextReady(task), delay); } @Override @@ -367,31 +377,27 @@ class LazyTraceThreadPoolTaskScheduler extends ThreadPoolTaskScheduler { @Override public ScheduledFuture schedule(Runnable task, Instant startTime) { - return this.delegate.schedule(new TraceRunnable(tracing(), spanNamer(), task, this.beanName), 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, this.beanName), - 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, this.beanName), - 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, this.beanName), - 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, this.beanName), - delay); + return this.delegate.scheduleWithFixedDelay(traceRunnableWhenContextReady(task), delay); } private Tracer tracing() { diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorService.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorService.java index 97f505a74..781b00a3a 100644 --- a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorService.java +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorService.java @@ -29,6 +29,7 @@ import java.util.concurrent.TimeoutException; import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.internal.ContextUtil; /** * A decorator class for {@link ExecutorService} to support tracing in Executors. diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorService.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorService.java index 67875abd9..699f44887 100644 --- a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorService.java +++ b/spring-cloud-sleuth-instrumentation/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-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptor.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptor.java index d01ac42ef..5b879a0fd 100644 --- a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptor.java +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptor.java @@ -16,16 +16,24 @@ package org.springframework.cloud.sleuth.instrument.messaging; +import java.util.Iterator; +import java.util.Map; import java.util.concurrent.LinkedBlockingDeque; +import java.util.function.Function; 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.cloud.sleuth.Span; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.internal.SpanNameUtil; import org.springframework.cloud.sleuth.propagation.Propagator; +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; @@ -40,6 +48,7 @@ import org.springframework.messaging.support.ExecutorChannelInterceptor; import org.springframework.messaging.support.GenericMessage; import org.springframework.messaging.support.MessageHeaderAccessor; import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; /** * This starts and propagates {@link Span.Kind#PRODUCER} span for each message sent (via @@ -50,7 +59,8 @@ import org.springframework.util.ClassUtils; * @author Marcin Grzejszczak * @since 3.0.0 */ -public final class TracingChannelInterceptor extends ChannelInterceptorAdapter implements ExecutorChannelInterceptor { +public final class TracingChannelInterceptor extends ChannelInterceptorAdapter + implements ExecutorChannelInterceptor, ApplicationContextAware { /** * Name of the class in Spring Cloud Stream that is a direct channel. @@ -90,35 +100,37 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter i private final boolean hasDirectChannelClass; + private final boolean hasBinderTypeRegistry; + // special case of a Stream private final Class directWithAttributesChannelClass; + private ApplicationContext applicationContext; + private final Propagator propagator; private final ThreadLocalSpan threadLocalSpan = new ThreadLocalSpan(); - TracingChannelInterceptor(Tracer tracer, Propagator propagator) { - this(tracer, propagator, MessageHeaderPropagation.INSTANCE, MessageHeaderPropagation.INSTANCE); - } + private final Function remoteServiceNameMapper; public TracingChannelInterceptor(Tracer tracer, Propagator propagator, - Propagator.Setter setter, Propagator.Getter getter) { + Propagator.Setter setter, Propagator.Getter getter, + Function remoteServiceNameMapper) { this.tracer = tracer; this.propagator = propagator; this.injector = setter; this.extractor = getter; + this.remoteServiceNameMapper = remoteServiceNameMapper; this.integrationObjectSupportPresent = ClassUtils .isPresent("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; } - public static TracingChannelInterceptor create(Tracer tracer, Propagator propagator) { - return new TracingChannelInterceptor(tracer, propagator); - } - /** * Use this to create a span for processing the given message. Note: the result has no * name and is not started. This creates a child from identifiers extracted from the @@ -182,11 +194,20 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter i private String toRemoteServiceName(MessageHeaderAccessor headers) { for (String key : headers.getMessageHeaders().keySet()) { - if (key.startsWith("kafka_")) { - return "kafka"; + String remoteServiceName = this.remoteServiceNameMapper.apply(key); + if (StringUtils.hasText(remoteServiceName)) { + return remoteServiceName; } - else if (key.startsWith("amqp_")) { - return "rabbitmq"; + } + 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(); + String remoteServiceName = this.remoteServiceNameMapper.apply(binderName); + if (StringUtils.hasText(remoteServiceName)) { + return remoteServiceName; + } } } return REMOTE_SERVICE_NAME; @@ -456,6 +477,11 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter i return message == null; } + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + } class SpanAndScope { diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/ReactorSleuth.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/ReactorSleuth.java index 6a656adc0..05aceb2f9 100644 --- a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/ReactorSleuth.java +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/ReactorSleuth.java @@ -86,14 +86,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; } diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/LazyTraceClientHttpRequestInterceptor.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/LazyTraceClientHttpRequestInterceptor.java index e5f5e07c7..df8093b98 100644 --- a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/LazyTraceClientHttpRequestInterceptor.java +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/LazyTraceClientHttpRequestInterceptor.java @@ -20,6 +20,7 @@ import java.io.IOException; import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.sleuth.instrument.web.mvc.TracingClientHttpRequestInterceptor; +import org.springframework.cloud.sleuth.internal.ContextUtil; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; @@ -44,10 +45,17 @@ public class LazyTraceClientHttpRequestInterceptor implements ClientHttpRequestI @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-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/ContextUtil.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/internal/ContextUtil.java similarity index 71% rename from spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/ContextUtil.java rename to spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/internal/ContextUtil.java index d14c4f912..25c2f6779 100644 --- a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/ContextUtil.java +++ b/spring-cloud-sleuth-instrumentation/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,8 +35,13 @@ final class ContextUtil { private static final Log log = LogFactory.getLog(ContextUtil.class); - static boolean isContextUnusable(BeanFactory beanFactory) { - SleuthContextListener listener = SleuthContextListener.getBean(beanFactory); + /** + * @param beanFactory bean facotry + * @return {@code true} when context is not ready to be used + */ + public static boolean isContextUnusable(BeanFactory beanFactory) { + org.springframework.cloud.sleuth.internal.SleuthContextListener listener = org.springframework.cloud.sleuth.internal.SleuthContextListener + .getBean(beanFactory); boolean contextUnusable = listener.isUnusable(); if (contextUnusable && log.isDebugEnabled()) { log.debug("Context [" + Integer.toHexString(beanFactory.hashCode()) diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListener.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/internal/SleuthContextListener.java similarity index 80% rename from spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListener.java rename to spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/internal/SleuthContextListener.java index ffc119f03..a6ffeb027 100644 --- a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListener.java +++ b/spring-cloud-sleuth-instrumentation/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; @@ -33,10 +33,10 @@ import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.SmartApplicationListener; /** - * Checks whether Spring Context is up and running. + * Internal tool used by Sleuth. Do not use. * * @author Marcin Grzejszczak - * @since 3.0.0 + * @since 2.2.5 */ public class SleuthContextListener implements SmartApplicationListener { @@ -58,11 +58,18 @@ public class SleuthContextListener implements SmartApplicationListener { this.closed = closed; } - static SleuthContextListener getBean(BeanFactory beanFactory) { - if (beanFactory instanceof ConfigurableApplicationContext) { - beanFactory = ((ConfigurableApplicationContext) beanFactory).getBeanFactory(); + /** + * 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) { + BeanFactory bf = beanFactory; + if (bf instanceof ConfigurableApplicationContext) { + bf = ((ConfigurableApplicationContext) bf).getBeanFactory(); } - return CACHE.getOrDefault(beanFactory, new SleuthContextListener()); + return CACHE.getOrDefault(bf, new SleuthContextListener()); } @Override @@ -90,7 +97,11 @@ public class SleuthContextListener implements SmartApplicationListener { } } - boolean isUnusable() { + /** + * Verifies if context is unusable. + * @return 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-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutorTests.java b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutorTests.java index bf6da39c2..c5c7bd5f9 100644 --- a/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutorTests.java +++ b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutorTests.java @@ -87,7 +87,12 @@ public class LazyTraceScheduledThreadPoolExecutorTests { doReturn(tracer).when(beanFactory).getBean(Tracer.class); doReturn(parent).when(tracer).currentSpan(); doReturn(spanNamer).when(beanFactory).getBean(SpanNamer.class); - this.executor = spy(new LazyTraceScheduledThreadPoolExecutor(1, beanFactory, delegate, BEAN_NAME)); + this.executor = spy(new LazyTraceScheduledThreadPoolExecutor(1, beanFactory, delegate, BEAN_NAME) { + @Override + boolean isContextUnusable() { + return false; + } + }); } @Test diff --git a/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListenerTest.java b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/internal/SleuthContextListenerTest.java similarity index 96% rename from spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListenerTest.java rename to spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/internal/SleuthContextListenerTest.java index 92ed01d4a..3237a30d9 100644 --- a/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListenerTest.java +++ b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/internal/SleuthContextListenerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.async; +package org.springframework.cloud.sleuth.internal; import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.Test; diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java index ca5affe3b..bd4a0e8a0 100644 --- a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java @@ -35,6 +35,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; +import org.springframework.cloud.sleuth.internal.SleuthContextListenerAccessor; import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListenerAccessor.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListenerAccessor.java index d0b71a319..46fa56b3e 100644 --- a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListenerAccessor.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/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; diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java index 6be6765dc..88433a412 100644 --- a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java @@ -46,6 +46,7 @@ import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.TraceContext; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; +import org.springframework.cloud.sleuth.internal.SleuthContextListenerAccessor; import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; import static java.util.stream.Collectors.toList; diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorServiceTest.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorServiceTest.java index 8272cfff4..fc337c663 100644 --- a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorServiceTest.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorServiceTest.java @@ -36,6 +36,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; +import org.springframework.cloud.sleuth.internal.SleuthContextListenerAccessor; import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; /** diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptorTest.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptorTest.java index 4114a7c65..c74f549e4 100644 --- a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptorTest.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptorTest.java @@ -20,12 +20,15 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Function; import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.autoconfig.instrument.messaging.SleuthMessagingProperties; +import org.springframework.cloud.sleuth.autoconfig.instrument.messaging.TraceSpringIntegrationAutoConfiguration; import org.springframework.cloud.sleuth.exporter.FinishedSpan; import org.springframework.cloud.sleuth.test.TestSpanHandler; import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; @@ -42,14 +45,37 @@ import org.springframework.messaging.support.ErrorMessage; import org.springframework.messaging.support.ExecutorChannelInterceptor; import org.springframework.messaging.support.ExecutorSubscribableChannel; import org.springframework.messaging.support.MessageBuilder; +import org.springframework.util.StringUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.messaging.support.NativeMessageHeaderAccessor.NATIVE_HEADERS; public abstract class TracingChannelInterceptorTest implements TestTracingAwareSupplier { + /** + * Align with + * {@link TraceSpringIntegrationAutoConfiguration#remoteServiceNameMapper(SleuthMessagingProperties)}. + * @param properties messaging properties + * @return function mapping remote service name + */ + static Function remoteServiceNameMapper(SleuthMessagingProperties properties) { + return s -> { + if (!StringUtils.hasText(s)) { + return null; + } + if (s.startsWith("amqp") || s.startsWith("rabbit")) { + return properties.getMessaging().getRabbit().getRemoteServiceName(); + } + else if (s.startsWith("kafka")) { + return properties.getMessaging().getKafka().getRemoteServiceName(); + } + return null; + }; + } + protected ChannelInterceptor interceptor = new TracingChannelInterceptor(tracerTest().tracing().tracer(), - tracerTest().tracing().propagator()); + tracerTest().tracing().propagator(), MessageHeaderPropagation.INSTANCE, MessageHeaderPropagation.INSTANCE, + remoteServiceNameMapper(new SleuthMessagingProperties())); protected TestSpanHandler spans = tracerTest().handler();