Skip to content

Commit

Permalink
feat: Close session for unhandled error (#3091)
Browse files Browse the repository at this point in the history
Closes session as crashed for hybrids SDK when a new envelope with unhandled crashed event is captured, and start a new session.

Co-authored-by: Philipp Hofmann <philipp.hofmann@sentry.io>
  • Loading branch information
brustolin and philipphofmann authored Jun 15, 2023
1 parent 9d28681 commit b385962
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 86 deletions.
56 changes: 43 additions & 13 deletions Sources/Sentry/SentryHub.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#import "SentryEvent+Private.h"
#import "SentryFileManager.h"
#import "SentryId.h"
#import "SentryLevelMapper.h"
#import "SentryLog.h"
#import "SentryNSTimerWrapper.h"
#import "SentryPerformanceTracker.h"
Expand Down Expand Up @@ -590,37 +591,66 @@ - (void)captureEnvelope:(SentryEnvelope *)envelope

- (SentryEnvelope *)updateSessionState:(SentryEnvelope *)envelope
{
if ([self envelopeContainsEventWithErrorOrHigher:envelope.items]) {
SentrySession *currentSession = [self incrementSessionErrors];

if (currentSession != nil) {
// Create a new envelope with the session update
NSMutableArray<SentryEnvelopeItem *> *itemsToSend =
[[NSMutableArray alloc] initWithArray:envelope.items];
[itemsToSend addObject:[[SentryEnvelopeItem alloc] initWithSession:currentSession]];

return [[SentryEnvelope alloc] initWithHeader:envelope.header items:itemsToSend];
BOOL handled = YES;
if ([self envelopeContainsEventWithErrorOrHigher:envelope.items wasHandled:&handled]) {
SentrySession *currentSession;
@synchronized(_sessionLock) {
currentSession = handled ? [self incrementSessionErrors] : [_session copy];
if (currentSession == nil) {
return envelope;
}
if (!handled) {
[currentSession endSessionCrashedWithTimestamp:[_currentDateProvider date]];
// Setting _session to nil so startSession doesn't capture it again
_session = nil;
[self startSession];
}
}
}

// Create a new envelope with the session update
NSMutableArray<SentryEnvelopeItem *> *itemsToSend =
[[NSMutableArray alloc] initWithArray:envelope.items];
[itemsToSend addObject:[[SentryEnvelopeItem alloc] initWithSession:currentSession]];
return [[SentryEnvelope alloc] initWithHeader:envelope.header items:itemsToSend];
}
return envelope;
}

- (BOOL)envelopeContainsEventWithErrorOrHigher:(NSArray<SentryEnvelopeItem *> *)items
wasHandled:(BOOL *)handled;
{
for (SentryEnvelopeItem *item in items) {
if ([item.header.type isEqualToString:SentryEnvelopeItemTypeEvent]) {
// If there is no level the default is error
SentryLevel level = [SentrySerialization levelFromData:item.data];
NSDictionary *eventJson = [SentrySerialization deserializeEventEnvelopeItem:item.data];
if (eventJson == nil) {
return NO;
}

SentryLevel level = sentryLevelForString(eventJson[@"level"]);
if (level >= kSentryLevelError) {
*handled = [self eventContainsUnhandledError:eventJson];
return YES;
}
}
}

return NO;
}

- (BOOL)eventContainsUnhandledError:(NSDictionary *)eventDictionary
{
NSArray *exceptions = eventDictionary[@"exception"][@"values"];
for (NSDictionary *exception in exceptions) {
NSDictionary *mechanism = exception[@"mechanism"];
NSNumber *handled = mechanism[@"handled"];

if ([handled boolValue] == NO) {
return NO;
}
}
return YES;
}

- (void)reportFullyDisplayed
{
#if SENTRY_HAS_UIKIT
Expand Down
17 changes: 17 additions & 0 deletions Sources/Sentry/SentrySerialization.m
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,23 @@ + (SentryAppState *_Nullable)appStateWithData:(NSData *)data
return [[SentryAppState alloc] initWithJSONObject:appSateDictionary];
}

+ (NSDictionary *)deserializeEventEnvelopeItem:(NSData *)eventEnvelopeItemData
{
NSError *error = nil;
NSDictionary *eventDictionary = [NSJSONSerialization JSONObjectWithData:eventEnvelopeItemData
options:0
error:&error];
if (nil != error) {
[SentryLog
logWithMessage:[NSString
stringWithFormat:@"Failed to deserialize envelope item data: %@",
error]
andLevel:kSentryLevelError];
}

return eventDictionary;
}

+ (SentryLevel)levelFromData:(NSData *)eventEnvelopeItemData
{
NSError *error = nil;
Expand Down
3 changes: 2 additions & 1 deletion Sources/Sentry/include/SentryHub+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#import "SentryTracer.h"

@class SentryEnvelopeItem, SentryId, SentryScope, SentryTransaction, SentryDispatchQueueWrapper,
SentryEnvelope, SentryNSTimerWrapper;
SentryEnvelope, SentryNSTimerWrapper, SentrySession;

NS_ASSUME_NONNULL_BEGIN

Expand All @@ -11,6 +11,7 @@ SentryHub (Private)

@property (nonatomic, strong) NSArray<id<SentryIntegrationProtocol>> *installedIntegrations;
@property (nonatomic, strong) NSSet<NSString *> *installedIntegrationNames;
@property (nullable, nonatomic, strong) SentrySession *session;

- (void)addInstalledIntegration:(id<SentryIntegrationProtocol>)integration name:(NSString *)name;
- (void)removeAllIntegrations;
Expand Down
5 changes: 5 additions & 0 deletions Sources/Sentry/include/SentrySerialization.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ static int const SENTRY_BAGGAGE_MAX_SIZE = 8192;

+ (SentryAppState *_Nullable)appStateWithData:(NSData *)sessionData;

/**
* Retrieves the json object from an event envelope item data.
*/
+ (NSDictionary *)deserializeEventEnvelopeItem:(NSData *)eventEnvelopeItemData;

/**
* Extract the level from data of an envelopte item containing an event. Default is the 'error'
* level, see https://develop.sentry.dev/sdk/event-payloads/#optional-attributes
Expand Down
Loading

0 comments on commit b385962

Please sign in to comment.