Skip to content

Commit

Permalink
Merge pull request #1057 from bugsnag/release-v6.8.2
Browse files Browse the repository at this point in the history
Release v6.8.2
  • Loading branch information
nickdowell authored Mar 31, 2021
2 parents c6f2433 + 6be044b commit 0734e04
Show file tree
Hide file tree
Showing 65 changed files with 525 additions and 815 deletions.
4 changes: 2 additions & 2 deletions .jazzy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ author_url: "https://www.bugsnag.com"
author: "Bugsnag Inc"
clean: false # avoid deleting docs/.git
framework_root: "Bugsnag"
github_file_prefix: "https://github.com/bugsnag/bugsnag-cocoa/tree/v6.8.1/Bugsnag"
github_file_prefix: "https://github.com/bugsnag/bugsnag-cocoa/tree/v6.8.2/Bugsnag"
github_url: "https://github.com/bugsnag/bugsnag-cocoa"
hide_documentation_coverage: true
module: "Bugsnag"
module_version: "6.8.1"
module_version: "6.8.2"
objc: true
output: "docs"
readme: "README.md"
Expand Down
4 changes: 2 additions & 2 deletions Bugsnag.podspec.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Bugsnag",
"version": "6.8.1",
"version": "6.8.2",
"summary": "The Bugsnag crash reporting framework for Apple platforms.",
"homepage": "https://bugsnag.com",
"license": "MIT",
Expand All @@ -9,7 +9,7 @@
},
"source": {
"git": "https://github.com/bugsnag/bugsnag-cocoa.git",
"tag": "v6.8.1"
"tag": "v6.8.2"
},
"frameworks": [
"Foundation",
Expand Down
32 changes: 6 additions & 26 deletions Bugsnag.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

9 changes: 0 additions & 9 deletions Bugsnag/BugsnagCrashSentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,6 @@
notifier:(BugsnagNotifier *)notifier
onCrash:(BSGReportCallback)onCrash;

- (void)reportUserException:(NSString *)reportName
reason:(NSString *)reportMessage
handledState:(NSDictionary *)handledState
appState:(NSDictionary *)appState
callbackOverrides:(NSDictionary *)overrides
eventOverrides:(NSDictionary *)eventOverrides
metadata:(NSDictionary *)metadata
config:(NSDictionary *)config;

- (BSG_KSCrashType)mapKSToBSGCrashTypes:(BugsnagErrorTypes *)errorTypes;

@end
25 changes: 2 additions & 23 deletions Bugsnag/BugsnagCrashSentry.m
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ - (void)install:(BugsnagConfiguration *)config
// applies to unhandled errors
ksCrash.threadTracingEnabled = config.sendThreads != BSGThreadSendPolicyNever;

// User reported events are *always* handled
BSG_KSCrashType crashTypes = BSG_KSCrashTypeUserReported;
// User reported events do not go through KSCrash
BSG_KSCrashType crashTypes = 0;

// If Bugsnag is autodetecting errors then the types of event detected is configurable
// (otherwise it's just the user reported events)
Expand Down Expand Up @@ -65,25 +65,4 @@ - (BSG_KSCrashType)mapKSToBSGCrashTypes:(BugsnagErrorTypes *)errorTypes
| (errorTypes.machExceptions ? BSG_KSCrashTypeMachException : 0));
}

- (void)reportUserException:(NSString *)reportName
reason:(NSString *)reportMessage
handledState:(NSDictionary *)handledState
appState:(NSDictionary *)appState
callbackOverrides:(NSDictionary *)overrides
eventOverrides:(NSDictionary *)eventOverrides
metadata:(NSDictionary *)metadata
config:(NSDictionary *)config {
[[BSG_KSCrash sharedInstance] reportUserException:reportName
reason:reportMessage
handledState:handledState
appState:appState
callbackOverrides:overrides
eventOverrides:eventOverrides
metadata:metadata
config:config];

bsg_log_debug(@"Saved KSCrashReport for \"%@\" \"%@\"", handledState[@"severityReasonType"],
[[eventOverrides[@"exceptions"] firstObject] valueForKey:@"errorClass"]);
}

