Skip to content

Commit

Permalink
feat: make structured BugsnagError class
Browse files Browse the repository at this point in the history
  • Loading branch information
fractalwrench committed Apr 16, 2020
1 parent ca8e6ba commit 176033f
Show file tree
Hide file tree
Showing 19 changed files with 425 additions and 156 deletions.
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Bugsnag Notifiers on other platforms.

## Enhancements

* Create structured `BugsnagError` class
[#533](https://github.com/bugsnag/bugsnag-cocoa/pull/533)

* Create structured `BugsnagThread` class
[#532](https://github.com/bugsnag/bugsnag-cocoa/pull/532)

Expand All @@ -17,9 +20,6 @@ Bugsnag Notifiers on other platforms.
* Create structured `BugsnagStackframe` class
[#528](https://github.com/bugsnag/bugsnag-cocoa/pull/528)

* Create structured `BugsnagStackframe` class
[#528](https://github.com/bugsnag/bugsnag-cocoa/pull/528)

* Convert `event.app` from `NSDictionary` to a structured class
[#520](https://github.com/bugsnag/bugsnag-cocoa/pull/520)

Expand Down
4 changes: 4 additions & 0 deletions OSX/Bugsnag.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
E72352C21F55924A00436528 /* BSGConnectivity.m in Sources */ = {isa = PBXBuildFile; fileRef = E72352C01F55924A00436528 /* BSGConnectivity.m */; };
E72AE1F9241A4E7500ED8972 /* BugsnagPluginClient.m in Sources */ = {isa = PBXBuildFile; fileRef = E72AE1F7241A4E7500ED8972 /* BugsnagPluginClient.m */; };
E72AE1FA241A4E7500ED8972 /* BugsnagPluginClient.h in Headers */ = {isa = PBXBuildFile; fileRef = E72AE1F8241A4E7500ED8972 /* BugsnagPluginClient.h */; };
E73D443C243E192F001686F5 /* BugsnagErrorTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E73D443B243E192F001686F5 /* BugsnagErrorTest.m */; };
E7433AD21F4F64EF00C082D1 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A2C8FF11C6BC3A800846019 /* libz.tbd */; };
E7433AD31F4F64F400C082D1 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A2C8FF31C6BC3AE00846019 /* libc++.tbd */; };
E7529F8E243C8EBF006B4932 /* RegisterErrorData.h in Headers */ = {isa = PBXBuildFile; fileRef = E7529F8C243C8EBF006B4932 /* RegisterErrorData.h */; };
Expand Down Expand Up @@ -281,6 +282,7 @@
E72AE1F8241A4E7500ED8972 /* BugsnagPluginClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BugsnagPluginClient.h; path = ../Source/BugsnagPluginClient.h; sourceTree = "<group>"; };
E7529F8C243C8EBF006B4932 /* RegisterErrorData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterErrorData.h; path = ../Source/RegisterErrorData.h; sourceTree = "<group>"; };
E7529F8D243C8EBF006B4932 /* RegisterErrorData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RegisterErrorData.m; path = ../Source/RegisterErrorData.m; sourceTree = "<group>"; };
E73D443B243E192F001686F5 /* BugsnagErrorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagErrorTest.m; sourceTree = "<group>"; };
E7529F9F243CAE34006B4932 /* BugsnagThreadTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagThreadTest.m; sourceTree = "<group>"; };
E7529FA1243CAE3F006B4932 /* BugsnagThreadSerializationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagThreadSerializationTest.m; path = ../iOS/BugsnagTests/BugsnagThreadSerializationTest.m; sourceTree = "<group>"; };
E762E9F71F73F7E900E82B43 /* BugsnagHandledStateTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagHandledStateTest.m; path = ../Tests/BugsnagHandledStateTest.m; sourceTree = SOURCE_ROOT; };
Expand Down Expand Up @@ -540,6 +542,7 @@
4B775FD222CBE02A004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m */,
8AD9FA851E0862DC002859A7 /* BugsnagConfigurationTests.m */,
E7A9E56C2436365300D99F8A /* BugsnagDeviceTest.m */,
E73D443B243E192F001686F5 /* BugsnagErrorTest.m */,
00D7ACA223E984B300FBE4A7 /* BugsnagEventTests.m */,
E762E9F71F73F7E900E82B43 /* BugsnagHandledStateTest.m */,
E7AB4B9D2423E184004F015A /* BugsnagOnBreadcrumbTest.m */,
Expand Down Expand Up @@ -1149,6 +1152,7 @@
E7CE78BF1FD94E77001D07E0 /* KSCrashSentry_Signal_Tests.m in Sources */,
8A530CC922FDD74300F0C108 /* KSCrashIdentifierTests.m in Sources */,
4B406C1822CAD96400464D1D /* BugsnagCollectionsBSGDictMergeTest.m in Sources */,
E73D443C243E192F001686F5 /* BugsnagErrorTest.m in Sources */,
00D7ACA423E9854C00FBE4A7 /* BugsnagEventTests.m in Sources */,
E7CE78CB1FD94E77001D07E0 /* KSSysCtl_Tests.m in Sources */,
E790C42324324528006FFB26 /* BugsnagClientMirrorTest.m in Sources */,
Expand Down
4 changes: 2 additions & 2 deletions Source/BugsnagClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -889,8 +889,8 @@ - (void)notify:(NSException *)exception

int depth = (int)(BSGNotifierStackFrameCount + event.depth);

NSString *eventErrorClass = event.errorClass ?: NSStringFromClass([NSException class]);
NSString *eventMessage = event.errorMessage ?: @"";
NSString *eventErrorClass = event.errors[0].errorClass ?: NSStringFromClass([NSException class]);
NSString *eventMessage = event.errors[0].errorMessage ?: @"";

[self.crashSentry reportUserException:eventErrorClass
reason:eventMessage
Expand Down
4 changes: 2 additions & 2 deletions Source/BugsnagConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ typedef BOOL (^BugsnagOnBreadcrumbBlock)(BugsnagBreadcrumb *_Nonnull breadcrumb)
*/
typedef void(^BugsnagOnSessionBlock)(NSMutableDictionary *_Nonnull sessionPayload);

typedef NS_OPTIONS(NSUInteger, BSGErrorType) {
typedef NS_OPTIONS(NSUInteger, BSGEnabledErrorType) {
BSGErrorTypesNone NS_SWIFT_NAME(None) = 0,
BSGErrorTypesOOMs NS_SWIFT_NAME(OOMs) = 1 << 0,
BSGErrorTypesNSExceptions NS_SWIFT_NAME(NSExceptions) = 1 << 1,
Expand Down Expand Up @@ -170,7 +170,7 @@ typedef NS_OPTIONS(NSUInteger, BSGErrorType) {
* Passed down to KSCrash in BugsnagCrashSentry.
* Defaults to all-true
*/
@property BSGErrorType enabledErrorTypes;
@property BSGEnabledErrorType enabledErrorTypes;

/**
* Required declaration to suppress a superclass designated-initializer error
Expand Down
4 changes: 2 additions & 2 deletions Source/BugsnagCrashSentry.m
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ - (void)install:(BugsnagConfiguration *)config
// If Bugsnag is autodetecting errors then the types of event detected is configurable
// (otherwise it's just the user reported events)
if (config.autoDetectErrors) {
BSGErrorType errorTypes = [config enabledErrorTypes];
BSGEnabledErrorType errorTypes = [config enabledErrorTypes];
// Translate the relevant BSGErrorTypes bitfield into the equivalent BSG_KSCrashType one
crashTypes = crashTypes | [self mapKSToBSGCrashTypes:errorTypes];
}
Expand All @@ -58,7 +58,7 @@ - (void)install:(BugsnagConfiguration *)config
* @param bsgCrashMask The BSGErrorType bitfield
* @returns A BSG_KSCrashType equivalent (with the above caveats) to the input
*/
- (BSG_KSCrashType)mapKSToBSGCrashTypes:(BSGErrorType)bsgCrashMask
- (BSG_KSCrashType)mapKSToBSGCrashTypes:(BSGEnabledErrorType)bsgCrashMask
{
BSG_KSCrashType crashType;
crashType = (bsgCrashMask & BSGErrorTypesNSExceptions ? BSG_KSCrashTypeNSException : 0)
Expand Down
30 changes: 27 additions & 3 deletions Source/BugsnagError.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,37 @@

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN
@class BugsnagStackframe;

typedef NS_OPTIONS(NSUInteger, BSGErrorType) {
BSGErrorTypeCocoa,
BSGErrorTypeC,
BSGErrorTypeReactNativeJs
};

/**
* An Error represents information extracted from an NSError, NSException, or other error source.
*/
@interface BugsnagError : NSObject

@end
/**
* The class of the error generating the report
*/
@property(nullable) NSString *errorClass;

/**
* The message of or reason for the error generating the report
*/
@property(nullable) NSString *errorMessage;

NS_ASSUME_NONNULL_END
/**
* Sets a representation of this error's stacktrace
*/
@property(readonly, nonnull) NSArray<BugsnagStackframe *> *stacktrace;

/**
* The type of the captured error
*/
@property BSGErrorType type;

@end
105 changes: 105 additions & 0 deletions Source/BugsnagError.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,112 @@
//

#import "BugsnagError.h"
#import "BugsnagKeys.h"
#import "BugsnagStackframe.h"
#import "BugsnagStacktrace.h"
#import "BugsnagCollections.h"
#import "RegisterErrorData.h"
#import "BugsnagThread.h"

NSString *_Nonnull BSGSerializeErrorType(BSGErrorType errorType) {
switch (errorType) {
case BSGErrorTypeCocoa:
return @"cocoa";
case BSGErrorTypeC:
return @"c";
case BSGErrorTypeReactNativeJs:
return @"reactnativejs";
default:
return nil;
}
}

NSString *_Nonnull BSGParseErrorClass(NSDictionary *error, NSString *errorType) {
NSString *errorClass;

if ([errorType isEqualToString:BSGKeyCppException]) {
errorClass = error[BSGKeyCppException][BSGKeyName];
} else if ([errorType isEqualToString:BSGKeyMach]) {
errorClass = error[BSGKeyMach][BSGKeyExceptionName];
} else if ([errorType isEqualToString:BSGKeySignal]) {
errorClass = error[BSGKeySignal][BSGKeyName];
} else if ([errorType isEqualToString:@"nsexception"]) {
errorClass = error[@"nsexception"][BSGKeyName];
} else if ([errorType isEqualToString:BSGKeyUser]) {
errorClass = error[@"user_reported"][BSGKeyName];
}

if (!errorClass) { // use a default value
errorClass = @"Exception";
}
return errorClass;
}

NSString *BSGParseErrorMessage(NSDictionary *report, NSDictionary *error, NSString *errorType) {
if ([errorType isEqualToString:BSGKeyMach] || error[BSGKeyReason] == nil) {
NSString *diagnosis = [report valueForKeyPath:@"crash.diagnosis"];
if (diagnosis && ![diagnosis hasPrefix:@"No diagnosis"]) {
return [[diagnosis componentsSeparatedByString:@"\n"] firstObject];
}
}
return error[BSGKeyReason] ?: @"";
}

@interface BugsnagStackframe ()
- (NSDictionary *)toDictionary;
@end

@interface BugsnagStacktrace ()
@property NSMutableArray<BugsnagStackframe *> *trace;
@end

@implementation BugsnagError

- (instancetype)initWithEvent:(NSDictionary *)event errorReportingThread:(BugsnagThread *)thread {
if (self = [super init]) {
NSDictionary *error = [event valueForKeyPath:@"crash.error"];
NSString *errorType = error[BSGKeyType];
_errorClass = BSGParseErrorClass(error, errorType);
_errorMessage = BSGParseErrorMessage(event, error, errorType);
_type = BSGErrorTypeCocoa;

if (![[event valueForKeyPath:@"user.state.didOOM"] boolValue]) {
NSArray *threadDict = [event valueForKeyPath:@"crash.threads"];
RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:threadDict];
if (data) {
_errorClass = data.errorClass;
_errorMessage = data.errorMessage;
}
_stacktrace = thread.stacktrace;
}
}
return self;
}

- (NSDictionary *)findErrorReportingThread:(NSDictionary *)event {
NSArray *threads = [event valueForKeyPath:@"crash.threads"];

for (NSDictionary *thread in threads) {
if ([thread[@"crashed"] boolValue]) {
return thread;
}
}
return nil;
}

- (NSDictionary *)toDictionary {
NSMutableDictionary *dict = [NSMutableDictionary new];
BSGDictInsertIfNotNil(dict, self.errorClass, BSGKeyErrorClass);
BSGDictInsertIfNotNil(dict, self.errorMessage, BSGKeyMessage);
BSGDictInsertIfNotNil(dict, BSGSerializeErrorType(self.type), BSGKeyType);

NSMutableArray *frames = [NSMutableArray new];
for (BugsnagStackframe *frame in self.stacktrace) {
[frames addObject:[frame toDictionary]];
}

BSGDictSetSafeObject(dict, frames, BSGKeyStacktrace);
return dict;
}

@end
26 changes: 7 additions & 19 deletions Source/BugsnagEvent.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
@class BugsnagDeviceWithState;
@class BugsnagMetadata;
@class BugsnagThread;
@class BugsnagError;

typedef NS_ENUM(NSUInteger, BSGSeverity) {
BSGSeverityError,
Expand All @@ -30,19 +31,6 @@ typedef NS_ENUM(NSUInteger, BSGSeverity) {
// MARK: - Initialisation
// -----------------------------------------------------------------------------

/**
* Create a new crash report from a JSON crash report generated by
* BugsnagCrashSentry
*
* @param report a BugsnagCrashSentry JSON report
* @param metadata additional report info encoded as a string
*
* @return a Bugsnag crash report
*/
- (instancetype _Nonnull)initWithKSReport:(NSDictionary *_Nonnull)report
fileMetadata:(NSString *_Nonnull)metadata
__deprecated_msg("Use initWithKSReport: instead.");

/**
* Create a new crash report from a JSON crash report generated by
* BugsnagCrashSentry
Expand Down Expand Up @@ -93,14 +81,14 @@ initWithErrorName:(NSString *_Nonnull)name
* The severity of the error generating the report
*/
@property(readwrite) BSGSeverity severity;

/**
* The class of the error generating the report
*/
@property(readwrite, copy, nonnull) NSString *errorClass;
/**
* The message of or reason for the error generating the report
* Information extracted from the error that caused the event. The list contains
* at least one error that represents the root cause, with subsequent elements populated
* from the cause.
*/
@property(readwrite, copy, nullable) NSString *errorMessage;
@property(readonly, nonnull) NSMutableArray<BugsnagError *> *errors;

/**
* Customized hash for grouping this report with other errors
*/
Expand Down
Loading

0 comments on commit 176033f

Please sign in to comment.