Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
bd0c3b4
Check for mixed SDK versions
adinauer Mar 18, 2025
f50173b
Format code
getsentry-bot Mar 20, 2025
a8875cf
format + api
adinauer Mar 20, 2025
c0eea6a
Init noops if mixed versions detected
adinauer Mar 20, 2025
5c09766
Add BuildConfig
adinauer Mar 20, 2025
735ff28
config entries for agentless module sdk names
adinauer Mar 20, 2025
fe20321
Add MANIFEST.MF for JARs
adinauer Mar 20, 2025
901830d
Throw on startup; use manifests for backend; reuse code for otel
adinauer Mar 21, 2025
65a7a6f
Format code
getsentry-bot Mar 21, 2025
62cb98f
Merge branch 'main' into feat/crash-on-startup-with-mixed-versions
adinauer Mar 24, 2025
954610d
Format code
getsentry-bot Mar 24, 2025
8144647
changelog
adinauer Mar 24, 2025
7d1942e
api
adinauer Mar 24, 2025
f062c3e
Merge branch 'main' into feat/manifest-for-jars
adinauer Mar 24, 2025
e72dff6
changelog
adinauer Mar 24, 2025
72c554b
Merge branch 'main' into feat/detect-mixed-sdk-versions
adinauer Mar 24, 2025
60e4cd3
Merge branch 'feat/detect-mixed-sdk-versions' into feat/noop-on-mixed…
adinauer Mar 24, 2025
b1ae3b4
Merge branch 'feat/noop-on-mixed-versions-android' into feat/manifest…
adinauer Mar 24, 2025
e13f518
Merge branch 'feat/manifest-for-jars' into feat/crash-on-startup-with…
adinauer Mar 24, 2025
c3d50ab
Remove duplicate addPackage calls
adinauer Mar 25, 2025
8185388
remove test assertion for package
adinauer Mar 25, 2025
f8ba72d
Introduce fatal SDK logger
adinauer Mar 26, 2025
04db954
Format code
getsentry-bot Mar 26, 2025
dac2bad
Merge branch 'main' into feat/fatal-logger
adinauer Mar 27, 2025
1549087
changelog
adinauer Mar 27, 2025
5e48870
api
adinauer Mar 27, 2025
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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,19 @@

