diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index 4141ad9837d..bbacd3150c7 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -200,6 +200,7 @@ object Config { val SENTRY_SPRING_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.spring" val SENTRY_SPRING_BOOT_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.spring-boot" val SENTRY_SPRING_BOOT_JAKARTA_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.spring-boot.jakarta" + val SENTRY_OPENTELEMETRY_AGENT_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.opentelemetry.agent" val group = "io.sentry" val description = "SDK for sentry.io" val versionNameProp = "versionName" diff --git a/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts index 32f3bf1f84c..495e200f248 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts @@ -147,6 +147,10 @@ tasks { attributes.put("Can-Retransform-Classes", "true") attributes.put("Implementation-Vendor", "Sentry") attributes.put("Implementation-Version", "sentry-${project.version}-otel-${Config.Libs.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) } } 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 fe51dd65e83..9058ee78550 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 @@ -4,19 +4,87 @@ import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; +import io.sentry.Instrumenter; +import io.sentry.Sentry; +import io.sentry.SentryOptions; +import io.sentry.protocol.SdkVersion; +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; import java.util.HashMap; import java.util.Map; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public final class SentryAutoConfigurationCustomizerProvider implements AutoConfigurationCustomizerProvider { @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) { + Sentry.init( + options -> { + options.setEnableExternalConfiguration(true); + options.setInstrumenter(Instrumenter.OTEL); + final @Nullable SdkVersion sdkVersion = createSdkVersion(options); + if (sdkVersion != null) { + options.setSdkVersion(sdkVersion); + } + }); + } + autoConfiguration .addTracerProviderCustomizer(this::configureSdkTracerProvider) .addPropertiesSupplier(this::getDefaultProperties); } + private @Nullable SdkVersion createSdkVersion(final @NotNull SentryOptions sentryOptions) { + SdkVersion sdkVersion = sentryOptions.getSdkVersion(); + + try { + final @NotNull Enumeration resources = + ClassLoader.getSystemClassLoader().getResources("META-INF/MANIFEST.MF"); + while (resources.hasMoreElements()) { + try { + final @NotNull Manifest manifest = new Manifest(resources.nextElement().openStream()); + final @Nullable Attributes mainAttributes = manifest.getMainAttributes(); + if (mainAttributes != null) { + final @Nullable String name = mainAttributes.getValue("Sentry-Opentelemetry-SDK-Name"); + final @Nullable String version = mainAttributes.getValue("Sentry-Version-Name"); + + if (name != null && version != null) { + sdkVersion = SdkVersion.updateSdkVersion(sdkVersion, name, version); + sdkVersion.addPackage("maven:io.sentry:sentry-opentelemetry-agent", version); + final @Nullable String otelVersion = + mainAttributes.getValue("Sentry-Opentelemetry-Version-Name"); + if (otelVersion != null) { + sdkVersion.addPackage("maven:io.opentelemetry:opentelemetry-sdk", otelVersion); + } + final @Nullable String otelJavaagentVersion = + mainAttributes.getValue("Sentry-Opentelemetry-Javaagent-Version-Name"); + if (otelJavaagentVersion != null) { + sdkVersion.addPackage( + "maven:io.opentelemetry.javaagent:opentelemetry-javaagent", + otelJavaagentVersion); + } + } + } + } catch (Exception e) { + // ignore + } + } + } catch (IOException e) { + // ignore + } + + return sdkVersion; + } + private SdkTracerProviderBuilder configureSdkTracerProvider( SdkTracerProviderBuilder tracerProvider, ConfigProperties config) { return tracerProvider.addSpanProcessor(new SentrySpanProcessor()); 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 72e0e489075..85ba4b3b304 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 @@ -75,10 +75,8 @@ public Context extract( Context modifiedContext = context.with(SentryOtelKeys.SENTRY_TRACE_KEY, sentryTraceHeader); final @Nullable String baggageString = getter.get(carrier, BaggageHeader.BAGGAGE_HEADER); - if (baggageString != null) { - Baggage baggage = Baggage.fromHeader(baggageString); - modifiedContext = modifiedContext.with(SentryOtelKeys.SENTRY_BAGGAGE_KEY, baggage); - } + Baggage baggage = Baggage.fromHeader(baggageString); + modifiedContext = modifiedContext.with(SentryOtelKeys.SENTRY_BAGGAGE_KEY, baggage); Span wrappedSpan = Span.wrap(otelSpanContext); modifiedContext = modifiedContext.with(wrappedSpan); diff --git a/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/CustomEventProcessor.java b/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/CustomEventProcessor.java deleted file mode 100644 index b74310a6761..00000000000 --- a/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/CustomEventProcessor.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.sentry.samples.spring.boot; - -import io.sentry.EventProcessor; -import io.sentry.Hint; -import io.sentry.SentryEvent; -import io.sentry.protocol.SentryRuntime; -import org.jetbrains.annotations.NotNull; -import org.springframework.boot.SpringBootVersion; -import org.springframework.stereotype.Component; - -/** - * Custom {@link EventProcessor} implementation lets modifying {@link SentryEvent}s before they are - * sent to Sentry. - */ -@Component -public class CustomEventProcessor implements EventProcessor { - private final String springBootVersion; - - public CustomEventProcessor(String springBootVersion) { - this.springBootVersion = springBootVersion; - } - - public CustomEventProcessor() { - this(SpringBootVersion.getVersion()); - } - - @Override - public @NotNull SentryEvent process(@NotNull SentryEvent event, @NotNull Hint hint) { - final SentryRuntime runtime = new SentryRuntime(); - runtime.setVersion(springBootVersion); - runtime.setName("Spring Boot"); - event.getContexts().setRuntime(runtime); - return event; - } -} diff --git a/sentry/src/main/java/io/sentry/Baggage.java b/sentry/src/main/java/io/sentry/Baggage.java index aca3fdf0fd9..283274d4ded 100644 --- a/sentry/src/main/java/io/sentry/Baggage.java +++ b/sentry/src/main/java/io/sentry/Baggage.java @@ -35,7 +35,7 @@ public final class Baggage { final @NotNull ILogger logger; @NotNull - public static Baggage fromHeader(final String headerValue) { + public static Baggage fromHeader(final @Nullable String headerValue) { return Baggage.fromHeader( headerValue, false, HubAdapter.getInstance().getOptions().getLogger()); } diff --git a/sentry/src/main/java/io/sentry/config/ClasspathPropertiesLoader.java b/sentry/src/main/java/io/sentry/config/ClasspathPropertiesLoader.java index 56b2ce83c31..1a730e8f164 100644 --- a/sentry/src/main/java/io/sentry/config/ClasspathPropertiesLoader.java +++ b/sentry/src/main/java/io/sentry/config/ClasspathPropertiesLoader.java @@ -16,14 +16,18 @@ final class ClasspathPropertiesLoader implements PropertiesLoader { private final @NotNull ILogger logger; public ClasspathPropertiesLoader( - @NotNull String fileName, @NotNull ClassLoader classLoader, @NotNull ILogger logger) { + @NotNull String fileName, @Nullable ClassLoader classLoader, @NotNull ILogger logger) { this.fileName = fileName; - this.classLoader = classLoader; + // bootstrap classloader is represented as null, so using system classloader instead + if (classLoader == null) { + this.classLoader = ClassLoader.getSystemClassLoader(); + } else { + this.classLoader = classLoader; + } this.logger = logger; } public ClasspathPropertiesLoader(@NotNull ILogger logger) { - // TODO check not null this("sentry.properties", ClasspathPropertiesLoader.class.getClassLoader(), logger); } diff --git a/sentry/src/test/java/io/sentry/SentryTracerTest.kt b/sentry/src/test/java/io/sentry/SentryTracerTest.kt index 5f0b9f2dd20..fed5bcadc67 100644 --- a/sentry/src/test/java/io/sentry/SentryTracerTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTracerTest.kt @@ -10,6 +10,8 @@ import org.mockito.kotlin.never import org.mockito.kotlin.spy import org.mockito.kotlin.times import org.mockito.kotlin.verify +import java.time.LocalDateTime +import java.time.ZoneOffset import java.util.Date import kotlin.test.Test import kotlin.test.assertEquals @@ -119,6 +121,15 @@ class SentryTracerTest { assertEquals(SpanStatus.ABORTED, tracer.status) } + @Test + fun `when transaction is finished with status and timestamp, timestamp and status are set`() { + val tracer = fixture.getSut() + val date = Date.from(LocalDateTime.of(2022, 12, 24, 23, 59, 58, 0).toInstant(ZoneOffset.UTC)) + tracer.finish(SpanStatus.ABORTED, date) + assertEquals(tracer.timestamp, DateUtils.dateToSeconds(date)) + assertEquals(SpanStatus.ABORTED, tracer.status) + } + @Test fun `when transaction is finished, transaction is captured`() { val tracer = fixture.getSut()