Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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<SentryAndroidOptions> 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);
}
}
5 changes: 5 additions & 0 deletions packages/core/etc/sentry-react-native.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -179,6 +183,7 @@ export interface BaseReactNativeOptions {
enableNetworkBreadcrumbs?: boolean;
enableNetworkEventBreadcrumbs?: boolean;
enableStallTracking?: boolean;
enableSystemEventBreadcrumbs?: boolean;
enableTombstone?: boolean;
// @internal
enableTurboModuleTracking?: boolean;
Expand Down
57 changes: 57 additions & 0 deletions packages/core/src/js/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
40 changes: 40 additions & 0 deletions packages/core/test/wrapper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading