Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create structured BugsnagError class #533

Merged
merged 5 commits into from
Apr 21, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ Bugsnag Notifiers on other platforms.
* Add `originalError` property to `BugsnagEvent`
[#541](https://github.com/bugsnag/bugsnag-cocoa/pull/541)

* Remove attachCustomStacktrace from public API
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: attachCustomStacktrace formatting.

[#547](https://github.com/bugsnag/bugsnag-cocoa/pull/547)

* 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 @@ -32,9 +38,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 @@ -62,6 +62,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 @@ -297,6 +298,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 @@ -562,6 +564,7 @@
4B775FD222CBE02A004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m */,
8AD9FA851E0862DC002859A7 /* BugsnagConfigurationTests.m */,
E7A9E56C2436365300D99F8A /* BugsnagDeviceTest.m */,
E73D443B243E192F001686F5 /* BugsnagErrorTest.m */,
00D7ACA223E984B300FBE4A7 /* BugsnagEventTests.m */,
E762E9F71F73F7E900E82B43 /* BugsnagHandledStateTest.m */,
E71DB9D824470FCF00D0161E /* BugsnagMetadataRedactionTest.m */,
Expand Down Expand Up @@ -1168,6 +1171,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 @@ -891,8 +891,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 @@ -76,7 +76,7 @@ typedef BOOL (^BugsnagOnBreadcrumbBlock)(BugsnagBreadcrumb *_Nonnull breadcrumb)
*/
typedef BOOL (^BugsnagOnSessionBlock)(BugsnagSession *_Nonnull session);

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 @@ -183,7 +183,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
32 changes: 7 additions & 25 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 @@ -74,12 +62,6 @@ initWithErrorName:(NSString *_Nonnull)name
handledState:(BugsnagHandledState *_Nonnull)handledState
session:(BugsnagSession *_Nullable)session;

/**
* Prepend a custom stacktrace with a provided type to the crash report
*/
- (void)attachCustomStacktrace:(NSArray *_Nonnull)frames
withType:(NSString *_Nonnull)type;

// -----------------------------------------------------------------------------
// MARK: - Properties
// -----------------------------------------------------------------------------
Expand All @@ -93,14 +75,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