@end
58 changes: 22 additions & 36 deletions Bugsnag/Client/BugsnagClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ - (NSDictionary *)BSG_mergedInto:(NSDictionary *)dest;
* @param writer report writer which will receive updated metadata
*/
void BSSerializeDataCrashHandler(const BSG_KSCrashReportWriter *writer, int type) {
BOOL isCrash = BSG_KSCrashTypeUserReported != type;
BOOL isCrash = YES;
if (hasRecordedSessions) { // a session is available
// persist session info
writer->addStringElement(writer, "id", (const char *) sessionId);
Expand Down Expand Up @@ -564,15 +564,18 @@ - (void)computeDidCrashLastLaunch {

// Did the app crash in a way that was detected by KSCrash?
if (bsg_kscrashstate_currentState()->crashedLastLaunch || !access(crashSentinelPath, F_OK)) {
bsg_log_info(@"Last run terminated due to a crash.");
unlink(crashSentinelPath);
didCrash = YES;
}
// Was the app terminated while the main thread was hung?
else if ((self.eventFromLastLaunch = [self loadFatalAppHangEvent])) {
bsg_log_info(@"Last run terminated during an app hang.");
didCrash = YES;
}
// Was the app terminated while in the foreground? (probably an OOM)
else if ([self shouldReportOOM]) {
bsg_log_info(@"Last run terminated abnormally; likely Out Of Memory.");
self.eventFromLastLaunch = [self generateOutOfMemoryEvent];
didCrash = YES;
}
Expand Down Expand Up @@ -776,6 +779,7 @@ - (NSString *)context {
// see notify:handledState:block for further info

- (void)notifyError:(NSError *_Nonnull)error {
bsg_log_debug(@"Notify called with %@", error);
BugsnagHandledState *state = [BugsnagHandledState handledStateWithSeverityReason:HandledError
severity:BSGSeverityWarning
attrValue:error.domain];
Expand All @@ -789,6 +793,7 @@ - (void)notifyError:(NSError *_Nonnull)error {
- (void)notifyError:(NSError *)error
block:(BugsnagOnErrorBlock)block
{
bsg_log_debug(@"Notify called with %@", error);
BugsnagHandledState *state = [BugsnagHandledState handledStateWithSeverityReason:HandledError
severity:BSGSeverityWarning
attrValue:error.domain];
Expand Down Expand Up @@ -828,6 +833,7 @@ - (BOOL)appendNSErrorInfo:(NSError *)error
}

- (void)notify:(NSException *_Nonnull)exception {
bsg_log_debug(@"Notify called with %@", exception);
BugsnagHandledState *state =
[BugsnagHandledState handledStateWithSeverityReason:HandledException];
[self notify:exception handledState:state block:nil];
Expand All @@ -836,6 +842,7 @@ - (void)notify:(NSException *_Nonnull)exception {
- (void)notify:(NSException *)exception
block:(BugsnagOnErrorBlock)block
{
bsg_log_debug(@"Notify called with %@", exception);
BugsnagHandledState *state =
[BugsnagHandledState handledStateWithSeverityReason:HandledException];
[self notify:exception handledState:state block:block];
Expand Down Expand Up @@ -899,25 +906,18 @@ - (void)notify:(NSException *)exception
}

/**
* Notify Bugsnag of an exception. Only intended for React Native/Unity use.
* Notify Bugsnag of an exception. Used for user-reported (handled) errors, React Native, and Unity.
*
* @param event the event
* @param block Configuration block for adding additional report information
*/
- (void)notifyInternal:(BugsnagEvent *_Nonnull)event
block:(BugsnagOnErrorBlock)block
{
if ([self shouldNotifyEvent:event withOnErrorBlock:block]) {
[self deliverEvent:event];
[self addAutoBreadcrumbForEvent:event];
}
}

- (BOOL)shouldNotifyEvent:(nonnull BugsnagEvent *)event withOnErrorBlock:(nullable BugsnagOnErrorBlock)block {
NSString *errorClass = event.errors.firstObject.errorClass;
if ([self.configuration shouldDiscardErrorClass:errorClass]) {
bsg_log_info(@"Discarding event because errorClass \"%@\" matched configuration.discardClasses", errorClass);
return NO;
return;
}

// enhance device information with additional metadata
Expand All @@ -930,7 +930,7 @@ - (BOOL)shouldNotifyEvent:(nonnull BugsnagEvent *)event withOnErrorBlock:(nullab
BOOL originalUnhandledValue = event.unhandled;
@try {
if (block != nil && !block(event)) { // skip notifying if callback false
return NO;
return;
}
} @catch (NSException *exception) {
bsg_log_err(@"Error from onError callback: %@", exception);
Expand All @@ -939,40 +939,26 @@ - (BOOL)shouldNotifyEvent:(nonnull BugsnagEvent *)event withOnErrorBlock:(nullab
[event notifyUnhandledOverridden];
}

return YES;
}

- (void)deliverEvent:(BugsnagEvent *)event {
if (event.handledState.unhandled) {
[self.sessionTracker handleUnhandledErrorEvent];
} else {
[self.sessionTracker handleHandledErrorEvent];
}

// Temporary conditional until all (non-crash) events can be sent via -uploadEvent:
if (event == self.appHangEvent) {
if (event.unhandled) {
// Unhandled Javscript exceptions from React Native result in the app being terminated shortly after the
// call to notifyInternal, so the event needs to be persisted to disk for sending in the next session.
// The fatal "RCTFatalException" / "Unhandled JS Exception" is explicitly ignored by
// BugsnagReactNativePlugin's OnSendErrorBlock.
[self.eventUploader storeEvent:event];
// Replicate previous delivery mechanism's behaviour of waiting 1 second before delivering the event.
// This should prevent potential duplicate uploads of unhandled errors where the app subsequently terminates.
[self.eventUploader performSelector:@selector(uploadStoredEvents) withObject:nil afterDelay:1];
} else {
[self.eventUploader uploadEvent:event completionHandler:nil];
return;
}

// apiKey not added to event JSON by default, need to add it here
// for when it is read next
NSMutableDictionary *eventOverrides = [[event toJsonWithRedactedKeys:self.configuration.redactedKeys] mutableCopy];
eventOverrides[BSGKeyApiKey] = event.apiKey;

// handled errors should persist any information edited by the user
// in a section within the KSCrash report so it can be read
// when the error is delivered
[self.crashSentry reportUserException:@""
reason:@""
handledState:[event.handledState toJson]
appState:[self.state toDictionary]
callbackOverrides:event.overrides
eventOverrides:eventOverrides
metadata:[event.metadata toDictionary]
config:self.configuration.dictionaryRepresentation];

[self.eventUploader uploadStoredEvents];
[self addAutoBreadcrumbForEvent:event];
}

// MARK: - Breadcrumbs
Expand Down
11 changes: 2 additions & 9 deletions Bugsnag/Delivery/BSGEventUploadObjectOperation.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

#import "BSGEventUploadObjectOperation.h"

#import "BSGJSONSerialization.h"
#import "BugsnagEvent.h"
#import "BugsnagLogger.h"

Expand All @@ -25,14 +24,8 @@ - (BugsnagEvent *)loadEventAndReturnError:(NSError **)errorPtr {
return self.event;
}

- (void)storeEventPayload:(NSDictionary *)eventPayload inDirectory:(NSString *)directory {
NSString *file = [[directory stringByAppendingPathComponent:[NSUUID UUID].UUIDString] stringByAppendingPathExtension:@"json"];
NSError *error = nil;
if ([BSGJSONSerialization writeJSONObject:eventPayload toFile:file options:0 error:&error]) {
[self.delegate uploadOperationDidStoreEventPayload:self];
} else {
bsg_log_err(@"Error encountered while saving event payload for retry: %@", error);
}
- (BOOL)shouldStoreEventPayloadForRetry {
return YES;
}

- (NSString *)name {
Expand Down
8 changes: 3 additions & 5 deletions Bugsnag/Delivery/BSGEventUploadOperation.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,8 @@ NS_ASSUME_NONNULL_BEGIN
/// To be implemented by subclasses that load their data from a file.
- (void)deleteEvent;

/// Called if the event upload failed but should be retried later.
///
/// To be implemented by subclasses that need to persist the payload for a future retry.
- (void)storeEventPayload:(NSDictionary *)eventPayload inDirectory:(NSString *)directory;
/// Whether the payload should be stored so that it can be retried later.
@property (readonly, nonatomic) BOOL shouldStoreEventPayloadForRetry;

@end

Expand All @@ -58,7 +56,7 @@ NS_ASSUME_NONNULL_BEGIN

@property (readonly, nonatomic) BugsnagNotifier *notifier;

- (void)uploadOperationDidStoreEventPayload:(BSGEventUploadOperation *)uploadOperation;
- (void)storeEventPayload:(NSDictionary *)eventPayload;

@end

Expand Down
7 changes: 3 additions & 4 deletions Bugsnag/Delivery/BSGEventUploadOperation.m
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ - (void)runWithDelegate:(id<BSGEventUploadOperationDelegate>)delegate completion

case BugsnagApiClientDeliveryStatusFailed:
bsg_log_debug(@"Upload failed; will retry event %@", self.name);
[self storeEventPayload:eventPayload inDirectory:[BSGFileLocations current].events];
if (self.shouldStoreEventPayloadForRetry) {
[delegate storeEventPayload:eventPayload];
}
break;

case BugsnagApiClientDeliveryStatusUndeliverable:
Expand All @@ -138,9 +140,6 @@ - (BugsnagEvent *)loadEventAndReturnError:(NSError **)errorPtr {
- (void)deleteEvent {
}

- (void)storeEventPayload:(NSDictionary *)eventPayload inDirectory:(NSString *)directory {
}

// MARK: Asynchronous NSOperation implementation

- (void)start {
Expand Down
2 changes: 2 additions & 0 deletions Bugsnag/Delivery/BSGEventUploader.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ NS_ASSUME_NONNULL_BEGIN

- (instancetype)initWithConfiguration:(BugsnagConfiguration *)configuration notifier:(BugsnagNotifier *)notifier;

- (void)storeEvent:(BugsnagEvent *)event;

- (void)uploadEvent:(BugsnagEvent *)event completionHandler:(nullable void (^)(void))completionHandler;

- (void)uploadStoredEvents;
Expand Down
19 changes: 18 additions & 1 deletion Bugsnag/Delivery/BSGEventUploader.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
#import "BSGEventUploadKSCrashReportOperation.h"
#import "BSGEventUploadObjectOperation.h"
#import "BSGFileLocations.h"
#import "BSGJSONSerialization.h"
#import "BugsnagConfiguration.h"
#import "BugsnagEvent+Private.h"
#import "BugsnagLogger.h"


Expand Down Expand Up @@ -60,7 +62,16 @@ - (void)dealloc {

// MARK: - Public API

- (void)storeEvent:(BugsnagEvent *)event {
[self storeEventPayload:[event toJsonWithRedactedKeys:self.configuration.redactedKeys]];
}

- (void)uploadEvent:(BugsnagEvent *)event completionHandler:(nullable void (^)(void))completionHandler {
NSUInteger operationCount = self.uploadQueue.operationCount;
if (operationCount >= self.configuration.maxPersistedEvents) {
bsg_log_warn(@"Dropping notification, %lu outstanding requests", (unsigned long)operationCount);
return;
}
BSGEventUploadObjectOperation *operation = [[BSGEventUploadObjectOperation alloc] initWithEvent:event delegate:self];
operation.completionBlock = completionHandler;
[self.uploadQueue addOperation:operation];
Expand Down Expand Up @@ -170,7 +181,13 @@ - (void)deleteExcessFiles:(NSMutableArray<NSString *> *)sortedEventFiles {

// MARK: - BSGEventUploadOperationDelegate

- (void)uploadOperationDidStoreEventPayload:(BSGEventUploadOperation *)uploadOperation {
- (void)storeEventPayload:(NSDictionary *)eventPayload {
NSString *file = [[self.eventsDirectory stringByAppendingPathComponent:[NSUUID UUID].UUIDString] stringByAppendingPathExtension:@"json"];
NSError *error = nil;
if (![BSGJSONSerialization writeJSONObject:eventPayload toFile:file options:0 error:&error]) {
bsg_log_err(@"Error encountered while saving event payload for retry: %@", error);
return;
}
[self deleteExcessFiles:[self sortedEventFiles]];
}

Expand Down
7 changes: 7 additions & 0 deletions Bugsnag/Helpers/BSG_RFC3339DateTool.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,11 @@
*/
+ (NSString *)stringFromUNIXTimestamp:(unsigned long long)timestamp;

/**
* Determines whether a string might contain an RFC3339 formatted date.
*
* Useful if the overhead of -dateFromString: needs to be avoided.
*/
+ (BOOL)isLikelyDateString:(NSString *)string;

@end
24 changes: 24 additions & 0 deletions Bugsnag/Helpers/BSG_RFC3339DateTool.m
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,28 @@ + (NSString *)stringFromUNIXTimestamp:(unsigned long long)timestamp {
[self stringFromDate:[NSDate dateWithTimeIntervalSince1970:timestamp]];
}

+ (BOOL)isLikelyDateString:(NSString *)string {
const char *ptr = string.UTF8String;
return (string.length >= 19 &&
isdigit(ptr[0]) &&
isdigit(ptr[1]) &&
isdigit(ptr[2]) &&
isdigit(ptr[3]) &&
'-' == (ptr[4]) &&
isdigit(ptr[5]) &&
isdigit(ptr[6]) &&
'-' == (ptr[7]) &&
isdigit(ptr[8]) &&
isdigit(ptr[9]) &&
'T' == ptr[10] &&
isdigit(ptr[11]) &&
isdigit(ptr[12]) &&
':' == (ptr[13]) &&
isdigit(ptr[14]) &&
isdigit(ptr[15]) &&
':' == (ptr[16]) &&
isdigit(ptr[17]) &&
isdigit(ptr[18]));
}

@end
Loading

0 comments on commit 0734e04

Please sign in to comment.