diff --git a/CHANGELOG.md b/CHANGELOG.md index f85dad51cd..ff00a15ead 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Features - Add SENTRY_AUTO_INIT environment variable to control OpenTelemetry Agent init ([#2410](https://github.com/getsentry/sentry-java/pull/2410)) +- Add OpenTelemetryLinkErrorEventProcessor for linking errors to traces created via OpenTelemetry ([#2418](https://github.com/getsentry/sentry-java/pull/2418)) ## 6.9.1 diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java index b11e988dd3..86c5bab31b 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java @@ -28,6 +28,7 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) { options -> { options.setEnableExternalConfiguration(true); options.setInstrumenter(Instrumenter.OTEL); + options.addEventProcessor(new OpenTelemetryLinkErrorEventProcessor()); final @Nullable SdkVersion sdkVersion = createSdkVersion(options); if (sdkVersion != null) { options.setSdkVersion(sdkVersion); diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api index 2347a99db7..18c73a9b68 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api +++ b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api @@ -1,3 +1,8 @@ +public final class io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor : io/sentry/EventProcessor { + public fun ()V + public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; +} + public final class io/sentry/opentelemetry/OtelSpanInfo { public fun (Ljava/lang/String;Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V public fun getDescription ()Ljava/lang/String; @@ -26,13 +31,6 @@ public final class io/sentry/opentelemetry/SentrySpanProcessor : io/opentelemetr public fun onStart (Lio/opentelemetry/context/Context;Lio/opentelemetry/sdk/trace/ReadWriteSpan;)V } -public final class io/sentry/opentelemetry/SentrySpanStorage { - public fun get (Ljava/lang/String;)Lio/sentry/ISpan; - public static fun getInstance ()Lio/sentry/opentelemetry/SentrySpanStorage; - public fun removeAndGet (Ljava/lang/String;)Lio/sentry/ISpan; - public fun store (Ljava/lang/String;Lio/sentry/ISpan;)V -} - public final class io/sentry/opentelemetry/SpanDescriptionExtractor { public fun ()V public fun extractSpanDescription (Lio/opentelemetry/sdk/trace/ReadableSpan;)Lio/sentry/opentelemetry/OtelSpanInfo; 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 new file mode 100644 index 0000000000..f01d9ec3ed --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java @@ -0,0 +1,50 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanId; +import io.opentelemetry.api.trace.TraceId; +import io.sentry.EventProcessor; +import io.sentry.Hint; +import io.sentry.HubAdapter; +import io.sentry.ISpan; +import io.sentry.Instrumenter; +import io.sentry.SentryEvent; +import io.sentry.SentrySpanStorage; +import io.sentry.SpanContext; +import io.sentry.protocol.SentryId; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class OpenTelemetryLinkErrorEventProcessor implements EventProcessor { + + private final @NotNull SentrySpanStorage spanStorage = SentrySpanStorage.getInstance(); + + @Override + public @Nullable SentryEvent process(final @NotNull SentryEvent event, final @NotNull Hint hint) { + if (Instrumenter.OTEL.equals(HubAdapter.getInstance().getOptions().getInstrumenter())) { + @NotNull final Span otelSpan = Span.current(); + @NotNull final String traceId = otelSpan.getSpanContext().getTraceId(); + @NotNull final String spanId = otelSpan.getSpanContext().getSpanId(); + + if (TraceId.isValid(traceId) && SpanId.isValid(spanId)) { + final @Nullable ISpan sentrySpan = spanStorage.get(spanId); + if (sentrySpan != null) { + final @NotNull SpanContext sentrySpanSpanContext = sentrySpan.getSpanContext(); + final @NotNull String operation = sentrySpanSpanContext.getOperation(); + final @Nullable io.sentry.SpanId parentSpanId = sentrySpanSpanContext.getParentSpanId(); + final @NotNull SpanContext spanContext = + new SpanContext( + new SentryId(traceId), + new io.sentry.SpanId(spanId), + operation, + parentSpanId, + null); + + event.getContexts().setTrace(spanContext); + } + } + } + + return event; + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java index 85ba4b3b30..f82f747c85 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java @@ -11,6 +11,7 @@ import io.sentry.Baggage; import io.sentry.BaggageHeader; import io.sentry.ISpan; +import io.sentry.SentrySpanStorage; import io.sentry.SentryTraceHeader; import io.sentry.exception.InvalidSentryTraceHeaderException; import java.util.Arrays; diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java index 0daa6cab64..fba597a48c 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java @@ -19,6 +19,7 @@ import io.sentry.ISpan; import io.sentry.ITransaction; import io.sentry.Instrumenter; +import io.sentry.SentrySpanStorage; import io.sentry.SentryTraceHeader; import io.sentry.SpanId; import io.sentry.SpanStatus; diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 6993f1f6b2..85f6c24022 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -1562,6 +1562,13 @@ public abstract interface class io/sentry/SentryOptions$TracesSamplerCallback { public abstract fun sample (Lio/sentry/SamplingContext;)Ljava/lang/Double; } +public final class io/sentry/SentrySpanStorage { + public fun get (Ljava/lang/String;)Lio/sentry/ISpan; + public static fun getInstance ()Lio/sentry/SentrySpanStorage; + public fun removeAndGet (Ljava/lang/String;)Lio/sentry/ISpan; + public fun store (Ljava/lang/String;Lio/sentry/ISpan;)V +} + public final class io/sentry/SentryTraceHeader { public static final field SENTRY_TRACE_HEADER Ljava/lang/String; public fun (Lio/sentry/protocol/SentryId;Lio/sentry/SpanId;Ljava/lang/Boolean;)V diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanStorage.java b/sentry/src/main/java/io/sentry/SentrySpanStorage.java similarity index 84% rename from sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanStorage.java rename to sentry/src/main/java/io/sentry/SentrySpanStorage.java index 9898fc5c4c..b260f06e5c 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanStorage.java +++ b/sentry/src/main/java/io/sentry/SentrySpanStorage.java @@ -1,12 +1,15 @@ -package io.sentry.opentelemetry; +package io.sentry; -import io.sentry.ISpan; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +/** + * Has been moved to `sentry` gradle module to include it in the bootstrap classloader without + * having to introduce yet another module for OpenTelemetry support. + */ @ApiStatus.Internal public final class SentrySpanStorage { private static volatile @Nullable SentrySpanStorage INSTANCE;