diff --git a/android/build.gradle b/android/build.gradle index ddfa71d0..592fd861 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -24,6 +24,6 @@ android { } dependencies { - compile 'com.bugsnag:bugsnag-android:4.11.0' + compile 'com.bugsnag:bugsnag-android:4.12.0' compile 'com.facebook.react:react-native:+' } diff --git a/android/src/main/java/com/bugsnag/BugsnagReactNative.java b/android/src/main/java/com/bugsnag/BugsnagReactNative.java index 2c0a8f7b..10fc4ea8 100644 --- a/android/src/main/java/com/bugsnag/BugsnagReactNative.java +++ b/android/src/main/java/com/bugsnag/BugsnagReactNative.java @@ -84,6 +84,16 @@ public void startSession() { Bugsnag.startSession(); } + @ReactMethod + public void stopSession() { + Bugsnag.stopSession(); + } + + @ReactMethod + public void resumeSession() { + Bugsnag.resumeSession(); + } + @ReactMethod public void startWithOptions(ReadableMap options) { String apiKey = null; @@ -278,7 +288,7 @@ private void configureRuntimeOptions(Client client, ReadableMap options) { // The launch event session is skipped because autoCaptureSessions // was not set when Bugsnag was first initialized. Manually sending a // session to compensate. - client.startSession(); + client.resumeSession(); } } } diff --git a/cocoa/BugsnagReactNative.h b/cocoa/BugsnagReactNative.h index 3705f718..2bedd774 100644 --- a/cocoa/BugsnagReactNative.h +++ b/cocoa/BugsnagReactNative.h @@ -38,5 +38,7 @@ - (void)setUser:(NSDictionary *)userInfo; - (void)clearUser; - (void)startSession; +- (void)stopSession; +- (void)resumeSession; @end diff --git a/cocoa/BugsnagReactNative.m b/cocoa/BugsnagReactNative.m index 859b4128..d8cb33dd 100644 --- a/cocoa/BugsnagReactNative.m +++ b/cocoa/BugsnagReactNative.m @@ -209,6 +209,20 @@ + (void)startWithConfiguration:(BugsnagConfiguration *)config { [Bugsnag startSession]; } +RCT_EXPORT_METHOD(stopSession) { + if (![Bugsnag bugsnagStarted]) { + return; + } + [Bugsnag stopSession]; +} + +RCT_EXPORT_METHOD(resumeSession) { + if (![Bugsnag bugsnagStarted]) { + return; + } + [Bugsnag resumeSession]; +} + RCT_EXPORT_METHOD(clearUser) { if (![Bugsnag bugsnagStarted]) { return; @@ -280,7 +294,7 @@ + (void)startWithConfiguration:(BugsnagConfiguration *)config { // The launch event session is skipped because shouldAutoCaptureSessions // was not set when Bugsnag was first initialized. Manually sending a // session to compensate. - [Bugsnag startSession]; + [Bugsnag resumeSession]; } } diff --git a/cocoa/vendor/bugsnag-cocoa/Source/Bugsnag.h b/cocoa/vendor/bugsnag-cocoa/Source/Bugsnag.h index 18784334..a99f1e90 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/Bugsnag.h +++ b/cocoa/vendor/bugsnag-cocoa/Source/Bugsnag.h @@ -211,13 +211,60 @@ static NSString *_Nonnull const BugsnagSeverityInfo = @"info"; (BOOL)writeBinaryImagesForUserReported; /** - * Manually starts tracking a new session. + * Starts tracking a new session. * - * Sessions automatically start when the application enters the foreground state, and end when the application exits - * the foreground.If you wish to manually start sessions, simply call this method from the relevant part of your - * application. Starting a new session will automatically end the previous one. + * By default, sessions are automatically started when the application enters the foreground. + * If you wish to manually call startSession at + * the appropriate time in your application instead, the default behaviour can be disabled via + * shouldAutoCaptureSessions. + * + * Any errors which occur in an active session count towards your application's + * stability score. You can prevent errors from counting towards your stability + * score by calling stopSession and resumeSession at the appropriate + * time in your application. + * + * @see stopSession: + * @see resumeSession: */ - + (void)startSession; +/** + * Stops tracking a session. + * + * When a session is stopped, errors will not count towards your application's + * stability score. This can be advantageous if you do not wish these calculations to + * include a certain type of error, for example, a crash in a background service. + * You should disable automatic session tracking via shouldAutoCaptureSessions if you call this method. + * + * A stopped session can be resumed by calling resumeSession, + * which will make any subsequent errors count towards your application's + * stability score. Alternatively, an entirely new session can be created by calling startSession. + * + * @see startSession: + * @see resumeSession: + */ ++ (void)stopSession; + +/** + * Resumes a session which has previously been stopped, or starts a new session if none exists. + * + * If a session has already been resumed or started and has not been stopped, calling this + * method will have no effect. You should disable automatic session tracking via + * shouldAutoCaptureSessions if you call this method. + * + * It's important to note that sessions are stored in memory for the lifetime of the + * application process and are not persisted on disk. Therefore calling this method on app + * startup would start a new session, rather than continuing any previous session. + * + * You should call this at the appropriate time in your application when you wish to + * resume a previously started session. Any subsequent errors which occur in your application + * will be reported to Bugsnag and will count towards your application's stability score. + * + * @see startSession: + * @see stopSession: + * + * @return true if a previous session was resumed, false if a new session was started. + */ ++ (BOOL)resumeSession; + @end diff --git a/cocoa/vendor/bugsnag-cocoa/Source/Bugsnag.m b/cocoa/vendor/bugsnag-cocoa/Source/Bugsnag.m index f28394c2..d0d3d6e1 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/Bugsnag.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/Bugsnag.m @@ -226,6 +226,20 @@ + (void)startSession { } } ++ (void)stopSession { + if ([self bugsnagStarted]) { + [self.notifier stopSession]; + } +} + ++ (BOOL)resumeSession { + if ([self bugsnagStarted]) { + return [self.notifier resumeSession]; + } else { + return false; + } +} + + (NSDateFormatter *)payloadDateFormatter { static NSDateFormatter *formatter; static dispatch_once_t onceToken; diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagApiClient.h b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagApiClient.h index 1c37a305..d1cca8ea 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagApiClient.h +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagApiClient.h @@ -7,7 +7,7 @@ @class BugsnagConfiguration; -typedef void (^RequestCompletion)(id data, BOOL success, NSError *error); +typedef void (^RequestCompletion)(NSUInteger reportCount, BOOL success, NSError *error); @interface BugsnagApiClient : NSObject @@ -21,11 +21,11 @@ typedef void (^RequestCompletion)(id data, BOOL success, NSError *error); - (NSOperation *)deliveryOperation; -- (void)sendData:(id)data - withPayload:(NSDictionary *)payload - toURL:(NSURL *)url - headers:(NSDictionary *)headers - onCompletion:(RequestCompletion)onCompletion; +- (void)sendItems:(NSUInteger)count + withPayload:(NSDictionary *)payload + toURL:(NSURL *)url + headers:(NSDictionary *)headers + onCompletion:(RequestCompletion)onCompletion; @property(readonly) NSOperationQueue *sendQueue; @property(readonly) BugsnagConfiguration *config; diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagApiClient.m b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagApiClient.m index 8550286c..2463844f 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagApiClient.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagApiClient.m @@ -49,11 +49,11 @@ - (NSOperation *)deliveryOperation { #pragma mark - Delivery -- (void)sendData:(id)data - withPayload:(NSDictionary *)payload - toURL:(NSURL *)url - headers:(NSDictionary *)headers - onCompletion:(RequestCompletion)onCompletion { +- (void)sendItems:(NSUInteger)count + withPayload:(NSDictionary *)payload + toURL:(NSURL *)url + headers:(NSDictionary *)headers + onCompletion:(RequestCompletion)onCompletion { @try { NSError *error = nil; @@ -64,7 +64,7 @@ - (void)sendData:(id)data if (jsonData == nil) { if (onCompletion) { - onCompletion(data, NO, error); + onCompletion(0, NO, error); } return; } @@ -79,7 +79,7 @@ - (void)sendData:(id)data NSURLResponse *_Nullable response, NSError *_Nullable requestErr) { if (onCompletion) { - onCompletion(data, requestErr == nil, requestErr); + onCompletion(count, requestErr == nil, requestErr); } }]; [task resume]; @@ -92,13 +92,13 @@ - (void)sendData:(id)data returningResponse:&response error:&error]; if (onCompletion) { - onCompletion(data, error == nil, error); + onCompletion(count, error == nil, error); } #pragma clang diagnostic pop } } @catch (NSException *exception) { if (onCompletion) { - onCompletion(data, NO, + onCompletion(count, NO, [NSError errorWithDomain:exception.reason code:420 userInfo:@{BSGKeyException: exception}]); diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagCrashReport.h b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagCrashReport.h index 6d82b7a5..027a9fde 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagCrashReport.h +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagCrashReport.h @@ -39,6 +39,18 @@ NSString *_Nonnull BSGFormatSeverity(BSGSeverity severity); @interface BugsnagCrashReport : NSObject +/** + * 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; + /** * Create a new crash report from a JSON crash report generated by * BugsnagCrashSentry @@ -193,6 +205,10 @@ __deprecated_msg("Use toJson: instead."); */ @property(readwrite, copy, nullable) NSDictionary *appState; +/** + * If YES, a complete report was not able to be obtained at generation time + */ +@property (readonly, nonatomic, getter=isIncomplete) BOOL incomplete; /** * Returns the enhanced error message for the thread, or nil if none exists. diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagCrashReport.m b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagCrashReport.m index 00eb3676..87c9aa5d 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagCrashReport.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagCrashReport.m @@ -70,7 +70,8 @@ } NSString *_Nonnull BSGParseErrorClass(NSDictionary *error, - NSString *errorType) { + NSString *errorType, + NSString *fallbackValue) { NSString *errorClass; if ([errorType isEqualToString:BSGKeyCppException]) { @@ -86,7 +87,7 @@ } if (!errorClass) { // use a default value - errorClass = @"Exception"; + errorClass = fallbackValue.length > 0 ? fallbackValue : @"Exception"; } return errorClass; } @@ -184,6 +185,13 @@ + (instancetype)errorDataFromThreads:(NSArray *)threads; - (instancetype)initWithClass:(NSString *_Nonnull)errorClass message:(NSString *_Nonnull)errorMessage NS_DESIGNATED_INITIALIZER; @end +@interface FallbackReportData : NSObject +@property (nonatomic, strong) NSString *errorClass; +@property (nonatomic, getter=isUnhandled) BOOL unhandled; +@property (nonatomic) BSGSeverity severity; +- (instancetype)initWithMetadata:(NSString *)metadata; +@end + @interface BugsnagCrashReport () /** @@ -212,25 +220,33 @@ @interface BugsnagCrashReport () @property(nonatomic, readwrite, copy, nullable) NSDictionary *customException; @property(nonatomic) BugsnagSession *session; +@property (nonatomic, readwrite, getter=isIncomplete) BOOL incomplete; @end @implementation BugsnagCrashReport - (instancetype)initWithKSReport:(NSDictionary *)report { + return [self initWithKSReport:report fileMetadata:@""]; +} + +- (instancetype)initWithKSReport:(NSDictionary *)report + fileMetadata:(NSString *)metadata { if (self = [super init]) { + FallbackReportData *fallback = [[FallbackReportData alloc] initWithMetadata:metadata]; _notifyReleaseStages = [report valueForKeyPath:@"user.config.notifyReleaseStages"]; _releaseStage = BSGParseReleaseStage(report); _error = [report valueForKeyPath:@"crash.error"]; + _incomplete = report.count == 0; _errorType = _error[BSGKeyType]; _threads = [report valueForKeyPath:@"crash.threads"]; RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:_threads]; if (data) { - _errorClass = data.errorClass; + _errorClass = data.errorClass ?: fallback.errorClass; _errorMessage = data.errorMessage; } else { - _errorClass = BSGParseErrorClass(_error, _errorType); + _errorClass = BSGParseErrorClass(_error, _errorType, fallback.errorClass); _errorMessage = BSGParseErrorMessage(report, _error, _errorType); } _binaryImages = report[@"binary_images"]; @@ -259,7 +275,7 @@ - (instancetype)initWithKSReport:(NSDictionary *)report { // only makes sense to use serialised value for handled exceptions _depth = [[report valueForKeyPath:@"user.state.crash.depth"] unsignedIntegerValue]; - } else { // the event was unhandled. + } else if (_errorType != nil) { // the event was unhandled. BOOL isSignal = [BSGKeySignal isEqualToString:_errorType]; SeverityReasonType severityReason = isSignal ? Signal : UnhandledException; @@ -268,6 +284,11 @@ - (instancetype)initWithKSReport:(NSDictionary *)report { severity:BSGSeverityError attrValue:_errorClass]; _depth = 0; + } else { // Incomplete report + SeverityReasonType severityReason = [fallback isUnhandled] ? UnhandledException : HandledError; + _handledState = [BugsnagHandledState handledStateWithSeverityReason:severityReason + severity:fallback.severity + attrValue:nil]; } _severity = _handledState.currentSeverity; @@ -478,7 +499,11 @@ - (NSDictionary *)toJson { BSGDictSetSafeObject(event, BSGFormatSeverity(self.severity), BSGKeySeverity); BSGDictSetSafeObject(event, [self breadcrumbs], BSGKeyBreadcrumbs); BSGDictSetSafeObject(event, metaData, BSGKeyMetaData); - + + if ([self isIncomplete]) { + BSGDictSetSafeObject(event, @YES, BSGKeyIncomplete); + } + NSDictionary *device = [self.device bsg_mergedInto:self.deviceState]; BSGDictSetSafeObject(event, device, BSGKeyDevice); @@ -620,6 +645,42 @@ - (NSString *_Nullable)enhancedErrorMessageForThread:(NSDictionary *_Nullable)th @end +@implementation FallbackReportData + +- (instancetype)initWithMetadata:(NSString *)metadata { + if (self = [super init]) { + NSString *separator = @"-"; + NSString *location = metadata; + NSRange range = [location rangeOfString:separator options:NSBackwardsSearch]; + if (range.location != NSNotFound) { + _errorClass = [location substringFromIndex:range.location + 1]; + location = [location substringToIndex:range.location]; + } + range = [location rangeOfString:separator options:NSBackwardsSearch]; + if (range.location != NSNotFound) { + NSString *value = [location substringFromIndex:range.location + 1]; + _unhandled = ![value isEqualToString:@"h"]; + location = [location substringToIndex:range.location + 1]; + } else { + _unhandled = YES; + } + range = [location rangeOfString:separator options:NSBackwardsSearch]; + if (range.location != NSNotFound) { + NSString *value = [location substringFromIndex:range.location]; + if ([value isEqualToString:@"w"]) { + _severity = BSGSeverityWarning; + } else if ([value isEqualToString:@"i"]) { + _severity = BSGSeverityInfo; + } else { + _severity = BSGSeverityError; + } + } + } + return self; +} + +@end + @implementation RegisterErrorData + (instancetype)errorDataFromThreads:(NSArray *)threads { for (NSDictionary *thread in threads) { diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagErrorReportApiClient.m b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagErrorReportApiClient.m index 0bf3b80b..18b2d1ce 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagErrorReportApiClient.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagErrorReportApiClient.m @@ -30,11 +30,11 @@ - (void)main { @autoreleasepool { @try { [[BSG_KSCrash sharedInstance] - sendAllReportsWithCompletion:^(NSArray *filteredReports, + sendAllReportsWithCompletion:^(NSUInteger sentReportCount, BOOL completed, NSError *error) { if (error) { bsg_log_warn(@"Failed to send reports: %@", error); - } else if (filteredReports.count > 0) { + } else if (sentReportCount > 0) { bsg_log_info(@"Reports sent."); } }]; diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagFileStore.h b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagFileStore.h index 48a10510..fad1e36a 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagFileStore.h +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagFileStore.h @@ -40,6 +40,12 @@ */ - (NSArray *)allFiles; +/** Get a list of all files by filename. + * + * @return A collection of file contents indexed by filename. + */ +- (NSDictionary *)allFilesByName; + /** Delete a file. * * @param fileId The file ID. diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagFileStore.m b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagFileStore.m index 5fadd7a0..1eee9774 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagFileStore.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagFileStore.m @@ -128,13 +128,17 @@ - (NSUInteger)fileCount { } - (NSArray *)allFiles { + return [[self allFilesByName] allValues]; +} + +- (NSDictionary *)allFilesByName { NSArray *fileIds = [self fileIds]; - NSMutableArray *files = - [NSMutableArray arrayWithCapacity:[fileIds count]]; + NSMutableDictionary *files = + [NSMutableDictionary dictionaryWithCapacity:[fileIds count]]; for (NSString *fileId in fileIds) { NSDictionary *fileContents = [self fileWithId:fileId]; if (fileContents != nil) { - [files addObject:fileContents]; + [files setObject:fileContents forKey:fileId]; } } diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagKeys.h b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagKeys.h index 54a89693..aabd1560 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagKeys.h +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagKeys.h @@ -19,6 +19,7 @@ static NSString *const BSGKeyName = @"name"; static NSString *const BSGKeyTimestamp = @"timestamp"; static NSString *const BSGKeyType = @"type"; static NSString *const BSGKeyMetaData = @"metaData"; +static NSString *const BSGKeyIncomplete = @"incomplete"; static NSString *const BSGKeyId = @"id"; static NSString *const BSGKeyUser = @"user"; static NSString *const BSGKeyEmail = @"email"; diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagNotifier.h b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagNotifier.h index a4a9b612..c3ff3faa 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagNotifier.h +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagNotifier.h @@ -47,6 +47,8 @@ - (void)start; - (void)startSession; +- (void)stopSession; +- (BOOL)resumeSession; /** * Notify Bugsnag of an exception diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagNotifier.m b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagNotifier.m index 36bfca04..378bf5b8 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.17.3"; +NSString *const NOTIFIER_VERSION = @"5.19.0"; NSString *const NOTIFIER_URL = @"https://github.com/bugsnag/bugsnag-cocoa"; NSString *const BSTabCrash = @"crash"; NSString *const BSAttributeDepth = @"depth"; @@ -157,6 +157,7 @@ void BSSerializeJSONDictionary(NSDictionary *dictionary, char **destination) { */ void BSGWriteSessionCrashData(BugsnagSession *session) { if (session == nil) { + hasRecordedSessions = false; return; } // copy session id @@ -411,6 +412,14 @@ - (void)startSession { [self.sessionTracker startNewSession]; } +- (void)stopSession { + [self.sessionTracker stopSession]; +} + +- (BOOL)resumeSession { + return [self.sessionTracker resumeSession]; +} + - (void)flushPendingReports { [self.errorReportApiClient flushPendingData]; } @@ -519,7 +528,7 @@ - (void)notify:(NSString *)exceptionName configuration:self.configuration metaData:[self.configuration.metaData toDictionary] handledState:handledState - session:self.sessionTracker.currentSession]; + session:self.sessionTracker.runningSession]; if (block) { block(report); } diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSession.h b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSession.h index bec896e7..6d26febd 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSession.h +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSession.h @@ -20,11 +20,14 @@ - (_Nonnull instancetype)initWithDictionary:(NSDictionary *_Nonnull)dict; - (NSDictionary *_Nonnull)toJson; +- (void)stop; +- (void)resume; @property(readonly) NSString *_Nonnull sessionId; @property(readonly) NSDate *_Nonnull startedAt; @property(readonly) BugsnagUser *_Nullable user; @property(readonly) BOOL autoCaptured; +@property(readonly, getter=isStopped) BOOL stopped; @property NSUInteger unhandledCount; @property NSUInteger handledCount; diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSession.m b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSession.m index bcc525d2..8e90cdda 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSession.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSession.m @@ -16,6 +16,10 @@ static NSString *const kBugsnagStartedAt = @"startedAt"; static NSString *const kBugsnagUser = @"user"; +@interface BugsnagSession () +@property(readwrite, getter=isStopped) BOOL stopped; +@end + @implementation BugsnagSession - (instancetype)initWithId:(NSString *_Nonnull)sessionId @@ -59,4 +63,12 @@ - (NSDictionary *)toJson { return [NSDictionary dictionaryWithDictionary:dict]; } +- (void)stop { + self.stopped = YES; +} + +- (void)resume { + self.stopped = NO; +} + @end diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSessionTracker.h b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSessionTracker.h index 0ead0ffd..eded96c6 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSessionTracker.h +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSessionTracker.h @@ -32,6 +32,9 @@ typedef void (^SessionTrackerCallback)(BugsnagSession *newSession); */ - (void)startNewSession; +- (void)stopSession; +- (BOOL)resumeSession; + /** Record a new auto-captured session if neededed. Auto-captured sessions are only recorded and sent if -[BugsnagConfiguration shouldAutoCaptureSessions] is YES @@ -60,7 +63,9 @@ typedef void (^SessionTrackerCallback)(BugsnagSession *newSession); */ - (void)handleHandledErrorEvent; - -@property (nonatomic, strong, readonly) BugsnagSession *currentSession; +/** + * Retrieves the running session, or nil if the session is stopped or has not yet been started/resumed. + */ +@property (nonatomic, strong, readonly) BugsnagSession *runningSession; @end diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSessionTracker.m b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSessionTracker.m index 55fb94e6..95e5ec92 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSessionTracker.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSessionTracker.m @@ -56,6 +56,36 @@ - (void)startNewSession { [self startNewSessionWithAutoCaptureValue:NO]; } +- (void)stopSession { + [[self currentSession] stop]; + + if (self.callback) { + self.callback(nil); + } +} + +- (BOOL)resumeSession { + BugsnagSession *session = self.currentSession; + + if (session == nil) { + [self startNewSessionWithAutoCaptureValue:NO]; + return NO; + } else { + BOOL stopped = session.isStopped; + [session resume]; + return stopped; + } +} + +- (BugsnagSession *)runningSession { + BugsnagSession *session = self.currentSession; + + if (session == nil || session.isStopped) { + return nil; + } + return session; +} + - (void)startNewSessionIfAutoCaptureEnabled { if (self.config.shouldAutoCaptureSessions && [self.config shouldSendReports]) { [self startNewSessionWithAutoCaptureValue:YES]; @@ -67,6 +97,7 @@ - (void)startNewSessionWithAutoCaptureValue:(BOOL)isAutoCaptured { bsg_log_err(@"The session tracking endpoint has not been set. Session tracking is disabled"); return; } + self.currentSession = [[BugsnagSession alloc] initWithId:[[NSUUID UUID] UUIDString] startDate:[NSDate date] user:self.config.currentUser @@ -95,14 +126,16 @@ - (void)handleAppForegroundEvent { } - (void)handleHandledErrorEvent { - if (self.currentSession == nil) { + BugsnagSession *session = [self runningSession]; + + if (session == nil) { return; } - @synchronized (self.currentSession) { - self.currentSession.handledCount++; - if (self.callback && (self.config.shouldAutoCaptureSessions || !self.currentSession.autoCaptured)) { - self.callback(self.currentSession); + @synchronized (session) { + session.handledCount++; + if (self.callback && (self.config.shouldAutoCaptureSessions || !session.autoCaptured)) { + self.callback(session); } } } diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSessionTrackingApiClient.m b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSessionTrackingApiClient.m index b140ecb7..e63719e7 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSessionTrackingApiClient.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSessionTrackingApiClient.m @@ -47,11 +47,11 @@ - (void)deliverSessionsInStore:(BugsnagSessionFileStore *)store { @"Bugsnag-API-Key": apiKey, @"Bugsnag-Sent-At": [BSG_RFC3339DateTool stringFromDate:[NSDate new]] }; - [self sendData:payload - withPayload:[payload toJson] - toURL:sessionURL - headers:HTTPHeaders - onCompletion:^(id data, BOOL success, NSError *error) { + [self sendItems:sessions.count + withPayload:[payload toJson] + toURL:sessionURL + headers:HTTPHeaders + onCompletion:^(NSUInteger sentCount, BOOL success, NSError *error) { if (success && error == nil) { bsg_log_info(@"Sent %lu sessions to Bugsnag", (unsigned long) sessionCount); diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSink.m b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSink.m index 0fc2517a..f8192071 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSink.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagSink.m @@ -56,15 +56,18 @@ - (instancetype)initWithApiClient:(BugsnagErrorReportApiClient *)apiClient { // - the report-specific `notifyReleaseStages` property is unset and the global // `notifyReleaseStages` property // and it contains the current stage -- (void)filterReports:(NSArray *)reports +- (void)filterReports:(NSDictionary *)reports onCompletion:(BSG_KSCrashReportFilterCompletion)onCompletion { NSMutableArray *bugsnagReports = [NSMutableArray new]; BugsnagConfiguration *configuration = [Bugsnag configuration]; - for (NSDictionary *report in reports) { - BugsnagCrashReport *bugsnagReport = [[BugsnagCrashReport alloc] initWithKSReport:report]; - BOOL incompleteReport = (![@"standard" isEqualToString:[report valueForKeyPath:@"report.type"]] || - [[report objectForKey:@"incomplete"] boolValue]); + for (NSString *fileKey in reports) { + NSDictionary *report = reports[fileKey]; + BugsnagCrashReport *bugsnagReport = [[BugsnagCrashReport alloc] initWithKSReport:report + fileMetadata:fileKey]; + BOOL incompleteReport = ([bugsnagReport isIncomplete] + || ![@"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]; @@ -109,7 +112,7 @@ - (void)filterReports:(NSArray *)reports if (bugsnagReports.count == 0) { if (onCompletion) { - onCompletion(bugsnagReports, YES, nil); + onCompletion(bugsnagReports.count, YES, nil); } return; } @@ -120,7 +123,7 @@ - (void)filterReports:(NSArray *)reports #pragma clang diagnostic ignored "-Wdeprecated-declarations" for (BugsnagBeforeNotifyHook hook in configuration.beforeNotifyHooks) { if (reportData) { - reportData = hook(reports, reportData); + reportData = hook(bugsnagReports, reportData); } else { break; } @@ -129,16 +132,16 @@ - (void)filterReports:(NSArray *)reports if (reportData == nil) { if (onCompletion) { - onCompletion(@[], YES, nil); + onCompletion(0, YES, nil); } return; } - [self.apiClient sendData:bugsnagReports - withPayload:reportData - toURL:configuration.notifyURL - headers:[configuration errorApiHeaders] - onCompletion:onCompletion]; + [self.apiClient sendItems:bugsnagReports.count + withPayload:reportData + toURL:configuration.notifyURL + headers:[configuration errorApiHeaders] + onCompletion:onCompletion]; } diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m index deb835d1..13ec842a 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m @@ -320,12 +320,12 @@ - (void)sendAllReportsWithCompletion: (BSG_KSCrashReportFilterCompletion)onCompletion { [self.crashReportStore pruneFilesLeaving:self.maxStoredReports]; - NSArray *reports = [self allReports]; + NSDictionary *reports = [self allReportsByFilename]; BSG_KSLOG_INFO(@"Sending %d crash reports", [reports count]); [self sendReports:reports - onCompletion:^(NSArray *filteredReports, BOOL completed, + onCompletion:^(NSUInteger sentReportCount, BOOL completed, NSError *error) { BSG_KSLOG_DEBUG(@"Process finished with completion: %d", completed); if (error != nil) { @@ -336,7 +336,7 @@ - (void)sendAllReportsWithCompletion: self.deleteBehaviorAfterSendAll == BSG_KSCDeleteAlways) { [self deleteAllReports]; } - bsg_kscrash_i_callCompletion(onCompletion, filteredReports, + bsg_kscrash_i_callCompletion(onCompletion, sentReportCount, completed, error); }]; } @@ -357,6 +357,7 @@ - (void)reportUserException:(NSString *)name const char *cName = [name cStringUsingEncoding:NSUTF8StringEncoding]; const char *cReason = [reason cStringUsingEncoding:NSUTF8StringEncoding]; bsg_kscrash_reportUserException(cName, cReason, + [handledState[@"currentSeverity"] UTF8String], [self encodeAsJSONString:handledState], [self encodeAsJSONString:overrides], [self encodeAsJSONString:metadata], @@ -404,16 +405,16 @@ - (NSString *)crashReportsPath { return self.crashReportStore.path; } -- (void)sendReports:(NSArray *)reports +- (void)sendReports:(NSDictionary *)reports onCompletion:(BSG_KSCrashReportFilterCompletion)onCompletion { if ([reports count] == 0) { - bsg_kscrash_i_callCompletion(onCompletion, reports, YES, nil); + bsg_kscrash_i_callCompletion(onCompletion, 0, YES, nil); return; } if (self.sink == nil) { bsg_kscrash_i_callCompletion( - onCompletion, reports, NO, + onCompletion, 0, NO, [NSError bsg_errorWithDomain:[[self class] description] code:0 description:@"No sink set. Crash reports not sent."]); @@ -421,9 +422,9 @@ - (void)sendReports:(NSArray *)reports } [self.sink filterReports:reports - onCompletion:^(NSArray *filteredReports, BOOL completed, + onCompletion:^(NSUInteger sentReportCount, BOOL completed, NSError *error) { - bsg_kscrash_i_callCompletion(onCompletion, filteredReports, + bsg_kscrash_i_callCompletion(onCompletion, sentReportCount, completed, error); }]; } @@ -432,6 +433,10 @@ - (NSArray *)allReports { return [self.crashReportStore allFiles]; } +- (NSDictionary *)allReportsByFilename { + return [self.crashReportStore allFilesByName]; +} + - (BOOL)redirectConsoleLogsToFile:(NSString *)fullPath overwrite:(BOOL)overwrite { if (bsg_kslog_setLogFilename([fullPath UTF8String], overwrite)) { diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashAdvanced.h b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashAdvanced.h index 82692373..24682d03 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashAdvanced.h +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashAdvanced.h @@ -85,6 +85,10 @@ typedef enum { */ - (NSArray *)allReports; +/** Get all reports as dictionaries, indexed by file name. + */ +- (NSDictionary *)allReportsByFilename; + #pragma mark - Configuration - /** Init BSG_KSCrash instance with custom report files directory path. */ @@ -156,7 +160,7 @@ typedef enum { * @param reports The reports to send. * @param onCompletion Called when sending is complete (nil = ignore). */ -- (void)sendReports:(NSArray *)reports +- (void)sendReports:(NSDictionary *)reports onCompletion:(BSG_KSCrashReportFilterCompletion)onCompletion; @end diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c index 1ebc3a93..0165b7de 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c @@ -63,11 +63,53 @@ static char *bsg_g_stateFilePath; // ============================================================================ #pragma mark - Utility - // ============================================================================ +static const int bsg_filepath_len = 512; +static const int bsg_error_class_filepath_len = 21; +static const char bsg_filepath_context_sep = '-'; static inline BSG_KSCrash_Context *crashContext(void) { return &bsg_g_crashReportContext; } +int bsg_create_filepath(char *base, char filepath[bsg_filepath_len], char severity, char error_class[bsg_error_class_filepath_len]) { + int length; + for (length = 0; length < bsg_filepath_len; length++) { + if (base[length] == '\0') { + break; + } + filepath[length] = base[length]; + } + if (length > 5) // Remove initial .json from path + length -= 5; + + // append contextual info + BSG_KSCrash_Context *context = crashContext(); + filepath[length++] = bsg_filepath_context_sep; + filepath[length++] = severity; + filepath[length++] = bsg_filepath_context_sep; + // 'h' for handled vs 'u'nhandled + filepath[length++] = context->crash.crashType == BSG_KSCrashTypeUserReported ? 'h' : 'u'; + filepath[length++] = bsg_filepath_context_sep; + for (int i = 0; error_class != NULL && i < bsg_error_class_filepath_len; i++) { + char c = error_class[i]; + if (c == '\0') + break; + else if (c == 47 || c > 126 || c <= 0) + // disallow '/' and characters outside of the ascii range + continue; + filepath[length++] = c; + } + // add suffix + filepath[length++] = '.'; + filepath[length++] = 'j'; + filepath[length++] = 's'; + filepath[length++] = 'o'; + filepath[length++] = 'n'; + filepath[length++] = '\0'; + + return length; +} + // ============================================================================ #pragma mark - Callbacks - // ============================================================================ @@ -78,7 +120,7 @@ static inline BSG_KSCrash_Context *crashContext(void) { * * This function gets passed as a callback to a crash handler. */ -void bsg_kscrash_i_onCrash(void) { +void bsg_kscrash_i_onCrash(char severity, char *errorClass) { BSG_KSLOG_DEBUG("Updating application state to note crash."); bsg_kscrashstate_notifyAppCrash(); @@ -92,8 +134,9 @@ void bsg_kscrash_i_onCrash(void) { bsg_kscrashreport_writeMinimalReport(context, bsg_g_recrashReportFilePath); } else { - bsg_kscrashreport_writeStandardReport(context, - bsg_g_crashReportFilePath); + char filepath[bsg_filepath_len]; + bsg_create_filepath(bsg_g_crashReportFilePath, filepath, severity, errorClass); + bsg_kscrashreport_writeStandardReport(context, filepath); } } @@ -242,6 +285,7 @@ void bsg_kscrash_setCrashNotifyCallback( } void bsg_kscrash_reportUserException(const char *name, const char *reason, + const char *severity, const char *handledState, const char *overrides, const char *metadata, @@ -249,7 +293,7 @@ void bsg_kscrash_reportUserException(const char *name, const char *reason, const char *config, int discardDepth, bool terminateProgram) { - bsg_kscrashsentry_reportUserException(name, reason, handledState, overrides, + bsg_kscrashsentry_reportUserException(name, reason, severity, handledState, overrides, metadata, appState, config, discardDepth, terminateProgram); } diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.h b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.h index 9df92454..2aeb81cc 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.h +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.h @@ -187,6 +187,7 @@ void bsg_kscrash_setCrashNotifyCallback( * Terminate the program instead. */ void bsg_kscrash_reportUserException(const char *name, const char *reason, + const char *severity, const char *handledState, const char *overrides, const char *metadata, diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry.c b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry.c index a2cc2d98..50b0bd1c 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry.c +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry.c @@ -96,7 +96,7 @@ static bool bsg_g_threads_are_running = true; BSG_KSCrashType bsg_kscrashsentry_installWithContext(BSG_KSCrash_SentryContext *context, BSG_KSCrashType crashTypes, - void (*onCrash)(void)) { + void (*onCrash)(char, char *)) { if (bsg_ksmachisBeingTraced()) { if (context->reportWhenDebuggerIsAttached) { BSG_KSLOG_WARN("KSCrash: App is running in a debugger. Crash " @@ -206,7 +206,7 @@ void bsg_kscrashsentry_resumeThreads(void) { } void bsg_kscrashsentry_clearContext(BSG_KSCrash_SentryContext *context) { - void (*onCrash)(void) = context->onCrash; + void (*onCrash)(char, char *) = context->onCrash; bool threadTracingEnabled = context->threadTracingEnabled; bool reportWhenDebuggerIsAttached = context->reportWhenDebuggerIsAttached; bool suspendThreadsForUserReported = context->suspendThreadsForUserReported; diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry.h b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry.h index 1e8e7331..b6d51008 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry.h +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry.h @@ -51,7 +51,7 @@ typedef struct BSG_KSCrash_SentryContext { // Caller defined values. Caller must fill these out prior to installation. /** Called by the crash handler when a crash is detected. */ - void (*onCrash)(void); + void (*onCrash)(char, char[21]); /** If true, will suspend threads for user reported exceptions. */ bool suspendThreadsForUserReported; @@ -162,7 +162,7 @@ typedef struct BSG_KSCrash_SentryContext { BSG_KSCrashType bsg_kscrashsentry_installWithContext(BSG_KSCrash_SentryContext *context, BSG_KSCrashType crashTypes, - void (*onCrash)(void)); + void (*onCrash)(char, char *)); /** Uninstall crash sentry. * diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm index f7de5f03..1456c83d 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm @@ -179,7 +179,9 @@ static void CPPExceptionTerminate(void) { bsg_g_context->crashReason = description; BSG_KSLOG_DEBUG(@"Calling main crash handler."); - bsg_g_context->onCrash(); + char errorClass[21]; + strncpy(errorClass, bsg_g_context->CPPException.name, sizeof(errorClass)); + bsg_g_context->onCrash('e', errorClass); BSG_KSLOG_DEBUG( @"Crash handling complete. Restoring original handlers."); diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_Deadlock.m b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_Deadlock.m index cb7e5b0b..29706dd5 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_Deadlock.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_Deadlock.m @@ -114,7 +114,7 @@ - (void)handleDeadlock { bsg_g_context->registersAreValid = false; BSG_KSLOG_DEBUG(@"Calling main crash handler."); - bsg_g_context->onCrash(); + bsg_g_context->onCrash('e', ""); BSG_KSLOG_DEBUG(@"Crash handling complete. Restoring original handlers."); bsg_kscrashsentry_uninstall(BSG_KSCrashTypeAll); diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_MachException.c b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_MachException.c index 146ee01d..5ce551ba 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_MachException.c +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_MachException.c @@ -285,7 +285,9 @@ void *ksmachexc_i_handleExceptions(void *const userData) { bsg_g_context->mach.subcode = exceptionMessage.code[1]; BSG_KSLOG_DEBUG("Calling main crash handler."); - bsg_g_context->onCrash(); + char errorClass[21]; + strncpy(errorClass, bsg_ksmachexceptionName(bsg_g_context->mach.type), sizeof(errorClass)); + bsg_g_context->onCrash('e', errorClass); BSG_KSLOG_DEBUG( "Crash handling complete. Restoring original handlers."); diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_NSException.m b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_NSException.m index b72084eb..10a46fb0 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_NSException.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_NSException.m @@ -128,7 +128,9 @@ void bsg_recordException(NSException *exception) { bsg_g_context->stackTraceLength = (int)numFrames; BSG_KSLOG_DEBUG(@"Calling main crash handler."); - bsg_g_context->onCrash(); + char errorClass[21]; + strncpy(errorClass, bsg_g_context->NSException.name, sizeof(errorClass)); + bsg_g_context->onCrash('e', errorClass); } } diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_Signal.c b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_Signal.c index 51bf8a2e..1cc26f8e 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_Signal.c +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_Signal.c @@ -107,7 +107,18 @@ void bsg_kssighndl_i_handleSignal(int sigNum, siginfo_t *signalInfo, bsg_g_context->signal.signalInfo = signalInfo; BSG_KSLOG_DEBUG("Calling main crash handler."); - bsg_g_context->onCrash(); + char errorClass[21]; + const char *sigName = bsg_kssignal_signalName(sigNum); + if (sigName != NULL) { + for (int i = 0; i < sizeof(errorClass); i++) { + char c = sigName[i]; + if (c == '\0') { + break; + } + errorClass[i] = c; + } + } + bsg_g_context->onCrash('e', errorClass); BSG_KSLOG_DEBUG( "Crash handling complete. Restoring original handlers."); diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_User.c b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_User.c index fddd6991..e7a397e9 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_User.c +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_User.c @@ -48,6 +48,7 @@ void bsg_kscrashsentry_uninstallUserExceptionHandler(void) { } void bsg_kscrashsentry_reportUserException(const char *name, const char *reason, + const char *severity, const char *handledState, const char *overrides, const char *metadata, @@ -92,7 +93,11 @@ void bsg_kscrashsentry_reportUserException(const char *name, const char *reason, bsg_g_context->userException.state = appState; BSG_KSLOG_DEBUG("Calling main crash handler."); - bsg_g_context->onCrash(); + char errorClass[21]; + strncpy(errorClass, bsg_g_context->userException.name, sizeof(errorClass)); + // default to 'w'arning level severity + char severityChar = severity != NULL && strlen(severity) > 0 ? severity[0] : 'w'; + bsg_g_context->onCrash(severityChar, errorClass); if (terminateProgram) { bsg_kscrashsentry_uninstall(BSG_KSCrashTypeAll); diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_User.h b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_User.h index 103f3382..f68f583e 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_User.h +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_User.h @@ -67,6 +67,7 @@ void bsg_kscrashsentry_uninstallUserExceptionHandler(void); * Terminate the program instead. */ void bsg_kscrashsentry_reportUserException(const char *name, const char *reason, + const char *severity, const char *handledState, const char *overrides, const char *metadata, diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSCrashCallCompletion.h b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSCrashCallCompletion.h index d212ea4c..bc047172 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSCrashCallCompletion.h +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSCrashCallCompletion.h @@ -29,10 +29,10 @@ /** Conditionally call a completion method if it's not nil. * * @param onCompletion The completion block. If nil, this function does nothing. - * @param filteredReports The parameter to send as "filteredReports". + * @param sentReportCount The number of reports successfully handled. * @param completed The parameter to send as "completed". * @param error The parameter to send as "error". */ void bsg_kscrash_i_callCompletion( - BSG_KSCrashReportFilterCompletion onCompletion, NSArray *filteredReports, + BSG_KSCrashReportFilterCompletion onCompletion, NSUInteger sentReportCount, BOOL completed, NSError *error); diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSCrashCallCompletion.m b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSCrashCallCompletion.m index 7a7f2027..181bdbb0 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSCrashCallCompletion.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSCrashCallCompletion.m @@ -27,9 +27,9 @@ #import "BSG_KSCrashCallCompletion.h" void bsg_kscrash_i_callCompletion( - BSG_KSCrashReportFilterCompletion onCompletion, NSArray *filteredReports, + BSG_KSCrashReportFilterCompletion onCompletion, NSUInteger reportCount, BOOL completed, NSError *error) { if (onCompletion) { - onCompletion(filteredReports, completed, error); + onCompletion(reportCount, completed, error); } } diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Reporting/Filters/BSG_KSCrashReportFilter.h b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Reporting/Filters/BSG_KSCrashReportFilter.h index f09806c5..7f73fa20 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Reporting/Filters/BSG_KSCrashReportFilter.h +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Reporting/Filters/BSG_KSCrashReportFilter.h @@ -37,7 +37,7 @@ * @param reports The reports to process. * @param onCompletion Block to call when processing is complete. */ -- (void)filterReports:(NSArray *)reports +- (void)filterReports:(NSDictionary *)reports onCompletion:(BSG_KSCrashReportFilterCompletion)onCompletion; @end diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Reporting/Filters/BSG_KSCrashReportFilterCompletion.h b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Reporting/Filters/BSG_KSCrashReportFilterCompletion.h index 9462707d..19177ad8 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Reporting/Filters/BSG_KSCrashReportFilterCompletion.h +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Reporting/Filters/BSG_KSCrashReportFilterCompletion.h @@ -28,13 +28,12 @@ /** Callback for filter operations. * - * @param filteredReports The filtered reports (may be incomplete if "completed" - * is false). + * @param sentReportCount The number of reports successfully sent. * @param completed True if filtering completed. * Can be false due to a non-erroneous condition (such as a * user cancelling the operation). * @param error Non-nil if an error occurred. */ -typedef void (^BSG_KSCrashReportFilterCompletion)(NSArray *filteredReports, +typedef void (^BSG_KSCrashReportFilterCompletion)(NSUInteger sentReportCount, BOOL completed, NSError *error); diff --git a/cocoa/vendor/bugsnag-cocoa/iOS/Bugsnag.xcodeproj/project.pbxproj b/cocoa/vendor/bugsnag-cocoa/iOS/Bugsnag.xcodeproj/project.pbxproj index d8cc3ea4..fe98c995 100644 --- a/cocoa/vendor/bugsnag-cocoa/iOS/Bugsnag.xcodeproj/project.pbxproj +++ b/cocoa/vendor/bugsnag-cocoa/iOS/Bugsnag.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 8A12006A221C36420008C9C3 /* BSGFilepathTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A120069221C36420008C9C3 /* BSGFilepathTests.m */; }; 8A2C8F231C6BBD2300846019 /* Bugsnag.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A2C8F181C6BBD2300846019 /* Bugsnag.framework */; }; 8A2C8F4F1C6BBE3C00846019 /* Bugsnag.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A2C8F3D1C6BBE3C00846019 /* Bugsnag.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8A2C8F501C6BBE3C00846019 /* Bugsnag.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A2C8F3E1C6BBE3C00846019 /* Bugsnag.m */; }; @@ -34,6 +35,7 @@ 8A627CD01EC2A5FD00F7C04E /* BSGSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A627CCF1EC2A5FD00F7C04E /* BSGSerialization.h */; }; 8A627CD21EC2A62900F7C04E /* BSGSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A627CD11EC2A62900F7C04E /* BSGSerialization.m */; }; 8AE1BC951DEFCE8B00D16CEF /* BugsnagConfigurationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8AE1BC941DEFCE8B00D16CEF /* BugsnagConfigurationSpec.m */; }; + E70E52152216E41C00A590AB /* BugsnagSessionTrackerStopTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E70E52142216E41C00A590AB /* BugsnagSessionTrackerStopTest.m */; }; E70EE0781FD7039E00FA745C /* RFC3339DateTool_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = E70EE0771FD7039D00FA745C /* RFC3339DateTool_Tests.m */; }; E70EE07E1FD703D600FA745C /* NSError+SimpleConstructor_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = E70EE07A1FD703D500FA745C /* NSError+SimpleConstructor_Tests.m */; }; E70EE07F1FD703D600FA745C /* NSDictionary+Merge_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = E70EE07B1FD703D500FA745C /* NSDictionary+Merge_Tests.m */; }; @@ -410,6 +412,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 8A120069221C36420008C9C3 /* BSGFilepathTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BSGFilepathTests.m; path = ../../Tests/BSGFilepathTests.m; sourceTree = ""; }; 8A2C8F181C6BBD2300846019 /* Bugsnag.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Bugsnag.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8A2C8F1D1C6BBD2300846019 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; 8A2C8F221C6BBD2300846019 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -445,6 +448,7 @@ 8A627CCF1EC2A5FD00F7C04E /* BSGSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BSGSerialization.h; path = ../Source/BSGSerialization.h; sourceTree = SOURCE_ROOT; }; 8A627CD11EC2A62900F7C04E /* BSGSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BSGSerialization.m; path = ../Source/BSGSerialization.m; sourceTree = SOURCE_ROOT; }; 8AE1BC941DEFCE8B00D16CEF /* BugsnagConfigurationSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagConfigurationSpec.m; path = ../Tests/BugsnagConfigurationSpec.m; sourceTree = SOURCE_ROOT; }; + E70E52142216E41C00A590AB /* BugsnagSessionTrackerStopTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BugsnagSessionTrackerStopTest.m; path = ../../Tests/BugsnagSessionTrackerStopTest.m; sourceTree = ""; }; E70EE0771FD7039D00FA745C /* RFC3339DateTool_Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RFC3339DateTool_Tests.m; path = ../Tests/KSCrash/RFC3339DateTool_Tests.m; sourceTree = SOURCE_ROOT; }; E70EE07A1FD703D500FA745C /* NSError+SimpleConstructor_Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSError+SimpleConstructor_Tests.m"; path = "../Tests/KSCrash/NSError+SimpleConstructor_Tests.m"; sourceTree = SOURCE_ROOT; }; E70EE07B1FD703D500FA745C /* NSDictionary+Merge_Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+Merge_Tests.m"; path = "../Tests/KSCrash/NSDictionary+Merge_Tests.m"; sourceTree = SOURCE_ROOT; }; @@ -732,6 +736,8 @@ F429554A50F3ABE60537F70E /* BugsnagKSCrashSysInfoParserTest.m */, F42954B7D892334E7551F0F3 /* RegisterErrorDataTest.m */, F429551527EAE3AFE1F605FE /* BugsnagThreadTest.m */, + E70E52142216E41C00A590AB /* BugsnagSessionTrackerStopTest.m */, + 8A120069221C36420008C9C3 /* BSGFilepathTests.m */, ); name = Tests; path = BugsnagTests; @@ -1079,7 +1085,7 @@ 8A2C8F0F1C6BBD2300846019 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0910; + LastUpgradeCheck = 1010; ORGANIZATIONNAME = Bugsnag; TargetAttributes = { 8A2C8F171C6BBD2300846019 = { @@ -1243,7 +1249,9 @@ E78C1EF31FCC615400B976D3 /* BugsnagSessionTrackingPayloadTest.m in Sources */, E78C1EF11FCC2F1700B976D3 /* BugsnagSessionTrackerTest.m in Sources */, F4295995C3259BF7D9730BC4 /* BugsnagKSCrashSysInfoParserTest.m in Sources */, + E70E52152216E41C00A590AB /* BugsnagSessionTrackerStopTest.m in Sources */, F4295F017754324FD52CCE46 /* RegisterErrorDataTest.m in Sources */, + 8A12006A221C36420008C9C3 /* BSGFilepathTests.m in Sources */, F42952D83435C02F8D891C40 /* BugsnagThreadTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1336,12 +1344,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -1391,12 +1401,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; diff --git a/cocoa/vendor/bugsnag-cocoa/iOS/Bugsnag.xcodeproj/xcshareddata/xcschemes/Bugsnag.xcscheme b/cocoa/vendor/bugsnag-cocoa/iOS/Bugsnag.xcodeproj/xcshareddata/xcschemes/Bugsnag.xcscheme index 1d9fb8c3..4af397f0 100644 --- a/cocoa/vendor/bugsnag-cocoa/iOS/Bugsnag.xcodeproj/xcshareddata/xcschemes/Bugsnag.xcscheme +++ b/cocoa/vendor/bugsnag-cocoa/iOS/Bugsnag.xcodeproj/xcshareddata/xcschemes/Bugsnag.xcscheme @@ -1,6 +1,6 @@ + codeCoverageEnabled = "YES" + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -57,7 +56,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/index.d.ts b/index.d.ts index 4fcacaaa..a68f9657 100644 --- a/index.d.ts +++ b/index.d.ts @@ -16,6 +16,10 @@ export class Client { public startSession(): void; + public stopSession(): void; + + public resumeSession(): void; + public enableConsoleBreadcrumbs(): void; public disableConsoleBreadCrumbs(): void; diff --git a/src/Bugsnag.js b/src/Bugsnag.js index 5cd4c485..8d9949ac 100644 --- a/src/Bugsnag.js +++ b/src/Bugsnag.js @@ -128,10 +128,68 @@ export class Client { NativeClient.clearUser() } + /** + * Starts tracking a new session. You should disable automatic session tracking via + * `autoCaptureSessions` if you call this method. + * + * You should call this at the appropriate time in your application when you wish to start a + * session. Any subsequent errors which occur in your application will be reported to + * Bugsnag and will count towards your application's + * [stability score](https://docs.bugsnag.com/product/releases/releases-dashboard/#stability-score). + * This will start a new session even if there is already an existing + * session; you should call `resumeSession()` if you only want to start a session + * when one doesn't already exist. + * + * @see `resumeSession()` + * @see `stopSession()` + * @see `autoCaptureSessions` + */ startSession = () => { NativeClient.startSession() } + /** + * Stops tracking a session. You should disable automatic session tracking via + * `autoCaptureSessions` if you call this method. + * + * You should call this at the appropriate time in your application when you wish to stop a + * session. Any subsequent errors which occur in your application will still be reported to + * Bugsnag but will not count towards your application's + * [stability score](https://docs.bugsnag.com/product/releases/releases-dashboard/#stability-score). + * This can be advantageous if, for example, you do not wish the + * stability score to include crashes in a background service. + * + * @see `startSession()` + * @see `resumeSession()` + * @see `autoCaptureSessions` + */ + stopSession = () => { + NativeClient.stopSession() + } + + /** + * Resumes a session which has previously been stopped, or starts a new session if none exists. + * If a session has already been resumed or started and has not been stopped, calling this + * method will have no effect. You should disable automatic session tracking via + * `autoCaptureSessions` if you call this method. + * + * It's important to note that sessions are stored in memory for the lifetime of the + * application process and are not persisted on disk. Therefore calling this method on app + * startup would start a new session, rather than continuing any previous session. + * + * You should call this at the appropriate time in your application when you wish to resume + * a previously started session. Any subsequent errors which occur in your application will + * be reported to Bugsnag and will count towards your application's + * [stability score](https://docs.bugsnag.com/product/releases/releases-dashboard/#stability-score). + * + * @see `startSession()` + * @see `stopSession()` + * @see `autoCaptureSessions` + */ + resumeSession = () => { + NativeClient.resumeSession() + } + /** * Leaves a 'breadcrumb' log message. The most recent breadcrumbs * are attached to subsequent error reports. diff --git a/test/app.ts b/test/app.ts index ad5ec8ea..dfcb8187 100644 --- a/test/app.ts +++ b/test/app.ts @@ -9,3 +9,6 @@ client.notify(new Error('flop')) client.setUser('123', 'B. Nag', 'bugs.nag@bugsnag.com') client.setUser(undefined, undefined, undefined) client.setUser() +client.stopSession() +client.startSession() +client.resumeSession()