committed by
Marcin Grzejszczak
parent
827e079f12
commit
05d5678eba
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2016-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.benchmarks.jmh;
|
||||
|
||||
public class Pair {
|
||||
final String key;
|
||||
final String value;
|
||||
|
||||
public Pair(String key, String value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String asProp() {
|
||||
return this.key + "=" + this.value;
|
||||
}
|
||||
|
||||
public static Pair of(String key, String value) {
|
||||
return new Pair(key, value);
|
||||
}
|
||||
|
||||
public static Pair noHook() {
|
||||
return new Pair("spring.sleuth.reactor.decorate-hooks", "false");
|
||||
}
|
||||
|
||||
public static Pair noSleuth() {
|
||||
return new Pair("spring.sleuth.enabled", "false");
|
||||
}
|
||||
|
||||
public static Pair onEach() {
|
||||
return new Pair("spring.sleuth.reactor.decorate-on-each", "true");
|
||||
}
|
||||
|
||||
public static Pair onLast() {
|
||||
return new Pair("spring.sleuth.reactor.decorate-on-each", "false");
|
||||
}
|
||||
}
|
||||
@@ -40,9 +40,9 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Warmup(iterations = 5, time = 1)
|
||||
@Fork(2)
|
||||
@Measurement(iterations = 10, time = 1)
|
||||
@Warmup(iterations = 10, time = 1)
|
||||
@Fork(4)
|
||||
@BenchmarkMode(Mode.SampleTime)
|
||||
@OutputTimeUnit(TimeUnit.MICROSECONDS)
|
||||
@Threads(Threads.MAX)
|
||||
|
||||
@@ -389,8 +389,9 @@ To turn off this feature, set the `spring.sleuth.quartz.enabled` property to `fa
|
||||
|
||||
This feature is available for all tracer implementations.
|
||||
|
||||
We have three modes of instrumenting reactor based applications that can be set via `spring.sleuth.reactor.instrumentation-type` property:
|
||||
We have the following modes of instrumenting reactor based applications that can be set via `spring.sleuth.reactor.instrumentation-type` property:
|
||||
|
||||
* `ON_HOOKS` - With the new Reactor https://github.com/reactor/reactor-core/pull/2566[queue wrapping mechanism] (Reactor 3.4.3) we're instrumenting the way threads are switched by Reactor. This should lead to feature parity with `ON_EACH` with low performance impact.
|
||||
* `ON_EACH` - wraps every Reactor operator in a trace representation.
|
||||
Passes the tracing context in most cases.
|
||||
This mode might lead to drastic performance degradation.
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
|
||||
|
||||
/**
|
||||
* Sleuth Reactor settings.
|
||||
@@ -58,6 +59,8 @@ public class SleuthReactorProperties {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@DeprecatedConfigurationProperty(reason = "An enum is a more clear solution",
|
||||
replacement = "spring.sleuth.reactor.instrumentation-type=DECORATE_ON_EACH")
|
||||
@Deprecated
|
||||
public boolean isDecorateOnEach() {
|
||||
warn();
|
||||
@@ -86,6 +89,13 @@ public class SleuthReactorProperties {
|
||||
|
||||
public enum InstrumentationType {
|
||||
|
||||
/**
|
||||
* Uses the new decorate queues feature from Project Reactor. Should allow the
|
||||
* feature set of {@link InstrumentationType#DECORATE_ON_EACH} with the least
|
||||
* impact on the performance.
|
||||
*/
|
||||
DECORATE_QUEUES,
|
||||
|
||||
/**
|
||||
* Decorates on each operator, will be less performing, but logging will always
|
||||
* contain the tracing entries in each operator.
|
||||
|
||||
@@ -18,8 +18,13 @@ package org.springframework.cloud.sleuth.autoconfig.instrument.reactor;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.AbstractQueue;
|
||||
import java.util.Iterator;
|
||||
import java.util.Queue;
|
||||
import java.util.function.Function;
|
||||
|
||||
import brave.propagation.CurrentTraceContext;
|
||||
import brave.propagation.TraceContext;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.reactivestreams.Publisher;
|
||||
@@ -47,6 +52,7 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.springframework.cloud.sleuth.autoconfig.instrument.reactor.TraceReactorAutoConfiguration.SLEUTH_REACTOR_EXECUTOR_SERVICE_KEY;
|
||||
import static org.springframework.cloud.sleuth.autoconfig.instrument.reactor.TraceReactorAutoConfiguration.TraceReactorConfiguration.SLEUTH_TRACE_REACTOR_KEY;
|
||||
@@ -78,6 +84,8 @@ public class TraceReactorAutoConfiguration {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TraceReactorConfiguration.class);
|
||||
|
||||
static final boolean IS_QUEUE_WRAPPER_ON_THE_CLASSPATH = isQueueWrapperOnTheClasspath();
|
||||
|
||||
@Autowired
|
||||
ConfigurableApplicationContext springContext;
|
||||
|
||||
@@ -91,6 +99,10 @@ public class TraceReactorAutoConfiguration {
|
||||
return new HookRegisteringBeanDefinitionRegistryPostProcessor(context);
|
||||
}
|
||||
|
||||
private static boolean isQueueWrapperOnTheClasspath() {
|
||||
return ReflectionUtils.findMethod(Hooks.class, "addQueueWrapper", String.class, Function.class) != null;
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(RefreshScope.class)
|
||||
static class HooksRefresherConfiguration {
|
||||
@@ -128,7 +140,15 @@ class HooksRefresher implements ApplicationListener<RefreshScopeRefreshedEvent>
|
||||
Hooks.resetOnEachOperator(SLEUTH_TRACE_REACTOR_KEY);
|
||||
Hooks.resetOnLastOperator(SLEUTH_TRACE_REACTOR_KEY);
|
||||
Hooks.resetOnLastOperator(SLEUTH_REACTOR_EXECUTOR_SERVICE_KEY);
|
||||
Hooks.removeQueueWrapper(SLEUTH_TRACE_REACTOR_KEY);
|
||||
switch (this.reactorProperties.getInstrumentationType()) {
|
||||
case DECORATE_QUEUES:
|
||||
if (TraceReactorAutoConfiguration.TraceReactorConfiguration.IS_QUEUE_WRAPPER_ON_THE_CLASSPATH) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Adding queue wrapper instrumentation");
|
||||
}
|
||||
HookRegisteringBeanDefinitionRegistryPostProcessor.addQueueWrapper(context);
|
||||
}
|
||||
case DECORATE_ON_EACH:
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Decorating onEach operator instrumentation");
|
||||
@@ -177,27 +197,45 @@ class HookRegisteringBeanDefinitionRegistryPostProcessor implements BeanDefiniti
|
||||
ConfigurableEnvironment environment = springContext.getEnvironment();
|
||||
SleuthReactorProperties.InstrumentationType property = environment.getProperty(
|
||||
"spring.sleuth.reactor.instrumentation-type", SleuthReactorProperties.InstrumentationType.class,
|
||||
SleuthReactorProperties.InstrumentationType.DECORATE_ON_EACH);
|
||||
Boolean decorateOnEach = environment.getProperty("spring.sleuth.reactor.decorate-on-each", Boolean.class, true);
|
||||
if (!decorateOnEach) {
|
||||
SleuthReactorProperties.InstrumentationType.DECORATE_QUEUES);
|
||||
if (wrapperNotOnClasspathHooksPropertyTurnedOn(property)) {
|
||||
log.warn(
|
||||
"You're using the deprecated [spring.sleuth.reactor.decorate-on-each] property. Please use the [spring.sleuth.reactor.instrumentation-type] one instead.");
|
||||
decorateOnLast(ReactorSleuth.scopePassingSpanOperator(springContext));
|
||||
"You have explicitly set the decorate hooks option but you're using an old version of Reactor. Please upgrade to the latest Boot version (at least 2.4.3). Will fall back to the previous reactor instrumentation mode");
|
||||
property = SleuthReactorProperties.InstrumentationType.DECORATE_ON_EACH;
|
||||
}
|
||||
else if (property == SleuthReactorProperties.InstrumentationType.DECORATE_ON_EACH) {
|
||||
decorateOnEach(springContext);
|
||||
decorateOnLast(onLastOperatorForOnEachInstrumentation(springContext));
|
||||
if (property == SleuthReactorProperties.InstrumentationType.DECORATE_QUEUES) {
|
||||
addQueueWrapper(springContext);
|
||||
decorateScheduler(springContext);
|
||||
}
|
||||
else if (property == SleuthReactorProperties.InstrumentationType.DECORATE_ON_LAST) {
|
||||
decorateOnLast(ReactorSleuth.scopePassingSpanOperator(springContext));
|
||||
decorateScheduler(springContext);
|
||||
}
|
||||
else if (property == SleuthReactorProperties.InstrumentationType.MANUAL) {
|
||||
decorateOnLast(ReactorSleuth.springContextSpanOperator(springContext));
|
||||
else {
|
||||
Boolean decorateOnEach = environment.getProperty("spring.sleuth.reactor.decorate-on-each", Boolean.class,
|
||||
true);
|
||||
if (!decorateOnEach) {
|
||||
log.warn(
|
||||
"You're using the deprecated [spring.sleuth.reactor.decorate-on-each] property. Please use the [spring.sleuth.reactor.instrumentation-type] one instead.");
|
||||
decorateOnLast(ReactorSleuth.scopePassingSpanOperator(springContext));
|
||||
}
|
||||
else if (property == SleuthReactorProperties.InstrumentationType.DECORATE_ON_EACH) {
|
||||
decorateOnEach(springContext);
|
||||
decorateOnLast(onLastOperatorForOnEachInstrumentation(springContext));
|
||||
decorateScheduler(springContext);
|
||||
}
|
||||
else if (property == SleuthReactorProperties.InstrumentationType.DECORATE_ON_LAST) {
|
||||
decorateOnLast(ReactorSleuth.scopePassingSpanOperator(springContext));
|
||||
decorateScheduler(springContext);
|
||||
}
|
||||
else if (property == SleuthReactorProperties.InstrumentationType.MANUAL) {
|
||||
decorateOnLast(ReactorSleuth.springContextSpanOperator(springContext));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean wrapperNotOnClasspathHooksPropertyTurnedOn(
|
||||
SleuthReactorProperties.InstrumentationType property) {
|
||||
return property == SleuthReactorProperties.InstrumentationType.DECORATE_QUEUES
|
||||
&& !TraceReactorAutoConfiguration.TraceReactorConfiguration.IS_QUEUE_WRAPPER_ON_THE_CLASSPATH;
|
||||
}
|
||||
|
||||
private static void decorateScheduler(ConfigurableApplicationContext springContext) {
|
||||
Schedulers.onScheduleHook(TraceReactorAutoConfiguration.SLEUTH_REACTOR_EXECUTOR_SERVICE_KEY,
|
||||
ReactorSleuth.scopePassingOnScheduleHook(springContext));
|
||||
@@ -218,6 +256,13 @@ class HookRegisteringBeanDefinitionRegistryPostProcessor implements BeanDefiniti
|
||||
ReactorSleuth.onEachOperatorForOnEachInstrumentation(springContext));
|
||||
}
|
||||
|
||||
static void addQueueWrapper(ConfigurableApplicationContext springContext) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Decorating queues");
|
||||
}
|
||||
Hooks.addQueueWrapper(SLEUTH_TRACE_REACTOR_KEY, queue -> traceQueue(springContext, queue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (log.isTraceEnabled()) {
|
||||
@@ -225,7 +270,97 @@ class HookRegisteringBeanDefinitionRegistryPostProcessor implements BeanDefiniti
|
||||
}
|
||||
Hooks.resetOnEachOperator(SLEUTH_TRACE_REACTOR_KEY);
|
||||
Hooks.resetOnLastOperator(SLEUTH_TRACE_REACTOR_KEY);
|
||||
Hooks.removeQueueWrapper(SLEUTH_REACTOR_EXECUTOR_SERVICE_KEY);
|
||||
Schedulers.resetOnScheduleHook(TraceReactorAutoConfiguration.SLEUTH_REACTOR_EXECUTOR_SERVICE_KEY);
|
||||
}
|
||||
|
||||
private static Queue<?> traceQueue(ConfigurableApplicationContext springContext, Queue<?> queue) {
|
||||
if (!springContext.isActive()) {
|
||||
return queue;
|
||||
}
|
||||
CurrentTraceContext currentTraceContext = springContext.getBean(CurrentTraceContext.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
Queue envelopeQueue = queue;
|
||||
return new AbstractQueue<Object>() {
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return envelopeQueue.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(Object o) {
|
||||
TraceContext traceContext = currentTraceContext.get();
|
||||
return envelopeQueue.offer(new Envelope(o, traceContext));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object poll() {
|
||||
Object object = envelopeQueue.poll();
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
else if (object instanceof Envelope) {
|
||||
Envelope envelope = (Envelope) object;
|
||||
restoreTheContext(envelope);
|
||||
return envelope.body;
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
private void restoreTheContext(Envelope envelope) {
|
||||
if (envelope.traceContext != null) {
|
||||
currentTraceContext.maybeScope(envelope.traceContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object peek() {
|
||||
Object peek = queue.peek();
|
||||
if (peek instanceof Envelope) {
|
||||
Envelope envelope = (Envelope) peek;
|
||||
restoreTheContext(envelope);
|
||||
return (envelope).body;
|
||||
}
|
||||
return peek;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Iterator<Object> iterator() {
|
||||
Iterator<?> iterator = queue.iterator();
|
||||
return new Iterator<Object>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object next() {
|
||||
Object next = iterator.next();
|
||||
if (next instanceof Envelope) {
|
||||
Envelope envelope = (Envelope) next;
|
||||
restoreTheContext(envelope);
|
||||
return (envelope).body;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static class Envelope {
|
||||
|
||||
final Object body;
|
||||
|
||||
final TraceContext traceContext;
|
||||
|
||||
Envelope(Object body, TraceContext traceContext) {
|
||||
this.body = body;
|
||||
this.traceContext = traceContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,16 +16,13 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.autoconfig.instrument.reactor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Hooks;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.springframework.cloud.sleuth.autoconfig.instrument.reactor.TraceReactorAutoConfiguration.SLEUTH_REACTOR_EXECUTOR_SERVICE_KEY;
|
||||
import static org.springframework.cloud.sleuth.autoconfig.instrument.reactor.TraceReactorAutoConfiguration.TraceReactorConfiguration.SLEUTH_TRACE_REACTOR_KEY;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@@ -41,9 +38,12 @@ public final class TraceReactorAutoConfigurationAccessorConfiguration {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Cleaning up hooks");
|
||||
}
|
||||
Hooks.resetOnEachOperator(SLEUTH_TRACE_REACTOR_KEY);
|
||||
Hooks.resetOnLastOperator(SLEUTH_TRACE_REACTOR_KEY);
|
||||
Schedulers.resetOnScheduleHook(SLEUTH_REACTOR_EXECUTOR_SERVICE_KEY);
|
||||
try {
|
||||
new HookRegisteringBeanDefinitionRegistryPostProcessor(null).close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setup(ConfigurableApplicationContext context) {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2013-2021 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.autoconfig.instrument.web;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceHandlerAdapter;
|
||||
import org.springframework.web.reactive.HandlerAdapter;
|
||||
import org.springframework.web.reactive.function.server.support.HandlerFunctionAdapter;
|
||||
|
||||
class TraceHandlerFunctionAdapterBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
TraceHandlerFunctionAdapterBeanPostProcessor(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof HandlerFunctionAdapter) {
|
||||
return new TraceHandlerAdapter((HandlerAdapter) bean, this.beanFactory);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.autoconfig.instrument.web;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.cloud.sleuth.CurrentTraceContext;
|
||||
@@ -45,4 +46,9 @@ class TraceWebFluxConfiguration {
|
||||
return traceWebFilter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
TraceHandlerFunctionAdapterBeanPostProcessor traceHandlerFunctionAdapterBeanPostProcessor(BeanFactory beanFactory) {
|
||||
return new TraceHandlerFunctionAdapterBeanPostProcessor(beanFactory);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.web.reactive.HandlerAdapter;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
import org.springframework.web.reactive.function.server.HandlerFunction;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* Tracing representation of a {@link HandlerAdapter}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.2
|
||||
*/
|
||||
public class TraceHandlerAdapter implements HandlerAdapter {
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
private final HandlerAdapter delegate;
|
||||
|
||||
public TraceHandlerAdapter(HandlerAdapter delegate, BeanFactory beanFactory) {
|
||||
this.delegate = delegate;
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Object handler) {
|
||||
return this.delegate.supports(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
|
||||
HandlerFunction<?> handlerFunction = (HandlerFunction<?>) handler;
|
||||
TraceHandlerFunction traceHandlerFunction = new TraceHandlerFunction(handlerFunction, this.beanFactory);
|
||||
return this.delegate.handle(exchange, traceHandlerFunction);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.CurrentTraceContext;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.web.reactive.function.server.HandlerFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
|
||||
/**
|
||||
* Tracing representation of a {@link HandlerFunction}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.2
|
||||
*/
|
||||
public class TraceHandlerFunction implements HandlerFunction {
|
||||
|
||||
private final HandlerFunction<?> delegate;
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
private CurrentTraceContext currentTraceContext;
|
||||
|
||||
public TraceHandlerFunction(HandlerFunction<?> delegate, BeanFactory beanFactory) {
|
||||
this.delegate = delegate;
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<?> handle(ServerRequest serverRequest) {
|
||||
AtomicReference<CurrentTraceContext.Scope> scope = new AtomicReference<>();
|
||||
return Mono.just(scope)
|
||||
.doFirst(() -> serverRequest.attribute(TraceWebFilter.TRACE_REQUEST_ATTR)
|
||||
.ifPresent(span -> scope.set(currentTraceContext().maybeScope(((Span) span).context()))))
|
||||
.flatMap(r -> this.delegate.handle(serverRequest)).doFinally(signalType -> {
|
||||
CurrentTraceContext.Scope spanInScope = scope.get();
|
||||
if (spanInScope != null) {
|
||||
spanInScope.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private CurrentTraceContext currentTraceContext() {
|
||||
if (this.currentTraceContext == null) {
|
||||
this.currentTraceContext = this.beanFactory.getBean(CurrentTraceContext.class);
|
||||
}
|
||||
return this.currentTraceContext;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -97,11 +97,11 @@ public class TraceWebFilter implements WebFilter, Ordered, ApplicationContextAwa
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
String uri = exchange.getRequest().getPath().pathWithinApplication().value();
|
||||
Mono<Void> source = chain.filter(exchange);
|
||||
boolean tracePresent = isTracePresent();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Received a request to uri [" + uri + "]");
|
||||
}
|
||||
Mono<Void> source = chain.filter(exchange);
|
||||
boolean tracePresent = isTracePresent();
|
||||
return new MonoWebFilterTrace(source, exchange, tracePresent, this);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
logging.level.org.springframework.cloud: DEBUG
|
||||
logging.level.org.springframework.cloud.sleuth.autoconfig.instrument.reactor: TRACE
|
||||
logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR
|
||||
logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.autoconfig.instrument.reactor;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class Issue866Configuration {
|
||||
|
||||
private static final Log log = LogFactory.getLog(Issue866Configuration.class);
|
||||
|
||||
/**
|
||||
* We don't want to force direct dependencies between components because Spring might
|
||||
* just properly setup the context we want to ensure that the HRBDRPP is always
|
||||
* executed before any other object is started.
|
||||
*/
|
||||
public static TestHook hook;
|
||||
|
||||
@Bean
|
||||
HookRegisteringBeanDefinitionRegistryPostProcessor overridingProcessorForTests(
|
||||
ConfigurableApplicationContext context) {
|
||||
log.info("Registering a HookRegisteringBeanDefinitionRegistryPostProcessor for context [" + context + "]");
|
||||
TestHook hook = new TestHook(context);
|
||||
Issue866Configuration.hook = hook;
|
||||
return hook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Hook.
|
||||
*/
|
||||
public static class TestHook extends HookRegisteringBeanDefinitionRegistryPostProcessor {
|
||||
|
||||
/**
|
||||
* Whether the hook was called.
|
||||
*/
|
||||
public boolean executed = false;
|
||||
|
||||
public TestHook(ConfigurableApplicationContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
super.postProcessBeanFactory(beanFactory);
|
||||
this.executed = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -87,6 +87,7 @@ public abstract class SleuthSpanCreatorAspectFluxTests {
|
||||
public void setup() {
|
||||
this.spans.clear();
|
||||
this.testBean.reset();
|
||||
this.tracer.withSpan(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -69,6 +69,7 @@ public abstract class SleuthSpanCreatorAspectMonoTests {
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.spans.clear();
|
||||
this.tracer.withSpan(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -165,6 +166,7 @@ public abstract class ScopePassingSpanSubscriberSpringBootTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("Will work only for on each - by accident")
|
||||
public void should_pass_tracing_info_when_using_reactor_async_processor() {
|
||||
final AtomicReference<TraceContext> spanInOperation = new AtomicReference<>();
|
||||
|
||||
|
||||
@@ -77,13 +77,13 @@ public abstract class FlatMapTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_work_with_flat_maps(CapturedOutput capture) {
|
||||
public void should_work_with_flat_maps_with_on_queues_instrumentation(CapturedOutput capture) {
|
||||
// given
|
||||
ConfigurableApplicationContext context = new SpringApplicationBuilder(FlatMapTests.TestConfiguration.class,
|
||||
testConfiguration(), Issue866Configuration.class)
|
||||
.web(WebApplicationType.REACTIVE)
|
||||
.properties("server.port=0", "spring.jmx.enabled=false",
|
||||
"spring.application.name=TraceWebFluxTests", "security.basic.enabled=false",
|
||||
"spring.application.name=TraceWebFluxOnQueuesTests", "security.basic.enabled=false",
|
||||
"management.security.enabled=false")
|
||||
.run();
|
||||
assertReactorTracing(context, capture, () -> context.getBean(TestConfiguration.class).spanInFoo);
|
||||
@@ -98,8 +98,22 @@ public abstract class FlatMapTests {
|
||||
testConfiguration(), Issue866Configuration.class)
|
||||
.web(WebApplicationType.REACTIVE)
|
||||
.properties("server.port=0", "spring.jmx.enabled=false",
|
||||
"spring.sleuth.reactor.decorate-on-each=false",
|
||||
"spring.application.name=TraceWebFlux2Tests", "security.basic.enabled=false",
|
||||
"spring.sleuth.reactor.instrumentation-type=DECORATE_ON_LAST",
|
||||
"spring.application.name=TraceWebFluxOnLastTests", "security.basic.enabled=false",
|
||||
"management.security.enabled=false")
|
||||
.run();
|
||||
assertReactorTracing(context, capture, () -> context.getBean(TestConfiguration.class).spanInFoo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_work_with_flat_maps_with_on_each_operator_instrumentation(CapturedOutput capture) {
|
||||
// given
|
||||
ConfigurableApplicationContext context = new SpringApplicationBuilder(FlatMapTests.TestConfiguration.class,
|
||||
testConfiguration(), Issue866Configuration.class)
|
||||
.web(WebApplicationType.REACTIVE)
|
||||
.properties("server.port=0", "spring.jmx.enabled=false",
|
||||
"spring.sleuth.reactor.instrumentation-type=DECORATE_ON_EACH",
|
||||
"spring.application.name=TraceWebFluxOnEachTests", "security.basic.enabled=false",
|
||||
"management.security.enabled=false")
|
||||
.run();
|
||||
assertReactorTracing(context, capture, () -> context.getBean(TestConfiguration.class).spanInFoo);
|
||||
@@ -113,7 +127,7 @@ public abstract class FlatMapTests {
|
||||
.web(WebApplicationType.REACTIVE)
|
||||
.properties("server.port=0", "spring.jmx.enabled=false",
|
||||
"spring.sleuth.reactor.instrumentation-type=MANUAL",
|
||||
"spring.application.name=TraceWebFlux3Tests", "security.basic.enabled=false",
|
||||
"spring.application.name=TraceWebFluxOnManualTests", "security.basic.enabled=false",
|
||||
"management.security.enabled=false")
|
||||
.run();
|
||||
assertReactorTracing(context, capture, () -> context.getBean(TestManualConfiguration.class).spanInFoo);
|
||||
|
||||
@@ -53,6 +53,7 @@ public abstract class IgnoreAutoConfiguredSkipPatternsIntegrationTests {
|
||||
@AfterEach
|
||||
public void clearSpans() {
|
||||
this.spans.clear();
|
||||
this.tracer.withSpan(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user