From b8854feb2dfce5ece9396ec6ba305ec8a2d086af Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Tue, 28 Sep 2021 13:54:54 +0200 Subject: [PATCH] Feat: Attach Java vendor and version to events and transactions. (#1703) --- CHANGELOG.md | 2 + .../spring/boot/CustomEventProcessor.java | 15 ++--- sentry/api/sentry.api | 6 ++ .../main/java/io/sentry/SentryOptions.java | 5 ++ .../sentry/SentryRuntimeEventProcessor.java | 46 +++++++++++++ .../main/java/io/sentry/util/Platform.java | 28 ++++++++ .../sentry/SentryRuntimeEventProcessorTest.kt | 66 +++++++++++++++++++ 7 files changed, 160 insertions(+), 8 deletions(-) create mode 100644 sentry/src/main/java/io/sentry/SentryRuntimeEventProcessor.java create mode 100644 sentry/src/main/java/io/sentry/util/Platform.java create mode 100644 sentry/src/test/java/io/sentry/SentryRuntimeEventProcessorTest.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 864f4a5121..7086207b01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +* Feat: Attach Java vendor and version to events and transactions (#1703) + ## 5.2.0 * Feat: Allow setting proguard via Options and/or external resources (#1728) 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 index 7a52dc7d2e..2b11d4406e 100644 --- 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 @@ -5,6 +5,7 @@ import io.sentry.protocol.SentryRuntime; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.springframework.boot.SpringBootVersion; import org.springframework.stereotype.Component; /** @@ -13,23 +14,21 @@ */ @Component public class CustomEventProcessor implements EventProcessor { - private final String javaVersion; - private final String javaVendor; + private final String springBootVersion; - public CustomEventProcessor(String javaVersion, String javaVendor) { - this.javaVersion = javaVersion; - this.javaVendor = javaVendor; + public CustomEventProcessor(String springBootVersion) { + this.springBootVersion = springBootVersion; } public CustomEventProcessor() { - this(System.getProperty("java.version"), System.getProperty("java.vendor")); + this(SpringBootVersion.getVersion()); } @Override public @NotNull SentryEvent process(@NotNull SentryEvent event, @Nullable Object hint) { final SentryRuntime runtime = new SentryRuntime(); - runtime.setVersion(javaVersion); - runtime.setName(javaVendor); + runtime.setVersion(springBootVersion); + runtime.setName("Spring Boot"); event.getContexts().setRuntime(runtime); return event; } diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index db06a9c8ca..fc20cf5d61 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -1987,6 +1987,12 @@ public final class io/sentry/util/Pair { public fun getSecond ()Ljava/lang/Object; } +public final class io/sentry/util/Platform { + public fun ()V + public static fun isAndroid ()Z + public static fun isJvm ()Z +} + public final class io/sentry/util/StringUtils { public static fun capitalize (Ljava/lang/String;)Ljava/lang/String; public static fun getStringAfterDot (Ljava/lang/String;)Ljava/lang/String; diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 3048010d26..5c83ca633a 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -7,6 +7,7 @@ import io.sentry.transport.ITransportGate; import io.sentry.transport.NoOpEnvelopeCache; import io.sentry.transport.NoOpTransportGate; +import io.sentry.util.Platform; import java.io.File; import java.util.ArrayList; import java.util.HashMap; @@ -1584,6 +1585,10 @@ private SentryOptions(final boolean empty) { eventProcessors.add(new MainEventProcessor(this)); eventProcessors.add(new DuplicateEventDetectionEventProcessor(this)); + if (Platform.isJvm()) { + eventProcessors.add(new SentryRuntimeEventProcessor()); + } + setSentryClientName(BuildConfig.SENTRY_JAVA_SDK_NAME + "/" + BuildConfig.VERSION_NAME); setSdkVersion(createSdkVersion()); } diff --git a/sentry/src/main/java/io/sentry/SentryRuntimeEventProcessor.java b/sentry/src/main/java/io/sentry/SentryRuntimeEventProcessor.java new file mode 100644 index 0000000000..184e8e9885 --- /dev/null +++ b/sentry/src/main/java/io/sentry/SentryRuntimeEventProcessor.java @@ -0,0 +1,46 @@ +package io.sentry; + +import io.sentry.protocol.SentryRuntime; +import io.sentry.protocol.SentryTransaction; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** Attaches Java vendor and version to events and transactions. */ +final class SentryRuntimeEventProcessor implements EventProcessor { + private final @Nullable String javaVersion; + private final @Nullable String javaVendor; + + public SentryRuntimeEventProcessor( + final @Nullable String javaVersion, final @Nullable String javaVendor) { + this.javaVersion = javaVersion; + this.javaVendor = javaVendor; + } + + public SentryRuntimeEventProcessor() { + this(System.getProperty("java.version"), System.getProperty("java.vendor")); + } + + @Override + public @NotNull SentryEvent process( + final @NotNull SentryEvent event, final @Nullable Object hint) { + return process(event); + } + + @Override + public @NotNull SentryTransaction process( + final @NotNull SentryTransaction transaction, final @Nullable Object hint) { + return process(transaction); + } + + private @NotNull T process(final @NotNull T event) { + if (event.getContexts().getRuntime() == null) { + event.getContexts().setRuntime(new SentryRuntime()); + } + final SentryRuntime runtime = event.getContexts().getRuntime(); + if (runtime != null && runtime.getName() == null && runtime.getVersion() == null) { + runtime.setName(javaVendor); + runtime.setVersion(javaVersion); + } + return event; + } +} diff --git a/sentry/src/main/java/io/sentry/util/Platform.java b/sentry/src/main/java/io/sentry/util/Platform.java new file mode 100644 index 0000000000..6a5a092ce6 --- /dev/null +++ b/sentry/src/main/java/io/sentry/util/Platform.java @@ -0,0 +1,28 @@ +package io.sentry.util; + +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +public final class Platform { + private static boolean isAndroid; + + static { + // System#getProperty can throw an exception if there is a security manager is configured and + // does not allow accessing system properties + try { + // All system properties on Android: + // https://developer.android.com/reference/java/lang/System#getProperties() + isAndroid = "The Android Project".equals(System.getProperty("java.vendor")); + } catch (Exception e) { + isAndroid = false; + } + } + + public static boolean isAndroid() { + return isAndroid; + } + + public static boolean isJvm() { + return !isAndroid; + } +} diff --git a/sentry/src/test/java/io/sentry/SentryRuntimeEventProcessorTest.kt b/sentry/src/test/java/io/sentry/SentryRuntimeEventProcessorTest.kt new file mode 100644 index 0000000000..58a95c5c7d --- /dev/null +++ b/sentry/src/test/java/io/sentry/SentryRuntimeEventProcessorTest.kt @@ -0,0 +1,66 @@ +package io.sentry + +import io.sentry.protocol.SentryRuntime +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +class SentryRuntimeEventProcessorTest { + + private val eventProcessor = SentryRuntimeEventProcessor("16", "OpenJDK") + + @Test + fun `when event does not have a runtime, sets runtime`() { + val event = eventProcessor.process(SentryEvent(), null) + assertNotNull(event) { + assertNotNull(it.contexts.runtime) { runtime -> + assertEquals("OpenJDK", runtime.name) + assertEquals("16", runtime.version) + } + } + } + + @Test + fun `when event has runtime with null name and null version, sets runtime`() { + val event = SentryEvent() + event.contexts.setRuntime(SentryRuntime()) + val result = eventProcessor.process(event, null) + assertNotNull(result) { + assertNotNull(it.contexts.runtime) { runtime -> + assertEquals("OpenJDK", runtime.name) + assertEquals("16", runtime.version) + } + } + } + + @Test + fun `when event has runtime with null name and version set, does not change runtime`() { + val event = SentryEvent() + val runtime = SentryRuntime() + runtime.version = "1.1" + event.contexts.setRuntime(runtime) + val result = eventProcessor.process(event, null) + assertNotNull(result) { + assertNotNull(it.contexts.runtime) { runtime -> + assertNull(runtime.name) + assertEquals("1.1", runtime.version) + } + } + } + + @Test + fun `when event has runtime with null version and name set, does not change runtime`() { + val event = SentryEvent() + val runtime = SentryRuntime() + runtime.name = "Java" + event.contexts.setRuntime(runtime) + val result = eventProcessor.process(event, null) + assertNotNull(result) { + assertNotNull(it.contexts.runtime) { runtime -> + assertNull(runtime.version) + assertEquals("Java", runtime.name) + } + } + } +}