From baf8bd59a1944c158a41d133376c01cfbe2d27ed Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 13 Dec 2022 08:06:11 +0100 Subject: [PATCH] Add tests for linking errors to otel transactions --- .../OpenTelemetryLinkErrorEventProcessor.java | 14 +++++- .../test/kotlin/SentrySpanProcessorTest.kt | 47 +++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java index f01d9ec3ed..e528afe2e4 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java @@ -6,6 +6,7 @@ import io.sentry.EventProcessor; import io.sentry.Hint; import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ISpan; import io.sentry.Instrumenter; import io.sentry.SentryEvent; @@ -14,14 +15,25 @@ import io.sentry.protocol.SentryId; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; public final class OpenTelemetryLinkErrorEventProcessor implements EventProcessor { + private final @NotNull IHub hub; private final @NotNull SentrySpanStorage spanStorage = SentrySpanStorage.getInstance(); + public OpenTelemetryLinkErrorEventProcessor() { + this(HubAdapter.getInstance()); + } + + @TestOnly + OpenTelemetryLinkErrorEventProcessor(final @NotNull IHub hub) { + this.hub = hub; + } + @Override public @Nullable SentryEvent process(final @NotNull SentryEvent event, final @NotNull Hint hint) { - if (Instrumenter.OTEL.equals(HubAdapter.getInstance().getOptions().getInstrumenter())) { + if (Instrumenter.OTEL.equals(hub.getOptions().getInstrumenter())) { @NotNull final Span otelSpan = Span.current(); @NotNull final String traceId = otelSpan.getSpanContext().getTraceId(); @NotNull final String spanId = otelSpan.getSpanContext().getSpanId(); diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SentrySpanProcessorTest.kt b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SentrySpanProcessorTest.kt index 4c3df4b72b..0c6fe77f26 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SentrySpanProcessorTest.kt +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SentrySpanProcessorTest.kt @@ -20,10 +20,12 @@ import io.opentelemetry.sdk.trace.SdkTracerProvider import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import io.sentry.Baggage import io.sentry.BaggageHeader +import io.sentry.Hint import io.sentry.IHub import io.sentry.ISpan import io.sentry.ITransaction import io.sentry.Instrumenter +import io.sentry.SentryEvent import io.sentry.SentryOptions import io.sentry.SentryTraceHeader import io.sentry.SpanStatus @@ -66,6 +68,7 @@ class SentrySpanProcessorTest { val hub = mock() val transaction = mock() val span = mock() + val spanContext = mock() lateinit var openTelemetry: OpenTelemetry lateinit var tracer: Tracer val sentryTrace = SentryTraceHeader(SENTRY_TRACE_HEADER_STRING) @@ -76,6 +79,11 @@ class SentrySpanProcessorTest { whenever(hub.options).thenReturn(options) whenever(hub.startTransaction(any(), any())).thenReturn(transaction) + whenever(spanContext.operation).thenReturn("spanContextOp") + whenever(spanContext.parentSpanId).thenReturn(io.sentry.SpanId("cedf5b7571cb4972")) + + whenever(transaction.spanContext).thenReturn(spanContext) + whenever(span.spanContext).thenReturn(spanContext) whenever(span.toSentryTrace()).thenReturn(sentryTrace) whenever(transaction.toSentryTrace()).thenReturn(sentryTrace) @@ -322,6 +330,40 @@ class SentrySpanProcessorTest { thenTransactionIsFinished() } + @Test + fun `links error to OTEL transaction`() { + fixture.setup() + val extractedContext = whenExtractingHeaders() + + extractedContext.makeCurrent().use { _ -> + val otelSpan = givenSpanBuilder().startSpan() + thenTransactionIsStarted(otelSpan, isContinued = true) + + otelSpan.makeCurrent().use { _ -> + val processedEvent = OpenTelemetryLinkErrorEventProcessor(fixture.hub).process(SentryEvent(), Hint()) + val traceContext = processedEvent!!.contexts.trace!! + + assertEquals("2722d9f6ec019ade60c776169d9a8904", traceContext.traceId.toString()) + assertEquals(otelSpan.spanContext.spanId, traceContext.spanId.toString()) + assertEquals("cedf5b7571cb4972", traceContext.parentSpanId.toString()) + assertEquals("spanContextOp", traceContext.operation) + } + + otelSpan.end() + thenTransactionIsFinished() + } + } + + @Test + fun `does not link error to OTEL transaction if instrumenter does not match`() { + fixture.options.instrumenter = Instrumenter.SENTRY + fixture.setup() + + val processedEvent = OpenTelemetryLinkErrorEventProcessor(fixture.hub).process(SentryEvent(), Hint()) + + thenNoTraceContextHasBeenAddedToEvent(processedEvent) + } + private fun givenSpanBuilder(spanKind: SpanKind = SpanKind.SERVER, parentSpan: Span? = null): SpanBuilder { val spanName = if (parentSpan == null) "testspan" else "childspan" val spanBuilder = fixture.tracer @@ -430,6 +472,11 @@ class SentrySpanProcessorTest { verify(fixture.transaction).setContext(eq("otel"), any()) verify(fixture.transaction).finish(eq(SpanStatus.OK), any()) } + + private fun thenNoTraceContextHasBeenAddedToEvent(event: SentryEvent?) { + assertNotNull(event) + assertNull(event.contexts.trace) + } } class HeaderGetter : TextMapGetter {