@sentry/react-native
Version:
Official Sentry SDK for react-native
208 lines • 8.86 kB
JavaScript
import { eventFromException, eventFromMessage } from '@sentry/browser';
import { _INTERNAL_flushLogsBuffer, addAutoIpAddressToSession, Client, dateTimestampInSeconds, debug, SentryError, } from '@sentry/core';
import { Alert } from 'react-native';
import { getDevServer } from './integrations/debugsymbolicatorutils';
import { defaultSdkInfo } from './integrations/sdkinfo';
import { getDefaultSidecarUrl } from './integrations/spotlight';
import { MOBILE_REPLAY_INTEGRATION_NAME } from './replay/mobilereplay';
import { createUserFeedbackEnvelope, items } from './utils/envelope';
import { ignoreRequireCycleLogs } from './utils/ignorerequirecyclelogs';
import { mergeOutcomes } from './utils/outcome';
import { ReactNativeLibraries } from './utils/rnlibraries';
import { NATIVE } from './wrapper';
const DEFAULT_FLUSH_INTERVAL = 5000;
/**
* The Sentry React Native SDK Client.
*
* @see ReactNativeClientOptions for documentation on configuration options.
* @see SentryClient for usage documentation.
*/
export class ReactNativeClient extends Client {
/**
* Creates a new React Native SDK instance.
* @param options Configuration options for this SDK.
*/
constructor(options) {
var _a, _b, _c, _d;
ignoreRequireCycleLogs((_a = ReactNativeLibraries.ReactNativeVersion) === null || _a === void 0 ? void 0 : _a.version);
options._metadata = Object.assign(Object.assign({}, options._metadata), { sdk: Object.assign(Object.assign({}, (((_b = options._metadata) === null || _b === void 0 ? void 0 : _b.sdk) || defaultSdkInfo)), { settings: Object.assign({
// Only allow IP inferral by Relay if sendDefaultPii is true
infer_ip: options.sendDefaultPii ? 'auto' : 'never' }, (_d = (_c = options._metadata) === null || _c === void 0 ? void 0 : _c.sdk) === null || _d === void 0 ? void 0 : _d.settings) }) });
// We default this to true, as it is the safer scenario
options.parentSpanIsAlwaysRootSpan =
options.parentSpanIsAlwaysRootSpan === undefined ? true : options.parentSpanIsAlwaysRootSpan;
// enableLogs must be disabled before calling super() to avoid logs being captured.
// This makes a copy of the user defined value, so we can restore it later for the native usaege.
const originalEnableLogs = options.enableLogs;
if (options.enableLogs && options.logsOrigin === 'native') {
debug.log('disabling Sentry logs on JavaScript due to rule set by logsOrigin');
options.enableLogs = false;
}
super(options);
this._outcomesBuffer = [];
if (options.sendDefaultPii === true) {
this.on('beforeSendSession', addAutoIpAddressToSession);
}
if (options.enableLogs) {
this.on('flush', () => {
_INTERNAL_flushLogsBuffer(this);
});
this.on('afterCaptureLog', () => {
if (this._logFlushIdleTimeout) {
clearTimeout(this._logFlushIdleTimeout);
}
this._logFlushIdleTimeout = setTimeout(() => {
_INTERNAL_flushLogsBuffer(this);
}, DEFAULT_FLUSH_INTERVAL);
});
}
// Restore original settings for enabling Native options.
options.enableLogs = originalEnableLogs;
}
/**
* @inheritDoc
*/
eventFromException(exception, hint = {}) {
return eventFromException(this._options.stackParser, exception, hint, this._options.attachStacktrace);
}
/**
* @inheritDoc
*/
eventFromMessage(message, level, hint) {
return eventFromMessage(this._options.stackParser, message, level, hint, this._options.attachStacktrace);
}
/**
* If native client is available it will trigger a native crash.
* Use this only for testing purposes.
*/
nativeCrash() {
NATIVE.nativeCrash();
}
/**
* @inheritDoc
*/
close() {
// As super.close() flushes queued events, we wait for that to finish before closing the native SDK.
return super.close().then((result) => {
return NATIVE.closeNativeSdk().then(() => result);
});
}
/**
* Sends user feedback to Sentry.
* @deprecated Use `Sentry.captureFeedback` instead.
*/
captureUserFeedback(feedback) {
const envelope = createUserFeedbackEnvelope(feedback, {
metadata: this._options._metadata,
dsn: this.getDsn(),
tunnel: undefined,
});
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.sendEnvelope(envelope);
}
/**
* @inheritdoc
*/
sendEnvelope(envelope) {
const outcomes = this._clearOutcomes();
this._outcomesBuffer = mergeOutcomes(this._outcomesBuffer, outcomes);
if (this._options.sendClientReports) {
this._attachClientReportTo(this._outcomesBuffer, envelope);
}
let shouldClearOutcomesBuffer = true;
if (this._isEnabled() && this._transport && this._dsn) {
this.emit('beforeEnvelope', envelope);
this._transport.send(envelope).then(null, reason => {
if (reason instanceof SentryError) {
// SentryError is thrown by SyncPromise
shouldClearOutcomesBuffer = false;
// If this is called asynchronously we want the _outcomesBuffer to be cleared
debug.error('SentryError while sending event, keeping outcomes buffer:', reason);
}
else {
debug.error('Error while sending event:', reason);
}
});
}
else {
debug.error('Transport disabled');
}
if (shouldClearOutcomesBuffer) {
this._outcomesBuffer = []; // if send fails synchronously the _outcomesBuffer will stay intact
}
return Promise.resolve({});
}
/**
* @inheritDoc
*/
init() {
super.init();
this._initNativeSdk();
}
/**
* Register a hook on this client.
*
* (Generic method signature to allow for custom React Native Client events.)
*/
on(hook, callback) {
// @ts-expect-error on from the base class doesn't support generic types
return super.on(hook, callback);
}
/**
* Emit a hook that was previously registered via `on()`.
*
* (Generic method signature to allow for custom React Native Client events.)
*/
emit(hook, ...rest) {
// @ts-expect-error emit from the base class doesn't support generic types
super.emit(hook, ...rest);
}
/**
* Starts native client with dsn and options
*/
_initNativeSdk() {
var _a;
NATIVE.initNativeSdk(Object.assign(Object.assign({}, this._options), { defaultSidecarUrl: getDefaultSidecarUrl(), devServerUrl: ((_a = getDevServer()) === null || _a === void 0 ? void 0 : _a.url) || '', mobileReplayOptions: this._integrations[MOBILE_REPLAY_INTEGRATION_NAME] &&
'options' in this._integrations[MOBILE_REPLAY_INTEGRATION_NAME]
? this._integrations[MOBILE_REPLAY_INTEGRATION_NAME].options
: undefined }))
.then((result) => {
return result;
}, () => {
this._showCannotConnectDialog();
return false;
})
.then((didCallNativeInit) => {
var _a, _b;
(_b = (_a = this._options).onReady) === null || _b === void 0 ? void 0 : _b.call(_a, { didCallNativeInit });
this.emit('afterInit');
})
.then(undefined, error => {
debug.error('The OnReady callback threw an error: ', error);
});
}
/**
* If the user is in development mode, and the native nagger is enabled then it will show an alert.
*/
_showCannotConnectDialog() {
if (__DEV__ && this._options.enableNativeNagger) {
Alert.alert('Sentry', 'Warning, could not connect to Sentry native SDK.\nIf you do not want to use the native component please pass `enableNative: false` in the options.\nVisit: https://docs.sentry.io/platforms/react-native/ for more details.');
}
}
/**
* Attaches a client report from outcomes to the envelope.
*/
_attachClientReportTo(outcomes, envelope) {
if (outcomes.length > 0) {
const clientReportItem = [
{ type: 'client_report' },
{
timestamp: dateTimestampInSeconds(),
discarded_events: outcomes,
},
];
envelope[items].push(clientReportItem);
}
}
}
//# sourceMappingURL=client.js.map