diff --git a/CHANGELOG.md b/CHANGELOG.md index c2c0ae7794..29b8df1fc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Expose `enableNetworkBreadcrumbs` (iOS) to disable native HTTP request breadcrumbs ([#6764](https://github.com/getsentry/sentry-react-native/pull/6764)) - Expose `enableNetworkEventBreadcrumbs` (Android) to disable native network connectivity breadcrumbs ([#6764](https://github.com/getsentry/sentry-react-native/pull/6764)) +- Expose native breadcrumb off-switches: `enableAutoBreadcrumbTracking` (iOS); `enableActivityLifecycleBreadcrumbs`, `enableAppLifecycleBreadcrumbs`, `enableSystemEventBreadcrumbs`, `enableAppComponentBreadcrumbs` (Android) ([#6768](https://github.com/getsentry/sentry-react-native/pull/6768)) ### Fixes 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 ccc9740c24..de3ec607e0 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 @@ -217,6 +217,21 @@ static void getSentryAndroidOptions( options.setEnableNetworkEventBreadcrumbs( rnOptions.getBoolean("enableNetworkEventBreadcrumbs")); } + if (rnOptions.hasKey("enableActivityLifecycleBreadcrumbs")) { + options.setEnableActivityLifecycleBreadcrumbs( + rnOptions.getBoolean("enableActivityLifecycleBreadcrumbs")); + } + if (rnOptions.hasKey("enableAppLifecycleBreadcrumbs")) { + options.setEnableAppLifecycleBreadcrumbs( + rnOptions.getBoolean("enableAppLifecycleBreadcrumbs")); + } + if (rnOptions.hasKey("enableSystemEventBreadcrumbs")) { + options.setEnableSystemEventBreadcrumbs(rnOptions.getBoolean("enableSystemEventBreadcrumbs")); + } + if (rnOptions.hasKey("enableAppComponentBreadcrumbs")) { + options.setEnableAppComponentBreadcrumbs( + rnOptions.getBoolean("enableAppComponentBreadcrumbs")); + } if (rnOptions.hasKey("spotlight")) { if (rnOptions.getType("spotlight") == ReadableType.Boolean) { options.setEnableSpotlight(rnOptions.getBoolean("spotlight")); diff --git a/packages/core/android/src/test/java/io/sentry/react/RNSentryStartTest.java b/packages/core/android/src/test/java/io/sentry/react/RNSentryStartTest.java index 1c4c63ae66..9853414399 100644 --- a/packages/core/android/src/test/java/io/sentry/react/RNSentryStartTest.java +++ b/packages/core/android/src/test/java/io/sentry/react/RNSentryStartTest.java @@ -8,6 +8,7 @@ import com.facebook.react.bridge.ReadableMap; import io.sentry.ILogger; import io.sentry.android.core.SentryAndroidOptions; +import java.util.function.Predicate; import org.junit.Test; /** @@ -47,4 +48,49 @@ public void enableNetworkEventBreadcrumbsPreservesNativeDefaultWhenUnset() { // Native default is true; omitting the RN option must not change it. assertTrue(optionsFrom(rnOptions).isEnableNetworkEventBreadcrumbs()); } + + /** + * Asserts the on-by-default breadcrumb off-switch {@code key} forwards both values onto {@code + * getter} and preserves the native default (true) when omitted. + */ + private static void assertBreadcrumbOffSwitchForwarded( + String key, Predicate getter) { + final ReadableMap forwardsFalse = mock(ReadableMap.class); + when(forwardsFalse.hasKey(key)).thenReturn(true); + when(forwardsFalse.getBoolean(key)).thenReturn(false); + assertFalse(getter.test(optionsFrom(forwardsFalse))); + + final ReadableMap forwardsTrue = mock(ReadableMap.class); + when(forwardsTrue.hasKey(key)).thenReturn(true); + when(forwardsTrue.getBoolean(key)).thenReturn(true); + assertTrue(getter.test(optionsFrom(forwardsTrue))); + + // Native default is true; omitting the RN option must not change it. + assertTrue(getter.test(optionsFrom(mock(ReadableMap.class)))); + } + + @Test + public void enableActivityLifecycleBreadcrumbsForwarded() { + assertBreadcrumbOffSwitchForwarded( + "enableActivityLifecycleBreadcrumbs", + SentryAndroidOptions::isEnableActivityLifecycleBreadcrumbs); + } + + @Test + public void enableAppLifecycleBreadcrumbsForwarded() { + assertBreadcrumbOffSwitchForwarded( + "enableAppLifecycleBreadcrumbs", SentryAndroidOptions::isEnableAppLifecycleBreadcrumbs); + } + + @Test + public void enableSystemEventBreadcrumbsForwarded() { + assertBreadcrumbOffSwitchForwarded( + "enableSystemEventBreadcrumbs", SentryAndroidOptions::isEnableSystemEventBreadcrumbs); + } + + @Test + public void enableAppComponentBreadcrumbsForwarded() { + assertBreadcrumbOffSwitchForwarded( + "enableAppComponentBreadcrumbs", SentryAndroidOptions::isEnableAppComponentBreadcrumbs); + } } diff --git a/packages/core/etc/sentry-react-native.api.md b/packages/core/etc/sentry-react-native.api.md index d19ba75f15..81d56e48d9 100644 --- a/packages/core/etc/sentry-react-native.api.md +++ b/packages/core/etc/sentry-react-native.api.md @@ -159,9 +159,13 @@ export interface BaseReactNativeOptions { attachViewHierarchy?: boolean; autoInitializeNativeSdk?: boolean; beforeScreenshot?: (event: Event_2, hint: EventHint) => boolean; + enableActivityLifecycleBreadcrumbs?: boolean; enableAnrFingerprinting?: boolean; + enableAppComponentBreadcrumbs?: boolean; enableAppHangTracking?: boolean; + enableAppLifecycleBreadcrumbs?: boolean; enableAppStartTracking?: boolean; + enableAutoBreadcrumbTracking?: boolean; enableAutoConsoleLogs?: boolean; enableAutoPerformanceTracing?: boolean; enableAutoSessionTracking?: boolean; @@ -179,6 +183,7 @@ export interface BaseReactNativeOptions { enableNetworkBreadcrumbs?: boolean; enableNetworkEventBreadcrumbs?: boolean; enableStallTracking?: boolean; + enableSystemEventBreadcrumbs?: boolean; enableTombstone?: boolean; // @internal enableTurboModuleTracking?: boolean; diff --git a/packages/core/src/js/options.ts b/packages/core/src/js/options.ts index 5b6a291d4f..8b735588a0 100644 --- a/packages/core/src/js/options.ts +++ b/packages/core/src/js/options.ts @@ -261,6 +261,63 @@ export interface BaseReactNativeOptions { */ enableNetworkEventBreadcrumbs?: boolean; + /** + * When enabled, the native iOS SDK automatically records breadcrumbs for system events + * (such as memory warnings, orientation changes, keyboard show/hide and screenshots). + * + * Disable this to stop the native layer from adding these system-event breadcrumbs and + * reduce breadcrumb noise. + * + * @default true + * @platform ios + */ + enableAutoBreadcrumbTracking?: boolean; + + /** + * When enabled, the native Android SDK records breadcrumbs for activity lifecycle events + * (`created`, `started`, `resumed`, `paused`, `stopped`, `destroyed`). + * + * Disable this to stop the native layer from adding activity lifecycle breadcrumbs. + * + * @default true + * @platform android + */ + enableActivityLifecycleBreadcrumbs?: boolean; + + /** + * When enabled, the native Android SDK records breadcrumbs for app lifecycle events + * (the app entering the foreground or background). + * + * Disable this to stop the native layer from adding app lifecycle breadcrumbs. + * + * @default true + * @platform android + */ + enableAppLifecycleBreadcrumbs?: boolean; + + /** + * When enabled, the native Android SDK records breadcrumbs for system events + * (such as low battery, low storage, device charging, screen on/off, airplane mode and + * `ACTION_*` intents). + * + * Disable this to stop the native layer from adding system-event breadcrumbs. + * + * @default true + * @platform android + */ + enableSystemEventBreadcrumbs?: boolean; + + /** + * When enabled, the native Android SDK records breadcrumbs for app component events + * (such as low-memory `onTrimMemory` callbacks and configuration/orientation changes). + * + * Disable this to stop the native layer from adding app component breadcrumbs. + * + * @default true + * @platform android + */ + enableAppComponentBreadcrumbs?: boolean; + /** * Use this feature to enable the Sentry MetricKit integration. * diff --git a/packages/core/test/wrapper.test.ts b/packages/core/test/wrapper.test.ts index 6d10e3120b..d536db278f 100644 --- a/packages/core/test/wrapper.test.ts +++ b/packages/core/test/wrapper.test.ts @@ -234,6 +234,46 @@ describe('Tests Native Wrapper', () => { expect(initParameter.enableNetworkEventBreadcrumbs).toBe(false); }); + test('forwards enableAutoBreadcrumbTracking to the Native SDK', async () => { + await NATIVE.initNativeSdk({ + dsn: VALID_DSN, + enableNative: true, + autoInitializeNativeSdk: true, + enableAutoBreadcrumbTracking: false, + 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.enableAutoBreadcrumbTracking).toBe(false); + }); + + test('forwards the Android breadcrumb off-switches to the Native SDK', async () => { + await NATIVE.initNativeSdk({ + dsn: VALID_DSN, + enableNative: true, + autoInitializeNativeSdk: true, + enableActivityLifecycleBreadcrumbs: false, + enableAppLifecycleBreadcrumbs: false, + enableSystemEventBreadcrumbs: false, + enableAppComponentBreadcrumbs: false, + 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.enableActivityLifecycleBreadcrumbs).toBe(false); + expect(initParameter.enableAppLifecycleBreadcrumbs).toBe(false); + expect(initParameter.enableSystemEventBreadcrumbs).toBe(false); + expect(initParameter.enableAppComponentBreadcrumbs).toBe(false); + }); + test('filter beforeSend when initializing Native SDK', async () => { await NATIVE.initNativeSdk({ dsn: VALID_DSN,