From 1ad868528da06685d0e115d3851d3a3bf4087064 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 25 Jun 2024 15:26:18 +0200 Subject: [PATCH 1/6] improve changelog --- CHANGELOG.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 715567cb42..c015f1e3c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,11 +11,15 @@ ### Features - Our `sentry-opentelemetry-agent` has been completely reworked and now plays nicely with the rest of the Java SDK - - NOTE: Not all features have been implemented yet for the OpenTelemetry agent. - - You can add `sentry-opentelemetry-agent` to your setup by downloading the latest release and using it when starting up your application - - `SENTRY_PROPERTIES_FILE=sentry.properties java -javaagent:sentry-opentelemetry-agent-x.x.x.jar -jar your-application.jar` - - Please use `sentry.properties` or environment variables to configure the SDK as the agent is now in charge of initializing the SDK and options coming from things like logging integrations or our Spring Boot integration will not take effect. - - You may find the [docs page](https://docs.sentry.io/platforms/java/tracing/instrumentation/opentelemetry/#using-sentry-opentelemetry-agent-with-auto-initialization) useful. While we haven't updated it yet to reflect the changes described here, the section about using the agent with auto init should still be vaild. + - You may also want to give this new agent a try even if you haven't used OpenTelemetry (with Sentry) before. It offers support for [many more libraries and frameworks](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md), improving on our trace propagation, `Scopes` (used to be `Hub`) propagation as well as performance instrumentation (i.e. more spans). + - If you are using a framework we did not support before and currently resort to manual instrumentation, please give the agent a try. See [here for a list of supported libraries, frameworks and application servers](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md). + - NOTE: Not all features have been implemented yet for the OpenTelemetry agent. Features of note that are not working yet: + - Metrics + - Measurements + - `forceFinish` on transaction + - `scheduleFinish` on transaction + - see [#3436](https://github.com/getsentry/sentry-java/issues/3436) for a more up-to-date list of features we have (not) implemented + - Please see "Installing `sentry-opentelemetry-agent`" for more details on how to set up the agent. - What's new about the Agent - When the OpenTelemetry Agent is used, Sentry API creates OpenTelemetry spans under the hood, handing back a wrapper object which bridges the gap between traditional Sentry API and OpenTelemetry. We might be replacing some of the Sentry performance API in the future. - This is achieved by configuring the SDK to use `OtelSpanFactory` instead of `DefaultSpanFactory` which is done automatically by the auto init of the Java Agent. @@ -30,13 +34,49 @@ ### Fixes -- `TracesSampler` is now only created once in `SentryOptions` instead of creating a new one for every `Hub` (which is now `Scopes`). This means we're now creating fewwer `SecureRandom` instances. +- `TracesSampler` is now only created once in `SentryOptions` instead of creating a new one for every `Hub` (which is now `Scopes`). This means we're now creating fewer `SecureRandom` instances. - Move onFinishCallback before span or transaction is finished ([#3459](https://github.com/getsentry/sentry-java/pull/3459)) - Add timestamp when a profile starts ([#3442](https://github.com/getsentry/sentry-java/pull/3442)) - Move fragment auto span finish to onFragmentStarted ([#3424](https://github.com/getsentry/sentry-java/pull/3424)) - Remove profiling timeout logic and disable profiling on API 21 ([#3478](https://github.com/getsentry/sentry-java/pull/3478)) - Properly reset metric flush flag on metric emission ([#3493](https://github.com/getsentry/sentry-java/pull/3493)) +### Migration Guide / Deprecations + +- Classes used for the previous version of the Sentry OpenTelemetry Java Agent have been deprecated (`SentrySpanProcessor`, `SentryPropagator`, `OpenTelemetryLinkErrorEventProcessor`) +- Sentry OpenTelemetry Java Agent has been reworked and now allows you to manually create spans using Sentry API as well. +- Please see "Installing `sentry-opentelemetry-agent`" for more details on how to set up the agent. + +### Installing `sentry-opentelemetry-agent` + +### Upgrading from a previous agent +If you've been using the previous version of `sentry-opentelemetry-agent`, simply replace the agent JAR with the [latest release](https://central.sonatype.com/artifact/io.sentry/sentry-opentelemetry-agent?smo=true) and start your application. That should be it. + +### New to the agent +If you've not been using OpenTelemetry before, you can add `sentry-opentelemetry-agent` to your setup by downloading the latest release and using it when starting up your application + - `SENTRY_PROPERTIES_FILE=sentry.properties java -javaagent:sentry-opentelemetry-agent-x.x.x.jar -jar your-application.jar` + - Please use `sentry.properties` or environment variables to configure the SDK as the agent is now in charge of initializing the SDK and options coming from things like logging integrations or our Spring Boot integration will not take effect. + - You may find the [docs page](https://docs.sentry.io/platforms/java/tracing/instrumentation/opentelemetry/#using-sentry-opentelemetry-agent-with-auto-initialization) useful. While we haven't updated it yet to reflect the changes described here, the section about using the agent with auto init should still be valid. + +If you want to skip auto initialization of the SDK performed by the agent, please follow the steps above and set the environment variable `SENTRY_AUTO_INIT` to `false` then add the following to your `Sentry.init`: + +``` +Sentry.init(options -> { + options.setDsn("https://3d2ac63d6e1a4c6e9214443678f119a3@o87286.ingest.us.sentry.io/1801383"); + OpenTelemetryUtil.applyOpenTelemetryOptions(options); + ... +}); +``` + +If you're using our Spring (Boot) integration with auto init, use the following: +``` +@Bean +Sentry.OptionsConfiguration optionsConfiguration() { + return (options) -> { + OpenTelemetryUtil.applyOpenTelemetryOptions(options); + }; +} +``` ### Dependencies From 2ab67ff00054152f1830f8f87bfbd08055555741 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 25 Jun 2024 15:27:32 +0200 Subject: [PATCH 2/6] bump otel versions --- buildSrc/src/main/java/Config.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index a3ccdc3af5..9bed7be1b2 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -151,9 +151,9 @@ object Config { val apolloKotlin = "com.apollographql.apollo3:apollo-runtime:3.8.2" object OpenTelemetry { - val otelVersion = "1.37.0" + val otelVersion = "1.39.0" val otelAlphaVersion = "$otelVersion-alpha" - val otelJavaagentVersion = "2.3.0" + val otelJavaagentVersion = "2.5.0" val otelJavaagentAlphaVersion = "$otelJavaagentVersion-alpha" val otelSemanticConvetionsVersion = "1.23.1-alpha" From 9ce1e9cda20bc5a2ed25df5eb777f54f6b7b33ad Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 25 Jun 2024 15:31:13 +0200 Subject: [PATCH 3/6] merge fix; pr comments --- .../SentryAutoConfigurationCustomizerProvider.java | 2 -- sentry/api/sentry.api | 3 --- sentry/src/main/java/io/sentry/ISpan.java | 3 --- sentry/src/main/java/io/sentry/Sentry.java | 4 ++++ sentry/src/main/java/io/sentry/Span.java | 5 ----- 5 files changed, 4 insertions(+), 13 deletions(-) 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 cff1035457..2ae24c2f6b 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 @@ -56,8 +56,6 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) { } } - ContextStorage.addWrapper((storage) -> new SentryContextStorage(storage)); - autoConfiguration .addTracerProviderCustomizer(this::configureSdkTracerProvider) .addPropertiesSupplier(this::getDefaultProperties); diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 6057a37f14..146d17461f 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -953,7 +953,6 @@ public abstract interface class io/sentry/ISpan { public abstract fun getContexts ()Lio/sentry/protocol/Contexts; public abstract fun getData (Ljava/lang/String;)Ljava/lang/Object; public abstract fun getDescription ()Ljava/lang/String; - public abstract fun getEventId ()Lio/sentry/protocol/SentryId; public abstract fun getFinishDate ()Lio/sentry/SentryDate; public abstract fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; public abstract fun getOperation ()Ljava/lang/String; @@ -1551,7 +1550,6 @@ public final class io/sentry/NoOpSpan : io/sentry/ISpan { public fun getContexts ()Lio/sentry/protocol/Contexts; public fun getData (Ljava/lang/String;)Ljava/lang/Object; public fun getDescription ()Ljava/lang/String; - public fun getEventId ()Lio/sentry/protocol/SentryId; public fun getFinishDate ()Lio/sentry/SentryDate; public static fun getInstance ()Lio/sentry/NoOpSpan; public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; @@ -3112,7 +3110,6 @@ public final class io/sentry/Span : io/sentry/ISpan { public fun getData ()Ljava/util/Map; public fun getData (Ljava/lang/String;)Ljava/lang/Object; public fun getDescription ()Ljava/lang/String; - public fun getEventId ()Lio/sentry/protocol/SentryId; public fun getFinishDate ()Lio/sentry/SentryDate; public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; public fun getMeasurements ()Ljava/util/Map; diff --git a/sentry/src/main/java/io/sentry/ISpan.java b/sentry/src/main/java/io/sentry/ISpan.java index f624bb79ef..0b8f931033 100644 --- a/sentry/src/main/java/io/sentry/ISpan.java +++ b/sentry/src/main/java/io/sentry/ISpan.java @@ -280,9 +280,6 @@ ISpan startChild( @Nullable TracesSamplingDecision getSamplingDecision(); - // @NotNull - // SentryId getEventId(); - @ApiStatus.Internal @NotNull ISentryLifecycleToken makeCurrent(); diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 3091a925c4..7206df9b37 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -278,10 +278,14 @@ private static synchronized void init( options.getLogger().log(SentryLevel.INFO, "GlobalHubMode: '%s'", String.valueOf(globalHubMode)); Sentry.globalHubMode = globalHubMode; + globalScope.replaceOptions(options); final IScopes scopes = getCurrentScopes(); final IScope rootScope = new Scope(options); final IScope rootIsolationScope = new Scope(options); + rootScopes = new Scopes(rootScope, rootIsolationScope, globalScope, "Sentry.init"); + + getScopesStorage().set(rootScopes); scopes.close(true); globalScope.bindClient(new SentryClient(rootScopes.getOptions())); diff --git a/sentry/src/main/java/io/sentry/Span.java b/sentry/src/main/java/io/sentry/Span.java index 9a17a1a83a..12fcf87c20 100644 --- a/sentry/src/main/java/io/sentry/Span.java +++ b/sentry/src/main/java/io/sentry/Span.java @@ -306,11 +306,6 @@ public boolean isFinished() { return context.getSamplingDecision(); } - // @Override - // public @NotNull SentryId getEventId() { - // return new SentryId(UUID.nameUUIDFromBytes(getSpanId().toString().getBytes())); - // } - @Override public void setThrowable(final @Nullable Throwable throwable) { this.throwable = throwable; From a4a07a9b42b31ca4aa4422bade6082c6e55dc0cf Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 25 Jun 2024 15:34:24 +0200 Subject: [PATCH 4/6] workaround for agent non auto init --- ...ryAutoConfigurationCustomizerProvider.java | 5 +++- .../api/sentry-opentelemetry-bootstrap.api | 5 ++++ .../opentelemetry/OpenTelemetryUtil.java | 18 ++++++++++++ .../sentry/opentelemetry/OtelSpanWrapper.java | 2 +- sentry/api/sentry.api | 6 ++++ .../io/sentry/SentrySpanFactoryHolder.java | 29 +++++++++++++++++++ .../src/main/java/io/sentry/SentryTracer.java | 1 + 7 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OpenTelemetryUtil.java create mode 100644 sentry/src/main/java/io/sentry/SentrySpanFactoryHolder.java 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 2ae24c2f6b..2b1b79c521 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 d9907781b5..31aaa4ba9a 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 0000000000..02fc706e61 --- /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 2a023e115b..b5ee906e19 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 146d17461f..a106b61618 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 0000000000..cde9bc2a4f --- /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 5b4f9f273f..ce739986ca 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(), From e4e60d2a9c3e0a5f28348e3998ad0e3baaf7894b Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 25 Jun 2024 15:48:58 +0200 Subject: [PATCH 5/6] Customize OTel ThreadLocal storage behaviour --- .../build.gradle.kts | 1 + ...ryAutoConfigurationCustomizerProvider.java | 12 ++- .../api/sentry-opentelemetry-bootstrap.api | 6 ++ .../SentryOtelThreadLocalStorage.java | 85 +++++++++++++++++++ 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelThreadLocalStorage.java diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts index 475796d246..c0e002ee31 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts @@ -24,6 +24,7 @@ dependencies { exclude(group = "io.opentelemetry") exclude(group = "io.opentelemetry.javaagent") } +// compileOnly(projects.sentryOpentelemetry.sentryOpentelemetryBootstrap) implementation(projects.sentryOpentelemetry.sentryOpentelemetryBootstrap) compileOnly(Config.Libs.OpenTelemetry.otelSdk) 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 2b1b79c521..66b55a4f0b 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 @@ -32,9 +32,19 @@ public final class SentryAutoConfigurationCustomizerProvider 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); + /** + * We're currently overriding the storage mechanism to allow for cleanup of non closed OTel + * scopes. These happen when using e.g. Sentry static API due to getCurrentScopes() invoking + * Context.makeCurrent and then ignoring the returned lifecycle token (OTel Scope). After fixing + * the classloader problem (sentry bootstrap dependency is currently in agent classloader) we + * can revisit and try again to set the storage instead of overriding it in the wrapper. We + * should try to use OTels StorageProvider mechanism instead. + */ + // ContextStorage.addWrapper((storage) -> new SentryContextStorage(storage)); + ContextStorage.addWrapper( + (storage) -> new SentryContextStorage(new SentryOtelThreadLocalStorage())); if (isSentryAutoInitEnabled()) { Sentry.init( 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 31aaa4ba9a..0511fed18d 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api @@ -164,6 +164,12 @@ public final class io/sentry/opentelemetry/SentryOtelKeys { public fun ()V } +public final class io/sentry/opentelemetry/SentryOtelThreadLocalStorage : io/opentelemetry/context/ContextStorage { + public fun ()V + public fun attach (Lio/opentelemetry/context/Context;)Lio/opentelemetry/context/Scope; + public fun current ()Lio/opentelemetry/context/Context; +} + public final class io/sentry/opentelemetry/SentryWeakSpanStorage { public static fun getInstance ()Lio/sentry/opentelemetry/SentryWeakSpanStorage; public fun getSentrySpan (Lio/opentelemetry/api/trace/SpanContext;)Lio/sentry/opentelemetry/OtelSpanWrapper; diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelThreadLocalStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelThreadLocalStorage.java new file mode 100644 index 0000000000..e44afc5a2d --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelThreadLocalStorage.java @@ -0,0 +1,85 @@ +/* + * Adapted from https://github.com/open-telemetry/opentelemetry-java/blob/0aacc55d1e3f5cc6dbb4f8fa26bcb657b01a7bc9/context/src/main/java/io/opentelemetry/context/ThreadLocalContextStorage.java + * + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.sentry.opentelemetry; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextStorage; +import io.opentelemetry.context.Scope; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +/** + * Workaround to make OpenTelemetry context storage work for Sentry since Sentry sometimes forks + * Context without cleaning up. We are not yet sure if this is something we can easliy fix, since + * Sentry static API makes heavy use of getCurrentScopes and there is no easy way of knowing when to + * restore previous Context. + */ +@ApiStatus.Experimental +@ApiStatus.Internal +public final class SentryOtelThreadLocalStorage implements ContextStorage { + private static final Logger logger = + Logger.getLogger(SentryOtelThreadLocalStorage.class.getName()); + + private static final ThreadLocal THREAD_LOCAL_STORAGE = new ThreadLocal<>(); + + @Override + public Scope attach(Context toAttach) { + if (toAttach == null) { + // Null context not allowed so ignore it. + return NoopScope.INSTANCE; + } + + Context beforeAttach = current(); + if (toAttach == beforeAttach) { + return NoopScope.INSTANCE; + } + + THREAD_LOCAL_STORAGE.set(toAttach); + + return new SentryScopeImpl(beforeAttach); + } + + private static class SentryScopeImpl implements Scope { + @Nullable private final Context beforeAttach; + private boolean closed; + + private SentryScopeImpl(@Nullable Context beforeAttach) { + this.beforeAttach = beforeAttach; + } + + @Override + public void close() { + // if (!closed && current() == toAttach) { + // Used to make OTel thread local storage compatible with Sentry where cleanup isn't always + // performed correctly + if (!closed) { + closed = true; + THREAD_LOCAL_STORAGE.set(beforeAttach); + } else { + logger.log( + Level.FINE, + " Trying to close scope which does not represent current context. Ignoring the call."); + } + } + } + + @Override + @Nullable + public Context current() { + return THREAD_LOCAL_STORAGE.get(); + } + + enum NoopScope implements Scope { + INSTANCE; + + @Override + public void close() {} + } +} From 88b8457c30f4009025eac56ae960f241a7a15e22 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 25 Jun 2024 15:49:41 +0200 Subject: [PATCH 6/6] fix changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c015f1e3c3..de32df6a2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,10 +49,10 @@ ### Installing `sentry-opentelemetry-agent` -### Upgrading from a previous agent +#### Upgrading from a previous agent If you've been using the previous version of `sentry-opentelemetry-agent`, simply replace the agent JAR with the [latest release](https://central.sonatype.com/artifact/io.sentry/sentry-opentelemetry-agent?smo=true) and start your application. That should be it. -### New to the agent +#### New to the agent If you've not been using OpenTelemetry before, you can add `sentry-opentelemetry-agent` to your setup by downloading the latest release and using it when starting up your application - `SENTRY_PROPERTIES_FILE=sentry.properties java -javaagent:sentry-opentelemetry-agent-x.x.x.jar -jar your-application.jar` - Please use `sentry.properties` or environment variables to configure the SDK as the agent is now in charge of initializing the SDK and options coming from things like logging integrations or our Spring Boot integration will not take effect.