diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bc796cdc6..b5233eabb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ ## Unreleased +### Features + +- Add `anrProfilingSampleRate` option to profile ANRs on Android ([#6673](https://github.com/getsentry/sentry-react-native/pull/6673)) + ### Dependencies - Bump Cocoa SDK from v9.26.1 to v9.27.0 ([#6670](https://github.com/getsentry/sentry-react-native/pull/6670)) diff --git a/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/RNSentryStartTest.kt b/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/RNSentryStartTest.kt index 2b42b0b90e..0c64b573ab 100644 --- a/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/RNSentryStartTest.kt +++ b/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/RNSentryStartTest.kt @@ -400,6 +400,38 @@ class RNSentryStartTest { assertEquals(5000L, options.ndkAppHangTimeoutIntervalMillis) } + @Test + fun `when anrProfilingSampleRate is set, the ANR profiling sample rate is applied`() { + val rnOptions = JavaOnlyMap.of("anrProfilingSampleRate", 0.5) + val options = SentryAndroidOptions() + + RNSentryStart.getSentryAndroidOptions(options, rnOptions, logger) + + assertEquals(0.5, options.anrProfilingSampleRate!!, 0.0) + assertTrue("ANR profiling should be enabled", options.isAnrProfilingEnabled) + } + + @Test + fun `when anrProfilingSampleRate is not set, it remains at default (disabled)`() { + val rnOptions = JavaOnlyMap() + val options = SentryAndroidOptions() + + RNSentryStart.getSentryAndroidOptions(options, rnOptions, logger) + + assertNull("ANR profiling sample rate should be null by default", options.anrProfilingSampleRate) + assertFalse("ANR profiling should be disabled by default", options.isAnrProfilingEnabled) + } + + @Test + fun `when anrProfilingSampleRate is not a number, it is ignored`() { + val rnOptions = JavaOnlyMap.of("anrProfilingSampleRate", "invalid") + val options = SentryAndroidOptions() + + RNSentryStart.getSentryAndroidOptions(options, rnOptions, logger) + + assertNull("ANR profiling sample rate should remain null", options.anrProfilingSampleRate) + } + @Test fun `network detail replay options are forwarded to the native replay options`() { val mobileReplayOptions = diff --git a/packages/core/android/src/main/java/io/sentry/react/RNSentryStart.java b/packages/core/android/src/main/java/io/sentry/react/RNSentryStart.java index 2ee396b239..5e7f750c15 100644 --- a/packages/core/android/src/main/java/io/sentry/react/RNSentryStart.java +++ b/packages/core/android/src/main/java/io/sentry/react/RNSentryStart.java @@ -202,6 +202,10 @@ static void getSentryAndroidOptions( if (rnOptions.hasKey("enableAnrFingerprinting")) { options.setEnableAnrFingerprinting(rnOptions.getBoolean("enableAnrFingerprinting")); } + if (rnOptions.hasKey("anrProfilingSampleRate") + && rnOptions.getType("anrProfilingSampleRate") == ReadableType.Number) { + options.setAnrProfilingSampleRate(rnOptions.getDouble("anrProfilingSampleRate")); + } if (rnOptions.hasKey("enableNdkAppHangTracking")) { options.setEnableNdkAppHangTracking(rnOptions.getBoolean("enableNdkAppHangTracking")); } diff --git a/packages/core/src/js/options.ts b/packages/core/src/js/options.ts index 3780539caf..6b59decd06 100644 --- a/packages/core/src/js/options.ts +++ b/packages/core/src/js/options.ts @@ -103,6 +103,21 @@ export interface BaseReactNativeOptions { */ enableAnrFingerprinting?: boolean; + /** + * Sample rate for profiling ANR (Application Not Responding) events. + * + * When set to a value greater than `0.0`, the SDK profiles the main thread while an ANR is + * happening and attaches the resulting profile to the ANR event. The value is the probability + * (`0.0`–`1.0`) that any given ANR is profiled. + * + * Requires ANR detection, which is enabled by default. This is independent of UI/transaction + * profiling configured via `profilesSampleRate` and `_experiments.profilingOptions`. + * + * @default undefined (ANR profiling disabled) + * @platform android + */ + anrProfilingSampleRate?: number; + /** * When enabled, all the threads are automatically attached to all logged events on Android * diff --git a/packages/core/test/wrapper.test.ts b/packages/core/test/wrapper.test.ts index dee934f4bd..c98a7527e4 100644 --- a/packages/core/test/wrapper.test.ts +++ b/packages/core/test/wrapper.test.ts @@ -177,6 +177,23 @@ describe('Tests Native Wrapper', () => { expect(debug.warn).toHaveBeenLastCalledWith('Note: Native Sentry SDK is disabled.'); }); + test('forwards anrProfilingSampleRate to the Native SDK', async () => { + await NATIVE.initNativeSdk({ + dsn: VALID_DSN, + enableNative: true, + autoInitializeNativeSdk: true, + anrProfilingSampleRate: 0.5, + devServerUrl: undefined, + defaultSidecarUrl: undefined, + mobileReplayOptions: undefined, + }); + + expect(RNSentry.initNativeSdk).toHaveBeenCalled(); + // @ts-expect-error mock value + const initParameter = RNSentry.initNativeSdk.mock.calls[0][0]; + expect(initParameter.anrProfilingSampleRate).toBe(0.5); + }); + test('filter beforeSend when initializing Native SDK', async () => { await NATIVE.initNativeSdk({ dsn: VALID_DSN,