diff --git a/tracing-opentelemetry/src/integrationTest/java/com/couchbase/client/tracing/opentelemetry/OpenTelemetryIntegrationTest.java b/tracing-opentelemetry/src/integrationTest/java/com/couchbase/client/tracing/opentelemetry/OpenTelemetryIntegrationTest.java index e6fa1c302..34eb65576 100644 --- a/tracing-opentelemetry/src/integrationTest/java/com/couchbase/client/tracing/opentelemetry/OpenTelemetryIntegrationTest.java +++ b/tracing-opentelemetry/src/integrationTest/java/com/couchbase/client/tracing/opentelemetry/OpenTelemetryIntegrationTest.java @@ -24,37 +24,36 @@ import com.couchbase.client.test.ClusterAwareIntegrationTest; import com.couchbase.client.test.Services; import com.couchbase.client.test.TestNodeConfig; -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; -import io.opentelemetry.sdk.trace.SdkTracerProvider; -import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.context.Scope; +import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension; +import java.time.Duration; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; - -import java.time.Duration; +import org.junit.jupiter.api.extension.RegisterExtension; import static com.couchbase.client.java.ClusterOptions.clusterOptions; import static com.couchbase.client.test.Util.waitUntilCondition; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; class OpenTelemetryIntegrationTest extends ClusterAwareIntegrationTest { private static ClusterEnvironment environment; private static Cluster cluster; private static Collection collection; - private static final InMemorySpanExporter exporter = InMemorySpanExporter.create(); - private static SdkTracerProvider tracerProvider; + + @RegisterExtension + static final OpenTelemetryExtension otelTesting = OpenTelemetryExtension.create(); @BeforeAll static void beforeAll() { - tracerProvider = SdkTracerProvider.builder().addSpanProcessor(SimpleSpanProcessor.create(exporter)).build(); - TestNodeConfig config = config().firstNodeWith(Services.KV).get(); environment = ClusterEnvironment .builder() - .requestTracer(OpenTelemetryRequestTracer.wrap(tracerProvider.get("integrationTest"))) + .requestTracer(OpenTelemetryRequestTracer.wrap(otelTesting.getOpenTelemetry())) .build(); cluster = Cluster.connect( @@ -72,11 +71,51 @@ static void beforeAll() { static void afterAll() { cluster.disconnect(); environment.shutdown(); - tracerProvider.shutdown(); } @Test void capturesTraceSpans() { + Span parent = otelTesting.getOpenTelemetry().getTracer("integrationtest") + .spanBuilder("test") + .setSpanKind(SpanKind.SERVER) + .startSpan(); + try (Scope ignored = parent.makeCurrent()) { + collection.get("myid"); + } catch (DocumentNotFoundException ignored) { + // expected + } finally { + parent.end(); + } + + waitUntilCondition(() -> { + otelTesting.assertTraces() + .hasTracesSatisfyingExactly( + trace -> trace.hasSpansSatisfyingExactly( + span -> span + .hasName("test") + .hasKind(SpanKind.SERVER), + span -> span + .hasName("get") + .hasParentSpanId(parent.getSpanContext().getSpanId()) + .hasKind(SpanKind.INTERNAL) + .hasAttributesSatisfying(attributes -> assertThat(attributes) + .containsEntry("db.system", "couchbase") + .containsEntry("db.operation", "get") + .containsEntry("db.couchbase.service", "kv") + .containsEntry("db.couchbase.collection", "_default") + .containsEntry("db.couchbase.scope", "_default")), + span -> span + .hasName("dispatch_to_server") + .hasKind(SpanKind.INTERNAL) + .hasAttributesSatisfying(attributes -> assertThat(attributes) + .containsEntry("db.system", "couchbase")) + )); + return true; + }); + } + + @Test + void stressTest() { int numRequests = 100; for (int i = 0; i < 100; i++) { try { @@ -86,7 +125,10 @@ void capturesTraceSpans() { } } - waitUntilCondition(() -> exporter.getFinishedSpanItems().size() >= numRequests); + waitUntilCondition(() -> { + otelTesting.assertTraces().hasSizeGreaterThanOrEqualTo(numRequests); + return true; + }); } } diff --git a/tracing-opentelemetry/src/main/java/com/couchbase/client/tracing/opentelemetry/OpenTelemetryRequestSpan.java b/tracing-opentelemetry/src/main/java/com/couchbase/client/tracing/opentelemetry/OpenTelemetryRequestSpan.java index 45e9565a1..1e2713031 100644 --- a/tracing-opentelemetry/src/main/java/com/couchbase/client/tracing/opentelemetry/OpenTelemetryRequestSpan.java +++ b/tracing-opentelemetry/src/main/java/com/couchbase/client/tracing/opentelemetry/OpenTelemetryRequestSpan.java @@ -78,9 +78,7 @@ public void addEvent(String name, Instant timestamp) { @Override public void end() { - try (Scope scope = span.makeCurrent()) { - span.end(); - } + span.end(); } @Override diff --git a/tracing-opentelemetry/src/main/java/com/couchbase/client/tracing/opentelemetry/OpenTelemetryRequestTracer.java b/tracing-opentelemetry/src/main/java/com/couchbase/client/tracing/opentelemetry/OpenTelemetryRequestTracer.java index 66a6beabf..2420cbacf 100644 --- a/tracing-opentelemetry/src/main/java/com/couchbase/client/tracing/opentelemetry/OpenTelemetryRequestTracer.java +++ b/tracing-opentelemetry/src/main/java/com/couchbase/client/tracing/opentelemetry/OpenTelemetryRequestTracer.java @@ -18,6 +18,7 @@ import com.couchbase.client.core.cnc.RequestSpan; import com.couchbase.client.core.cnc.RequestTracer; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanBuilder; import io.opentelemetry.api.trace.Tracer; @@ -33,23 +34,23 @@ public class OpenTelemetryRequestTracer implements RequestTracer { /** - * Holds the actual OT tracer. + * Holds the actual OTel tracer. */ private final Tracer tracer; /** - * Wraps the OpenTelemetry tracer and returns a datatype that can be passed into the requestTracer method of the + * Wraps OpenTelemetry and returns a datatype that can be passed into the requestTracer method of the * environment. * - * @param tracer the tracer to wrap. - * @return the wrapped tracer ready to be passed in. + * @param openTelemetry the OpenTelemetry instance to wrap. + * @return the wrapped OpenTelemetry ready to be passed in. */ - public static OpenTelemetryRequestTracer wrap(final Tracer tracer) { - return new OpenTelemetryRequestTracer(tracer); + public static OpenTelemetryRequestTracer wrap(final OpenTelemetry openTelemetry) { + return new OpenTelemetryRequestTracer(openTelemetry); } - private OpenTelemetryRequestTracer(Tracer tracer) { - this.tracer = tracer; + private OpenTelemetryRequestTracer(OpenTelemetry openTelemetry) { + this.tracer = openTelemetry.getTracer("com.couchbase.client.jvm"); } private Span castSpan(final RequestSpan requestSpan) { @@ -68,19 +69,17 @@ private Span castSpan(final RequestSpan requestSpan) { * Returns the inner OpenTelemetry tracer. */ public Tracer tracer() { - return tracer; + return tracer; } @Override public RequestSpan requestSpan(String operationName, RequestSpan parent) { SpanBuilder spanBuilder = tracer.spanBuilder(operationName); + Context parentContext = Context.current(); if (parent != null) { - spanBuilder.setParent(Context.current().with(castSpan(parent))); - } else { - spanBuilder.setNoParent(); + parentContext = parentContext.with(castSpan(parent)); } - Span span = spanBuilder.startSpan(); - span.makeCurrent().close(); + Span span = spanBuilder.setParent(parentContext).startSpan(); return OpenTelemetryRequestSpan.wrap(span); }