Uses ConfigurableApplicationContext consistently in Reactor (#1545)
Before, we used ConfigurableApplicationContext or BeanFactory eventhough we already had a reference to ConfigurableApplicationContext. This uses the latter consistently, avoiding a state condition that caused more code. This also corrects some misnamed tests and adjusts them to verify only what they are responsible for.
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* 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.reactor;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import brave.propagation.CurrentTraceContext;
|
||||
import brave.propagation.CurrentTraceContext.Scope;
|
||||
import brave.propagation.TraceContext;
|
||||
import brave.sampler.Sampler;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
/**
|
||||
* Like {@link ScopePassingSpanSubscriberTests}, except this tests wiring with spring boot
|
||||
* config.
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = ScopePassingSpanSubscriberSpringBootTests.Config.class,
|
||||
webEnvironment = SpringBootTest.WebEnvironment.NONE)
|
||||
public class ScopePassingSpanSubscriberSpringBootTests {
|
||||
|
||||
@Autowired
|
||||
CurrentTraceContext currentTraceContext;
|
||||
|
||||
TraceContext context = TraceContext.newBuilder().traceId(1).spanId(1).sampled(true)
|
||||
.build();
|
||||
|
||||
TraceContext context2 = TraceContext.newBuilder().traceId(1).spanId(2).sampled(true)
|
||||
.build();
|
||||
|
||||
@Test
|
||||
public void should_pass_tracing_info_when_using_reactor() {
|
||||
final AtomicReference<TraceContext> spanInOperation = new AtomicReference<>();
|
||||
Publisher<Integer> traced = Flux.just(1, 2, 3);
|
||||
|
||||
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||
Flux.from(traced).map(d -> d + 1).map(d -> d + 1).map((d) -> {
|
||||
spanInOperation.set(this.currentTraceContext.get());
|
||||
return d + 1;
|
||||
}).map(d -> d + 1).subscribe(d -> {
|
||||
});
|
||||
}
|
||||
|
||||
then(this.currentTraceContext.get()).isNull();
|
||||
then(spanInOperation.get()).isEqualTo(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_support_reactor_fusion_optimization() {
|
||||
final AtomicReference<TraceContext> spanInOperation = new AtomicReference<>();
|
||||
|
||||
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||
Mono.just(1).flatMap(d -> Flux.just(d + 1).collectList().map(p -> p.get(0)))
|
||||
.map(d -> d + 1).map((d) -> {
|
||||
spanInOperation.set(this.currentTraceContext.get());
|
||||
return d + 1;
|
||||
}).map(d -> d + 1).subscribe(d -> {
|
||||
});
|
||||
}
|
||||
|
||||
then(this.currentTraceContext.get()).isNull();
|
||||
then(spanInOperation.get()).isEqualTo(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_pass_tracing_info_when_using_reactor_async() {
|
||||
final AtomicReference<TraceContext> spanInOperation = new AtomicReference<>();
|
||||
|
||||
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||
Flux.just(1, 2, 3).publishOn(Schedulers.single()).log("reactor.1")
|
||||
.map(d -> d + 1).map(d -> d + 1)
|
||||
.publishOn(Schedulers.newSingle("secondThread")).log("reactor.2")
|
||||
.map((d) -> {
|
||||
spanInOperation.set(this.currentTraceContext.get());
|
||||
return d + 1;
|
||||
}).map(d -> d + 1).blockLast();
|
||||
|
||||
Awaitility.await()
|
||||
.untilAsserted(() -> then(spanInOperation.get()).isEqualTo(context));
|
||||
then(this.currentTraceContext.get()).isEqualTo(context);
|
||||
}
|
||||
|
||||
then(this.currentTraceContext.get()).isNull();
|
||||
|
||||
try (Scope ws = this.currentTraceContext.newScope(context2)) {
|
||||
Flux.just(1, 2, 3).publishOn(Schedulers.single()).log("reactor.")
|
||||
.map(d -> d + 1).map(d -> d + 1).map((d) -> {
|
||||
spanInOperation.set(this.currentTraceContext.get());
|
||||
return d + 1;
|
||||
}).map(d -> d + 1).blockLast();
|
||||
|
||||
then(this.currentTraceContext.get()).isEqualTo(context2);
|
||||
then(spanInOperation.get()).isEqualTo(context2);
|
||||
}
|
||||
|
||||
then(this.currentTraceContext.get()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onlyConsidersContextDuringSubscribe() {
|
||||
Mono<TraceContext> fromMono = Mono.fromCallable(this.currentTraceContext::get);
|
||||
|
||||
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||
then(fromMono.map(context -> context).block()).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkTraceIdDuringZipOperation() {
|
||||
final AtomicReference<TraceContext> spanInOperation = new AtomicReference<>();
|
||||
final AtomicReference<TraceContext> spanInZipOperation = new AtomicReference<>();
|
||||
|
||||
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||
Mono.fromCallable(this.currentTraceContext::get).map(span -> span)
|
||||
.doOnNext(spanInOperation::set)
|
||||
.zipWith(Mono.fromCallable(this.currentTraceContext::get)
|
||||
.map(span -> span).doOnNext(spanInZipOperation::set))
|
||||
.block();
|
||||
}
|
||||
|
||||
then(spanInZipOperation).hasValue(context);
|
||||
then(spanInOperation).hasValue(context);
|
||||
}
|
||||
|
||||
// #646
|
||||
@Test
|
||||
public void should_work_for_mono_just_with_flat_map() {
|
||||
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||
Mono.just("value1")
|
||||
.flatMap(request -> Mono.just("value2").then(Mono.just("foo")))
|
||||
.map(a -> "qwe").block();
|
||||
}
|
||||
}
|
||||
|
||||
// #1030
|
||||
@Test
|
||||
public void checkTraceIdFromSubscriberContext() {
|
||||
final AtomicReference<TraceContext> spanInSubscriberContext = new AtomicReference<>();
|
||||
|
||||
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||
Mono.subscriberContext().map(context -> this.currentTraceContext.get())
|
||||
.doOnNext(spanInSubscriberContext::set).block();
|
||||
}
|
||||
|
||||
then(spanInSubscriberContext).hasValue(context); // ok here
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_pass_tracing_info_into_inner_publishers() {
|
||||
final AtomicReference<TraceContext> spanInOperation = new AtomicReference<>();
|
||||
|
||||
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||
Flux.range(0, 5)
|
||||
.flatMap(it -> Mono.delay(Duration.ofMillis(1))
|
||||
.map(context -> this.currentTraceContext.get())
|
||||
.doOnNext(spanInOperation::set))
|
||||
.blockFirst();
|
||||
}
|
||||
|
||||
then(spanInOperation.get()).isEqualTo(context);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
Sampler sampler() {
|
||||
return Sampler.ALWAYS_SAMPLE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,32 +16,59 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.reactor;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
import brave.propagation.CurrentTraceContext;
|
||||
import brave.propagation.CurrentTraceContext.Scope;
|
||||
import brave.propagation.TraceContext;
|
||||
import org.assertj.core.presentation.StandardRepresentation;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.BDDMockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
import reactor.core.CoreSubscriber;
|
||||
import reactor.core.publisher.BaseSubscriber;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.context.Context;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.cloud.sleuth.instrument.reactor.ReactorSleuth.scopePassingSpanOperator;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ScopePassingSpanSubscriberTests {
|
||||
|
||||
CurrentTraceContext currentTraceContext = CurrentTraceContext.Default.create();
|
||||
static {
|
||||
// AssertJ will recognise QueueSubscription implements queue and try to invoke
|
||||
// iterator. That's not allowed, and will cause an exception
|
||||
// Fuseable$QueueSubscription.NOT_SUPPORTED_MESSAGE.
|
||||
// This ensures AssertJ uses normal toString.
|
||||
StandardRepresentation.registerFormatterForType(ScopePassingSpanSubscriber.class,
|
||||
Objects::toString);
|
||||
}
|
||||
|
||||
final CurrentTraceContext currentTraceContext = CurrentTraceContext.Default.create();
|
||||
|
||||
TraceContext context = TraceContext.newBuilder().traceId(1).spanId(1).sampled(true)
|
||||
.build();
|
||||
|
||||
TraceContext context2 = TraceContext.newBuilder().traceId(1).spanId(2).sampled(true)
|
||||
.build();
|
||||
|
||||
AnnotationConfigApplicationContext springContext = new AnnotationConfigApplicationContext();
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
springContext.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_propagate_current_context() {
|
||||
ScopePassingSpanSubscriber<?> subscriber = new ScopePassingSpanSubscriber<>(null,
|
||||
@@ -53,28 +80,96 @@ public class ScopePassingSpanSubscriberTests {
|
||||
@Test
|
||||
public void should_set_empty_context_when_context_is_null() {
|
||||
ScopePassingSpanSubscriber<?> subscriber = new ScopePassingSpanSubscriber<>(null,
|
||||
null, this.currentTraceContext, null);
|
||||
Context.empty(), this.currentTraceContext, null);
|
||||
|
||||
then(subscriber.currentContext().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_put_current_span_to_context() {
|
||||
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||
CoreSubscriber<?> subscriber = ReactorSleuth.scopePassingSpanSubscription(
|
||||
beanFactory(), new BaseSubscriber<Object>() {
|
||||
});
|
||||
try (Scope ws = this.currentTraceContext.newScope(context2)) {
|
||||
CoreSubscriber<?> subscriber = new ScopePassingSpanSubscriber<>(
|
||||
new BaseSubscriber<Object>() {
|
||||
}, Context.empty(), currentTraceContext, context);
|
||||
|
||||
then(subscriber.currentContext().get(TraceContext.class)).isEqualTo(context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private BeanFactory beanFactory() {
|
||||
BeanFactory beanFactory = BDDMockito.mock(BeanFactory.class);
|
||||
BDDMockito.given(beanFactory.getBean(CurrentTraceContext.class))
|
||||
.willReturn(this.currentTraceContext);
|
||||
return beanFactory;
|
||||
@Test
|
||||
public void should_not_trace_scalar_flows() {
|
||||
springContext.registerBean(CurrentTraceContext.class, () -> currentTraceContext);
|
||||
springContext.refresh();
|
||||
|
||||
Function<? super Publisher<Integer>, ? extends Publisher<Integer>> transformer = scopePassingSpanOperator(
|
||||
this.springContext);
|
||||
|
||||
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||
Subscriber<Object> assertNoSpanSubscriber = new CoreSubscriber<Object>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscription s) {
|
||||
s.request(Long.MAX_VALUE);
|
||||
assertThat(s).isNotInstanceOf(ScopePassingSpanSubscriber.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Object o) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
Subscriber<Object> assertSpanSubscriber = new CoreSubscriber<Object>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscription s) {
|
||||
s.request(Long.MAX_VALUE);
|
||||
assertThat(s).isInstanceOf(ScopePassingSpanSubscriber.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Object o) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
};
|
||||
transformer.apply(Mono.just(1).hide()).subscribe(assertSpanSubscriber);
|
||||
|
||||
transformer.apply(Mono.just(1)).subscribe(assertNoSpanSubscriber);
|
||||
|
||||
transformer.apply(Mono.<Integer>error(new Exception()).hide())
|
||||
.subscribe(assertSpanSubscriber);
|
||||
|
||||
transformer.apply(Mono.error(new Exception()))
|
||||
.subscribe(assertNoSpanSubscriber);
|
||||
|
||||
transformer.apply(Mono.<Integer>empty().hide())
|
||||
.subscribe(assertSpanSubscriber);
|
||||
|
||||
transformer.apply(Mono.empty()).subscribe(assertNoSpanSubscriber);
|
||||
|
||||
}
|
||||
|
||||
Awaitility.await().untilAsserted(() -> {
|
||||
then(this.currentTraceContext.get()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,333 +0,0 @@
|
||||
/*
|
||||
* 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.reactor;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.sampler.Sampler;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
import reactor.core.CoreSubscriber;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = SpanSubscriberTests.Config.class,
|
||||
webEnvironment = SpringBootTest.WebEnvironment.NONE)
|
||||
public class SpanSubscriberTests {
|
||||
|
||||
private static final Log log = LogFactory.getLog(SpanSubscriberTests.class);
|
||||
|
||||
@Autowired
|
||||
Tracer tracer;
|
||||
|
||||
@Autowired
|
||||
ConfigurableApplicationContext factory;
|
||||
|
||||
@Test
|
||||
public void should_pass_tracing_info_when_using_reactor() {
|
||||
Span span = this.tracer.nextSpan().name("foo").start();
|
||||
final AtomicReference<Span> spanInOperation = new AtomicReference<>();
|
||||
Publisher<Integer> traced = Flux.just(1, 2, 3);
|
||||
log.info("Hello");
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
||||
Flux.from(traced).map(d -> d + 1).map(d -> d + 1).map((d) -> {
|
||||
spanInOperation.set(this.tracer.currentSpan());
|
||||
return d + 1;
|
||||
}).map(d -> d + 1).subscribe(System.out::println);
|
||||
}
|
||||
finally {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(spanInOperation.get().context().spanId()).isEqualTo(span.context().spanId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_support_reactor_fusion_optimization() {
|
||||
Span span = this.tracer.nextSpan().name("foo").start();
|
||||
final AtomicReference<Span> spanInOperation = new AtomicReference<>();
|
||||
log.info("Hello");
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
||||
Mono.just(1).flatMap(d -> Flux.just(d + 1).collectList().map(p -> p.get(0)))
|
||||
.map(d -> d + 1).map((d) -> {
|
||||
spanInOperation.set(this.tracer.currentSpan());
|
||||
return d + 1;
|
||||
}).map(d -> d + 1).subscribe(System.out::println);
|
||||
}
|
||||
finally {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(spanInOperation.get().context().spanId()).isEqualTo(span.context().spanId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_trace_scalar_flows() {
|
||||
Span span = this.tracer.nextSpan().name("foo").start();
|
||||
log.info("Hello");
|
||||
|
||||
// Disable global hooks for local hook testing
|
||||
TraceReactorAutoConfigurationAccessorConfiguration.close();
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
||||
|
||||
Function<? super Publisher<Integer>, ? extends Publisher<Integer>> transformer = ReactorSleuth
|
||||
.scopePassingSpanOperator(this.factory);
|
||||
|
||||
Subscriber<Object> assertNoSpanSubscriber = new CoreSubscriber<Object>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscription s) {
|
||||
s.request(Long.MAX_VALUE);
|
||||
assertThat(s).isNotInstanceOf(ScopePassingSpanSubscriber.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Object o) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
Subscriber<Object> assertSpanSubscriber = new CoreSubscriber<Object>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscription s) {
|
||||
s.request(Long.MAX_VALUE);
|
||||
assertThat(s).isInstanceOf(ScopePassingSpanSubscriber.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Object o) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
};
|
||||
transformer.apply(Mono.just(1).hide()).subscribe(assertSpanSubscriber);
|
||||
|
||||
transformer.apply(Mono.just(1)).subscribe(assertNoSpanSubscriber);
|
||||
|
||||
transformer.apply(Mono.<Integer>error(new Exception()).hide())
|
||||
.subscribe(assertSpanSubscriber);
|
||||
|
||||
transformer.apply(Mono.error(new Exception()))
|
||||
.subscribe(assertNoSpanSubscriber);
|
||||
|
||||
transformer.apply(Mono.<Integer>empty().hide())
|
||||
.subscribe(assertSpanSubscriber);
|
||||
|
||||
transformer.apply(Mono.empty()).subscribe(assertNoSpanSubscriber);
|
||||
|
||||
}
|
||||
finally {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
Awaitility.await().untilAsserted(() -> {
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
});
|
||||
|
||||
TraceReactorAutoConfigurationAccessorConfiguration.setup(this.factory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_pass_tracing_info_when_using_reactor_async() {
|
||||
Span span = this.tracer.nextSpan().name("foo").start();
|
||||
final AtomicReference<Span> spanInOperation = new AtomicReference<>();
|
||||
log.info("Hello");
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
||||
Flux.just(1, 2, 3).publishOn(Schedulers.single()).log("reactor.1")
|
||||
.map(d -> d + 1).map(d -> d + 1)
|
||||
.publishOn(Schedulers.newSingle("secondThread")).log("reactor.2")
|
||||
.map((d) -> {
|
||||
spanInOperation.set(this.tracer.currentSpan());
|
||||
return d + 1;
|
||||
}).map(d -> d + 1).blockLast();
|
||||
|
||||
Awaitility.await().untilAsserted(() -> {
|
||||
then(spanInOperation.get().context().traceId())
|
||||
.isEqualTo(span.context().traceId());
|
||||
});
|
||||
then(this.tracer.currentSpan()).isEqualTo(span);
|
||||
}
|
||||
finally {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
Span foo2 = this.tracer.nextSpan().name("foo").start();
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(foo2)) {
|
||||
Flux.just(1, 2, 3).publishOn(Schedulers.single()).log("reactor.")
|
||||
.map(d -> d + 1).map(d -> d + 1).map((d) -> {
|
||||
spanInOperation.set(this.tracer.currentSpan());
|
||||
return d + 1;
|
||||
}).map(d -> d + 1).blockLast();
|
||||
|
||||
then(this.tracer.currentSpan()).isEqualTo(foo2);
|
||||
// parent cause there's an async span in the meantime
|
||||
then(spanInOperation.get().context().traceId())
|
||||
.isEqualTo(foo2.context().traceId());
|
||||
}
|
||||
finally {
|
||||
foo2.finish();
|
||||
}
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkSequenceOfOperations() {
|
||||
Span parentSpan = this.tracer.nextSpan().name("foo").start();
|
||||
log.info("Hello");
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(parentSpan)) {
|
||||
final Long spanId = Mono.fromCallable(this.tracer::currentSpan)
|
||||
.map(span -> span.context().spanId()).block();
|
||||
then(spanId).isNotNull();
|
||||
|
||||
final Long secondSpanId = Mono.fromCallable(this.tracer::currentSpan)
|
||||
.map(span -> span.context().spanId()).block();
|
||||
then(secondSpanId).isEqualTo(spanId); // different trace ids here
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkTraceIdDuringZipOperation() {
|
||||
Span initSpan = this.tracer.nextSpan().name("foo").start();
|
||||
final AtomicReference<Long> spanInOperation = new AtomicReference<>();
|
||||
final AtomicReference<Long> spanInZipOperation = new AtomicReference<>();
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(initSpan)) {
|
||||
Mono.fromCallable(this.tracer::currentSpan)
|
||||
.map(span -> span.context().spanId()).doOnNext(spanInOperation::set)
|
||||
.zipWith(Mono.fromCallable(this.tracer::currentSpan)
|
||||
.map(span -> span.context().spanId())
|
||||
.doOnNext(spanInZipOperation::set))
|
||||
.block();
|
||||
}
|
||||
|
||||
then(spanInZipOperation).hasValue(initSpan.context().spanId()); // ok here
|
||||
then(spanInOperation).hasValue(initSpan.context().spanId()); // Expecting
|
||||
// <AtomicReference[null]>
|
||||
// to have value:
|
||||
// <1L> but did
|
||||
// not.
|
||||
}
|
||||
|
||||
// #646
|
||||
@Test
|
||||
public void should_work_for_mono_just_with_flat_map() {
|
||||
Span initSpan = this.tracer.nextSpan().name("foo").start();
|
||||
log.info("Hello");
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(initSpan)) {
|
||||
Mono.just("value1")
|
||||
.flatMap(request -> Mono.just("value2").then(Mono.just("foo")))
|
||||
.map(a -> "qwe").block();
|
||||
}
|
||||
}
|
||||
|
||||
// #1030
|
||||
@Test
|
||||
public void checkTraceIdFromSubscriberContext() {
|
||||
Span initSpan = this.tracer.nextSpan().name("foo").start();
|
||||
final AtomicReference<Long> spanInSubscriberContext = new AtomicReference<>();
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(initSpan)) {
|
||||
Mono.subscriberContext()
|
||||
.map(context -> this.tracer.currentSpan().context().spanId())
|
||||
.doOnNext(spanInSubscriberContext::set).block();
|
||||
}
|
||||
|
||||
then(spanInSubscriberContext).hasValue(initSpan.context().spanId()); // ok here
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_pass_tracing_info_into_inner_publishers() {
|
||||
Span span = this.tracer.nextSpan().name("foo").start();
|
||||
final AtomicReference<Span> spanInOperation = new AtomicReference<>();
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
||||
Flux.range(0, 5)
|
||||
.flatMap(it -> Mono.delay(Duration.ofMillis(1))
|
||||
.map(context -> this.tracer.currentSpan())
|
||||
.doOnNext(spanInOperation::set))
|
||||
.blockFirst();
|
||||
}
|
||||
finally {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
then(spanInOperation.get().context().spanId()).isEqualTo(span.context().spanId());
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
Sampler sampler() {
|
||||
return Sampler.ALWAYS_SAMPLE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user