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

(feat) Increased detail in handled event breadcrumb #493

Merged
merged 10 commits into from
Mar 30, 2020
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ Bugsnag Notifiers on other platforms.
`locale`) that were missing from the OOM reports.
[#444](https://github.com/bugsnag/bugsnag-cocoa/pull/444)

* Increased the detail in handled event breadcrumbs
[#493](https://github.com/bugsnag/bugsnag-cocoa/pull/493)

## 5.23.0 (2019-12-10)

This release removes support for reporting 'partial' or 'minimal' crash reports
Expand Down
45 changes: 25 additions & 20 deletions Source/BugsnagClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ - (void)setupConnectivityListener {
}];
}

// MARK: - Notify

- (void)notifyError:(NSError *)error
block:(void (^)(BugsnagEvent *))block {
Expand Down Expand Up @@ -661,8 +662,9 @@ - (void)notifyOutOfMemoryEvent {
}

- (void)notify:(NSException *)exception
handledState:(BugsnagHandledState *_Nonnull)handledState
block:(void (^)(BugsnagEvent *))block {
handledState:(BugsnagHandledState *_Nonnull)handledState
block:(void (^)(BugsnagEvent *))block
{
NSString *exceptionName = exception.name ?: NSStringFromClass([exception class]);
NSString *message = exception.reason;
if (handledState.unhandled) {
Expand All @@ -671,16 +673,18 @@ - (void)notify:(NSException *)exception
[self.sessionTracker handleHandledErrorEvent];
}

BugsnagEvent *report = [[BugsnagEvent alloc]
BugsnagEvent *event = [[BugsnagEvent alloc]
initWithErrorName:exceptionName
errorMessage:message
configuration:self.configuration
metadata:[self.configuration.metadata toDictionary]
handledState:handledState
session:self.sessionTracker.runningSession];

if (block) {
block(report);
block(event);
}

// We discard 5 stack frames (including this one) by default,
// and sum that with the number specified by report.depth:
//
Expand All @@ -691,35 +695,36 @@ - (void)notify:(NSException *)exception
// 3 -[BugsnagCrashSentry reportUserException:reason:]
// 4 -[BugsnagClient notify:message:block:]

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

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

[self.crashSentry reportUserException:reportName
reason:reportMessage
[self.crashSentry reportUserException:eventErrorClass
reason:eventMessage
originalException:exception
handledState:[handledState toJson]
appState:[self.state toDictionary]
callbackOverrides:report.overrides
metadata:[report.metadata copy]
callbackOverrides:event.overrides
metadata:[event.metadata copy]
config:[self.configuration.config toDictionary]
discardDepth:depth];

[self addBreadcrumbWithBlock:^(BugsnagBreadcrumb *_Nonnull crumb) {
crumb.type = BSGBreadcrumbTypeError;
crumb.message = reportName;
crumb.metadata = @{
BSGKeyMessage : reportMessage,
BSGKeySeverity : BSGFormatSeverity(report.severity)
};
crumb.type = BSGBreadcrumbTypeError;
crumb.message = eventErrorClass;
crumb.metadata = @{
BSGKeyMessage : eventMessage,
robinmacharg marked this conversation as resolved.
Show resolved Hide resolved
BSGKeyErrorClass : eventErrorClass,
BSGKeyUnhandled : [[event handledState] unhandled] ? @YES : @NO,
BSGKeySeverity : BSGFormatSeverity(event.severity)
};
}];
[self flushPendingReports];
}

- (void)addBreadcrumbWithBlock:
(void (^_Nonnull)(BugsnagBreadcrumb *_Nonnull))block {
- (void)addBreadcrumbWithBlock:(void (^_Nonnull)(BugsnagBreadcrumb *_Nonnull))block
{
[self.configuration.breadcrumbs addBreadcrumbWithBlock:block];
[self serializeBreadcrumbs];
}
Expand Down
84 changes: 84 additions & 0 deletions Tests/BugsnagClientTests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//
// BugsnagClientTests.m
// Tests
//
// Created by Robin Macharg on 18/03/2020.
// Copyright © 2020 Bugsnag. All rights reserved.
//

#import "Bugsnag.h"
#import "BugsnagClient.h"
#import "BugsnagTestConstants.h"
#import <XCTest/XCTest.h>

@interface BugsnagClientTests : XCTestCase
@end

@interface Bugsnag ()
+ (BugsnagConfiguration *)configuration;
+ (BugsnagClient *)client;
@end

@interface BugsnagClient ()
- (void)orientationChanged:(NSNotification *)notif;
@end

@interface BugsnagBreadcrumb ()
- (NSDictionary *)objectValue;
@end

@implementation BugsnagClientTests

/**
* A boilerplate helper method to setup Bugsnag
*/
-(void)setUpBugsnagWillCallNotify:(bool)willNotify {
NSError *error;
BugsnagConfiguration *configuration = [[BugsnagConfiguration alloc] initWithApiKey:DUMMY_APIKEY_32CHAR_1 error:&error];
if (willNotify) {
[configuration addOnSendBlock:^bool(BugsnagEvent * _Nonnull event) { return false; }];
}
[Bugsnag startBugsnagWithConfiguration:configuration];
}

/**
* Handled events leave a breadcrumb when norify() is called. Test that values are inserted
robinmacharg marked this conversation as resolved.
Show resolved Hide resolved
* correctly.
*/
- (void)testBreadcrumbEnhancedValues {
robinmacharg marked this conversation as resolved.
Show resolved Hide resolved

[self setUpBugsnagWillCallNotify:false];

NSException *ex = [[NSException alloc] initWithName:@"myName" reason:@"myReason1" userInfo:nil];

__block NSString *eventErrorClass;
__block NSString *eventErrorMessage;
__block BOOL eventUnhandled;
__block NSString *eventSeverity;

// Check that the event is passed the apiKey
[Bugsnag notify:ex block:^(BugsnagEvent * _Nonnull event) {
XCTAssertEqual(event.apiKey, DUMMY_APIKEY_32CHAR_1);

// Grab the values that end up in the event for later comparison
eventErrorClass = [event errorClass];
eventErrorMessage = [event errorMessage];
eventUnhandled = [event valueForKeyPath:@"handledState.unhandled"] ? YES : NO;
eventSeverity = BSGFormatSeverity([event severity]);
}];

// Check that we can change it
[Bugsnag notify:ex];

NSDictionary *breadcrumb = [[[[Bugsnag client] configuration] breadcrumbs][1] objectValue];
NSDictionary *metadata = [breadcrumb valueForKey:@"metaData"];

XCTAssertEqualObjects([breadcrumb valueForKey:@"type"], @"error");
XCTAssertEqualObjects([breadcrumb valueForKey:@"message"], eventErrorClass);
XCTAssertEqualObjects([metadata valueForKey:@"errorClass"], eventErrorClass);
XCTAssertEqualObjects([metadata valueForKey:@"message"], eventErrorMessage);
XCTAssertEqual((bool)[metadata valueForKey:@"unhandled"], eventUnhandled);
XCTAssertEqualObjects([metadata valueForKey:@"severity"], eventSeverity);
}

@end
14 changes: 9 additions & 5 deletions iOS/Bugsnag.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
0061D84924067AF80041C068 /* BSG_SSKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = 0061D84524067AF70041C068 /* BSG_SSKeychain.m */; };
0061D84A24067AF80041C068 /* BSG_SSKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = 0061D84524067AF70041C068 /* BSG_SSKeychain.m */; };
0061D84B24067AF80041C068 /* BSG_SSKeychain.h in Headers */ = {isa = PBXBuildFile; fileRef = 0061D84624067AF80041C068 /* BSG_SSKeychain.h */; };
0089B70324221EDE00D5A7F2 /* BugsnagClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0089B70224221EDE00D5A7F2 /* BugsnagClientTests.m */; };
009131BC23F46279000810D9 /* BugsnagMetadataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 009131BB23F46279000810D9 /* BugsnagMetadataTests.m */; };
009131BE23F5884E000810D9 /* BugsnagBaseUnitTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 009131BD23F5884E000810D9 /* BugsnagBaseUnitTest.m */; };
00D7ACAD23E9C63000FBE4A7 /* BugsnagTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00D7ACAC23E9C63000FBE4A7 /* BugsnagTests.m */; };
Expand Down Expand Up @@ -432,6 +433,7 @@
0061D84424067AF70041C068 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
0061D84524067AF70041C068 /* BSG_SSKeychain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSG_SSKeychain.m; sourceTree = "<group>"; };
0061D84624067AF80041C068 /* BSG_SSKeychain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSG_SSKeychain.h; sourceTree = "<group>"; };
0089B70224221EDE00D5A7F2 /* BugsnagClientTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BugsnagClientTests.m; path = ../../Tests/BugsnagClientTests.m; sourceTree = "<group>"; };
009131BB23F46279000810D9 /* BugsnagMetadataTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BugsnagMetadataTests.m; path = ../../Tests/BugsnagMetadataTests.m; sourceTree = "<group>"; };
009131BD23F5884E000810D9 /* BugsnagBaseUnitTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BugsnagBaseUnitTest.m; path = ../../Tests/BugsnagBaseUnitTest.m; sourceTree = "<group>"; };
009131BF23F58930000810D9 /* BugsnagBaseUnitTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BugsnagBaseUnitTest.h; path = ../../Tests/BugsnagBaseUnitTest.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -779,9 +781,14 @@
8A2C8F261C6BBD2300846019 /* Tests */ = {
isa = PBXGroup;
children = (
000DF29323DB4B4900A883CE /* TestConstants.m */,
00F9393723FC4F63008C7073 /* BugsnagTestsDummyClass.h */,
00F9393823FC4F64008C7073 /* BugsnagTestsDummyClass.m */,
00D7ACAC23E9C63000FBE4A7 /* BugsnagTests.m */,
009131BD23F5884E000810D9 /* BugsnagBaseUnitTest.m */,
009131BF23F58930000810D9 /* BugsnagBaseUnitTest.h */,
8A2C8F8B1C6BBFDD00846019 /* BugsnagBreadcrumbsTest.m */,
0089B70224221EDE00D5A7F2 /* BugsnagClientTests.m */,
4B3B193422CA7B0900475354 /* BugsnagCollectionsBSGDictSetSafeObjectTest.m */,
4B775FCE22CBDEB4004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m */,
4BE6C42522CAD61A0056305D /* BugsnagCollectionsBSGDictMergeTest.m */,
Expand All @@ -791,6 +798,7 @@
E77316E11F73B46600A14F06 /* BugsnagHandledStateTest.m */,
F429554A50F3ABE60537F70E /* BugsnagKSCrashSysInfoParserTest.m */,
009131BB23F46279000810D9 /* BugsnagMetadataTests.m */,
E72AE1FF241A57B100ED8972 /* BugsnagPluginTest.m */,
E78C1EFB1FCC759B00B976D3 /* BugsnagSessionTest.m */,
E78C1EF01FCC2F1700B976D3 /* BugsnagSessionTrackerTest.m */,
E78C1EF21FCC615400B976D3 /* BugsnagSessionTrackingPayloadTest.m */,
Expand All @@ -807,11 +815,6 @@
8A2C8F951C6BC08600846019 /* report.json */,
000DF29223DB4A6B00A883CE /* Swift Tests */,
E70EE0891FD7047D00FA745C /* KSCrash */,
000DF29323DB4B4900A883CE /* TestConstants.m */,
00D7ACAC23E9C63000FBE4A7 /* BugsnagTests.m */,
00F9393723FC4F63008C7073 /* BugsnagTestsDummyClass.h */,
00F9393823FC4F64008C7073 /* BugsnagTestsDummyClass.m */,
E72AE1FF241A57B100ED8972 /* BugsnagPluginTest.m */,
);
name = Tests;
path = BugsnagTests;
Expand Down Expand Up @@ -1321,6 +1324,7 @@
E70EE0781FD7039E00FA745C /* RFC3339DateTool_Tests.m in Sources */,
8A530CC422FDD51F00F0C108 /* KSCrashIdentifierTests.m in Sources */,
000DF29423DB4B4900A883CE /* TestConstants.m in Sources */,
0089B70324221EDE00D5A7F2 /* BugsnagClientTests.m in Sources */,
8A4E733F1DC13281001F7CC8 /* BugsnagConfigurationTests.m in Sources */,
E784D25A1FD70C25004B01E1 /* KSJSONCodec_Tests.m in Sources */,
009131BC23F46279000810D9 /* BugsnagMetadataTests.m in Sources */,
Expand Down