diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d96e5c82d8..60e0cb9e6ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,15 @@ - Fix `ClassNotFoundException` for `io.sentry.spring.SentrySpringServletContainerInitializer` in `sentry-spring-jakarta` ([#2411](https://github.com/getsentry/sentry-java/issues/2411)) - Fix `sentry-samples-spring-jakarta` ([#2411](https://github.com/getsentry/sentry-java/issues/2411)) +### 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)) + +### Dependencies + +- Bump OpenTelemetry to 1.20.1 and OpenTelemetry Java Agent to 1.20.2 ([#2420](https://github.com/getsentry/sentry-java/pull/2420)) + ## 6.9.1 ### Fixes diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index ccfc0249ecd..3f15e428244 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -135,10 +135,19 @@ object Config { val apolloKotlin = "com.apollographql.apollo3:apollo-runtime:3.3.0" - val otelVersion = "1.19.0" - val otelAlphaVersion = "1.19.0-alpha" - val otelJavaagentVersion = "1.19.2" - val otelJavaagentAlphaVersion = "1.19.2-alpha" + object OpenTelemetry { + val otelVersion = "1.20.1" + val otelAlphaVersion = "1.20.1-alpha" + val otelJavaagentVersion = "1.20.2" + val otelJavaagentAlphaVersion = "1.20.2-alpha" + + val otelSdk = "io.opentelemetry:opentelemetry-sdk:$otelVersion" + val otelSemconv = "io.opentelemetry:opentelemetry-semconv:$otelAlphaVersion" + val otelJavaAgent = "io.opentelemetry.javaagent:opentelemetry-javaagent:$otelJavaagentVersion" + val otelJavaAgentExtensionApi = "io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api:$otelJavaagentAlphaVersion" + val otelJavaAgentTooling = "io.opentelemetry.javaagent:opentelemetry-javaagent-tooling:$otelJavaagentAlphaVersion" + val otelExtensionAutoconfigureSpi = "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:$otelVersion" + } } object AnnotationProcessors { diff --git a/sentry-opentelemetry/sentry-opentelemetry-agent/README.md b/sentry-opentelemetry/sentry-opentelemetry-agent/README.md index 0cb681aba62..92ddb89a1c4 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agent/README.md +++ b/sentry-opentelemetry/sentry-opentelemetry-agent/README.md @@ -38,6 +38,13 @@ Sentry.init( Using the `otel` instrumenter will ensure `Sentry` instrumentation will be done via OpenTelemetry and integrations as well as direct interactions with transactions and spans have no effect. +## Controlling auto initialization of Sentry + +By default if you pass either `SENTRY_DSN` or `SENTRY_PROPERTIES_FILE` as environment variable, +Sentry will automatically be initialized by this agent. To disable this behaviour, you can set +`SENTRY_AUTO_INIT=false` as environment variable. You will then have to initialize Sentry inside +the target application. + ## Debugging To enable debug logging for Sentry, please provide `SENTRY_DEBUG=true` as environment variable or diff --git a/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts index 495e200f248..80b68430db2 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts @@ -14,8 +14,12 @@ fun relocatePackages(shadowJar: ShadowJar) { // rewrite dependencies calling Logger.getLogger shadowJar.relocate("java.util.logging.Logger", "io.opentelemetry.javaagent.bootstrap.PatchLogger") - // rewrite library instrumentation dependencies - shadowJar.relocate("io.opentelemetry.instrumentation", "io.opentelemetry.javaagent.shaded.instrumentation") + // prevents conflict with library instrumentation, since these classes live in the bootstrap class loader + shadowJar.relocate("io.opentelemetry.instrumentation", "io.opentelemetry.javaagent.shaded.instrumentation") { + // Exclude resource providers since they live in the agent class loader + exclude("io.opentelemetry.instrumentation.resources.*") + exclude("io.opentelemetry.instrumentation.spring.resources.*") + } // relocate OpenTelemetry API usage shadowJar.relocate("io.opentelemetry.api", "io.opentelemetry.javaagent.shaded.io.opentelemetry.api") @@ -50,7 +54,7 @@ val upstreamAgent = configurations.create("upstreamAgent") { dependencies { bootstrapLibs(projects.sentry) javaagentLibs(projects.sentryOpentelemetry.sentryOpentelemetryAgentcustomization) - upstreamAgent("io.opentelemetry.javaagent:opentelemetry-javaagent:${Config.Libs.otelJavaagentVersion}") + upstreamAgent(Config.Libs.OpenTelemetry.otelJavaAgent) } fun isolateClasses(jars: Iterable): CopySpec { @@ -146,11 +150,11 @@ tasks { attributes.put("Can-Redefine-Classes", "true") attributes.put("Can-Retransform-Classes", "true") attributes.put("Implementation-Vendor", "Sentry") - attributes.put("Implementation-Version", "sentry-${project.version}-otel-${Config.Libs.otelJavaagentVersion}") + attributes.put("Implementation-Version", "sentry-${project.version}-otel-${Config.Libs.OpenTelemetry.otelJavaagentVersion}") attributes.put("Sentry-Version-Name", project.version) attributes.put("Sentry-Opentelemetry-SDK-Name", Config.Sentry.SENTRY_OPENTELEMETRY_AGENT_SDK_NAME) - attributes.put("Sentry-Opentelemetry-Version-Name", Config.Libs.otelVersion) - attributes.put("Sentry-Opentelemetry-Javaagent-Version-Name", Config.Libs.otelJavaagentVersion) + attributes.put("Sentry-Opentelemetry-Version-Name", Config.Libs.OpenTelemetry.otelVersion) + attributes.put("Sentry-Opentelemetry-Javaagent-Version-Name", Config.Libs.OpenTelemetry.otelJavaagentVersion) } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts index 0b0dc08ca1e..a9b7f3666fb 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts @@ -22,10 +22,10 @@ dependencies { compileOnly(projects.sentry) implementation(projects.sentryOpentelemetry.sentryOpentelemetryCore) - compileOnly("io.opentelemetry:opentelemetry-sdk:${Config.Libs.otelVersion}") - compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:${Config.Libs.otelVersion}") - compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api:${Config.Libs.otelJavaagentAlphaVersion}") - compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent-tooling:${Config.Libs.otelJavaagentAlphaVersion}") + compileOnly(Config.Libs.OpenTelemetry.otelSdk) + compileOnly(Config.Libs.OpenTelemetry.otelExtensionAutoconfigureSpi) + compileOnly(Config.Libs.OpenTelemetry.otelJavaAgentExtensionApi) + compileOnly(Config.Libs.OpenTelemetry.otelJavaAgentTooling) compileOnly(Config.CompileOnly.nopen) errorprone(Config.CompileOnly.nopenChecker) 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 0f2e4db9760..86c5bab31b9 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 @@ -23,14 +23,12 @@ public final class SentryAutoConfigurationCustomizerProvider @Override public void customize(AutoConfigurationCustomizer autoConfiguration) { - final @Nullable String sentryPropertiesFile = System.getenv("SENTRY_PROPERTIES_FILE"); - final @Nullable String sentryDsn = System.getenv("SENTRY_DSN"); - - if (sentryPropertiesFile != null || sentryDsn != null) { + if (isSentryAutoInitEnabled()) { Sentry.init( options -> { options.setEnableExternalConfiguration(true); options.setInstrumenter(Instrumenter.OTEL); + options.addEventProcessor(new OpenTelemetryLinkErrorEventProcessor()); final @Nullable SdkVersion sdkVersion = createSdkVersion(options); if (sdkVersion != null) { options.setSdkVersion(sdkVersion); @@ -43,6 +41,19 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) { .addPropertiesSupplier(this::getDefaultProperties); } + private boolean isSentryAutoInitEnabled() { + final @Nullable String sentryAutoInit = System.getenv("SENTRY_AUTO_INIT"); + + if (sentryAutoInit != null) { + return "true".equalsIgnoreCase(sentryAutoInit); + } else { + final @Nullable String sentryPropertiesFile = System.getenv("SENTRY_PROPERTIES_FILE"); + final @Nullable String sentryDsn = System.getenv("SENTRY_DSN"); + + return sentryPropertiesFile != null || sentryDsn != null; + } + } + private @Nullable SdkVersion createSdkVersion(final @NotNull SentryOptions sentryOptions) { SdkVersion sdkVersion = sentryOptions.getSdkVersion(); 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 2347a99db77..18c73a9b689 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/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-core/build.gradle.kts index c9c248ee01e..c8db1650b28 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-core/build.gradle.kts @@ -21,8 +21,8 @@ tasks.withType().configureEach { dependencies { compileOnly(projects.sentry) - compileOnly("io.opentelemetry:opentelemetry-sdk:${Config.Libs.otelVersion}") - compileOnly("io.opentelemetry:opentelemetry-semconv:${Config.Libs.otelAlphaVersion}") + compileOnly(Config.Libs.OpenTelemetry.otelSdk) + compileOnly(Config.Libs.OpenTelemetry.otelSemconv) compileOnly(Config.CompileOnly.nopen) errorprone(Config.CompileOnly.nopenChecker) @@ -37,8 +37,8 @@ dependencies { testImplementation(Config.TestLibs.mockitoKotlin) testImplementation(Config.TestLibs.awaitility) - testImplementation("io.opentelemetry:opentelemetry-sdk:${Config.Libs.otelVersion}") - testImplementation("io.opentelemetry:opentelemetry-semconv:${Config.Libs.otelAlphaVersion}") + testImplementation(Config.Libs.OpenTelemetry.otelSdk) + testImplementation(Config.Libs.OpenTelemetry.otelSemconv) } configure { 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 00000000000..f01d9ec3ed9 --- /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 85ba4b3b304..f82f747c85b 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 0daa6cab649..fba597a48c8 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 6993f1f6b2a..85f6c240224 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 9898fc5c4c0..b260f06e5c0 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;