mirror of
https://github.com/expo/react-native-sentry.git
synced 2026-11-15 09:18:10 -10:00
Merge pull request #8 from getsentry/feature/improve-stacktrace-merging
Improve stacktrace merging
This commit is contained in:
commit
063e0b26da
3 files changed
+118
-32
No files matched your search
+7
-1
@@ -142,7 +142,13 @@ These are functions you can call in your javascript code:
|
||||
// disable stacktrace merging
|
||||
Sentry.config("___DSN___", {
|
||||
deactivateStacktraceMerging: true,
|
||||
logLevel: SentryLog.Debug
|
||||
logLevel: SentryLog.Debug,
|
||||
// These two options will only be considered if stacktrace merging is active
|
||||
// Here you can add modules that should be ignored or exclude modules
|
||||
// that should no longer be ignored from stacktrace merging
|
||||
// ignoreModulesExclude: ["I18nManager"], // Exclude is always stronger than include
|
||||
// ignoreModulesInclude: ["RNSentry"], // Include modules that should be ignored too
|
||||
// ---------------------------------
|
||||
}).install();
|
||||
|
||||
// export an extra context
|
||||
|
||||
+43
-1
@@ -16,6 +16,47 @@
|
||||
[[rootView.bridge moduleForName:@"ExceptionsManager"] initWithDelegate:sentry];
|
||||
}
|
||||
|
||||
+ (NSNumberFormatter *)numberFormatter {
|
||||
static dispatch_once_t onceToken;
|
||||
static NSNumberFormatter *formatter = nil;
|
||||
dispatch_once(&onceToken, ^{
|
||||
formatter = [NSNumberFormatter new];
|
||||
formatter.numberStyle = NSNumberFormatterNoStyle;
|
||||
});
|
||||
return formatter;
|
||||
}
|
||||
|
||||
+ (NSRegularExpression *)frameRegex {
|
||||
static dispatch_once_t onceTokenRegex;
|
||||
static NSRegularExpression *regex = nil;
|
||||
dispatch_once(&onceTokenRegex, ^{
|
||||
// NSString *pattern = @"at (.+?) \\((?:(.+?):([0-9]+?):([0-9]+?))\\)"; // Regex with debugger
|
||||
NSString *pattern = @"(?:([^@]+)@(.+?):([0-9]+?):([0-9]+))"; // Regex without debugger
|
||||
regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:nil];
|
||||
});
|
||||
return regex;
|
||||
}
|
||||
|
||||
NSArray *SentryParseJavaScriptStacktrace(NSString *stacktrace) {
|
||||
NSNumberFormatter *formatter = [RNSentry numberFormatter];
|
||||
NSArray *lines = [stacktrace componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
||||
NSMutableArray *frames = [NSMutableArray array];
|
||||
for (NSString *line in lines) {
|
||||
NSRange searchedRange = NSMakeRange(0, [line length]);
|
||||
NSArray *matches = [[RNSentry frameRegex] matchesInString:line options:0 range:searchedRange];
|
||||
for (NSTextCheckingResult *match in matches) {
|
||||
NSString *matchText = [line substringWithRange:[match range]];
|
||||
[frames addObject:@{
|
||||
@"methodName": [line substringWithRange:[match rangeAtIndex:1]],
|
||||
@"column": [formatter numberFromString:[line substringWithRange:[match rangeAtIndex:4]]],
|
||||
@"lineNumber": [formatter numberFromString:[line substringWithRange:[match rangeAtIndex:3]]],
|
||||
@"file": [line substringWithRange:[match rangeAtIndex:2]]
|
||||
}];
|
||||
}
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (NSDictionary<NSString *, id> *)constantsToExport
|
||||
@@ -23,6 +64,7 @@ RCT_EXPORT_MODULE()
|
||||
return @{@"nativeClientAvailable": @YES};
|
||||
}
|
||||
|
||||
|
||||
RCT_EXPORT_METHOD(startWithDsnString:(NSString * _Nonnull)dsnString)
|
||||
{
|
||||
[SentryClient setShared:[[SentryClient alloc] initWithDsnString:[RCTConvert NSString:dsnString]]];
|
||||
@@ -47,7 +89,7 @@ RCT_EXPORT_METHOD(activateStacktraceMerging:(RCTPromiseResolveBlock)resolve
|
||||
if ([param isKindOfClass:NSDictionary.class] && param[@"__sentry_stack"]) {
|
||||
@synchronized ([SentryClient shared]) {
|
||||
[[SentryClient shared] addExtra:@"__sentry_address" value:[NSNumber numberWithUnsignedInteger:callNativeModuleAddress]];
|
||||
[[SentryClient shared] addExtra:@"__sentry_stack" value:param[@"__sentry_stack"]];
|
||||
[[SentryClient shared] addExtra:@"__sentry_stack" value:SentryParseJavaScriptStacktrace([RCTConvert NSString:param[@"__sentry_stack"]])];
|
||||
}
|
||||
} else {
|
||||
if (param != nil) {
|
||||
|
||||
+68
-30
@@ -1,4 +1,6 @@
|
||||
import { NativeModules } from 'react-native';
|
||||
import {
|
||||
NativeModules
|
||||
} from 'react-native';
|
||||
import Raven from 'raven-js';
|
||||
require('raven-js/plugins/react-native')(Raven);
|
||||
|
||||
@@ -6,6 +8,42 @@ 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,
|
||||
@@ -80,6 +118,14 @@ class NativeClient {
|
||||
if (options && options.logLevel) {
|
||||
RNSentry.setLogLevel(options.logLevel);
|
||||
}
|
||||
this._ignoreModulesExclude = [];
|
||||
if (options && options.ignoreModulesExclude) {
|
||||
this._ignoreModulesExclude = options.ignoreModulesExclude;
|
||||
}
|
||||
this._ignoreModulesInclude = [];
|
||||
if (options && options.ignoreModulesInclude) {
|
||||
this._ignoreModulesInclude = options.ignoreModulesInclude;
|
||||
}
|
||||
if (this._deactivateStacktraceMerging === false) {
|
||||
this._activateStacktraceMerging();
|
||||
}
|
||||
@@ -115,40 +161,30 @@ class NativeClient {
|
||||
if (this._activatedMerging) {
|
||||
return;
|
||||
}
|
||||
this._ignoredModules = {};
|
||||
__fbBatchedBridgeConfig.remoteModuleConfig.forEach((module, moduleID) => {
|
||||
if (module !== null &&
|
||||
this._ignoreModulesExclude.indexOf(module[0]) == -1 &&
|
||||
(DEFAULT_MODULE_IGNORES.indexOf(module[0]) >= 0 ||
|
||||
this._ignoreModulesInclude.indexOf(module[0]) >= 0)) {
|
||||
this._ignoredModules[moduleID] = true;
|
||||
}
|
||||
});
|
||||
this._activatedMerging = true;
|
||||
this._overwriteEnqueueNativeCall();
|
||||
});
|
||||
}
|
||||
|
||||
_overwriteEnqueueNativeCall = () => {
|
||||
let BatchedBridge = require('react-native/Libraries/BatchedBridge/BatchedBridge');
|
||||
let parseErrorStack = require('react-native/Libraries/Core/Devtools/parseErrorStack');
|
||||
let original = BatchedBridge.enqueueNativeCall;
|
||||
BatchedBridge.enqueueNativeCall = function(moduleID: number, methodID: number, params: Array<any>, onFail: ?Function, onSucc: ?Function) {
|
||||
const stack = parseErrorStack(new Error());
|
||||
let sendStacktrace = true;
|
||||
stack.forEach(function (frame) {
|
||||
if (frame.methodName) {
|
||||
const mN = frame.methodName
|
||||
if (mN.match("createAnimatedNode") ||
|
||||
mN.match("setupDevtools") ||
|
||||
mN.match("stopAnimation") ||
|
||||
mN.match("TimingAnimation") ||
|
||||
mN.match("startAnimatingNode") ||
|
||||
mN.match("AnimatedStyle") ||
|
||||
mN.match("_cancelLongPressDelayTimeout") ||
|
||||
mN.match("__startNativeAnimation") ||
|
||||
mN.match("extractEvents") ||
|
||||
mN.match("WebSocket.")) {
|
||||
sendStacktrace = false;
|
||||
}
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
if (sendStacktrace) {
|
||||
params.push({'__sentry_stack' : stack});
|
||||
}
|
||||
return original.apply(this, arguments);
|
||||
params.push({ '__sentry_stack': new Error().stack });
|
||||
return original.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,7 +199,9 @@ class RavenClient {
|
||||
if (options === null || options === undefined) {
|
||||
options = {};
|
||||
}
|
||||
Object.assign(options, { allowSecretKey: true });
|
||||
Object.assign(options, {
|
||||
allowSecretKey: true
|
||||
});
|
||||
Raven.config(dsn, options).install();
|
||||
}
|
||||
|
||||
@@ -186,7 +224,7 @@ class RavenClient {
|
||||
|
||||
captureMessage = async(message, options) => {
|
||||
if (options && options.level) {
|
||||
switch(options.level) {
|
||||
switch (options.level) {
|
||||
case SentrySeverity.Warning:
|
||||
options.level = 'warning';
|
||||
break;
|
||||
|
||||
Reference in new issue
Block a user