## 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))
- Add `MANIFEST.MF` to Sentry JARs ([#4272](https://github.com/getsentry/sentry-java/pull/4272))

### Fixes

Expand Down
10 changes: 10 additions & 0 deletions buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -251,12 +251,21 @@ object Config {
val SENTRY_SPRING_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.spring"
val SENTRY_SPRING_JAKARTA_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.spring.jakarta"
val SENTRY_SPRING_BOOT_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.spring-boot"
val SENTRY_SPRING_BOOT_STARTER_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.spring-boot-starter"
val SENTRY_SPRING_BOOT_JAKARTA_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.spring-boot.jakarta"
val SENTRY_SPRING_BOOT_STARTER_JAKARTA_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.spring-boot-starter.jakarta"
val SENTRY_OPENTELEMETRY_BOOTSTRAP_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.opentelemetry.bootstrap"
val SENTRY_OPENTELEMETRY_CORE_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.opentelemetry.core"
val SENTRY_OPENTELEMETRY_AGENT_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.opentelemetry.agent"
val SENTRY_OPENTELEMETRY_AGENTLESS_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.opentelemetry.agentless"
val SENTRY_OPENTELEMETRY_AGENTLESS_SPRING_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.opentelemetry.agentless-spring"
val SENTRY_OPENTELEMETRY_AGENTCUSTOMIZATION_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.opentelemetry.agentcustomization"
val SENTRY_OPENFEIGN_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.openfeign"
val SENTRY_APOLLO3_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.apollo3"
val SENTRY_APOLLO4_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.apollo4"
val SENTRY_APOLLO_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.apollo"
val SENTRY_GRAPHQL_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.graphql"
val SENTRY_GRAPHQL_CORE_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.graphql-core"
val SENTRY_GRAPHQL22_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.graphql22"
val SENTRY_QUARTZ_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.quartz"
val SENTRY_JDBC_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.jdbc"
Expand All @@ -265,6 +274,7 @@ object Config {
val SENTRY_COMPOSE_HELPER_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.compose.helper"
val SENTRY_OKHTTP_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.okhttp"
val SENTRY_REACTOR_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.reactor"
val SENTRY_KOTLIN_EXTENSIONS_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.kotlin-extensions"
val group = "io.sentry"
val description = "SDK for sentry.io"
val versionNameProp = "versionName"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package io.sentry.android.core;

import android.util.Log;
import io.sentry.ILogger;
import io.sentry.SentryLevel;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@ApiStatus.Internal
public final class AndroidFatalLogger implements ILogger {

private final @NotNull String tag;

public AndroidFatalLogger() {
this("Sentry");
}

public AndroidFatalLogger(final @NotNull String tag) {
this.tag = tag;
}

@SuppressWarnings("AnnotateFormatMethod")
@Override
public void log(
final @NotNull SentryLevel level,
final @NotNull String message,
final @Nullable Object... args) {
if (args == null || args.length == 0) {
Log.println(toLogcatLevel(level), tag, message);
} else {
Log.println(toLogcatLevel(level), tag, String.format(message, args));
}
}

@SuppressWarnings("AnnotateFormatMethod")
@Override
public void log(
final @NotNull SentryLevel level,
final @Nullable Throwable throwable,
final @NotNull String message,
final @Nullable Object... args) {
if (args == null || args.length == 0) {
log(level, message, throwable);
} else {
log(level, String.format(message, args), throwable);
}
}

@Override
public void log(
final @NotNull SentryLevel level,
final @NotNull String message,
final @Nullable Throwable throwable) {
Log.wtf(tag, message, throwable);
}

@Override
public boolean isEnabled(@Nullable SentryLevel level) {
return true;
}

private int toLogcatLevel(final @NotNull SentryLevel sentryLevel) {
return Log.ASSERT;
}
}
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 @@ -108,6 +110,7 @@ static void loadDefaultAndMetadataOptions(

// Firstly set the logger, if `debug=true` configured, logging can start asap.
options.setLogger(logger);
options.setFatalLogger(new AndroidFatalLogger());

options.setDefaultScopeType(ScopeType.CURRENT);
options.setOpenTelemetryMode(SentryOpenTelemetryMode.OFF);
Expand Down Expand Up @@ -198,6 +201,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 @@ -22,6 +22,13 @@ public class FragmentLifecycleIntegration(
Integration,
Closeable {

private companion object {
init {
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-android-fragment", BuildConfig.VERSION_NAME)
}
}

public constructor(application: Application) : this(
application = application,
filterFragmentLifecycleBreadcrumbs = FragmentLifecycleState.states,
Expand Down Expand Up @@ -50,8 +57,6 @@ public class FragmentLifecycleIntegration(
application.registerActivityLifecycleCallbacks(this)
options.logger.log(DEBUG, "FragmentLifecycleIntegration installed.")
addIntegrationToSdkVersion("FragmentLifecycle")
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-android-fragment", BuildConfig.VERSION_NAME)
}

override fun close() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ public class SentryNavigationListener @JvmOverloads constructor(

init {
addIntegrationToSdkVersion("NavigationListener")
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-android-navigation", BuildConfig.VERSION_NAME)
}

override fun onDestinationChanged(
Expand Down Expand Up @@ -195,5 +193,10 @@ public class SentryNavigationListener @JvmOverloads constructor(

public companion object {
public const val NAVIGATION_OP: String = "navigation"

init {
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-android-navigation", BuildConfig.VERSION_NAME)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sentry.android.ndk;

import io.sentry.SentryIntegrationPackageStorage;
import io.sentry.protocol.SdkVersion;
import org.jetbrains.annotations.Nullable;

Expand All @@ -8,6 +9,11 @@
*/
final class SentryNdkUtil {

static {
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-android-ndk", BuildConfig.VERSION_NAME);
}

private SentryNdkUtil() {}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ public class ReplayIntegration(
IConnectionStatusObserver,
IRateLimitObserver {

private companion object {
init {
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-android-replay", BuildConfig.VERSION_NAME)
}
}

// needed for the Java's call site
public constructor(context: Context, dateProvider: ICurrentDateProvider) : this(
context.appContext(),
Expand Down Expand Up @@ -151,8 +158,6 @@ public class ReplayIntegration(
}

addIntegrationToSdkVersion("Replay")
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-android-replay", BuildConfig.VERSION_NAME)

finalizePreviousReplay()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,20 @@ public class SentryTimberIntegration(
private lateinit var tree: SentryTimberTree
private lateinit var logger: ILogger

private companion object {
init {
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-android-timber", VERSION_NAME)
}
}

override fun register(scopes: IScopes, options: SentryOptions) {
logger = options.logger

tree = SentryTimberTree(scopes, minEventLevel, minBreadcrumbLevel)
Timber.plant(tree)

logger.log(SentryLevel.DEBUG, "SentryTimberIntegration installed.")
SentryIntegrationPackageStorage.getInstance().addPackage("maven:io.sentry:sentry-android-timber", VERSION_NAME)
addIntegrationToSdkVersion("Timber")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor(
SentryIntegrationPackageStorage.getInstance()
.addIntegration("Apollo3ClientError")
}
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-apollo-3", BuildConfig.VERSION_NAME)
}

private val regex: Regex by lazy {
Expand Down Expand Up @@ -453,5 +451,10 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor(
const val SENTRY_APOLLO_3_VARIABLES = "SENTRY-APOLLO-3-VARIABLES"
const val SENTRY_APOLLO_3_OPERATION_TYPE = "SENTRY-APOLLO-3-OPERATION-TYPE"
const val DEFAULT_CAPTURE_FAILED_REQUESTS = true

init {
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-apollo-3", BuildConfig.VERSION_NAME)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -323,9 +323,6 @@ class SentryApollo3InterceptorTest {
fun `sets SDKVersion Info`() {
assertNotNull(fixture.scopes.options.sdkVersion)
assert(fixture.scopes.options.sdkVersion!!.integrationSet.contains("Apollo3"))
val packageInfo = fixture.scopes.options.sdkVersion!!.packageSet.firstOrNull { pkg -> pkg.name == "maven:io.sentry:sentry-apollo-3" }
assertNotNull(packageInfo)
assert(packageInfo.version == BuildConfig.VERSION_NAME)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ class SentryApollo4HttpInterceptor @JvmOverloads constructor(
SentryIntegrationPackageStorage.getInstance()
.addIntegration("Apollo4ClientError")
}
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-apollo-4", BuildConfig.VERSION_NAME)
}

private val regex: Regex by lazy {
Expand Down Expand Up @@ -449,5 +447,10 @@ class SentryApollo4HttpInterceptor @JvmOverloads constructor(

companion object {
const val DEFAULT_CAPTURE_FAILED_REQUESTS = true

init {
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-apollo-4", BuildConfig.VERSION_NAME)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,17 @@ class SentryApolloInterceptor(
private val beforeSpan: BeforeSpanCallback? = null
) : ApolloInterceptor {

private companion object {
init {
SentryIntegrationPackageStorage.getInstance().addPackage("maven:io.sentry:sentry-apollo", BuildConfig.VERSION_NAME)
}
}

constructor(scopes: IScopes) : this(scopes, null)
constructor(beforeSpan: BeforeSpanCallback) : this(ScopesAdapter.getInstance(), beforeSpan)

init {
addIntegrationToSdkVersion("Apollo")
SentryIntegrationPackageStorage.getInstance().addPackage("maven:io.sentry:sentry-apollo", BuildConfig.VERSION_NAME)
}

override fun interceptAsync(request: InterceptorRequest, chain: ApolloInterceptorChain, dispatcher: Executor, callBack: CallBack) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,14 @@ public final class ComposeGestureTargetLocator implements GestureTargetLocator {
private volatile @Nullable SentryComposeHelper composeHelper;
private final @NotNull AutoClosableReentrantLock lock = new AutoClosableReentrantLock();

static {
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-compose", BuildConfig.VERSION_NAME);
}

public ComposeGestureTargetLocator(final @NotNull ILogger logger) {
this.logger = logger;
SentryIntegrationPackageStorage.getInstance().addIntegration("ComposeUserInteraction");
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-compose", BuildConfig.VERSION_NAME);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,14 @@ internal class SentryLifecycleObserver(
SentryNavigationListener(traceOriginAppendix = TRACE_ORIGIN_APPENDIX)
) : LifecycleEventObserver {

private companion object {
init {
SentryIntegrationPackageStorage.getInstance().addPackage("maven:io.sentry:sentry-compose", BuildConfig.VERSION_NAME)
}
}

init {
addIntegrationToSdkVersion("ComposeNavigation")
SentryIntegrationPackageStorage.getInstance().addPackage("maven:io.sentry:sentry-compose", BuildConfig.VERSION_NAME)
}

override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
Expand Down
13 changes: 13 additions & 0 deletions sentry-graphql-22/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,16 @@ buildConfig {
buildConfigField("String", "SENTRY_GRAPHQL22_SDK_NAME", "\"${Config.Sentry.SENTRY_GRAPHQL22_SDK_NAME}\"")
buildConfigField("String", "VERSION_NAME", "\"${project.version}\"")
}

tasks.jar {
manifest {
attributes(
"Sentry-Version-Name" to project.version,
"Sentry-SDK-Name" to Config.Sentry.SENTRY_GRAPHQL22_SDK_NAME,
"Sentry-SDK-Package-Name" to "maven:io.sentry:sentry-graphql-22",
"Implementation-Vendor" to "Sentry",
"Implementation-Title" to project.name,
"Implementation-Version" to project.version
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
public final class SentryInstrumentation
extends graphql.execution.instrumentation.SimplePerformantInstrumentation {

static {
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-graphql-22", BuildConfig.VERSION_NAME);
}

/**
* @deprecated please use {@link SentryGraphqlInstrumentation#SENTRY_SCOPES_CONTEXT_KEY}
*/
Expand Down Expand Up @@ -89,8 +94,6 @@ public SentryInstrumentation(
new SentryGraphqlInstrumentation(
beforeSpan, subscriptionHandler, exceptionReporter, ignoredErrorTypes, TRACE_ORIGIN);
SentryIntegrationPackageStorage.getInstance().addIntegration("GraphQL-v22");
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-graphql-22", BuildConfig.VERSION_NAME);
}

/**
Expand Down
Loading