Using lazy span subscriber only when context wasn't refreshed

fixes gh-949 gh-813
This commit is contained in:
Marcin Grzejszczak
2018-04-19 15:58:23 +02:00
parent a9a61cb89e
commit 51abe7bba7
4 changed files with 95 additions and 23 deletions

View File

@@ -20,13 +20,17 @@ import java.util.function.Function;
import java.util.function.Predicate;
import brave.Tracing;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.BeanFactory;
import reactor.core.CoreSubscriber;
import reactor.core.Fuseable;
import reactor.core.Scannable;
import reactor.core.publisher.ConnectableFlux;
import reactor.core.publisher.Operators;
import reactor.util.context.Context;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
/**
* Reactive Span pointcuts factories
@@ -36,6 +40,8 @@ import reactor.util.context.Context;
*/
public abstract class ReactorSleuth {
private static final Log log = LogFactory.getLog(ReactorSleuth.class);
/**
* Return a span operator pointcut given a {@link BeanFactory}. This can be used in reactor
* via {@link reactor.core.publisher.Flux#transform(Function)}, {@link
@@ -55,6 +61,10 @@ public abstract class ReactorSleuth {
if(scannable instanceof Fuseable && sub instanceof Fuseable.QueueSubscription){
return sub;
}
if (log.isTraceEnabled()) {
log.trace("Creating a lazy span subscriber with context "
+ "[" + sub.currentContext() + "] and name [" + scannable.name() + "]");
}
return new LazySpanSubscriber<T>(
new SpanSubscriptionProvider(
beanFactory,
@@ -78,6 +88,7 @@ public abstract class ReactorSleuth {
*
* @return a new lazy span operator pointcut
*/
@SuppressWarnings("unchecked")
public static <T> Function<? super Publisher<T>, ? extends Publisher<T>> scopePassingSpanOperator(
BeanFactory beanFactory) {
return Operators.lift(POINTCUT_FILTER, ((scannable, sub) -> {
@@ -85,26 +96,51 @@ public abstract class ReactorSleuth {
if(scannable instanceof Fuseable && sub instanceof Fuseable.QueueSubscription){
return sub;
}
if (contextRefreshed(beanFactory)) {
if (log.isTraceEnabled()) {
log.trace("Spring Context already refreshed. Creating a scope "
+ "passing span subscriber with Reactor Context "
+ "[" + sub.currentContext() + "] and name [" + scannable.name() + "]");
}
return scopePassingSpanSubscription(beanFactory, scannable, sub).get();
}
if (log.isTraceEnabled()) {
log.trace("Spring Context is not yet refreshed, falling back to lazy span subscriber. "
+ "Reactor Context is [" + sub.currentContext() + "] and name is [" + scannable.name() + "]");
}
return new LazySpanSubscriber<T>(
new SpanSubscriptionProvider(
beanFactory,
sub,
sub.currentContext(),
scannable.name()) {
@Override SpanSubscription newCoreSubscriber(Tracing tracing) {
return new ScopePassingSpanSubscriber<T>(
sub,
sub != null ? sub.currentContext() : Context.empty(),
tracing);
}
}
scopePassingSpanSubscription(beanFactory, scannable, sub)
);
}));
}
private static boolean contextRefreshed(BeanFactory beanFactory) {
try {
return beanFactory.getBean(ApplicationContextRefreshedListener.class).isRefreshed();
} catch (NoSuchBeanDefinitionException e) {
return false;
}
}
private static <T> SpanSubscriptionProvider scopePassingSpanSubscription(
BeanFactory beanFactory, Scannable scannable, CoreSubscriber<? super T> sub) {
return new SpanSubscriptionProvider(
beanFactory,
sub,
sub.currentContext(),
scannable.name()) {
@Override SpanSubscription newCoreSubscriber(Tracing tracing) {
return new ScopePassingSpanSubscriber<T>(
sub,
sub != null ? sub.currentContext() : Context.empty(),
tracing);
}
};
}
private static final Predicate<Scannable> POINTCUT_FILTER =
s -> !(s instanceof ConnectableFlux) && !(s instanceof Fuseable.ScalarCallable) && s.isScanAvailable();
private ReactorSleuth() {
}
}
}

View File

@@ -21,9 +21,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
import brave.Span;
import brave.Tracer;
import brave.Tracing;
import reactor.util.context.Context;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.util.context.Context;
/**
* A trace representation of the {@link Subscriber} that always
@@ -34,6 +36,8 @@ import reactor.util.context.Context;
*/
final class ScopePassingSpanSubscriber<T> extends AtomicBoolean implements SpanSubscription<T> {
private static final Log log = LogFactory.getLog(ScopePassingSpanSubscriber.class);
private final Span span;
private final Subscriber<? super T> subscriber;
private final Context context;
@@ -48,6 +52,9 @@ final class ScopePassingSpanSubscriber<T> extends AtomicBoolean implements SpanS
this.span = root;
this.context = ctx != null && root != null ? ctx.put(Span.class, root) :
ctx != null ? ctx : Context.empty();
if (log.isTraceEnabled()) {
log.trace("Root span [" + root + "], context [" + this.context + "]");
}
}
@Override public void onSubscribe(Subscription subscription) {

View File

@@ -19,9 +19,11 @@ package org.springframework.cloud.sleuth.instrument.reactor;
import java.util.function.Supplier;
import brave.Tracing;
import reactor.util.context.Context;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.reactivestreams.Subscriber;
import org.springframework.beans.factory.BeanFactory;
import reactor.util.context.Context;
/**
* Supplier to lazily start a {@link SpanSubscription}
@@ -30,6 +32,8 @@ import reactor.util.context.Context;
*/
class SpanSubscriptionProvider<T> implements Supplier<SpanSubscription<T>> {
private static final Log log = LogFactory.getLog(SpanSubscriptionProvider.class);
final BeanFactory beanFactory;
final Subscriber<? super T> subscriber;
final Context context;
@@ -43,6 +47,9 @@ class SpanSubscriptionProvider<T> implements Supplier<SpanSubscription<T>> {
this.subscriber = subscriber;
this.context = context;
this.name = name;
if (log.isTraceEnabled()) {
log.trace("Context [" + context + "], name [" + name + "]");
}
}
@Override public SpanSubscription<T> get() {

View File

@@ -16,11 +16,15 @@
package org.springframework.cloud.sleuth.instrument.reactor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Supplier;
import javax.annotation.PreDestroy;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import brave.Tracing;
import reactor.core.publisher.Hooks;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -35,11 +39,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.cloud.sleuth.instrument.async.TraceableScheduledExecutorService;
import org.springframework.cloud.sleuth.instrument.web.TraceWebFluxAutoConfiguration;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Hooks;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import org.springframework.context.event.ContextRefreshedEvent;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
@@ -84,6 +87,10 @@ public class TraceReactorAutoConfiguration {
static HookRegisteringBeanDefinitionRegistryPostProcessor traceHookRegisteringBeanDefinitionRegistryPostProcessor() {
return new HookRegisteringBeanDefinitionRegistryPostProcessor();
}
@Bean ApplicationContextRefreshedListener traceApplicationContextRefreshedListener() {
return new ApplicationContextRefreshedListener();
}
}
}
@@ -121,4 +128,19 @@ class HookRegisteringBeanDefinitionRegistryPostProcessor implements
}
};
}
}
}
class ApplicationContextRefreshedListener implements
ApplicationListener<ContextRefreshedEvent> {
AtomicBoolean refreshed = new AtomicBoolean();
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
this.refreshed.set(true);
}
boolean isRefreshed() {
return this.refreshed.get();
}
}