diff --git a/CHANGELOG.md b/CHANGELOG.md index c05e6282..6e80bec1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,37 @@ Changelog ========= +## 2.9.4 (2018-05-02) + +* Enable nativeSerializer to handle Error objects by extracting the stack and message from a given Error class before serialising it. [#239](https://github.com/bugsnag/bugsnag-react-native/issues/239) [#240](https://github.com/bugsnag/bugsnag-react-native/pull/240) [daisy1754](https://github.com/daisy1754) [Cawllec](https://github.com/Cawllec) + +* Upgrade bugsnag-android to v4.3.4: + - *Bug Fixes:* + - Avoid adding extra comma separator in JSON if File input is empty or null [#284](https://github.com/bugsnag/bugsnag-android/pull/284) + - Thread safety fixes to JSON file serialisation [#295](https://github.com/bugsnag/bugsnag-android/pull/295) + - Prevent potential automatic activity lifecycle breadcrumb crash [#300](https://github.com/bugsnag/bugsnag-android/pull/300) + - Fix serialisation issue with leading to incorrect dashboard display of breadcrumbs [#306](https://github.com/bugsnag/bugsnag-android/pull/306) + - Prevent duplicate reports being delivered in low connectivity situations [#270](https://github.com/bugsnag/bugsnag-android/pull/270) + - Fix possible NPE when reading default metadata filters [#263](https://github.com/bugsnag/bugsnag-android/pull/263) + - Prevent ConcurrentModificationException in Before notify/breadcrumb callbacks [#266](https://github.com/bugsnag/bugsnag-android/pull/266) + - Ensure that exception message is never null [#256](https://github.com/bugsnag/bugsnag-android/pull/256) + - Add payload version to JSON body [#244](https://github.com/bugsnag/bugsnag-android/pull/244) + - Update context tracking to use lifecycle callbacks rather than ActivityManager [#238](https://github.com/bugsnag/bugsnag-android/pull/238) + - *Enhancements:* + - Detect whether running on emulator [#245](https://github.com/bugsnag/bugsnag-android/pull/245) + - Add a callback for filtering breadcrumbs [#237](https://github.com/bugsnag/bugsnag-android/pull/237) +* Upgrade bugsnag-cocoa to v5.15.5: + - *Bug Fixes:* + - Changes report generation so that when a minimal or incomplete crash is recorded, essential app/device information is included in the report on the next application launch. [#239](https://github.com/bugsnag/bugsnag-cocoa/pull/239) + [#250](https://github.com/bugsnag/bugsnag-cocoa/pull/250) + - Ensure timezone is serialised in report payload. + [#248](https://github.com/bugsnag/bugsnag-cocoa/pull/248) + - Ensure error class and message are persisted when thread tracing is disabled [#245](https://github.com/bugsnag/bugsnag-cocoa/pull/245) + - Re-addapp name to the app tab of reports [#244](https://github.com/bugsnag/bugsnag-cocoa/pull/244) + - Add payload version to report body to preserve backwards compatibility with older versions of the error reporting API [#241](https://github.com/bugsnag/bugsnag-cocoa/pull/241) + - *Enhancements:* + -This release adds additional device metadata for filtering by whether an error occurred in a simulator ([#242](https://github.com/bugsnag/bugsnag-cocoa/pull/242)) and by processor word size ([#228](https://github.com/bugsnag/bugsnag-cocoa/pull/228)). + ## 2.9.3 (2018-03-16) ### Bug Fixes diff --git a/README.md b/README.md index f62e3098..917575b2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Bugsnag exception reporter for React Native -[![Documentation](https://img.shields.io/badge/documentation-2.9.3-blue.svg)](http://docs.bugsnag.com/platforms/react-native/) +[![Documentation](https://img.shields.io/badge/documentation-2.9.4-blue.svg)](http://docs.bugsnag.com/platforms/react-native/) Automatic [React Native crash reporting](https://www.bugsnag.com/platforms/react-native-error-reporting/) with Bugsnag helps you detect both native OS and JavaScript errors in your React Native apps. diff --git a/android/build.gradle b/android/build.gradle index 6f32b0fb..e88bcb43 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,11 +8,11 @@ android { minSdkVersion 16 targetSdkVersion 23 versionCode 4 - versionName '2.9.3' + versionName '2.9.4' } } dependencies { - compile 'com.bugsnag:bugsnag-android:4.3.1' + compile 'com.bugsnag:bugsnag-android:4.3.4' compile 'com.facebook.react:react-native:+' } diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagCrashReport.m b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagCrashReport.m index 42e313aa..090cd669 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagCrashReport.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagCrashReport.m @@ -483,7 +483,10 @@ - (NSDictionary *)toJson { NSMutableDictionary *appObj = [NSMutableDictionary new]; [appObj addEntriesFromDictionary:self.app]; - [appObj addEntriesFromDictionary:self.appState]; + + for (NSString *key in self.appState) { + BSGDictInsertIfNotNil(appObj, self.appState[key], key); + } if (self.dsymUUID) { BSGDictInsertIfNotNil(appObj, @[self.dsymUUID], @"dsymUUIDs"); diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagKSCrashSysInfoParser.m b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagKSCrashSysInfoParser.m index aa8f143c..ff632ccc 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagKSCrashSysInfoParser.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagKSCrashSysInfoParser.m @@ -13,6 +13,8 @@ #import "BugsnagConfiguration.h" #import "BugsnagLogger.h" +#define PLATFORM_WORD_SIZE sizeof(void*)*8 + NSDictionary *BSGParseDevice(NSDictionary *report) { NSMutableDictionary *device = [[report valueForKeyPath:@"user.state.deviceState"] mutableCopy]; @@ -22,7 +24,7 @@ BSGDictSetSafeObject(device, [[NSLocale currentLocale] localeIdentifier], @"locale"); - BSGDictSetSafeObject(device, report[@"time_zone"], @"timezone"); + BSGDictSetSafeObject(device, [report valueForKeyPath:@"system.time_zone"], @"timezone"); BSGDictSetSafeObject(device, [report valueForKeyPath:@"system.memory.usable"], @"totalMemory"); @@ -47,6 +49,13 @@ NSNumber *freeBytes = [fileSystemAttrs objectForKey:NSFileSystemFreeSize]; BSGDictSetSafeObject(device, freeBytes, @"freeDisk"); BSGDictSetSafeObject(device, report[@"system"][@"device_app_hash"], @"id"); + +#if TARGET_OS_SIMULATOR + BSGDictSetSafeObject(device, @YES, @"simulator"); +#elif TARGET_OS_IPHONE || TARGET_OS_TV + BSGDictSetSafeObject(device, @NO, @"simulator"); +#endif + return device; } @@ -62,6 +71,8 @@ BSGDictSetSafeObject(appState, @(activeTimeSinceLaunch), @"durationInForeground"); + + BSGDictSetSafeObject(appState, report[BSGKeyExecutableName], BSGKeyName); BSGDictSetSafeObject(appState, @(activeTimeSinceLaunch + backgroundTimeSinceLaunch), @"duration"); @@ -103,6 +114,7 @@ BSGDictSetSafeObject(deviceState, report[@"machine"], @"model"); BSGDictSetSafeObject(deviceState, report[@"system_name"], @"osName"); BSGDictSetSafeObject(deviceState, report[@"system_version"], @"osVersion"); + BSGDictSetSafeObject(deviceState, @(PLATFORM_WORD_SIZE), @"wordSize"); BSGDictSetSafeObject(deviceState, @"Apple", @"manufacturer"); BSGDictSetSafeObject(deviceState, report[@"jailbroken"], @"jailbroken"); return deviceState; diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagNotifier.m b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagNotifier.m index 1eb71722..8687fb43 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagNotifier.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagNotifier.m @@ -41,7 +41,7 @@ #import #endif -NSString *const NOTIFIER_VERSION = @"5.15.3"; +NSString *const NOTIFIER_VERSION = @"5.15.5"; NSString *const NOTIFIER_URL = @"https://github.com/bugsnag/bugsnag-cocoa"; NSString *const BSTabCrash = @"crash"; NSString *const BSAttributeDepth = @"depth"; diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSink.m b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSink.m index a95d9558..0fc2517a 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSink.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSink.m @@ -29,6 +29,7 @@ #import "BugsnagCollections.h" #import "BugsnagNotifier.h" #import "BugsnagKeys.h" +#import "BSG_KSSystemInfo.h" // This is private in Bugsnag, but really we want package private so define // it here. @@ -59,9 +60,40 @@ - (void)filterReports:(NSArray *)reports onCompletion:(BSG_KSCrashReportFilterCompletion)onCompletion { NSMutableArray *bugsnagReports = [NSMutableArray new]; BugsnagConfiguration *configuration = [Bugsnag configuration]; + for (NSDictionary *report in reports) { - BugsnagCrashReport *bugsnagReport = - [[BugsnagCrashReport alloc] initWithKSReport:report]; + BugsnagCrashReport *bugsnagReport = [[BugsnagCrashReport alloc] initWithKSReport:report]; + BOOL incompleteReport = (![@"standard" isEqualToString:[report valueForKeyPath:@"report.type"]] || + [[report objectForKey:@"incomplete"] boolValue]); + + if (incompleteReport) { // append app/device data as this is unlikely to change between sessions + NSDictionary *sysInfo = [BSG_KSSystemInfo systemInfo]; + + // reset any existing data as it will be corrupted/nil + bugsnagReport.appState = @{}; + bugsnagReport.deviceState = @{}; + + + NSMutableDictionary *appDict = [NSMutableDictionary new]; + BSGDictInsertIfNotNil(appDict, sysInfo[@BSG_KSSystemField_BundleVersion], @"bundleVersion"); + BSGDictInsertIfNotNil(appDict, sysInfo[@BSG_KSSystemField_BundleID], @"id"); + BSGDictInsertIfNotNil(appDict, configuration.releaseStage, @"releaseStage"); + BSGDictInsertIfNotNil(appDict, sysInfo[@BSG_KSSystemField_SystemName], @"type"); + BSGDictInsertIfNotNil(appDict, sysInfo[@BSG_KSSystemField_BundleShortVersion], @"version"); + + NSMutableDictionary *deviceDict = [NSMutableDictionary new]; + BSGDictInsertIfNotNil(deviceDict, sysInfo[@BSG_KSSystemField_Jailbroken], @"jailbroken"); + BSGDictInsertIfNotNil(deviceDict, [[NSLocale currentLocale] localeIdentifier], @"locale"); + BSGDictInsertIfNotNil(deviceDict, sysInfo[@"Apple"], @"manufacturer"); + BSGDictInsertIfNotNil(deviceDict, sysInfo[@BSG_KSSystemField_Machine], @"model"); + BSGDictInsertIfNotNil(deviceDict, sysInfo[@BSG_KSSystemField_Model], @"modelNumber"); + BSGDictInsertIfNotNil(deviceDict, sysInfo[@BSG_KSSystemField_SystemName], @"osName"); + BSGDictInsertIfNotNil(deviceDict, sysInfo[@BSG_KSSystemField_SystemVersion], @"osVersion"); + + bugsnagReport.app = appDict; + bugsnagReport.device = deviceDict; + } + if (![bugsnagReport shouldBeSent]) continue; BOOL shouldSend = YES; @@ -115,6 +147,7 @@ - (NSDictionary *)getBodyFromReports:(NSArray *)reports { NSMutableDictionary *data = [[NSMutableDictionary alloc] init]; BSGDictSetSafeObject(data, [Bugsnag notifier].details, BSGKeyNotifier); BSGDictSetSafeObject(data, [Bugsnag notifier].configuration.apiKey, BSGKeyApiKey); + BSGDictSetSafeObject(data, @"4.0", @"payloadVersion"); NSMutableArray *formatted = [[NSMutableArray alloc] initWithCapacity:[reports count]]; diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c index 6693fbfb..6c9da5cd 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c @@ -2084,9 +2084,9 @@ void bsg_kscrashreport_writeStandardReport( crashContext->config.introspectionRules.enabled, crashContext->config.searchThreadNames, crashContext->config.searchQueueNames); - bsg_kscrw_i_writeError(writer, BSG_KSCrashField_Error, - &crashContext->crash); } + bsg_kscrw_i_writeError(writer, BSG_KSCrashField_Error, + &crashContext->crash); } writer->endContainer(writer); diff --git a/package.json b/package.json index 1f564457..86491d7d 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,13 @@ "homepage": "https://www.bugsnag.com/platforms/react-native-error-reporting/", "repository": "https://github.com/bugsnag/bugsnag-react-native.git", "bugs": "https://github.com/bugsnag/bugsnag-react-native/issues", - "version": "2.9.3", + "version": "2.9.4", "license": "MIT", "main": "index.js", "types": "index.d.ts", "nativePackage": true, "dependencies": { + "iserror": "^0.0.2", "promise": "^7", "prop-types": "^15.6.0" }, diff --git a/src/NativeSerializer.js b/src/NativeSerializer.js index d1de5a39..25943276 100644 --- a/src/NativeSerializer.js +++ b/src/NativeSerializer.js @@ -1,3 +1,5 @@ +const isError = require('iserror') + const allowedMapObjectTypes = [ 'string', 'number', 'boolean' ] /** @@ -7,6 +9,9 @@ const allowedMapObjectTypes = [ 'string', 'number', 'boolean' ] const serializeForNativeLayer = (map, maxDepth = 10, depth = 0, seen = new Set()) => { seen.add(map) const output = {} + if (isError(map)) { + map = extractErrorDetails(map) + } for (const key in map) { if (!{}.hasOwnProperty.call(map, key)) continue @@ -35,4 +40,9 @@ const serializeForNativeLayer = (map, maxDepth = 10, depth = 0, seen = new Set() return output } -export default serializeForNativeLayer +const extractErrorDetails = (err) => { + const { message, stack, name } = err + return { message, stack, name } +} + +module.exports = serializeForNativeLayer diff --git a/src/__tests__/NativeSerializer.test.js b/src/__tests__/NativeSerializer.test.js index 6bd38b83..f441a945 100644 --- a/src/__tests__/NativeSerializer.test.js +++ b/src/__tests__/NativeSerializer.test.js @@ -134,3 +134,18 @@ test('serializeForNativeLayer handles deep nesting', () => { } }) }) + +test('serializeForNativeLayer handles error objects', () => { + const err = new Error('Oh no') + const serialized = serializeForNativeLayer(err) + expect(serialized['message']).toEqual({ + type: 'string', + value: 'Oh no' + }) + expect(serialized['stack']['type']).toEqual('string') + expect(serialized['stack']['value'].length).toBeGreaterThan(0) + expect(serialized['name']).toEqual({ + type: 'string', + value: 'Error' + }) +}) diff --git a/yarn.lock b/yarn.lock index d6b33121..078fd665 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2206,6 +2206,10 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" +iserror@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/iserror/-/iserror-0.0.2.tgz#bd53451fe2f668b9f2402c1966787aaa2c7c0bf5" + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"