|
@@ -7,13 +7,16 @@ package io.opentelemetry.instrumentation.reactor;
|
|
|
|
|
|
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry;
|
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
|
+import static org.assertj.core.api.Assertions.fail;
|
|
|
|
|
|
import io.opentelemetry.api.trace.Span;
|
|
|
import io.opentelemetry.api.trace.StatusCode;
|
|
|
import io.opentelemetry.api.trace.Tracer;
|
|
|
import io.opentelemetry.context.Context;
|
|
|
+import io.opentelemetry.context.Scope;
|
|
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
|
|
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
|
|
|
+import java.time.Duration;
|
|
|
import org.junit.jupiter.api.AfterAll;
|
|
|
import org.junit.jupiter.api.BeforeAll;
|
|
|
import org.junit.jupiter.api.Test;
|
|
@@ -22,6 +25,7 @@ import reactor.core.Scannable;
|
|
|
import reactor.core.publisher.Flux;
|
|
|
import reactor.core.publisher.Mono;
|
|
|
import reactor.core.publisher.UnicastProcessor;
|
|
|
+import reactor.test.StepVerifier;
|
|
|
|
|
|
class ReactorCoreTest extends AbstractReactorCoreTest {
|
|
|
|
|
@@ -229,6 +233,135 @@ class ReactorCoreTest extends AbstractReactorCoreTest {
|
|
|
.isPresent();
|
|
|
}
|
|
|
|
|
|
+ @Test
|
|
|
+ void doesNotOverrideInnerCurrentSpans() {
|
|
|
+ Flux<Object> publish =
|
|
|
+ Flux.create(
|
|
|
+ sink -> {
|
|
|
+ for (int i = 0; i < 2; i++) {
|
|
|
+ Span s = tracer.spanBuilder("inner").startSpan();
|
|
|
+ try (Scope scope = s.makeCurrent()) {
|
|
|
+ sink.next(i);
|
|
|
+ } finally {
|
|
|
+ s.end();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // as a result we'll have
|
|
|
+ // 1. publish subscriber that creates inner spans
|
|
|
+ // 2. tracing subscriber without current context - subscription was done outside any scope
|
|
|
+ // 3. inner subscriber that will add onNext attribute to inner spans
|
|
|
+ // I.e. tracing subscriber context (root) at subscription time will be different from inner in
|
|
|
+ // onNext
|
|
|
+ publish
|
|
|
+ .take(2)
|
|
|
+ .subscribe(
|
|
|
+ n -> {
|
|
|
+ assertThat(Span.current().getSpanContext().isValid()).isTrue();
|
|
|
+ Span.current().setAttribute("onNext", true);
|
|
|
+ },
|
|
|
+ error -> fail(error.getMessage()));
|
|
|
+
|
|
|
+ testing.waitAndAssertTraces(
|
|
|
+ trace ->
|
|
|
+ trace.hasSpansSatisfyingExactly(
|
|
|
+ span ->
|
|
|
+ span.hasName("inner")
|
|
|
+ .hasNoParent()
|
|
|
+ .hasAttributes(attributeEntry("onNext", true))),
|
|
|
+ trace ->
|
|
|
+ trace.hasSpansSatisfyingExactly(
|
|
|
+ span ->
|
|
|
+ span.hasName("inner")
|
|
|
+ .hasNoParent()
|
|
|
+ .hasAttributes(attributeEntry("onNext", true))));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void doesNotOverrideInnerCurrentSpansAsync() {
|
|
|
+ Flux<Object> publish =
|
|
|
+ Flux.create(
|
|
|
+ sink -> {
|
|
|
+ Span s = tracer.spanBuilder("inner").startSpan();
|
|
|
+ try (Scope scope = s.makeCurrent()) {
|
|
|
+ sink.next(s);
|
|
|
+ } finally {
|
|
|
+ s.end();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ publish
|
|
|
+ .take(1)
|
|
|
+ .delayElements(Duration.ofMillis(1))
|
|
|
+ .doOnNext(
|
|
|
+ span -> {
|
|
|
+ assertThat(Span.current().getSpanContext().isValid()).isTrue();
|
|
|
+ assertThat(Span.current()).isSameAs(span);
|
|
|
+ })
|
|
|
+ .subscribe(
|
|
|
+ span -> assertThat(Span.current()).isSameAs(span), error -> fail(error.getMessage()));
|
|
|
+
|
|
|
+ testing.waitAndAssertTraces(
|
|
|
+ trace -> trace.hasSpansSatisfyingExactly(span -> span.hasName("inner").hasNoParent()));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void doesNotOverrideInnerCurrentSpansWithThereIsOuterCurrent() {
|
|
|
+ Flux<Object> publish =
|
|
|
+ Flux.create(
|
|
|
+ sink -> {
|
|
|
+ for (int i = 0; i < 2; i++) {
|
|
|
+ Span s = tracer.spanBuilder("inner").startSpan();
|
|
|
+ try (Scope scope = s.makeCurrent()) {
|
|
|
+ sink.next(i);
|
|
|
+ } finally {
|
|
|
+ s.end();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // as a result we'll have
|
|
|
+ // 1. publish subscriber that creates inner spans
|
|
|
+ // 2. tracing subscriber with outer context - it was active at subscription time
|
|
|
+ // 3. inner subscriber that will add onNext attribute
|
|
|
+ // I.e. tracing subscriber context at subscription time will be different from inner in onNext
|
|
|
+ Span outer = tracer.spanBuilder("outer").startSpan();
|
|
|
+ try (Scope scope = outer.makeCurrent()) {
|
|
|
+ StepVerifier.create(
|
|
|
+ publish
|
|
|
+ .take(2)
|
|
|
+ .doOnNext(
|
|
|
+ n -> {
|
|
|
+ assertThat(Span.current().getSpanContext().isValid()).isTrue();
|
|
|
+ Span.current().setAttribute("onNext", true);
|
|
|
+ })
|
|
|
+ .subscriberContext(
|
|
|
+ // subscribers that know that their subscription can happen
|
|
|
+ // ahead of time and in the 'wrong' context, has to clean up 'wrong' context
|
|
|
+ context ->
|
|
|
+ ContextPropagationOperator.storeOpenTelemetryContext(
|
|
|
+ context, Context.root())))
|
|
|
+ .expectNextCount(2)
|
|
|
+ .verifyComplete();
|
|
|
+
|
|
|
+ outer.end();
|
|
|
+ }
|
|
|
+
|
|
|
+ testing.waitAndAssertTraces(
|
|
|
+ trace ->
|
|
|
+ trace.hasSpansSatisfyingExactly(
|
|
|
+ span -> span.hasName("outer").hasNoParent(),
|
|
|
+ span ->
|
|
|
+ span.hasName("inner")
|
|
|
+ .hasParent(trace.getSpan(0))
|
|
|
+ .hasAttributes(attributeEntry("onNext", true)),
|
|
|
+ span ->
|
|
|
+ span.hasName("inner")
|
|
|
+ .hasParent(trace.getSpan(0))
|
|
|
+ .hasAttributes(attributeEntry("onNext", true))));
|
|
|
+ }
|
|
|
+
|
|
|
private <T> Mono<T> monoSpan(Mono<T> mono, String spanName) {
|
|
|
return ContextPropagationOperator.ScalarPropagatingMono.create(mono)
|
|
|
.doOnEach(
|