mirror of
https://github.com/expo/react-native-sentry.git
synced 2026-11-14 19:12:02 -10:00
298 lines
8.0 KiB
JavaScript
298 lines
8.0 KiB
JavaScript
import {
|
|
NativeModules
|
|
} from 'react-native';
|
|
import Raven from 'raven-js';;
|
|
|
|
const {
|
|
RNSentry
|
|
} = NativeModules;
|
|
|
|
const DEFAULT_MODULE_IGNORES = [
|
|
"AccessibilityManager",
|
|
"ActionSheetManager",
|
|
"AlertManager",
|
|
"AppState",
|
|
"AsyncLocalStorage",
|
|
"Clipboard",
|
|
"DevLoadingView",
|
|
"DevMenu",
|
|
"ExceptionsManager",
|
|
"I18nManager",
|
|
"ImageEditingManager",
|
|
"ImageStoreManager",
|
|
"ImageViewManager",
|
|
"IOSConstants",
|
|
"JSCExecutor",
|
|
"JSCSamplingProfiler",
|
|
"KeyboardObserver",
|
|
"LinkingManager",
|
|
"LocationObserver",
|
|
"NativeAnimatedModule",
|
|
"NavigatorManager",
|
|
"NetInfo",
|
|
"Networking",
|
|
"RedBox",
|
|
"ScrollViewManager",
|
|
"SettingsManager",
|
|
"SourceCode",
|
|
"StatusBarManager",
|
|
"Timing",
|
|
"UIManager",
|
|
"Vibration",
|
|
"WebSocketModule",
|
|
"WebViewManager"
|
|
];
|
|
|
|
export const SentrySeverity = {
|
|
Fatal: 0,
|
|
Error: 1,
|
|
Warning: 2,
|
|
Info: 3,
|
|
Debug: 4
|
|
}
|
|
|
|
export const SentryLog = {
|
|
None: 0,
|
|
Error: 1,
|
|
Debug: 2,
|
|
Verbose: 3
|
|
}
|
|
|
|
export class Sentry {
|
|
static install() {
|
|
if (RNSentry && RNSentry.nativeClientAvailable) {
|
|
Sentry._nativeClient = new NativeClient(Sentry._dsn, Sentry.options);
|
|
}
|
|
Sentry._ravenClient = new RavenClient(Sentry._dsn, Sentry.options);
|
|
}
|
|
|
|
static config(dsn, options) {
|
|
if (dsn.constructor !== String) {
|
|
throw new Error('Sentry: A DSN must be provided');
|
|
}
|
|
Sentry._dsn = dsn;
|
|
Sentry.options = {
|
|
logLevel: SentryLog.None,
|
|
}
|
|
Object.assign(Sentry.options, options);
|
|
return Sentry;
|
|
}
|
|
|
|
static isNativeClientAvailable() {
|
|
return (Sentry._nativeClient);
|
|
}
|
|
|
|
static crash() {
|
|
throw new Error('Sentry: TEST crash');
|
|
}
|
|
|
|
static nativeCrash() {
|
|
if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.nativeCrash();
|
|
}
|
|
|
|
static setUserContext(user) {
|
|
Sentry._ravenClient.setUserContext(user);
|
|
if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.setUserContext(user);
|
|
}
|
|
|
|
static setTagsContext(tags) {
|
|
Sentry._ravenClient.setTagsContext(tags);
|
|
if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.setTagsContext(tags);
|
|
}
|
|
|
|
static setExtraContext(extra) {
|
|
Sentry._ravenClient.setExtraContext(extra);
|
|
if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.setExtraContext(extra);
|
|
}
|
|
|
|
static captureMessage(message, options) {
|
|
Sentry._ravenClient.captureMessage(message, options);
|
|
}
|
|
|
|
static captureException(ex, options) {
|
|
Sentry._ravenClient.captureException(ex, options);
|
|
}
|
|
|
|
static captureBreadcrumb(msg, options) {
|
|
Sentry._ravenClient.captureBreadcrumb(msg, options);
|
|
}
|
|
|
|
static clearContext(clearContext) {
|
|
if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.clearContext();
|
|
Sentry._ravenClient.clearContext();
|
|
}
|
|
|
|
static context(options, func, args) {
|
|
return Sentry._ravenClient.context(options, func, args);
|
|
}
|
|
|
|
static wrap(options, func, _before) {
|
|
return Sentry._ravenClient.wrap(options, func, _before);
|
|
}
|
|
|
|
// Private helpers
|
|
|
|
static _breadcrumbCallback(crumb) {
|
|
if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.captureBreadcrumb(crumb);
|
|
}
|
|
|
|
static _captureEvent(event) {
|
|
if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.captureEvent(event);
|
|
}
|
|
}
|
|
|
|
class NativeClient {
|
|
constructor(dsn, options) {
|
|
if (dsn.constructor !== String) {
|
|
throw new Error('Sentry: A DSN must be provided');
|
|
}
|
|
if (!RNSentry) {
|
|
throw new Error('Sentry: There is no native client installed.');
|
|
}
|
|
|
|
this._dsn = dsn;
|
|
this._activatedMerging = false;
|
|
this.options = {
|
|
ignoreModulesExclude: [],
|
|
ignoreModulesInclude: [],
|
|
deactivateStacktraceMerging: false
|
|
}
|
|
Object.assign(this.options, options);
|
|
|
|
RNSentry.startWithDsnString(this._dsn);
|
|
if (this.options.deactivateStacktraceMerging === false) {
|
|
this._activateStacktraceMerging();
|
|
}
|
|
RNSentry.setLogLevel(options.logLevel);
|
|
}
|
|
|
|
nativeCrash() {
|
|
RNSentry.crash();
|
|
}
|
|
|
|
captureEvent(event) {
|
|
RNSentry.captureEvent(event);
|
|
}
|
|
|
|
setUserContext(user) {
|
|
RNSentry.setUser(user);
|
|
}
|
|
|
|
setTagsContext(tags) {
|
|
RNSentry.setTags(tags);
|
|
}
|
|
|
|
setExtraContext(extra) {
|
|
RNSentry.setExtra(extra);
|
|
}
|
|
|
|
captureBreadcrumb(crumb) {
|
|
RNSentry.captureBreadcrumb(crumb);
|
|
}
|
|
|
|
clearContext() {
|
|
RNSentry.clearContext();
|
|
}
|
|
|
|
_activateStacktraceMerging = async() => {
|
|
return RNSentry.activateStacktraceMerging().then(activated => {
|
|
if (this._activatedMerging) {
|
|
return;
|
|
}
|
|
this._ignoredModules = {};
|
|
__fbBatchedBridgeConfig.remoteModuleConfig.forEach((module, moduleID) => {
|
|
if (module !== null &&
|
|
this.options.ignoreModulesExclude.indexOf(module[0]) === -1 &&
|
|
(DEFAULT_MODULE_IGNORES.indexOf(module[0]) >= 0 ||
|
|
this.options.ignoreModulesInclude.indexOf(module[0]) >= 0)) {
|
|
this._ignoredModules[moduleID] = true;
|
|
}
|
|
});
|
|
this._activatedMerging = true;
|
|
this._overwriteEnqueueNativeCall();
|
|
});
|
|
}
|
|
|
|
_overwriteEnqueueNativeCall() {
|
|
const BatchedBridge = require('react-native/Libraries/BatchedBridge/BatchedBridge');
|
|
const original = BatchedBridge.enqueueNativeCall;
|
|
const that = this;
|
|
BatchedBridge.enqueueNativeCall = function(moduleID: number, methodID: number, params: Array < any > , onFail: ? Function, onSucc : ? Function) {
|
|
if (that._ignoredModules[moduleID]) {
|
|
return original.apply(this, arguments);
|
|
}
|
|
params.push({
|
|
'__sentry_stack': new Error().stack
|
|
});
|
|
return original.apply(this, arguments);
|
|
}
|
|
}
|
|
}
|
|
|
|
class RavenClient {
|
|
constructor(dsn, options) {
|
|
if (dsn.constructor !== String) {
|
|
throw new Error('SentryClient: A DSN must be provided');
|
|
}
|
|
this._dsn = dsn;
|
|
this.options = {
|
|
allowSecretKey: true,
|
|
}
|
|
Object.assign(this.options, options);
|
|
Raven.addPlugin(require('./raven-plugin'), {
|
|
'nativeClientAvailable': Sentry.isNativeClientAvailable()
|
|
});
|
|
Raven.config(dsn, this.options).install();
|
|
if (options.logLevel >= SentryLog.Debug) {
|
|
Raven.debug = true;
|
|
}
|
|
if (Sentry.isNativeClientAvailable()) {
|
|
// We overwrite the default transport handler when the native
|
|
// client is available, because we want to send the event with native
|
|
Raven.setTransport((options) => {
|
|
Sentry._captureEvent(options.data);
|
|
});
|
|
Raven.setBreadcrumbCallback(Sentry._breadcrumbCallback);
|
|
const oldCaptureBreadcrumb = Raven.captureBreadcrumb;
|
|
Raven.captureBreadcrumb = function(obj) {
|
|
if (obj.data && typeof obj.data === 'object') {
|
|
obj.data = Object.assign({}, obj.data);
|
|
}
|
|
return oldCaptureBreadcrumb.apply(this, arguments);
|
|
}
|
|
}
|
|
}
|
|
|
|
setUserContext(user) {
|
|
Raven.setUserContext(user);
|
|
}
|
|
|
|
setTagsContext(tags) {
|
|
Raven.setTagsContext(tags);
|
|
}
|
|
|
|
setExtraContext(extra) {
|
|
Raven.setExtraContext(extra)
|
|
}
|
|
|
|
captureException(ex, options) {
|
|
Raven.captureException(ex, options);
|
|
}
|
|
|
|
captureBreadcrumb(msg, options) {
|
|
Raven.captureBreadcrumb(msg, options);
|
|
}
|
|
|
|
captureMessage(message, options) {
|
|
Raven.captureMessage(message, options);
|
|
}
|
|
|
|
context(options, func, args) {
|
|
return Raven.context(options, func, args);
|
|
}
|
|
|
|
wrap(options, func, _before) {
|
|
return Raven.wrap(options, func, _before);
|
|
}
|
|
}
|