Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

## Unreleased

### Behavioral Changes

- The Sentry SDK will now crash on startup if mixed versions have been detected ([#4277](https://github.com/getsentry/sentry-java/pull/4277))
- On `Sentry.init` / `SentryAndroid.init` the SDK now checks if all Sentry Java / Android SDK dependencies have the same version.
- While this may seem like a bad idea at first glance, mixing versions of dependencies has a very high chance of causing a crash later. We opted for a controlled crash that's hard to miss.
- Note: This detection only works for new versions of the SDK, so please take this as a reminder to check your SDK version alignment manually when upgrading the SDK to this version and then you should be good.
- The SDK will also print log messages if mixed versions have been detected at a later point. ([#4270](https://github.com/getsentry/sentry-java/pull/4270))
- This takes care of cases missed by the startup check above due to older versions.

### Features

- Increase http timeouts from 5s to 30s to have a better chance of events being delivered without retry ([#4276](https://github.com/getsentry/sentry-java/pull/4276))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import android.content.pm.PackageInfo;
import io.sentry.DeduplicateMultithreadedEventProcessor;
import io.sentry.DefaultCompositePerformanceCollector;
import io.sentry.DefaultVersionDetector;
import io.sentry.IContinuousProfiler;
import io.sentry.ILogger;
import io.sentry.ISentryLifecycleToken;
Expand All @@ -15,6 +16,7 @@
import io.sentry.NoOpConnectionStatusProvider;
import io.sentry.NoOpContinuousProfiler;
import io.sentry.NoOpTransactionProfiler;
import io.sentry.NoopVersionDetector;
import io.sentry.ScopeType;
import io.sentry.SendFireAndForgetEnvelopeSender;
import io.sentry.SendFireAndForgetOutboxSender;
Expand Down Expand Up @@ -198,6 +200,9 @@ static void initializeIntegrationsAndProcessors(
if (options.getDebugMetaLoader() instanceof NoOpDebugMetaLoader) {
options.setDebugMetaLoader(new AssetsDebugMetaLoader(context, options.getLogger()));
}
if (options.getVersionDetector() instanceof NoopVersionDetector) {
options.setVersionDetector(new DefaultVersionDetector(options));
}

final boolean isAndroidXScrollViewAvailable =
loadClass.isClassAvailable("androidx.core.view.ScrollingView", options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,11 @@
import io.sentry.Sentry;
import io.sentry.SentryIntegrationPackageStorage;
import io.sentry.SentryOptions;
import io.sentry.internal.ManifestVersionReader;
import io.sentry.protocol.SdkVersion;
import io.sentry.protocol.SentryPackage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand All @@ -31,7 +25,8 @@ public final class SentryAutoConfigurationCustomizerProvider
@Override
public void customize(AutoConfigurationCustomizer autoConfiguration) {
ensureSentryOtelStorageIsInitialized();
final @Nullable VersionInfoHolder versionInfoHolder = createVersionInfo();
final @Nullable ManifestVersionReader.VersionInfoHolder versionInfoHolder =
ManifestVersionReader.getInstance().readOpenTelemetryVersion();

if (isSentryAutoInitEnabled()) {
Sentry.init(
Expand All @@ -46,10 +41,10 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) {
}

if (versionInfoHolder != null) {
for (SentryPackage pkg : versionInfoHolder.packages) {
for (SentryPackage pkg : versionInfoHolder.getPackages()) {
SentryIntegrationPackageStorage.getInstance().addPackage(pkg.getName(), pkg.getVersion());
}
for (String integration : versionInfoHolder.integrations) {
for (String integration : versionInfoHolder.getIntegrations()) {
SentryIntegrationPackageStorage.getInstance().addIntegration(integration);
}
}
Expand Down Expand Up @@ -84,76 +79,21 @@ private boolean isSentryAutoInitEnabled() {
}
}

private @Nullable VersionInfoHolder createVersionInfo() {
VersionInfoHolder infoHolder = null;
try {
final @NotNull Enumeration<URL> 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) {
infoHolder = new VersionInfoHolder();
infoHolder.sdkName = name;
infoHolder.sdkVersion = version;
infoHolder.packages.add(
new SentryPackage("maven:io.sentry:sentry-opentelemetry-agent", version));
final @Nullable String otelVersion =
mainAttributes.getValue("Sentry-Opentelemetry-Version-Name");
if (otelVersion != null) {
infoHolder.packages.add(
new SentryPackage("maven:io.opentelemetry:opentelemetry-sdk", otelVersion));
infoHolder.integrations.add("OpenTelemetry");
}
final @Nullable String otelJavaagentVersion =
mainAttributes.getValue("Sentry-Opentelemetry-Javaagent-Version-Name");
if (otelJavaagentVersion != null) {
infoHolder.packages.add(
new SentryPackage(
"maven:io.opentelemetry.javaagent:opentelemetry-javaagent",
otelJavaagentVersion));
infoHolder.integrations.add("OpenTelemetry-Agent");
}
break;
}
}
} catch (Exception e) {
// ignore
}
}
} catch (IOException e) {
// ignore
}
return infoHolder;
}

private @Nullable SdkVersion createSdkVersion(
final @NotNull SentryOptions sentryOptions,
final @Nullable VersionInfoHolder versionInfoHolder) {
final @Nullable ManifestVersionReader.VersionInfoHolder versionInfoHolder) {
SdkVersion sdkVersion = sentryOptions.getSdkVersion();

if (versionInfoHolder != null
&& versionInfoHolder.sdkName != null
&& versionInfoHolder.sdkVersion != null) {
&& versionInfoHolder.getSdkName() != null
&& versionInfoHolder.getSdkVersion() != null) {
sdkVersion =
SdkVersion.updateSdkVersion(
sdkVersion, versionInfoHolder.sdkName, versionInfoHolder.sdkVersion);
sdkVersion, versionInfoHolder.getSdkName(), versionInfoHolder.getSdkVersion());
}
return sdkVersion;
}

private static class VersionInfoHolder {
private @Nullable String sdkName;
private @Nullable String sdkVersion;
private List<SentryPackage> packages = new ArrayList<>();
private List<String> integrations = new ArrayList<>();
}

private SdkTracerProviderBuilder configureSdkTracerProvider(
SdkTracerProviderBuilder tracerProvider, ConfigProperties config) {
return tracerProvider
Expand Down
35 changes: 35 additions & 0 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,11 @@ public final class io/sentry/DefaultSpanFactory : io/sentry/ISpanFactory {
public fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/CompositePerformanceCollector;)Lio/sentry/ITransaction;
}

public final class io/sentry/DefaultVersionDetector : io/sentry/IVersionDetector {
public fun <init> (Lio/sentry/SentryOptions;)V
public fun checkForMixedVersions ()Z
}

public final class io/sentry/DiagnosticLogger : io/sentry/ILogger {
public fun <init> (Lio/sentry/SentryOptions;Lio/sentry/ILogger;)V
public fun getLogger ()Lio/sentry/ILogger;
Expand Down Expand Up @@ -1098,6 +1103,10 @@ public abstract interface class io/sentry/ITransportFactory {
public abstract fun create (Lio/sentry/SentryOptions;Lio/sentry/RequestDetails;)Lio/sentry/transport/ITransport;
}

public abstract interface class io/sentry/IVersionDetector {
public abstract fun checkForMixedVersions ()Z
}

public final class io/sentry/InitPriority : java/lang/Enum {
public static final field HIGH Lio/sentry/InitPriority;
public static final field HIGHEST Lio/sentry/InitPriority;
Expand Down Expand Up @@ -1247,6 +1256,11 @@ public final class io/sentry/MainEventProcessor : io/sentry/EventProcessor, java
public fun process (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryTransaction;
}

public final class io/sentry/ManifestVersionDetector : io/sentry/IVersionDetector {
public fun <init> (Lio/sentry/SentryOptions;)V
public fun checkForMixedVersions ()Z
}

public abstract interface class io/sentry/MeasurementUnit {
public static final field NONE Ljava/lang/String;
public abstract fun apiName ()Ljava/lang/String;
Expand Down Expand Up @@ -1799,6 +1813,11 @@ public final class io/sentry/NoOpTransportFactory : io/sentry/ITransportFactory
public static fun getInstance ()Lio/sentry/NoOpTransportFactory;
}

public final class io/sentry/NoopVersionDetector : io/sentry/IVersionDetector {
public fun checkForMixedVersions ()Z
public static fun getInstance ()Lio/sentry/NoopVersionDetector;
}

public abstract interface class io/sentry/ObjectReader : java/io/Closeable {
public abstract fun beginArray ()V
public abstract fun beginObject ()V
Expand Down Expand Up @@ -3099,6 +3118,7 @@ public class io/sentry/SentryOptions {
public fun getTransactionProfiler ()Lio/sentry/ITransactionProfiler;
public fun getTransportFactory ()Lio/sentry/ITransportFactory;
public fun getTransportGate ()Lio/sentry/transport/ITransportGate;
public fun getVersionDetector ()Lio/sentry/IVersionDetector;
public final fun getViewHierarchyExporters ()Ljava/util/List;
public fun isAttachServerName ()Z
public fun isAttachStacktrace ()Z
Expand Down Expand Up @@ -3233,6 +3253,7 @@ public class io/sentry/SentryOptions {
public fun setTransactionProfiler (Lio/sentry/ITransactionProfiler;)V
public fun setTransportFactory (Lio/sentry/ITransportFactory;)V
public fun setTransportGate (Lio/sentry/transport/ITransportGate;)V
public fun setVersionDetector (Lio/sentry/IVersionDetector;)V
public fun setViewHierarchyExporters (Ljava/util/List;)V
}

Expand Down Expand Up @@ -4409,6 +4430,20 @@ public final class io/sentry/instrumentation/file/SentryFileWriter : java/io/Out
public fun <init> (Ljava/lang/String;Z)V
}

public final class io/sentry/internal/ManifestVersionReader {
public static fun getInstance ()Lio/sentry/internal/ManifestVersionReader;
public fun readManifestFiles ()V
public fun readOpenTelemetryVersion ()Lio/sentry/internal/ManifestVersionReader$VersionInfoHolder;
}

public final class io/sentry/internal/ManifestVersionReader$VersionInfoHolder {
public fun <init> ()V
public fun getIntegrations ()Ljava/util/List;
public fun getPackages ()Ljava/util/List;
public fun getSdkName ()Ljava/lang/String;
public fun getSdkVersion ()Ljava/lang/String;
}

public abstract interface class io/sentry/internal/debugmeta/IDebugMetaLoader {
public abstract fun loadDebugMeta ()Ljava/util/List;
}
Expand Down
19 changes: 19 additions & 0 deletions sentry/src/main/java/io/sentry/DefaultVersionDetector.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.sentry;

import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

@ApiStatus.Internal
public final class DefaultVersionDetector implements IVersionDetector {

private final @NotNull SentryOptions options;

public DefaultVersionDetector(final @NotNull SentryOptions options) {
this.options = options;
}

@Override
public boolean checkForMixedVersions() {
return SentryIntegrationPackageStorage.getInstance().checkForMixedVersions(options.getLogger());
}
}
11 changes: 11 additions & 0 deletions sentry/src/main/java/io/sentry/IVersionDetector.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.sentry;

public interface IVersionDetector {

/**
* Checks whether all installed Sentry Java SDK dependencies have the same version.
*
* @return true if mixed versions have been detected, false if all versions align
*/
boolean checkForMixedVersions();
}
21 changes: 21 additions & 0 deletions sentry/src/main/java/io/sentry/ManifestVersionDetector.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.sentry;

import io.sentry.internal.ManifestVersionReader;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

@ApiStatus.Internal
public final class ManifestVersionDetector implements IVersionDetector {

private final @NotNull SentryOptions options;

public ManifestVersionDetector(final @NotNull SentryOptions options) {
this.options = options;
}

@Override
public boolean checkForMixedVersions() {
ManifestVersionReader.getInstance().readManifestFiles();
return SentryIntegrationPackageStorage.getInstance().checkForMixedVersions(options.getLogger());
}
}
17 changes: 17 additions & 0 deletions sentry/src/main/java/io/sentry/NoopVersionDetector.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.sentry;

public final class NoopVersionDetector implements IVersionDetector {

private static final NoopVersionDetector instance = new NoopVersionDetector();

private NoopVersionDetector() {}

public static NoopVersionDetector getInstance() {
return instance;
}

@Override
public boolean checkForMixedVersions() {
return false;
}
}
13 changes: 13 additions & 0 deletions sentry/src/main/java/io/sentry/SentryOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,8 @@ public class SentryOptions {
private @NotNull SentryReplayOptions sessionReplay;

@ApiStatus.Experimental private boolean captureOpenTelemetryEvents = false;

private @NotNull IVersionDetector versionDetector = NoopVersionDetector.getInstance();
/**
* Adds an event processor
*
Expand Down Expand Up @@ -2541,6 +2543,17 @@ public void setEnableBackpressureHandling(final boolean enableBackpressureHandli
this.enableBackpressureHandling = enableBackpressureHandling;
}

@ApiStatus.Internal
@NotNull
public IVersionDetector getVersionDetector() {
return versionDetector;
}

@ApiStatus.Internal
public void setVersionDetector(final @NotNull IVersionDetector versionDetector) {
this.versionDetector = versionDetector;
}

/**
* Returns the rate the profiler will sample rates at. 100 hz means 100 traces in 1 second.
*
Expand Down
Loading
Loading