Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 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 @@ -11,6 +11,7 @@
### Fixes

- crashedLastRun now returns the correct value ([#4829](https://github.com/getsentry/sentry-react-native/pull/4829))
- Use engine-specific promise rejection tracking ([#4826](https://github.com/getsentry/sentry-react-native/pull/4826))

## 6.14.0

Expand Down
82 changes: 75 additions & 7 deletions packages/core/src/js/integrations/reactnativeerrorhandlers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import type { EventHint, Integration, SeverityLevel } from '@sentry/core';
import { addExceptionMechanism, captureException, getClient, getCurrentScope, logger } from '@sentry/core';

import {
addExceptionMechanism,
addGlobalUnhandledRejectionInstrumentationHandler,
captureException,
getClient,
getCurrentScope,
logger,
} from '@sentry/core';

import { isHermesEnabled, isWeb } from '../utils/environment';
import { createSyntheticError, isErrorLike } from '../utils/error';
import { RN_GLOBAL_OBJ } from '../utils/worldwide';
import { checkPromiseAndWarn, polyfillPromise, requireRejectionTracking } from './reactnativeerrorhandlersutils';
Expand Down Expand Up @@ -44,12 +52,70 @@ function setup(options: ReactNativeErrorHandlersOptions): void {
* Setup unhandled promise rejection tracking
*/
function setupUnhandledRejectionsTracking(patchGlobalPromise: boolean): void {
if (patchGlobalPromise) {
polyfillPromise();
}
try {
if (isHermesEnabled() && RN_GLOBAL_OBJ.HermesInternal?.enablePromiseRejectionTracker) {
Comment thread
krystofwoldrich marked this conversation as resolved.
Outdated
logger.log('Using Hermes native promise rejection tracking');

RN_GLOBAL_OBJ.HermesInternal.enablePromiseRejectionTracker({
allRejections: true,
onUnhandled: (id: string, error: unknown) => {
if (__DEV__) {
logger.warn(`Possible Unhandled Promise Rejection (id: ${id}):\n${error}`);
}

// Marking the rejection as handled to avoid breaking crash rate calculations.
// See: https://github.com/getsentry/sentry-react-native/issues/4141
captureException(error, {
data: { id },
originalException: error,
syntheticException: isErrorLike(error) ? undefined : createSyntheticError(),
mechanism: { handled: true, type: 'onunhandledrejection' },
Comment thread
lucas-zimerman marked this conversation as resolved.
Outdated
});
Comment thread
krystofwoldrich marked this conversation as resolved.
Outdated
},
onHandled: (id: string) => {
if (__DEV__) {
logger.warn(
`Promise Rejection Handled (id: ${id})\n` +
'This means you can ignore any previous messages of the form ' +
`"Possible Unhandled Promise Rejection (id: ${id})"`,
);
}
},
});

logger.log('Unhandled promise rejections will be caught by Sentry.');
} else if (isWeb()) {
logger.log('Using Browser JS promise rejection tracking for React Native Web');

// Use Sentry's built-in global unhandled rejection handler
addGlobalUnhandledRejectionInstrumentationHandler((error: unknown) => {
Comment thread
krystofwoldrich marked this conversation as resolved.
captureException(error, {
originalException: error,
syntheticException: isErrorLike(error) ? undefined : createSyntheticError(),
mechanism: { handled: false, type: 'onunhandledrejection' },
});
});

attachUnhandledRejectionHandler();
checkPromiseAndWarn();
logger.warn(
'Unhandled promise rejections will not be caught by Sentry. ' +
'See https://docs.sentry.io/platforms/react-native/troubleshooting/ for more details.',
);
} else if (patchGlobalPromise) {
// For JSC and other environments, use the existing approach
polyfillPromise();
attachUnhandledRejectionHandler();
checkPromiseAndWarn();
} else {
// For JSC and other environments, patching was disabled by user configuration
logger.log('Unhandled promise rejections will not be caught by Sentry.');
}
} catch (e) {
logger.warn(
'Failed to set up promise rejection tracking. ' +
'Unhandled promise rejections will not be caught by Sentry.' +
'See https://docs.sentry.io/platforms/react-native/troubleshooting/ for more details.',
);
}
}

function attachUnhandledRejectionHandler(): void {
Expand Down Expand Up @@ -77,6 +143,8 @@ function attachUnhandledRejectionHandler(): void {
promiseRejectionTrackingOptions.onUnhandled(id, error);
}

// Marking the rejection as handled to avoid breaking crash rate calculations.
// See: https://github.com/getsentry/sentry-react-native/issues/4141
captureException(error, {
data: { id },
originalException: error,
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/js/utils/worldwide.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@ import { GLOBAL_OBJ } from '@sentry/core';
import type { ErrorUtils } from 'react-native/types';

import type { ExpoGlobalObject } from './expoglobalobject';
export interface HermesPromiseRejectionTrackingOptions {
allRejections: boolean;
onUnhandled: (id: string, error: unknown) => void;
onHandled: (id: string) => void;
}

/** Internal Global object interface with common and Sentry specific properties */
export interface ReactNativeInternalGlobal extends InternalGlobal {
__sentry_rn_v4_registered?: boolean;
__sentry_rn_v5_registered?: boolean;
HermesInternal?: {
getRuntimeProperties?: () => Record<string, string | undefined>;
enablePromiseRejectionTracker?: (options: HermesPromiseRejectionTrackingOptions) => void;
};
Promise: unknown;
__turboModuleProxy: unknown;
Expand Down
Loading