|
1 | 1 | import type { EventHint, Integration, SeverityLevel } from '@sentry/core'; |
2 | | -import { addExceptionMechanism, captureException, getClient, getCurrentScope, logger } from '@sentry/core'; |
3 | | - |
| 2 | +import { |
| 3 | + addExceptionMechanism, |
| 4 | + addGlobalUnhandledRejectionInstrumentationHandler, |
| 5 | + captureException, |
| 6 | + getClient, |
| 7 | + getCurrentScope, |
| 8 | + logger, |
| 9 | +} from '@sentry/core'; |
| 10 | + |
| 11 | +import { isHermesEnabled, isWeb } from '../utils/environment'; |
4 | 12 | import { createSyntheticError, isErrorLike } from '../utils/error'; |
5 | 13 | import { RN_GLOBAL_OBJ } from '../utils/worldwide'; |
6 | 14 | import { checkPromiseAndWarn, polyfillPromise, requireRejectionTracking } from './reactnativeerrorhandlersutils'; |
@@ -44,49 +52,83 @@ function setup(options: ReactNativeErrorHandlersOptions): void { |
44 | 52 | * Setup unhandled promise rejection tracking |
45 | 53 | */ |
46 | 54 | function setupUnhandledRejectionsTracking(patchGlobalPromise: boolean): void { |
47 | | - if (patchGlobalPromise) { |
48 | | - polyfillPromise(); |
49 | | - } |
| 55 | + try { |
| 56 | + if ( |
| 57 | + isHermesEnabled() && |
| 58 | + RN_GLOBAL_OBJ.HermesInternal?.enablePromiseRejectionTracker && |
| 59 | + RN_GLOBAL_OBJ?.HermesInternal?.hasPromise?.() |
| 60 | + ) { |
| 61 | + logger.log('Using Hermes native promise rejection tracking'); |
| 62 | + |
| 63 | + RN_GLOBAL_OBJ.HermesInternal.enablePromiseRejectionTracker({ |
| 64 | + allRejections: true, |
| 65 | + onUnhandled: promiseRejectionTrackingOptions.onUnhandled, |
| 66 | + onHandled: promiseRejectionTrackingOptions.onHandled, |
| 67 | + }); |
50 | 68 |
|
51 | | - attachUnhandledRejectionHandler(); |
52 | | - checkPromiseAndWarn(); |
| 69 | + logger.log('Unhandled promise rejections will be caught by Sentry.'); |
| 70 | + } else if (isWeb()) { |
| 71 | + logger.log('Using Browser JS promise rejection tracking for React Native Web'); |
| 72 | + |
| 73 | + // Use Sentry's built-in global unhandled rejection handler |
| 74 | + addGlobalUnhandledRejectionInstrumentationHandler((error: unknown) => { |
| 75 | + captureException(error, { |
| 76 | + originalException: error, |
| 77 | + syntheticException: isErrorLike(error) ? undefined : createSyntheticError(), |
| 78 | + mechanism: { handled: false, type: 'onunhandledrejection' }, |
| 79 | + }); |
| 80 | + }); |
| 81 | + } else if (patchGlobalPromise) { |
| 82 | + // For JSC and other environments, use the existing approach |
| 83 | + polyfillPromise(); |
| 84 | + attachUnhandledRejectionHandler(); |
| 85 | + checkPromiseAndWarn(); |
| 86 | + } else { |
| 87 | + // For JSC and other environments, patching was disabled by user configuration |
| 88 | + logger.log('Unhandled promise rejections will not be caught by Sentry.'); |
| 89 | + } |
| 90 | + } catch (e) { |
| 91 | + logger.warn( |
| 92 | + 'Failed to set up promise rejection tracking. ' + |
| 93 | + 'Unhandled promise rejections will not be caught by Sentry.' + |
| 94 | + 'See https://docs.sentry.io/platforms/react-native/troubleshooting/ for more details.', |
| 95 | + ); |
| 96 | + } |
53 | 97 | } |
54 | 98 |
|
55 | | -function attachUnhandledRejectionHandler(): void { |
56 | | - const tracking = requireRejectionTracking(); |
| 99 | +const promiseRejectionTrackingOptions: PromiseRejectionTrackingOptions = { |
| 100 | + onUnhandled: (id, error: unknown, rejection = {}) => { |
| 101 | + if (__DEV__) { |
| 102 | + logger.warn(`Possible Unhandled Promise Rejection (id: ${id}):\n${rejection}`); |
| 103 | + } |
57 | 104 |
|
58 | | - const promiseRejectionTrackingOptions: PromiseRejectionTrackingOptions = { |
59 | | - onUnhandled: (id, rejection = {}) => { |
60 | | - // eslint-disable-next-line no-console |
61 | | - console.warn(`Possible Unhandled Promise Rejection (id: ${id}):\n${rejection}`); |
62 | | - }, |
63 | | - onHandled: id => { |
64 | | - // eslint-disable-next-line no-console |
65 | | - console.warn( |
| 105 | + // Marking the rejection as handled to avoid breaking crash rate calculations. |
| 106 | + // See: https://github.com/getsentry/sentry-react-native/issues/4141 |
| 107 | + captureException(error, { |
| 108 | + data: { id }, |
| 109 | + originalException: error, |
| 110 | + syntheticException: isErrorLike(error) ? undefined : createSyntheticError(), |
| 111 | + mechanism: { handled: true, type: 'onunhandledrejection' }, |
| 112 | + }); |
| 113 | + }, |
| 114 | + onHandled: id => { |
| 115 | + if (__DEV__) { |
| 116 | + logger.warn( |
66 | 117 | `Promise Rejection Handled (id: ${id})\n` + |
67 | 118 | 'This means you can ignore any previous messages of the form ' + |
68 | 119 | `"Possible Unhandled Promise Rejection (id: ${id}):"`, |
69 | 120 | ); |
70 | | - }, |
71 | | - }; |
| 121 | + } |
| 122 | + }, |
| 123 | +}; |
| 124 | + |
| 125 | +function attachUnhandledRejectionHandler(): void { |
| 126 | + const tracking = requireRejectionTracking(); |
72 | 127 |
|
73 | 128 | tracking.enable({ |
74 | 129 | allRejections: true, |
75 | | - onUnhandled: (id: string, error: unknown) => { |
76 | | - if (__DEV__) { |
77 | | - promiseRejectionTrackingOptions.onUnhandled(id, error); |
78 | | - } |
79 | | - |
80 | | - captureException(error, { |
81 | | - data: { id }, |
82 | | - originalException: error, |
83 | | - syntheticException: isErrorLike(error) ? undefined : createSyntheticError(), |
84 | | - mechanism: { handled: true, type: 'onunhandledrejection' }, |
85 | | - }); |
86 | | - }, |
87 | | - onHandled: (id: string) => { |
88 | | - promiseRejectionTrackingOptions.onHandled(id); |
89 | | - }, |
| 130 | + onUnhandled: promiseRejectionTrackingOptions.onUnhandled, |
| 131 | + onHandled: promiseRejectionTrackingOptions.onHandled, |
90 | 132 | }); |
91 | 133 | } |
92 | 134 |
|
|
0 commit comments