From ab7e6fda88379696c93bb843e51b29e767db8f79 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 11 Nov 2024 15:52:58 +0100 Subject: [PATCH] POTEL 50 - Auto config for Spring Boot combined with OTel but without agent (#3846) * Auto config for Spring Boot combined with OTel but without agent * changelog --- CHANGELOG.md | 4 + buildSrc/src/main/java/Config.kt | 1 + ...entry-opentelemetry-agentcustomization.api | 1 + ...ryAutoConfigurationCustomizerProvider.java | 27 +++---- .../api/sentry-opentelemetry-extra.api | 2 + .../OtelContextScopesStorage.java | 17 ++++ .../sentry/opentelemetry/OtelSpanFactory.java | 23 +++++- .../opentelemetry/SentryContextStorage.java | 5 -- .../build.gradle.kts | 12 ++- .../spring/boot/jakarta/CustomJob.java | 3 +- .../spring/boot/jakarta/PersonController.java | 27 +++++-- .../boot/jakarta/SentryDemoApplication.java | 77 ++++++++----------- .../spring/boot/jakarta/TodoController.java | 23 ++++-- .../src/main/resources/application.properties | 6 ++ .../build.gradle.kts | 1 + .../spring/boot/jakarta/CustomJob.java | 3 +- .../spring/boot/jakarta/PersonController.java | 43 ++++++++++- .../boot/jakarta/SentryDemoApplication.java | 66 ++++++++-------- sentry-spring-boot-jakarta/build.gradle.kts | 2 + .../boot/jakarta/SentryAutoConfiguration.java | 14 ++++ .../api/sentry-spring-jakarta.api | 6 ++ sentry-spring-jakarta/build.gradle.kts | 3 + .../SentryOpenTelemetryConfiguration.java | 36 +++++++++ sentry/api/sentry.api | 3 + .../java/io/sentry/DefaultScopesStorage.java | 3 + .../main/java/io/sentry/IScopesStorage.java | 2 + .../java/io/sentry/NoOpScopesStorage.java | 3 + .../java/io/sentry/ScopesStorageFactory.java | 7 ++ 28 files changed, 299 insertions(+), 121 deletions(-) create mode 100644 sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/opentelemetry/SentryOpenTelemetryConfiguration.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 8be6cc6470..effd9b572e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ ### Features +- Spring Boot now automatically detects if OpenTelemetry is available and makes use of it ([#3846](https://github.com/getsentry/sentry-java/pull/3846)) + - This is only enabled if there is no OpenTelemetry agent available + - We prefer to use the OpenTelemetry agent as it offers more auto instrumentation + - In some cases the OpenTelemetry agent cannot be used, please see https://opentelemetry.io/docs/zero-code/java/spring-boot-starter/ for more details on when to prefer the Agent and when the Spring Boot starter makes more sense. - Add `globalHubMode` to options ([#3805](https://github.com/getsentry/sentry-java/pull/3805)) - `globalHubMode` used to only be a param on `Sentry.init`. To make it easier to be used in e.g. Desktop environments, we now additionally added it as an option on SentryOptions that can also be set via `sentry.properties`. - If both the param on `Sentry.init` and the option are set, the option will win. By default the option is set to `null` meaning whatever is passed to `Sentry.init` takes effect. diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index 07f3f1debb..10e361e8d9 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -94,6 +94,7 @@ object Config { val springBoot3StarterSecurity = "org.springframework.boot:spring-boot-starter-security:$springBoot3Version" val springBoot3StarterJdbc = "org.springframework.boot:spring-boot-starter-jdbc:$springBoot3Version" val springBoot3StarterActuator = "org.springframework.boot:spring-boot-starter-actuator:$springBoot3Version" + val springBoot3StarterOpenTelemetry = "io.opentelemetry.instrumentation:opentelemetry-spring-boot-starter:${OpenTelemetry.otelJavaagentVersion}" val springWeb = "org.springframework:spring-webmvc" val springWebflux = "org.springframework:spring-webflux" diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/api/sentry-opentelemetry-agentcustomization.api b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/api/sentry-opentelemetry-agentcustomization.api index 342f71b5bb..c35b221aa0 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/api/sentry-opentelemetry-agentcustomization.api +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/api/sentry-opentelemetry-agentcustomization.api @@ -1,4 +1,5 @@ public final class io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider : io/opentelemetry/sdk/autoconfigure/spi/AutoConfigurationCustomizerProvider { + public static field skipInit Z public fun ()V public fun customize (Lio/opentelemetry/sdk/autoconfigure/spi/AutoConfigurationCustomizer;)V } 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 0c173a6d4f..2e71afcd08 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 @@ -1,6 +1,5 @@ package io.sentry.opentelemetry; -import io.opentelemetry.context.ContextStorage; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; @@ -13,7 +12,6 @@ import io.sentry.SentrySpanFactoryHolder; import io.sentry.protocol.SdkVersion; import io.sentry.protocol.SentryPackage; -import io.sentry.util.SpanUtils; import java.io.IOException; import java.net.URL; import java.util.ArrayList; @@ -29,31 +27,27 @@ public final class SentryAutoConfigurationCustomizerProvider implements AutoConfigurationCustomizerProvider { + public static volatile boolean skipInit = false; + @Override public void customize(AutoConfigurationCustomizer autoConfiguration) { + System.out.println("hello from agent"); final @Nullable VersionInfoHolder versionInfoHolder = createVersionInfo(); 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()) { + System.out.println("hello from before agent init"); Sentry.init( options -> { + System.out.println("hello from agent init options config block"); options.setEnableExternalConfiguration(true); options.setInitPriority(InitPriority.HIGH); - options.setIgnoredSpanOrigins(SpanUtils.ignoredSpanOriginsForOpenTelemetry()); - options.setSpanFactory(spanFactory); + OpenTelemetryUtil.applyOpenTelemetryOptions(options); + // + // options.setIgnoredSpanOrigins(SpanUtils.ignoredSpanOriginsForOpenTelemetry()); + // options.setSpanFactory(spanFactory); final @Nullable SdkVersion sdkVersion = createSdkVersion(options, versionInfoHolder); if (sdkVersion != null) { options.setSdkVersion(sdkVersion); @@ -76,6 +70,9 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) { } private boolean isSentryAutoInitEnabled() { + if (skipInit) { + return false; + } final @Nullable String sentryAutoInit = System.getenv("SENTRY_AUTO_INIT"); if (sentryAutoInit != null) { diff --git a/sentry-opentelemetry/sentry-opentelemetry-extra/api/sentry-opentelemetry-extra.api b/sentry-opentelemetry/sentry-opentelemetry-extra/api/sentry-opentelemetry-extra.api index bb749a7df1..4533891a18 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-extra/api/sentry-opentelemetry-extra.api +++ b/sentry-opentelemetry/sentry-opentelemetry-extra/api/sentry-opentelemetry-extra.api @@ -7,6 +7,7 @@ public final class io/sentry/opentelemetry/OtelContextScopesStorage : io/sentry/ public fun ()V public fun close ()V public fun get ()Lio/sentry/IScopes; + public fun init ()V public fun set (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; } @@ -20,6 +21,7 @@ public final class io/sentry/opentelemetry/OtelSpanContext : io/sentry/SpanConte public final class io/sentry/opentelemetry/OtelSpanFactory : io/sentry/ISpanFactory { public fun ()V + public fun (Lio/opentelemetry/api/OpenTelemetry;)V public fun createSpan (Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/SpanContext;Lio/sentry/ISpan;)Lio/sentry/ISpan; public fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; } diff --git a/sentry-opentelemetry/sentry-opentelemetry-extra/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java b/sentry-opentelemetry/sentry-opentelemetry-extra/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java index 0ec94c3015..0be68b6305 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-extra/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java +++ b/sentry-opentelemetry/sentry-opentelemetry-extra/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java @@ -3,6 +3,7 @@ import static io.sentry.opentelemetry.SentryOtelKeys.SENTRY_SCOPES_KEY; import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextStorage; import io.opentelemetry.context.Scope; import io.sentry.IScopes; import io.sentry.IScopesStorage; @@ -15,6 +16,22 @@ @SuppressWarnings("MustBeClosedChecker") public final class OtelContextScopesStorage implements IScopesStorage { + @Override + public void init() { + System.out.println("hello from OtelContextScopesStorage init"); + /** + * 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())); + } + @Override public @NotNull ISentryLifecycleToken set(@Nullable IScopes scopes) { final Context context = Context.current(); diff --git a/sentry-opentelemetry/sentry-opentelemetry-extra/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java b/sentry-opentelemetry/sentry-opentelemetry-extra/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java index 7869f6dc96..731db6a930 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-extra/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java +++ b/sentry-opentelemetry/sentry-opentelemetry-extra/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java @@ -1,13 +1,16 @@ package io.sentry.opentelemetry; import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanBuilder; import io.opentelemetry.api.trace.TraceFlags; import io.opentelemetry.api.trace.TraceState; import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.api.trace.TracerProvider; import io.opentelemetry.context.Context; import io.sentry.Baggage; +import io.sentry.BuildConfig; import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.ISpanFactory; @@ -33,6 +36,15 @@ public final class OtelSpanFactory implements ISpanFactory { private final @NotNull SentryWeakSpanStorage storage = SentryWeakSpanStorage.getInstance(); + private final @Nullable OpenTelemetry openTelemetry; + + public OtelSpanFactory(final @Nullable OpenTelemetry openTelemetry) { + this.openTelemetry = openTelemetry; + } + + public OtelSpanFactory() { + this(null); + } @Override public @NotNull ITransaction createTransaction( @@ -145,7 +157,14 @@ public final class OtelSpanFactory implements ISpanFactory { } private @NotNull Tracer getTracer() { - return GlobalOpenTelemetry.getTracer( - "sentry-instrumentation-scope-name", "sentry-instrumentation-scope-version"); + return getTracerProvider().get("sentry-opentelemetry", BuildConfig.VERSION_NAME); + } + + private @NotNull TracerProvider getTracerProvider() { + System.out.println("hello from " + toString()); + if (openTelemetry != null) { + return openTelemetry.getTracerProvider(); + } + return GlobalOpenTelemetry.getTracerProvider(); } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-extra/src/main/java/io/sentry/opentelemetry/SentryContextStorage.java b/sentry-opentelemetry/sentry-opentelemetry-extra/src/main/java/io/sentry/opentelemetry/SentryContextStorage.java index 6ce6f88816..4f3efa40c2 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-extra/src/main/java/io/sentry/opentelemetry/SentryContextStorage.java +++ b/sentry-opentelemetry/sentry-opentelemetry-extra/src/main/java/io/sentry/opentelemetry/SentryContextStorage.java @@ -3,20 +3,15 @@ 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.NotNull; @ApiStatus.Internal public final class SentryContextStorage implements ContextStorage { - private final @NotNull Logger logger = Logger.getLogger(SentryContextStorage.class.getName()); - private final @NotNull ContextStorage contextStorage; public SentryContextStorage(final @NotNull ContextStorage contextStorage) { this.contextStorage = contextStorage; - logger.log(Level.SEVERE, "SentryContextStorage ctor called"); } @Override diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/build.gradle.kts index 15c385d357..b26d02eeff 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/build.gradle.kts @@ -52,7 +52,11 @@ dependencies { implementation(projects.sentryLogback) implementation(projects.sentryGraphql22) implementation(projects.sentryQuartz) - implementation(Config.Libs.OpenTelemetry.otelSdk) +// implementation(Config.Libs.OpenTelemetry.otelSdk) + implementation("io.opentelemetry.instrumentation:opentelemetry-spring-boot-starter") + implementation(Config.Libs.springBoot3StarterOpenTelemetry) + implementation(projects.sentryOpentelemetry.sentryOpentelemetryBootstrap) + implementation(projects.sentryOpentelemetry.sentryOpentelemetryAgentcustomization) // database query tracing implementation(projects.sentryJdbc) @@ -67,6 +71,12 @@ dependencies { testImplementation(Config.Libs.apolloKotlin) } +dependencyManagement { + imports { + mavenBom("io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom:2.7.0") + } +} + configure { test { java.srcDir("src/test/java") diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/CustomJob.java b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/CustomJob.java index cac83e6d79..4cd609d67c 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/CustomJob.java +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/CustomJob.java @@ -4,7 +4,6 @@ import io.sentry.spring.jakarta.tracing.SentryTransaction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; /** @@ -18,7 +17,7 @@ public class CustomJob { private static final Logger LOGGER = LoggerFactory.getLogger(CustomJob.class); @SentryCheckIn("monitor_slug_1") - @Scheduled(fixedRate = 3 * 60 * 1000L) + // @Scheduled(fixedRate = 3 * 60 * 1000L) void execute() throws InterruptedException { LOGGER.info("Executing scheduled job"); Thread.sleep(2000L); diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/PersonController.java b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/PersonController.java index 341555b1e0..e298b7181b 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/PersonController.java +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/PersonController.java @@ -1,8 +1,9 @@ package io.sentry.samples.spring.boot.jakarta; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.annotations.WithSpan; import io.sentry.ISpan; import io.sentry.Sentry; import org.jetbrains.annotations.NotNull; @@ -19,19 +20,27 @@ @RequestMapping("/person/") public class PersonController { private final PersonService personService; - private final Tracer tracer; + private final OpenTelemetry openTelemetry; private static final Logger LOGGER = LoggerFactory.getLogger(PersonController.class); - public PersonController(PersonService personService, Tracer tracer) { + public PersonController(PersonService personService, OpenTelemetry openTelemetry) { this.personService = personService; - this.tracer = tracer; + this.openTelemetry = openTelemetry; } @GetMapping("{id}") + @WithSpan("personSpanThroughOtelAnnotation") Person person(@PathVariable Long id) { - Span span = tracer.spanBuilder("spanCreatedThroughOtelApi").startSpan(); + ISpan annotationSpan = Sentry.getSpan(); + System.out.println(annotationSpan); + Span span = + openTelemetry + .getTracer("tracerForSpringBootDemo") + .spanBuilder("spanCreatedThroughOtelApi") + .startSpan(); try (final @NotNull Scope spanScope = span.makeCurrent()) { - ISpan sentrySpan = Sentry.getSpan().startChild("spanCreatedThroughSentryApi"); + ISpan currentSpan = Sentry.getSpan(); + ISpan sentrySpan = currentSpan.startChild("spanCreatedThroughSentryApi"); try { LOGGER.error("Trying person with id={}", id, new RuntimeException("error while loading")); throw new IllegalArgumentException("Something went wrong [id=" + id + "]"); @@ -45,7 +54,11 @@ Person person(@PathVariable Long id) { @PostMapping Person create(@RequestBody Person person) { - Span span = tracer.spanBuilder("spanCreatedThroughOtelApi").startSpan(); + Span span = + openTelemetry + .getTracer("tracerForSpringBootDemo") + .spanBuilder("spanCreatedThroughOtelApi") + .startSpan(); try (final @NotNull Scope spanScope = span.makeCurrent()) { ISpan sentrySpan = Sentry.getSpan().startChild("spanCreatedThroughSentryApi"); try { diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/SentryDemoApplication.java b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/SentryDemoApplication.java index a5d508cc13..30267a5ffd 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/SentryDemoApplication.java +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/SentryDemoApplication.java @@ -1,21 +1,10 @@ package io.sentry.samples.spring.boot.jakarta; -import static io.sentry.quartz.SentryJobListener.SENTRY_SLUG_KEY; - -import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.api.trace.Tracer; -import io.sentry.samples.spring.boot.jakarta.quartz.SampleJob; -import java.util.Collections; -import org.quartz.JobDetail; -import org.quartz.SimpleTrigger; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.quartz.CronTriggerFactoryBean; -import org.springframework.scheduling.quartz.JobDetailFactoryBean; -import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean; import org.springframework.web.client.RestClient; import org.springframework.web.client.RestTemplate; import org.springframework.web.reactive.function.client.WebClient; @@ -42,37 +31,39 @@ RestClient restClient(RestClient.Builder builder) { return builder.build(); } - @Bean - public JobDetailFactoryBean jobDetail() { - JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean(); - jobDetailFactory.setJobClass(SampleJob.class); - jobDetailFactory.setDurability(true); - jobDetailFactory.setJobDataAsMap( - Collections.singletonMap(SENTRY_SLUG_KEY, "monitor_slug_job_detail")); - return jobDetailFactory; - } - - @Bean - public SimpleTriggerFactoryBean trigger(JobDetail job) { - SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); - trigger.setJobDetail(job); - trigger.setRepeatInterval(2 * 60 * 1000); // every two minutes - trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY); - trigger.setJobDataAsMap( - Collections.singletonMap(SENTRY_SLUG_KEY, "monitor_slug_simple_trigger")); - return trigger; - } + // @Bean + // public JobDetailFactoryBean jobDetail() { + // JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean(); + // jobDetailFactory.setJobClass(SampleJob.class); + // jobDetailFactory.setDurability(true); + // jobDetailFactory.setJobDataAsMap( + // Collections.singletonMap(SENTRY_SLUG_KEY, "monitor_slug_job_detail")); + // return jobDetailFactory; + // } + // + // @Bean + // public SimpleTriggerFactoryBean trigger(JobDetail job) { + // SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); + // trigger.setJobDetail(job); + // trigger.setRepeatInterval(2 * 60 * 1000); // every two minutes + // trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY); + // trigger.setJobDataAsMap( + // Collections.singletonMap(SENTRY_SLUG_KEY, "monitor_slug_simple_trigger")); + // return trigger; + // } + // + // @Bean + // public CronTriggerFactoryBean cronTrigger(JobDetail job) { + // CronTriggerFactoryBean trigger = new CronTriggerFactoryBean(); + // trigger.setJobDetail(job); + // trigger.setCronExpression("0 0/5 * ? * *"); // every five minutes + // return trigger; + // } - @Bean - public CronTriggerFactoryBean cronTrigger(JobDetail job) { - CronTriggerFactoryBean trigger = new CronTriggerFactoryBean(); - trigger.setJobDetail(job); - trigger.setCronExpression("0 0/5 * ? * *"); // every five minutes - return trigger; - } - - @Bean - public Tracer getTracer() { - return GlobalOpenTelemetry.getTracer("tracerForSpringBootDemo"); - } + // @Bean + // public Tracer getTracer(OpenTelemetry openTelemetry) { + // GlobalOpenTelemetry.set(openTelemetry); + // return openTelemetry.getTracer("tracerForSpringBootDemo"); + //// return GlobalOpenTelemetry.getTracer("tracerForSpringBootDemo"); + // } } diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/TodoController.java b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/TodoController.java index 8d86ddcb86..cfc34d3085 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/TodoController.java +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/java/io/sentry/samples/spring/boot/jakarta/TodoController.java @@ -1,7 +1,7 @@ package io.sentry.samples.spring.boot.jakarta; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Scope; import io.sentry.ISpan; import io.sentry.Sentry; @@ -22,19 +22,26 @@ public class TodoController { private final RestTemplate restTemplate; private final WebClient webClient; private final RestClient restClient; - private final Tracer tracer; + private final OpenTelemetry openTelemetry; public TodoController( - RestTemplate restTemplate, WebClient webClient, RestClient restClient, Tracer tracer) { + RestTemplate restTemplate, + WebClient webClient, + RestClient restClient, + OpenTelemetry openTelemetry) { this.restTemplate = restTemplate; this.webClient = webClient; this.restClient = restClient; - this.tracer = tracer; + this.openTelemetry = openTelemetry; } @GetMapping("/todo/{id}") Todo todo(@PathVariable Long id) { - Span otelSpan = tracer.spanBuilder("todoSpanOtelApi").startSpan(); + Span otelSpan = + openTelemetry + .getTracer("tracerForSpringBootDemo") + .spanBuilder("todoSpanOtelApi") + .startSpan(); try (final @NotNull Scope spanScope = otelSpan.makeCurrent()) { ISpan sentrySpan = Sentry.getSpan().startChild("todoSpanSentryApi"); try { @@ -67,7 +74,11 @@ Todo todoWebClient(@PathVariable Long id) { @GetMapping("/todo-restclient/{id}") Todo todoRestClient(@PathVariable Long id) { - Span span = tracer.spanBuilder("todoRestClientSpanOtelApi").startSpan(); + Span span = + openTelemetry + .getTracer("tracerForSpringBootDemo") + .spanBuilder("todoRestClientSpanOtelApi") + .startSpan(); try (final @NotNull Scope spanScope = span.makeCurrent()) { ISpan sentrySpan = Sentry.getSpan().startChild("todoRestClientSpanSentryApi"); try { diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/resources/application.properties index e00d4c855e..5cb32a0e4e 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/resources/application.properties @@ -31,3 +31,9 @@ spring.graphql.graphiql.enabled=true spring.graphql.websocket.path=/graphql spring.quartz.job-store-type=memory +otel.propagators=tracecontext,baggage,sentry +otel.resource.attributes.deployment.environment=dev +otel.resource.attributes.service.name=cart +otel.resource.attributes.service.namespace=shop + +logging.level.org.springframework: DEBUG diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-jakarta/build.gradle.kts index b7f7357ce7..9a4f372dfe 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-jakarta/build.gradle.kts @@ -52,6 +52,7 @@ dependencies { implementation(projects.sentryLogback) implementation(projects.sentryGraphql22) implementation(projects.sentryQuartz) + implementation(Config.Libs.OpenTelemetry.otelSdk) // database query tracing implementation(projects.sentryJdbc) diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta/src/main/java/io/sentry/samples/spring/boot/jakarta/CustomJob.java b/sentry-samples/sentry-samples-spring-boot-jakarta/src/main/java/io/sentry/samples/spring/boot/jakarta/CustomJob.java index cac83e6d79..4cd609d67c 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta/src/main/java/io/sentry/samples/spring/boot/jakarta/CustomJob.java +++ b/sentry-samples/sentry-samples-spring-boot-jakarta/src/main/java/io/sentry/samples/spring/boot/jakarta/CustomJob.java @@ -4,7 +4,6 @@ import io.sentry.spring.jakarta.tracing.SentryTransaction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; /** @@ -18,7 +17,7 @@ public class CustomJob { private static final Logger LOGGER = LoggerFactory.getLogger(CustomJob.class); @SentryCheckIn("monitor_slug_1") - @Scheduled(fixedRate = 3 * 60 * 1000L) + // @Scheduled(fixedRate = 3 * 60 * 1000L) void execute() throws InterruptedException { LOGGER.info("Executing scheduled job"); Thread.sleep(2000L); diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta/src/main/java/io/sentry/samples/spring/boot/jakarta/PersonController.java b/sentry-samples/sentry-samples-spring-boot-jakarta/src/main/java/io/sentry/samples/spring/boot/jakarta/PersonController.java index fc43c0a5e7..89ca301f76 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta/src/main/java/io/sentry/samples/spring/boot/jakarta/PersonController.java +++ b/sentry-samples/sentry-samples-spring-boot-jakarta/src/main/java/io/sentry/samples/spring/boot/jakarta/PersonController.java @@ -1,5 +1,11 @@ package io.sentry.samples.spring.boot.jakarta; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Scope; +import io.sentry.ISpan; +import io.sentry.Sentry; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; @@ -13,20 +19,49 @@ @RequestMapping("/person/") public class PersonController { private final PersonService personService; + private final Tracer tracer; private static final Logger LOGGER = LoggerFactory.getLogger(PersonController.class); - public PersonController(PersonService personService) { + public PersonController(PersonService personService, Tracer tracer) { this.personService = personService; + this.tracer = tracer; } @GetMapping("{id}") + // @WithSpan("personSpanThroughOtelAnnotation") Person person(@PathVariable Long id) { - LOGGER.error("Trying person with id={}", id, new RuntimeException("error while loading")); - throw new IllegalArgumentException("Something went wrong [id=" + id + "]"); + ISpan annotationSpan = Sentry.getSpan(); + System.out.println(annotationSpan); + Span span = tracer.spanBuilder("spanCreatedThroughOtelApi").startSpan(); + try (final @NotNull Scope spanScope = span.makeCurrent()) { + ISpan currentSpan = Sentry.getSpan(); + ISpan sentrySpan = currentSpan.startChild("spanCreatedThroughSentryApi"); + try { + LOGGER.error("Trying person with id={}", id, new RuntimeException("error while loading")); + throw new IllegalArgumentException("Something went wrong [id=" + id + "]"); + } finally { + sentrySpan.finish(); + } + } catch (Throwable t) { + LOGGER.error("creating span failed", t); + return null; + } finally { + span.end(); + } } @PostMapping Person create(@RequestBody Person person) { - return personService.create(person); + Span span = tracer.spanBuilder("spanCreatedThroughOtelApi").startSpan(); + try (final @NotNull Scope spanScope = span.makeCurrent()) { + ISpan sentrySpan = Sentry.getSpan().startChild("spanCreatedThroughSentryApi"); + try { + return personService.create(person); + } finally { + sentrySpan.finish(); + } + } finally { + span.end(); + } } } diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta/src/main/java/io/sentry/samples/spring/boot/jakarta/SentryDemoApplication.java b/sentry-samples/sentry-samples-spring-boot-jakarta/src/main/java/io/sentry/samples/spring/boot/jakarta/SentryDemoApplication.java index 8050cb8e74..3adb26eb74 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta/src/main/java/io/sentry/samples/spring/boot/jakarta/SentryDemoApplication.java +++ b/sentry-samples/sentry-samples-spring-boot-jakarta/src/main/java/io/sentry/samples/spring/boot/jakarta/SentryDemoApplication.java @@ -1,19 +1,12 @@ package io.sentry.samples.spring.boot.jakarta; -import static io.sentry.quartz.SentryJobListener.SENTRY_SLUG_KEY; - -import io.sentry.samples.spring.boot.jakarta.quartz.SampleJob; -import java.util.Collections; -import org.quartz.JobDetail; -import org.quartz.SimpleTrigger; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Tracer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.quartz.CronTriggerFactoryBean; -import org.springframework.scheduling.quartz.JobDetailFactoryBean; -import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean; import org.springframework.web.client.RestClient; import org.springframework.web.client.RestTemplate; import org.springframework.web.reactive.function.client.WebClient; @@ -40,32 +33,37 @@ RestClient restClient(RestClient.Builder builder) { return builder.build(); } - @Bean - public JobDetailFactoryBean jobDetail() { - JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean(); - jobDetailFactory.setJobClass(SampleJob.class); - jobDetailFactory.setDurability(true); - jobDetailFactory.setJobDataAsMap( - Collections.singletonMap(SENTRY_SLUG_KEY, "monitor_slug_job_detail")); - return jobDetailFactory; - } - - @Bean - public SimpleTriggerFactoryBean trigger(JobDetail job) { - SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); - trigger.setJobDetail(job); - trigger.setRepeatInterval(2 * 60 * 1000); // every two minutes - trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY); - trigger.setJobDataAsMap( - Collections.singletonMap(SENTRY_SLUG_KEY, "monitor_slug_simple_trigger")); - return trigger; - } + // @Bean + // public JobDetailFactoryBean jobDetail() { + // JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean(); + // jobDetailFactory.setJobClass(SampleJob.class); + // jobDetailFactory.setDurability(true); + // jobDetailFactory.setJobDataAsMap( + // Collections.singletonMap(SENTRY_SLUG_KEY, "monitor_slug_job_detail")); + // return jobDetailFactory; + // } + // + // @Bean + // public SimpleTriggerFactoryBean trigger(JobDetail job) { + // SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); + // trigger.setJobDetail(job); + // trigger.setRepeatInterval(2 * 60 * 1000); // every two minutes + // trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY); + // trigger.setJobDataAsMap( + // Collections.singletonMap(SENTRY_SLUG_KEY, "monitor_slug_simple_trigger")); + // return trigger; + // } + // + // @Bean + // public CronTriggerFactoryBean cronTrigger(JobDetail job) { + // CronTriggerFactoryBean trigger = new CronTriggerFactoryBean(); + // trigger.setJobDetail(job); + // trigger.setCronExpression("0 0/5 * ? * *"); // every five minutes + // return trigger; + // } @Bean - public CronTriggerFactoryBean cronTrigger(JobDetail job) { - CronTriggerFactoryBean trigger = new CronTriggerFactoryBean(); - trigger.setJobDetail(job); - trigger.setCronExpression("0 0/5 * ? * *"); // every five minutes - return trigger; + public Tracer otelTracer() { + return GlobalOpenTelemetry.getTracer("demoTracer"); } } diff --git a/sentry-spring-boot-jakarta/build.gradle.kts b/sentry-spring-boot-jakarta/build.gradle.kts index b99ee1bb69..262d4577ba 100644 --- a/sentry-spring-boot-jakarta/build.gradle.kts +++ b/sentry-spring-boot-jakarta/build.gradle.kts @@ -41,7 +41,9 @@ dependencies { compileOnly(Config.Libs.springBoot3StarterQuartz) compileOnly(Config.Libs.reactorCore) compileOnly(Config.Libs.contextPropagation) + compileOnly(Config.Libs.OpenTelemetry.otelSdk) compileOnly(projects.sentryOpentelemetry.sentryOpentelemetryCore) + compileOnly(projects.sentryOpentelemetry.sentryOpentelemetryAgentcustomization) annotationProcessor(platform(SpringBootPlugin.BOM_COORDINATES)) annotationProcessor(Config.AnnotationProcessors.springBootAutoConfigure) diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java index c80d9b5d45..5a453d643e 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java @@ -4,6 +4,7 @@ import graphql.GraphQLError; import io.sentry.EventProcessor; import io.sentry.IScopes; +import io.sentry.ISpanFactory; import io.sentry.ITransportFactory; import io.sentry.InitPriority; import io.sentry.Integration; @@ -28,6 +29,7 @@ import io.sentry.spring.jakarta.checkin.SentryQuartzConfiguration; import io.sentry.spring.jakarta.exception.SentryCaptureExceptionParameterPointcutConfiguration; import io.sentry.spring.jakarta.exception.SentryExceptionParameterAdviceConfiguration; +import io.sentry.spring.jakarta.opentelemetry.SentryOpenTelemetryConfiguration; import io.sentry.spring.jakarta.tracing.SentryAdviceConfiguration; import io.sentry.spring.jakarta.tracing.SentrySpanPointcutConfiguration; import io.sentry.spring.jakarta.tracing.SentryTracingFilter; @@ -117,10 +119,21 @@ static class HubConfiguration { return new InAppIncludesResolver(); } + // TODO ensure agent (auto-init) is not active + @Configuration(proxyBeanMethods = false) + @Import(SentryOpenTelemetryConfiguration.class) + @Open + @ConditionalOnClass({ + io.opentelemetry.api.OpenTelemetry.class, + io.sentry.opentelemetry.SentryAutoConfigurationCustomizerProvider.class + }) + static class OpenTelemetryConfiguration {} + @Bean public @NotNull IScopes sentryHub( final @NotNull List> optionsConfigurations, final @NotNull SentryProperties options, + final @NotNull ObjectProvider spanFactory, final @NotNull ObjectProvider gitProperties) { optionsConfigurations.forEach( optionsConfiguration -> optionsConfiguration.configure(options)); @@ -130,6 +143,7 @@ static class HubConfiguration { options.setRelease(git.getCommitId()); } }); + spanFactory.ifAvailable(options::setSpanFactory); options.setSentryClientName( BuildConfig.SENTRY_SPRING_BOOT_JAKARTA_SDK_NAME + "/" + BuildConfig.VERSION_NAME); diff --git a/sentry-spring-jakarta/api/sentry-spring-jakarta.api b/sentry-spring-jakarta/api/sentry-spring-jakarta.api index c8ba3ffb80..142a301378 100644 --- a/sentry-spring-jakarta/api/sentry-spring-jakarta.api +++ b/sentry-spring-jakarta/api/sentry-spring-jakarta.api @@ -202,6 +202,12 @@ public final class io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHand public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IScopes;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; } +public class io/sentry/spring/jakarta/opentelemetry/SentryOpenTelemetryConfiguration { + public fun ()V + public static fun openTelemetrySpanFactory (Lio/opentelemetry/api/OpenTelemetry;)Lio/sentry/ISpanFactory; + public fun sentryOpenTelemetryOptionsConfiguration ()Lio/sentry/Sentry$OptionsConfiguration; +} + public class io/sentry/spring/jakarta/tracing/SentryAdviceConfiguration { public fun ()V public fun sentrySpanAdvice ()Lorg/aopalliance/aop/Advice; diff --git a/sentry-spring-jakarta/build.gradle.kts b/sentry-spring-jakarta/build.gradle.kts index 5f200de907..348eb16e79 100644 --- a/sentry-spring-jakarta/build.gradle.kts +++ b/sentry-spring-jakarta/build.gradle.kts @@ -35,6 +35,7 @@ dependencies { compileOnly(Config.Libs.servletApiJakarta) compileOnly(Config.Libs.slf4jApi) compileOnly(Config.Libs.contextPropagation) + compileOnly(Config.Libs.OpenTelemetry.otelSdk) compileOnly(Config.Libs.springWebflux) @@ -46,6 +47,8 @@ dependencies { compileOnly(projects.sentryGraphql) compileOnly(projects.sentryGraphql22) compileOnly(projects.sentryQuartz) + compileOnly(projects.sentryOpentelemetry.sentryOpentelemetryAgentcustomization) + compileOnly(projects.sentryOpentelemetry.sentryOpentelemetryExtra) // tests testImplementation(projects.sentryTestSupport) diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/opentelemetry/SentryOpenTelemetryConfiguration.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/opentelemetry/SentryOpenTelemetryConfiguration.java new file mode 100644 index 0000000000..5c553a3326 --- /dev/null +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/opentelemetry/SentryOpenTelemetryConfiguration.java @@ -0,0 +1,36 @@ +package io.sentry.spring.jakarta.opentelemetry; + +import com.jakewharton.nopen.annotation.Open; +import io.opentelemetry.api.OpenTelemetry; +import io.sentry.ISpanFactory; +import io.sentry.Sentry; +import io.sentry.SentryOptions; +import io.sentry.opentelemetry.OpenTelemetryUtil; +import io.sentry.opentelemetry.OtelSpanFactory; +import io.sentry.opentelemetry.SentryAutoConfigurationCustomizerProvider; +import org.jetbrains.annotations.NotNull; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +@Open +public class SentryOpenTelemetryConfiguration { + + @Bean + @ConditionalOnMissingBean + public static ISpanFactory openTelemetrySpanFactory(OpenTelemetry openTelemetry) { + return new OtelSpanFactory(openTelemetry); + } + + @Bean + @ConditionalOnMissingBean(name = "sentryOpenTelemetryOptionsConfiguration") + public @NotNull Sentry.OptionsConfiguration + sentryOpenTelemetryOptionsConfiguration() { + return options -> { + SentryAutoConfigurationCustomizerProvider.skipInit = true; + // TODO set integration + OpenTelemetryUtil.applyOpenTelemetryOptions(options); + }; + } +} diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 57bb67df6a..0d64a96d91 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -371,6 +371,7 @@ public final class io/sentry/DefaultScopesStorage : io/sentry/IScopesStorage { public fun ()V public fun close ()V public fun get ()Lio/sentry/IScopes; + public fun init ()V public fun set (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; } @@ -909,6 +910,7 @@ public abstract interface class io/sentry/IScopes { public abstract interface class io/sentry/IScopesStorage { public abstract fun close ()V public abstract fun get ()Lio/sentry/IScopes; + public abstract fun init ()V public abstract fun set (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; } @@ -1592,6 +1594,7 @@ public final class io/sentry/NoOpScopesStorage : io/sentry/IScopesStorage { public fun close ()V public fun get ()Lio/sentry/IScopes; public static fun getInstance ()Lio/sentry/NoOpScopesStorage; + public fun init ()V public fun set (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; } diff --git a/sentry/src/main/java/io/sentry/DefaultScopesStorage.java b/sentry/src/main/java/io/sentry/DefaultScopesStorage.java index 6884370c9f..ba2cbfb811 100644 --- a/sentry/src/main/java/io/sentry/DefaultScopesStorage.java +++ b/sentry/src/main/java/io/sentry/DefaultScopesStorage.java @@ -9,6 +9,9 @@ public final class DefaultScopesStorage implements IScopesStorage { private static final @NotNull ThreadLocal currentScopes = new ThreadLocal<>(); + @Override + public void init() {} + @Override public ISentryLifecycleToken set(@Nullable IScopes scopes) { final @Nullable IScopes oldScopes = get(); diff --git a/sentry/src/main/java/io/sentry/IScopesStorage.java b/sentry/src/main/java/io/sentry/IScopesStorage.java index 394510a528..acf2b034d7 100644 --- a/sentry/src/main/java/io/sentry/IScopesStorage.java +++ b/sentry/src/main/java/io/sentry/IScopesStorage.java @@ -7,6 +7,8 @@ @ApiStatus.Internal public interface IScopesStorage { + void init(); + @NotNull ISentryLifecycleToken set(final @Nullable IScopes scopes); diff --git a/sentry/src/main/java/io/sentry/NoOpScopesStorage.java b/sentry/src/main/java/io/sentry/NoOpScopesStorage.java index 9e2d7f82d6..9e9064fe3e 100644 --- a/sentry/src/main/java/io/sentry/NoOpScopesStorage.java +++ b/sentry/src/main/java/io/sentry/NoOpScopesStorage.java @@ -11,6 +11,9 @@ public static NoOpScopesStorage getInstance() { return instance; } + @Override + public void init() {} + @Override public ISentryLifecycleToken set(@Nullable IScopes scopes) { return NoOpScopesLifecycleToken.getInstance(); diff --git a/sentry/src/main/java/io/sentry/ScopesStorageFactory.java b/sentry/src/main/java/io/sentry/ScopesStorageFactory.java index ab136b2ac9..153af101b3 100644 --- a/sentry/src/main/java/io/sentry/ScopesStorageFactory.java +++ b/sentry/src/main/java/io/sentry/ScopesStorageFactory.java @@ -14,6 +14,13 @@ public final class ScopesStorageFactory { public static @NotNull IScopesStorage create( final @NotNull LoadClass loadClass, final @NotNull ILogger logger) { + final @NotNull IScopesStorage storage = createInternal(loadClass, logger); + storage.init(); + return storage; + } + + private static @NotNull IScopesStorage createInternal( + final @NotNull LoadClass loadClass, final @NotNull ILogger logger) { if (loadClass.isClassAvailable(OTEL_SCOPES_STORAGE, logger)) { Class otelScopesStorageClazz = loadClass.loadClass(OTEL_SCOPES_STORAGE, logger); if (otelScopesStorageClazz != null) {