Merge branch '2.2.x'

This commit is contained in:
Marcin Grzejszczak
2020-12-01 22:05:23 +01:00
29 changed files with 307 additions and 100 deletions

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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<MessageHeaderAccessor> traceMessagePropagationSetter,
Propagator.Getter<MessageHeaderAccessor> traceMessagePropagationGetter) {
Propagator.Getter<MessageHeaderAccessor> traceMessagePropagationGetter,
SleuthMessagingProperties properties) {
return new TracingChannelInterceptor(tracer, propagator, traceMessagePropagationSetter,
traceMessagePropagationGetter);
traceMessagePropagationGetter, remoteServiceNameMapper(properties));
}
static Function<String, String> 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;
};
}
}

View File

@@ -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

View File

@@ -61,6 +61,9 @@ class TraceWebSocketAutoConfiguration extends AbstractWebSocketMessageBrokerConf
@Autowired
Propagator.Getter<MessageHeaderAccessor> 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)));
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}
}

View File

@@ -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));
}
}

View File

@@ -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;

View File

@@ -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;
/**

View File

@@ -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 <V> Callable<V> traceCallableWhenContextReady(Callable<V> delegate) {
if (isContextUnusable()) {
return delegate;
}
return new TraceCallable<>(tracing(), spanNamer(), delegate, this.beanName);
}
@Override
@SuppressWarnings("unchecked")
public <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) {
return (RunnableScheduledFuture<V>) ReflectionUtils.invokeMethod(this.decorateTaskRunnable, this.delegate,
new TraceRunnable(tracing(), spanNamer(), runnable, this.beanName), task);
traceRunnableWhenContextReady(runnable), task);
}
@Override
@SuppressWarnings("unchecked")
public <V> RunnableScheduledFuture<V> decorateTask(Callable<V> callable, RunnableScheduledFuture<V> task) {
return (RunnableScheduledFuture<V>) 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 <V> ScheduledFuture<V> schedule(Callable<V> 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 <T> Future<T> 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 <T> Future<T> submit(Callable<T> 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 <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return (RunnableFuture<T>) ReflectionUtils.invokeMethod(this.newTaskForRunnable, this.delegate,
new TraceRunnable(tracing(), spanNamer(), runnable, this.beanName), value);
traceRunnableWhenContextReady(runnable), value);
}
@Override
@SuppressWarnings("unchecked")
public <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return (RunnableFuture<T>) 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<Callable<T>> ts = new ArrayList<>();
for (Callable<T> task : tasks) {
if (!(task instanceof TraceCallable)) {
ts.add(new TraceCallable<>(tracing(), spanNamer(), task, this.beanName));
ts.add(traceCallableWhenContextReady(task));
}
}
return ts;

View File

@@ -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;

View File

@@ -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 <V> Callable<V> traceCallableWhenContextReady(Callable<V> 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 <T> Future<T> submit(Callable<T> 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 <T> ListenableFuture<T> submitListenable(Callable<T> 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() {

View File

@@ -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.

View File

@@ -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.

View File

@@ -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<String, String> remoteServiceNameMapper;
public TracingChannelInterceptor(Tracer tracer, Propagator propagator,
Propagator.Setter<MessageHeaderAccessor> setter, Propagator.Getter<MessageHeaderAccessor> getter) {
Propagator.Setter<MessageHeaderAccessor> setter, Propagator.Getter<MessageHeaderAccessor> getter,
Function<String, String> 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<Map.Entry<String, BinderType>> 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 {

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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())

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
/**

View File

@@ -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<String, String> 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();