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 2ae24c2f6b6..2b1b79c5217 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 @@ -9,6 +9,7 @@ import io.sentry.Sentry; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryOptions; +import io.sentry.SentrySpanFactoryHolder; import io.sentry.protocol.SdkVersion; import io.sentry.protocol.SentryPackage; import io.sentry.util.SpanUtils; @@ -32,13 +33,15 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) { final @Nullable VersionInfoHolder versionInfoHolder = createVersionInfo(); ContextStorage.addWrapper((storage) -> new SentryContextStorage(storage)); + final @NotNull OtelSpanFactory spanFactory = new OtelSpanFactory(); + SentrySpanFactoryHolder.setSpanFactory(spanFactory); if (isSentryAutoInitEnabled()) { Sentry.init( options -> { options.setEnableExternalConfiguration(true); options.setIgnoredSpanOrigins(SpanUtils.ignoredSpanOriginsForOpenTelemetry()); - options.setSpanFactory(new OtelSpanFactory()); + options.setSpanFactory(spanFactory); final @Nullable SdkVersion sdkVersion = createSdkVersion(options, versionInfoHolder); // TODO [POTEL] is detecting a version mismatch between application and agent possible? if (sdkVersion != null) { diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api index d9907781b57..31aaa4ba9a1 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api @@ -10,6 +10,11 @@ public final class io/sentry/opentelemetry/InternalSemanticAttributes { public fun ()V } +public final class io/sentry/opentelemetry/OpenTelemetryUtil { + public fun ()V + public static fun applyOpenTelemetryOptions (Lio/sentry/SentryOptions;)V +} + public final class io/sentry/opentelemetry/OtelContextScopesStorage : io/sentry/IScopesStorage { public fun ()V public fun close ()V diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OpenTelemetryUtil.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OpenTelemetryUtil.java new file mode 100644 index 00000000000..02fc706e610 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OpenTelemetryUtil.java @@ -0,0 +1,18 @@ +package io.sentry.opentelemetry; + +import io.sentry.SentryOptions; +import io.sentry.SentrySpanFactoryHolder; +import io.sentry.util.SpanUtils; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +@ApiStatus.Experimental +public final class OpenTelemetryUtil { + + public static void applyOpenTelemetryOptions(final @Nullable SentryOptions options) { + if (options != null) { + options.setSpanFactory(SentrySpanFactoryHolder.getSpanFactory()); + options.setIgnoredSpanOrigins(SpanUtils.ignoredSpanOriginsForOpenTelemetry()); + } + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java index 2a023e115bc..b5ee906e19e 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java @@ -58,7 +58,7 @@ public final class OtelSpanWrapper implements ISpan { * OtelSpanWrapper} and indirectly back to {@link io.opentelemetry.sdk.trace.data.SpanData} via * {@link Span}. Also see {@link SentryWeakSpanStorage}. */ - private final @NotNull WeakReference span; + private final @NotNull WeakReference span; // TODO [POTEL] bootstrap proxy private final @NotNull SpanContext context; // private final @NotNull SpanOptions options; diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 146d17461f5..a106b61618b 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -2936,6 +2936,12 @@ public abstract interface class io/sentry/SentryOptions$TracesSamplerCallback { public abstract fun sample (Lio/sentry/SamplingContext;)Ljava/lang/Double; } +public final class io/sentry/SentrySpanFactoryHolder { + public fun ()V + public static fun getSpanFactory ()Lio/sentry/ISpanFactory; + public static fun setSpanFactory (Lio/sentry/ISpanFactory;)V +} + public final class io/sentry/SentrySpanStorage { public fun get (Ljava/lang/String;)Lio/sentry/ISpan; public static fun getInstance ()Lio/sentry/SentrySpanStorage; diff --git a/sentry/src/main/java/io/sentry/SentrySpanFactoryHolder.java b/sentry/src/main/java/io/sentry/SentrySpanFactoryHolder.java new file mode 100644 index 00000000000..cde9bc2a4fb --- /dev/null +++ b/sentry/src/main/java/io/sentry/SentrySpanFactoryHolder.java @@ -0,0 +1,29 @@ +package io.sentry; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * NOTE: This just exists as a workaround for a bug. + * + *

What bug? When using sentry-opentelemetry-agent with SENTRY_AUTO_INIT=false a global storage + * for spans does not work correctly since it's loaded multiple times. Once for bootstrap + * classloader (a.k.a null) and once for the agent classloader. Since the agent is currently loading + * these classes into the agent classloader, there should not be a noticable problem, when using the + * default of SENTRY_AUTO_INIT=true. In the future we plan to have the agent also load the classes + * into the bootstrap classloader, then this hack should no longer be necessary. + */ +@ApiStatus.Experimental +public final class SentrySpanFactoryHolder { + + private static ISpanFactory spanFactory = new DefaultSpanFactory(); + + public static ISpanFactory getSpanFactory() { + return spanFactory; + } + + @ApiStatus.Internal + public static void setSpanFactory(final @NotNull ISpanFactory factory) { + spanFactory = factory; + } +} diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index 5b4f9f273f0..ce739986ca9 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -486,6 +486,7 @@ private ISpan createChild( finish(finishStatus.spanStatus); } }); + // TODO [POTEL] missing features // final Span span = // new Span( // root.getTraceId(),