@honeybadger-io/react-native 0.0.12 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -3
- package/{LICENSE → MIT-LICENSE} +0 -0
- package/README.md +14 -14
- package/example/App.js +12 -12
- package/example/index.js +2 -2
- package/example/package-lock.json +19209 -7534
- package/example/package.json +1 -1
- package/example/yarn.lock +6316 -6198
- package/index.d.ts +38 -38
- package/index.js +413 -412
- package/package.json +9 -7
package/index.js
CHANGED
|
@@ -13,10 +13,10 @@ const HoneybadgerNativeModule = NativeModules.HoneybadgerReactNative;
|
|
|
13
13
|
let _apiKey = null;
|
|
14
14
|
let _initialized = false;
|
|
15
15
|
let _context = {};
|
|
16
|
-
let _logLevel =
|
|
16
|
+
let _logLevel = 'warning'; // "error", "warning", "debug"
|
|
17
17
|
let _previousJSGlobalExceptionHandler = null;
|
|
18
|
-
let _revision =
|
|
19
|
-
let _projectRoot =
|
|
18
|
+
let _revision = '';
|
|
19
|
+
let _projectRoot = '';
|
|
20
20
|
let _reportErrors = true;
|
|
21
21
|
|
|
22
22
|
|
|
@@ -26,143 +26,143 @@ let _reportErrors = true;
|
|
|
26
26
|
// ----------------------------------------------------------------------------
|
|
27
27
|
const honeybadger = {
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
/**
|
|
30
30
|
* Initialize and configure the Honeybadger React Native library.
|
|
31
31
|
* @param {string} apiKey - Your Honeybadger API key.
|
|
32
32
|
* @param {boolean} [reportErrors=true] reportErrors - Whether to send error reports to Honeybadger (disable for dev environments, etc.)
|
|
33
33
|
* @param {string=} revision - The git revision of the current build.
|
|
34
34
|
* @param {string=} projectRoot - The path to the project root.
|
|
35
35
|
*/
|
|
36
|
-
|
|
36
|
+
configure ( apiKey, reportErrors = true, revision = '', projectRoot = '' )
|
|
37
|
+
{
|
|
38
|
+
if ( !isValidAPIKey(apiKey) )
|
|
37
39
|
{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
_apiKey = apiKey.trim();
|
|
45
|
-
_revision = (revision || '').trim();
|
|
46
|
-
_projectRoot = (projectRoot || '').trim();
|
|
47
|
-
_reportErrors = reportErrors;
|
|
48
|
-
|
|
49
|
-
if ( !_initialized )
|
|
50
|
-
{
|
|
51
|
-
setNativeExceptionHandler();
|
|
52
|
-
setJavaScriptErrorHandler();
|
|
53
|
-
_initialized = true;
|
|
54
|
-
}
|
|
55
|
-
},
|
|
40
|
+
informUserOfInvalidAPIKey();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
56
43
|
|
|
57
|
-
|
|
44
|
+
_apiKey = apiKey.trim();
|
|
45
|
+
_revision = (revision || '').trim();
|
|
46
|
+
_projectRoot = (projectRoot || '').trim();
|
|
47
|
+
_reportErrors = reportErrors;
|
|
48
|
+
|
|
49
|
+
if ( !_initialized )
|
|
50
|
+
{
|
|
51
|
+
setNativeExceptionHandler();
|
|
52
|
+
setJavaScriptErrorHandler();
|
|
53
|
+
_initialized = true;
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
58
|
* Send any kind of error, exception, object, String, etc. to Honeybadger.
|
|
59
59
|
* @param {string|object} err - The error string or object.
|
|
60
60
|
* @param {string|object} additionalData - Additional data to include with the error.
|
|
61
61
|
*/
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
62
|
+
notify ( err, additionalData )
|
|
63
|
+
{
|
|
64
|
+
if ( !isValidAPIKey(_apiKey) ) {
|
|
65
|
+
informUserOfInvalidAPIKey();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if ( !err || (isString(err) && err.trim().length === 0) || (isObject(err) && err.length === 0) ) {
|
|
70
|
+
logError('Honeybadger.notify() - invalid error');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if ( isString(err) ) {
|
|
75
|
+
err = {
|
|
76
|
+
'message' : err.trim(),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if ( isStringWithValue(additionalData) ) {
|
|
81
|
+
additionalData = {
|
|
82
|
+
'additionalData' : additionalData.trim(),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
else if ( !isObject(additionalData) ) {
|
|
86
|
+
additionalData = {};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const errName = safeStringFromField(err, 'name', 'Error via notify()');
|
|
90
|
+
const errMsg = safeStringFromField(err, 'message', 'Unknown error message');
|
|
91
|
+
|
|
92
|
+
let contextForThisError = {};
|
|
93
|
+
Object.assign(contextForThisError, _context);
|
|
94
|
+
Object.assign(contextForThisError, additionalData);
|
|
95
|
+
|
|
96
|
+
let payloadData = {
|
|
97
|
+
errorClass : `React Native ${(Platform.OS === 'ios' ? 'iOS' : 'Android')} ${errName}`,
|
|
98
|
+
errorMsg : errMsg,
|
|
99
|
+
details: {
|
|
100
|
+
initialHandler: 'notify',
|
|
101
|
+
},
|
|
102
|
+
context: contextForThisError,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
let backTrace = backTraceFromJavaScriptError(err);
|
|
106
|
+
|
|
107
|
+
if ( arrayHasValues(backTrace.framesFromComponentStack) ) {
|
|
108
|
+
payloadData.backTrace = backTrace.framesFromComponentStack;
|
|
109
|
+
payloadData.details.primaryBackTraceSource = 'ReactNativeComponentStack';
|
|
110
|
+
if ( arrayHasValues(backTrace.framesFromJavaScriptErrorStack) ) {
|
|
111
|
+
payloadData.details.javaScriptStackTrace = backTrace.framesFromJavaScriptErrorStack;
|
|
112
|
+
}
|
|
113
|
+
} else if ( arrayHasValues(backTrace.framesFromJavaScriptErrorStack) ) {
|
|
114
|
+
payloadData.backTrace = backTrace.framesFromJavaScriptErrorStack;
|
|
115
|
+
payloadData.details.primaryBackTraceSource = 'JavaScriptErrorStack';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
sendToHoneybadger(buildPayload(payloadData));
|
|
119
|
+
},
|
|
120
120
|
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
/**
|
|
123
123
|
* Include additional data whenever an error or an exception occurs. This can be called as many times as needed. New context data will be merged with any previously-set context data.
|
|
124
124
|
* @param {object} context - Additional data to include with all errors and exceptions.
|
|
125
125
|
*/
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
126
|
+
setContext ( context )
|
|
127
|
+
{
|
|
128
|
+
if ( isObject(context) ) {
|
|
129
|
+
Object.assign(_context, context);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
132
|
|
|
133
133
|
|
|
134
|
-
|
|
134
|
+
/**
|
|
135
135
|
* Clears/resets any data previously set through setContext().
|
|
136
136
|
* @param {object=} context - Optional new context to set.
|
|
137
137
|
*/
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
resetContext ( context )
|
|
139
|
+
{
|
|
140
|
+
_context = (isObject(context) ? context : {});
|
|
141
|
+
},
|
|
142
142
|
|
|
143
143
|
|
|
144
|
-
|
|
145
|
-
* Sets the logging level for the Honeybadger library.
|
|
144
|
+
/**
|
|
145
|
+
* Sets the logging level for the Honeybadger library.
|
|
146
146
|
* @param {('debug'|'warning'|'error')} [level=warning] - The logging level.
|
|
147
147
|
*/
|
|
148
|
-
|
|
148
|
+
setLogLevel ( level = 'warning' )
|
|
149
|
+
{
|
|
150
|
+
if ( !level ) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
switch (level.toLowerCase())
|
|
149
155
|
{
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
case "error":
|
|
159
|
-
_logLevel = level.toLowerCase();
|
|
160
|
-
break;
|
|
161
|
-
|
|
162
|
-
default:
|
|
163
|
-
logWarning("Honeybadger: Log level should be one of 'debug', 'warning', or 'error'.");
|
|
164
|
-
}
|
|
156
|
+
case 'debug':
|
|
157
|
+
case 'warning':
|
|
158
|
+
case 'error':
|
|
159
|
+
_logLevel = level.toLowerCase();
|
|
160
|
+
break;
|
|
161
|
+
|
|
162
|
+
default:
|
|
163
|
+
logWarning("Honeybadger: Log level should be one of 'debug', 'warning', or 'error'.");
|
|
165
164
|
}
|
|
165
|
+
}
|
|
166
166
|
};
|
|
167
167
|
|
|
168
168
|
|
|
@@ -175,177 +175,178 @@ export default honeybadger;
|
|
|
175
175
|
// -----------------------------------------------------------------------------
|
|
176
176
|
|
|
177
177
|
function informUserOfInvalidAPIKey() {
|
|
178
|
-
|
|
178
|
+
logError('Please initialize Honeybadger by calling configure() with a valid Honeybadger.io API key.');
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
|
|
182
182
|
function isValidAPIKey(apiKey) {
|
|
183
|
-
|
|
183
|
+
return apiKey && apiKey.trim().length > 0;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
|
|
187
187
|
|
|
188
|
-
function setJavaScriptErrorHandler()
|
|
188
|
+
function setJavaScriptErrorHandler()
|
|
189
189
|
{
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
_previousJSGlobalExceptionHandler = global.ErrorUtils.getGlobalHandler();
|
|
190
|
+
logDebug('Setting up the JavaScript global error handler.');
|
|
193
191
|
|
|
194
|
-
|
|
195
|
-
logDebug("JavaScript global error handler triggered.");
|
|
192
|
+
_previousJSGlobalExceptionHandler = global.ErrorUtils.getGlobalHandler();
|
|
196
193
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
});
|
|
194
|
+
global.ErrorUtils.setGlobalHandler(function(err, isFatal) {
|
|
195
|
+
logDebug('JavaScript global error handler triggered.');
|
|
200
196
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if ( _previousJSGlobalExceptionHandler && _previousJSGlobalExceptionHandler != this ) {
|
|
204
|
-
logDebug("Passing error to previous error handler.");
|
|
205
|
-
_previousJSGlobalExceptionHandler(err, isFatal);
|
|
206
|
-
}
|
|
197
|
+
onJavaScriptError(err, {
|
|
198
|
+
initialHandler: 'Global JavaScript Error Handler',
|
|
207
199
|
});
|
|
200
|
+
|
|
201
|
+
// Allowing the default error handler to process the error after
|
|
202
|
+
// we're done with it will show the useful RN red info box in dev.
|
|
203
|
+
if ( _previousJSGlobalExceptionHandler && _previousJSGlobalExceptionHandler != this ) {
|
|
204
|
+
logDebug('Passing error to previous error handler.');
|
|
205
|
+
_previousJSGlobalExceptionHandler(err, isFatal);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
|
|
211
211
|
|
|
212
212
|
function setNativeExceptionHandler() {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
213
|
+
if ( !HoneybadgerNativeModule ) {
|
|
214
|
+
logError('Honeybadger: The native module was not found. Please review the installation instructions.');
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
217
|
|
|
218
|
-
|
|
219
|
-
|
|
218
|
+
logDebug('Starting HoneyBadger native module.');
|
|
219
|
+
HoneybadgerNativeModule.start();
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
const nativeEventEmitter = new NativeEventEmitter(HoneybadgerNativeModule);
|
|
222
222
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
223
|
+
logDebug('Listening for native exceptions...');
|
|
224
|
+
nativeEventEmitter.addListener('native-exception-event', function(data) {
|
|
225
|
+
switch ( Platform.OS ) {
|
|
226
|
+
case 'ios': onNativeIOSException(data); break;
|
|
227
|
+
case 'android': onNativeAndroidException(data); break;
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
|
|
233
233
|
|
|
234
234
|
function sendToHoneybadger(payload) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
235
|
+
if ( !payload || !isValidAPIKey(_apiKey) || !_reportErrors ) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
238
|
|
|
239
239
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
const params = {
|
|
243
|
-
method: 'POST',
|
|
244
|
-
headers: {
|
|
245
|
-
'Content-Type' : 'application/json',
|
|
246
|
-
'Accept' : 'text/json, application/json',
|
|
247
|
-
'X-API-Key' : _apiKey,
|
|
248
|
-
'User-Agent' : buildUserAgent(),
|
|
249
|
-
},
|
|
250
|
-
body: JSON.stringify(payload)
|
|
251
|
-
};
|
|
240
|
+
logDebug('Sending error report to Honeybadger...');
|
|
252
241
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
242
|
+
const params = {
|
|
243
|
+
method: 'POST',
|
|
244
|
+
headers: {
|
|
245
|
+
'Content-Type' : 'application/json',
|
|
246
|
+
'Accept' : 'text/json, application/json',
|
|
247
|
+
'X-API-Key' : _apiKey,
|
|
248
|
+
'User-Agent' : buildUserAgent(),
|
|
249
|
+
},
|
|
250
|
+
body: JSON.stringify(payload)
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
fetch('https://api.honeybadger.io/v1/notices/js', params).then(response => {
|
|
254
|
+
if ( !response.ok ) {
|
|
255
|
+
logDebug(`Failed to post error to Honeybadger: ${response.status}`);
|
|
256
|
+
logDebug(response);
|
|
257
|
+
} else {
|
|
258
|
+
logDebug('Successful post to Honeybadger.');
|
|
259
|
+
logDebug(response);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
|
|
265
265
|
|
|
266
266
|
function buildUserAgent() {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
267
|
+
let reactNativeVersion = `${Platform.constants.reactNativeVersion.major}.${Platform.constants.reactNativeVersion.minor}.${Platform.constants.reactNativeVersion.patch}`;
|
|
268
|
+
const nativePlatformName = Platform.constants.systemName || (Platform.OS === 'ios' ? 'iOS' : 'Android');
|
|
269
|
+
const nativePlatformVersion = Platform.constants.osVersion || '';
|
|
270
|
+
let nativePlatform = `${nativePlatformName} ${nativePlatformVersion}`;
|
|
271
|
+
return `${pkg.name} ${pkg.version}; ${reactNativeVersion}; ${nativePlatform}`;
|
|
272
272
|
}
|
|
273
273
|
|
|
274
274
|
|
|
275
275
|
|
|
276
276
|
function buildPayload ( data ) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
277
|
+
let payload = {
|
|
278
|
+
notifier : {
|
|
279
|
+
name : pkg.name,
|
|
280
|
+
url : pkg.repository.url,
|
|
281
|
+
version : pkg.version,
|
|
282
|
+
},
|
|
283
|
+
error : {
|
|
284
|
+
class : data.errorClass || 'React Native Error',
|
|
285
|
+
message : data.errorMsg || 'Unknown Error',
|
|
286
|
+
backtrace : data.backTrace,
|
|
287
|
+
},
|
|
288
|
+
request : {
|
|
289
|
+
context: data.context,
|
|
290
|
+
},
|
|
291
|
+
server : {
|
|
292
|
+
// eslint-disable-next-line no-undef
|
|
293
|
+
environment_name: (__DEV__ ? 'development' : 'production'),
|
|
294
|
+
project_root: _projectRoot || '',
|
|
295
|
+
revision: _revision || ''
|
|
296
|
+
},
|
|
297
|
+
};
|
|
297
298
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}
|
|
299
|
+
if ( data.details && data.details.length > 0 ) {
|
|
300
|
+
payload.details = {
|
|
301
|
+
'React Native' : data.details
|
|
302
302
|
}
|
|
303
|
+
}
|
|
303
304
|
|
|
304
|
-
|
|
305
|
+
return payload;
|
|
305
306
|
}
|
|
306
307
|
|
|
307
308
|
|
|
308
309
|
|
|
309
310
|
function framesFromComponentStack(str) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
318
|
-
frames.push({
|
|
319
|
-
method: match && match.length > 1 ? match[1] : '',
|
|
320
|
-
file: match && match.length > 3 ? match[3] : '',
|
|
321
|
-
number: match && match.length > 4 ? match[4] : '',
|
|
322
|
-
});
|
|
311
|
+
str = str || '';
|
|
312
|
+
let frames = [];
|
|
313
|
+
const regex = /^\s*in\s(\S+)(\s\(at\s(\S+):(\S+)\)\s*$)?/gm;
|
|
314
|
+
let match;
|
|
315
|
+
while ( (match = regex.exec(str)) !== null ) {
|
|
316
|
+
if ( match.index === regex.lastIndex) {
|
|
317
|
+
regex.lastIndex++;
|
|
323
318
|
}
|
|
324
|
-
|
|
319
|
+
frames.push({
|
|
320
|
+
method: match && match.length > 1 ? match[1] : '',
|
|
321
|
+
file: match && match.length > 3 ? match[3] : '',
|
|
322
|
+
number: match && match.length > 4 ? match[4] : '',
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
return frames;
|
|
325
326
|
}
|
|
326
327
|
|
|
327
328
|
|
|
328
329
|
|
|
329
330
|
function logDebug(data) {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
331
|
+
if ( data && _logLevel.toLowerCase() === 'debug' ) {
|
|
332
|
+
console.log(data);
|
|
333
|
+
}
|
|
333
334
|
}
|
|
334
335
|
|
|
335
336
|
|
|
336
337
|
|
|
337
338
|
function logWarning(data) {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
339
|
+
if ( data && _logLevel.toLowerCase() === 'warning' || _logLevel.toLowerCase() === 'error' ) {
|
|
340
|
+
console.warn(data);
|
|
341
|
+
}
|
|
341
342
|
}
|
|
342
343
|
|
|
343
344
|
|
|
344
345
|
|
|
345
346
|
function logError(data) {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
347
|
+
if ( data ) {
|
|
348
|
+
console.error(data);
|
|
349
|
+
}
|
|
349
350
|
}
|
|
350
351
|
|
|
351
352
|
|
|
@@ -355,90 +356,90 @@ function logError(data) {
|
|
|
355
356
|
// ----------------------------------------------------------------------------
|
|
356
357
|
|
|
357
358
|
function onJavaScriptError(err, data) {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
359
|
+
if ( !err ) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
let payloadData = {
|
|
364
|
+
errorClass: `React Native ${(Platform.OS === 'ios' ? 'iOS' : 'Android')} Error`,
|
|
365
|
+
errorMsg: errorMessageFromJavaScriptError(err),
|
|
366
|
+
details: {
|
|
367
|
+
initialHandler: data.initialHandler || '',
|
|
368
|
+
},
|
|
369
|
+
context: _context || {},
|
|
370
|
+
};
|
|
370
371
|
|
|
371
|
-
|
|
372
|
+
let backTrace = backTraceFromJavaScriptError(err);
|
|
372
373
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
379
|
-
} else if ( arrayHasValues(backTrace.framesFromJavaScriptErrorStack) ) {
|
|
380
|
-
payloadData.backTrace = backTrace.framesFromJavaScriptErrorStack;
|
|
381
|
-
payloadData.details.primaryBackTraceSource = 'JavaScriptErrorStack';
|
|
374
|
+
if ( arrayHasValues(backTrace.framesFromComponentStack) ) {
|
|
375
|
+
payloadData.backTrace = backTrace.framesFromComponentStack;
|
|
376
|
+
payloadData.details.primaryBackTraceSource = 'ReactNativeComponentStack';
|
|
377
|
+
if ( arrayHasValues(backTrace.framesFromJavaScriptErrorStack) ) {
|
|
378
|
+
payloadData.details.javaScriptStackTrace = backTrace.framesFromJavaScriptErrorStack;
|
|
382
379
|
}
|
|
380
|
+
} else if ( arrayHasValues(backTrace.framesFromJavaScriptErrorStack) ) {
|
|
381
|
+
payloadData.backTrace = backTrace.framesFromJavaScriptErrorStack;
|
|
382
|
+
payloadData.details.primaryBackTraceSource = 'JavaScriptErrorStack';
|
|
383
|
+
}
|
|
383
384
|
|
|
384
|
-
|
|
385
|
+
sendToHoneybadger(buildPayload(payloadData));
|
|
385
386
|
}
|
|
386
387
|
|
|
387
388
|
|
|
388
389
|
|
|
389
390
|
function errorMessageFromJavaScriptError(err) {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
391
|
+
if ( !err ) {
|
|
392
|
+
return '';
|
|
393
|
+
}
|
|
393
394
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
395
|
+
if ( isStringWithValue(err) ) {
|
|
396
|
+
return err.trim();
|
|
397
|
+
}
|
|
398
|
+
else if ( isObject(err) && err.message && err.message.length > 0 ) {
|
|
399
|
+
return err.message;
|
|
400
|
+
}
|
|
400
401
|
|
|
401
|
-
|
|
402
|
+
return '';
|
|
402
403
|
}
|
|
403
404
|
|
|
404
405
|
|
|
405
406
|
|
|
406
407
|
function backTraceFromJavaScriptError(err) {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
408
|
+
return {
|
|
409
|
+
framesFromComponentStack: ( isObjectWithField(err, 'componentStack') ? framesFromComponentStack(err.componentStack) : []),
|
|
410
|
+
framesFromJavaScriptErrorStack: ( isObjectWithField(err, 'stack') ? framesFromJavaScriptErrorStack(err.stack) : []),
|
|
411
|
+
};
|
|
411
412
|
}
|
|
412
413
|
|
|
413
414
|
|
|
414
415
|
|
|
415
416
|
function framesFromJavaScriptErrorStack(stack) {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}
|
|
417
|
+
let frames = [];
|
|
418
|
+
let lines = stack.split('\n');
|
|
419
|
+
const javaScriptCoreRe = /^\s*(?:([^@]*)(?:\((.*?)\))?@)?(\S.*?):(\d+)(?::(\d+))?\s*$/i;
|
|
420
|
+
for ( let i = 0 ; i < lines.length ; ++i ) {
|
|
421
|
+
const line = lines[i];
|
|
422
|
+
const parts = javaScriptCoreRe.exec(line);
|
|
423
|
+
if ( parts ) {
|
|
424
|
+
frames.push({
|
|
425
|
+
file: parts[3] || '',
|
|
426
|
+
method: parts[1] || '',
|
|
427
|
+
number: (parts[4] ? +parts[4] : ''),
|
|
428
|
+
column: (parts[5] ? +parts[5] : ''),
|
|
429
|
+
});
|
|
430
|
+
} else if ( line.indexOf('[native code]') !== -1 ) {
|
|
431
|
+
let parts = line.split('@');
|
|
432
|
+
if ( parts && parts.length === 2 ) {
|
|
433
|
+
frames.push({
|
|
434
|
+
file: parts[1],
|
|
435
|
+
method: parts[0],
|
|
436
|
+
number: '',
|
|
437
|
+
column: '',
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
440
|
}
|
|
441
|
-
|
|
441
|
+
}
|
|
442
|
+
return frames;
|
|
442
443
|
}
|
|
443
444
|
|
|
444
445
|
|
|
@@ -449,34 +450,34 @@ function framesFromJavaScriptErrorStack(stack) {
|
|
|
449
450
|
|
|
450
451
|
function onNativeAndroidException(data)
|
|
451
452
|
{
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
453
|
+
let payloadData = {
|
|
454
|
+
errorClass: `React Native Android ${data.type}`,
|
|
455
|
+
errorMsg: data.message || '',
|
|
456
|
+
context: _context || {},
|
|
457
|
+
};
|
|
457
458
|
|
|
458
|
-
|
|
459
|
+
let backTrace = backTraceFromAndroidException(data);
|
|
459
460
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
461
|
+
if ( arrayHasValues(backTrace) ) {
|
|
462
|
+
payloadData.backTrace = backTrace;
|
|
463
|
+
}
|
|
463
464
|
|
|
464
|
-
|
|
465
|
+
sendToHoneybadger(buildPayload(payloadData));
|
|
465
466
|
}
|
|
466
467
|
|
|
467
468
|
|
|
468
469
|
|
|
469
470
|
function backTraceFromAndroidException(data)
|
|
470
471
|
{
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
472
|
+
if ( !data || !data.stackTrace ) return [];
|
|
473
|
+
return data.stackTrace.map ( (frame) => {
|
|
474
|
+
let method = ( isStringWithValue(frame.class) && isStringWithValue(frame.method) ) ? (frame.class + '.' + frame.method) : frame.method;
|
|
475
|
+
return {
|
|
476
|
+
method: method || '',
|
|
477
|
+
file: frame.file || '',
|
|
478
|
+
number: frame.line || ''
|
|
479
|
+
};
|
|
480
|
+
});
|
|
480
481
|
}
|
|
481
482
|
|
|
482
483
|
|
|
@@ -487,138 +488,138 @@ function backTraceFromAndroidException(data)
|
|
|
487
488
|
|
|
488
489
|
function onNativeIOSException(data)
|
|
489
490
|
{
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
491
|
+
let payloadData = {
|
|
492
|
+
errorClass: `React Native iOS ${data.type}`,
|
|
493
|
+
errorMsg: errorMessageFromIOSException(data),
|
|
494
|
+
details: {
|
|
495
|
+
errorDomain: data.errorDomain || '',
|
|
496
|
+
initialHandler: data.initialHandler || '',
|
|
497
|
+
userInfo: data.userInfo || {},
|
|
498
|
+
architecture: data.architecture || '',
|
|
499
|
+
},
|
|
500
|
+
context: _context || {},
|
|
501
|
+
};
|
|
501
502
|
|
|
502
|
-
|
|
503
|
+
let backTrace = backTraceFromIOSException(data);
|
|
503
504
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
}
|
|
511
|
-
if ( arrayHasValues(backTrace.framesFromIOSCallStack) ) {
|
|
512
|
-
payloadData.details.iosCallStack = backTrace.framesFromIOSCallStack;
|
|
513
|
-
}
|
|
505
|
+
if ( arrayHasValues(backTrace.framesFromComponentStack) )
|
|
506
|
+
{
|
|
507
|
+
payloadData.backTrace = backTrace.framesFromComponentStack;
|
|
508
|
+
payloadData.details.primaryBackTraceSource = 'ReactNativeComponentStack';
|
|
509
|
+
if ( arrayHasValues(backTrace.framesFromReactNativeIOSStackTrace) ) {
|
|
510
|
+
payloadData.details.reactNativeIOSStackTrace = backTrace.framesFromReactNativeIOSStackTrace;
|
|
514
511
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
payloadData.backTrace = backTrace.framesFromReactNativeIOSStackTrace;
|
|
518
|
-
payloadData.details.primaryBackTraceSource = 'ReactNativeIOSStackTrace';
|
|
519
|
-
if ( arrayHasValues(backTrace.framesFromIOSCallStack) ) {
|
|
520
|
-
payloadData.details.iosCallStack = backTrace.framesFromIOSCallStack;
|
|
521
|
-
}
|
|
512
|
+
if ( arrayHasValues(backTrace.framesFromIOSCallStack) ) {
|
|
513
|
+
payloadData.details.iosCallStack = backTrace.framesFromIOSCallStack;
|
|
522
514
|
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
515
|
+
}
|
|
516
|
+
else if ( arrayHasValues(backTrace.framesFromReactNativeIOSStackTrace) )
|
|
517
|
+
{
|
|
518
|
+
payloadData.backTrace = backTrace.framesFromReactNativeIOSStackTrace;
|
|
519
|
+
payloadData.details.primaryBackTraceSource = 'ReactNativeIOSStackTrace';
|
|
520
|
+
if ( arrayHasValues(backTrace.framesFromIOSCallStack) ) {
|
|
521
|
+
payloadData.details.iosCallStack = backTrace.framesFromIOSCallStack;
|
|
527
522
|
}
|
|
528
|
-
|
|
529
|
-
|
|
523
|
+
}
|
|
524
|
+
else if ( arrayHasValues(backTrace.framesFromIOSCallStack) )
|
|
525
|
+
{
|
|
526
|
+
payloadData.backTrace = backTrace.framesFromIOSCallStack;
|
|
527
|
+
payloadData.details.primaryBackTraceSource = 'iOSCallStack';
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
sendToHoneybadger(buildPayload(payloadData));
|
|
530
531
|
}
|
|
531
532
|
|
|
532
533
|
|
|
533
534
|
|
|
534
535
|
function errorMessageFromIOSException(data) {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
else if ( (data.name && data.name !== '') || (data.reason && data.reason !== '') ) {
|
|
550
|
-
return `${data.name} : ${data.reason}`.trim();
|
|
536
|
+
if ( !data ) {
|
|
537
|
+
return '';
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if ( data.localizedDescription && data.localizedDescription !== '' ) {
|
|
541
|
+
const localizedDescription = data.localizedDescription;
|
|
542
|
+
const startOfNativeIOSCallStack = localizedDescription.indexOf('callstack: (\n');
|
|
543
|
+
if ( startOfNativeIOSCallStack === -1 ) {
|
|
544
|
+
const lines = localizedDescription.split('\n');
|
|
545
|
+
return lines.length === 0 ? localizedDescription : lines[0].trim();
|
|
546
|
+
} else {
|
|
547
|
+
return localizedDescription.substr(0, startOfNativeIOSCallStack).trim();
|
|
551
548
|
}
|
|
549
|
+
}
|
|
550
|
+
else if ( (data.name && data.name !== '') || (data.reason && data.reason !== '') ) {
|
|
551
|
+
return `${data.name} : ${data.reason}`.trim();
|
|
552
|
+
}
|
|
552
553
|
|
|
553
|
-
|
|
554
|
+
return '';
|
|
554
555
|
}
|
|
555
556
|
|
|
556
557
|
|
|
557
558
|
|
|
558
559
|
function backTraceFromIOSException(data) {
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
560
|
+
return {
|
|
561
|
+
framesFromComponentStack: framesFromComponentStack(data.localizedDescription),
|
|
562
|
+
framesFromReactNativeIOSStackTrace: framesFromReactNativeIOSStackTrace(data),
|
|
563
|
+
framesFromIOSCallStack: framesFromIOSCallStack(data),
|
|
564
|
+
};
|
|
564
565
|
}
|
|
565
566
|
|
|
566
567
|
|
|
567
568
|
|
|
568
569
|
function framesFromReactNativeIOSStackTrace(data) {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
});
|
|
570
|
+
if ( !data.reactNativeStackTrace ) {
|
|
571
|
+
return [];
|
|
572
|
+
}
|
|
573
|
+
let frames = [];
|
|
574
|
+
data.reactNativeStackTrace.forEach( (frame) => {
|
|
575
|
+
frames.push({
|
|
576
|
+
method: frame.methodName || '',
|
|
577
|
+
number: frame.lineNumber || '',
|
|
578
|
+
file: frame.file || '',
|
|
579
|
+
column: frame.column || '',
|
|
580
580
|
});
|
|
581
|
-
|
|
581
|
+
});
|
|
582
|
+
return frames;
|
|
582
583
|
}
|
|
583
584
|
|
|
584
585
|
|
|
585
586
|
|
|
586
587
|
function framesFromIOSCallStack(data) {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
588
|
+
let callStack = [];
|
|
589
|
+
|
|
590
|
+
if ( isStringWithValue(data.localizedDescription) ) {
|
|
591
|
+
callStack = data.localizedDescription.split('\n').map(item => item.trim());
|
|
592
|
+
}
|
|
593
|
+
else if ( arrayHasValues(data.callStackSymbols) ) {
|
|
594
|
+
callStack = data.callStackSymbols.map(item => item.trim());
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
let frames = [];
|
|
598
|
+
const regex = /\d+\s+(\S+)\s+(\S+)\s(.+)\s\+\s(\d+)(\s+\((\S+):(\S+)\))?/gm;
|
|
599
|
+
let match;
|
|
600
|
+
callStack.forEach(element => {
|
|
601
|
+
while ( (match = regex.exec(element)) !== null ) {
|
|
602
|
+
if ( match.index === regex.lastIndex) {
|
|
603
|
+
regex.lastIndex++;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
let moduleName = match && match.length > 1 ? match[1] : '';
|
|
607
|
+
let stackAddress = match && match.length > 2 ? match[2] : '';
|
|
608
|
+
let loadAddress = match && match.length > 3 ? match[3] : '';
|
|
609
|
+
// let symbolOffset = match && match.length > 4 ? match[4] : '';
|
|
610
|
+
let file = match && match.length > 6 ? match[6] : '';
|
|
611
|
+
let line = match && match.length > 7 ? match[7] : '';
|
|
612
|
+
|
|
613
|
+
frames.push({
|
|
614
|
+
file: file || moduleName || '',
|
|
615
|
+
line: line || '',
|
|
616
|
+
method: loadAddress || '',
|
|
617
|
+
stack_address: stackAddress || '',
|
|
618
|
+
});
|
|
594
619
|
}
|
|
620
|
+
});
|
|
595
621
|
|
|
596
|
-
|
|
597
|
-
const regex = /\d+\s+(\S+)\s+(\S+)\s(.+)\s\+\s(\d+)(\s+\((\S+):(\S+)\))?/gm;
|
|
598
|
-
let match;
|
|
599
|
-
callStack.forEach(element => {
|
|
600
|
-
while ( (match = regex.exec(element)) !== null ) {
|
|
601
|
-
if ( match.index === regex.lastIndex) {
|
|
602
|
-
regex.lastIndex++;
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
let moduleName = match && match.length > 1 ? match[1] : '';
|
|
606
|
-
let stackAddress = match && match.length > 2 ? match[2] : '';
|
|
607
|
-
let loadAddress = match && match.length > 3 ? match[3] : '';
|
|
608
|
-
// let symbolOffset = match && match.length > 4 ? match[4] : '';
|
|
609
|
-
let file = match && match.length > 6 ? match[6] : '';
|
|
610
|
-
let line = match && match.length > 7 ? match[7] : '';
|
|
611
|
-
|
|
612
|
-
frames.push({
|
|
613
|
-
file: file || moduleName || '',
|
|
614
|
-
line: line || '',
|
|
615
|
-
method: loadAddress || '',
|
|
616
|
-
stack_address: stackAddress || '',
|
|
617
|
-
});
|
|
618
|
-
}
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
return frames;
|
|
622
|
+
return frames;
|
|
622
623
|
}
|
|
623
624
|
|
|
624
625
|
|
|
@@ -628,29 +629,29 @@ function framesFromIOSCallStack(data) {
|
|
|
628
629
|
// ----------------------------------------------------------------------------
|
|
629
630
|
|
|
630
631
|
function isObject(val) {
|
|
631
|
-
|
|
632
|
+
return val != null && (typeof val === 'object') && !(Array.isArray(val));
|
|
632
633
|
}
|
|
633
634
|
|
|
634
635
|
function isObjectWithField(possibleObj, field) {
|
|
635
|
-
|
|
636
|
+
return isObject(possibleObj) && (field in possibleObj);
|
|
636
637
|
}
|
|
637
638
|
|
|
638
639
|
function isString(val) {
|
|
639
|
-
|
|
640
|
+
return val != null && typeof val === 'string';
|
|
640
641
|
}
|
|
641
642
|
|
|
642
643
|
function isStringWithValue(val) {
|
|
643
|
-
|
|
644
|
+
return isString(val) && val.trim().length > 0;
|
|
644
645
|
}
|
|
645
646
|
|
|
646
647
|
function arrayHasValues(obj) {
|
|
647
|
-
|
|
648
|
+
return obj != null && Array.isArray(obj) && obj.length !== 0;
|
|
648
649
|
}
|
|
649
650
|
|
|
650
651
|
function safeStringFromField(obj, field, defaultValue = '') {
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
652
|
+
if ( isObjectWithField(obj, field) && isStringWithValue(obj[field]) ) {
|
|
653
|
+
return obj[field].trim();
|
|
654
|
+
}
|
|
655
|
+
return defaultValue;
|
|
655
656
|
}
|
|
656
657
|
|