Skip to content

Commit

Permalink
Merge 7a33aa9 into 9fa5d27
Browse files Browse the repository at this point in the history
  • Loading branch information
brustolin authored Jun 13, 2023
2 parents 9fa5d27 + 7a33aa9 commit 80127d9
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 6 deletions.
38 changes: 33 additions & 5 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,64 @@ - (void)captureEnvelope:(SentryEnvelope *)envelope

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

if (currentSession != nil) {
if (!handled) {
[currentSession endSessionCrashedWithTimestamp:[_currentDateProvider date]];

_session = [[SentrySession alloc] initWithReleaseName:_client.options.releaseName];
_session.environment = _client.options.environment;
[self.scope applyToSession:_session];
}

// 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
18 changes: 18 additions & 0 deletions Sources/Sentry/SentrySerialization.m
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,24 @@ + (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
39 changes: 39 additions & 0 deletions Tests/SentryTests/SentryHubTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,45 @@ class SentryHubTests: XCTestCase {
XCTAssertEqual(envelope, fixture.client.captureEnvelopeInvocations.first)
}

func testCaptureEnvelope_WithUnhandledException() {
sut.startSession()

let beginSession = sut.session

let event = TestData.event
event.level = .error
event.exceptions = [TestData.exception]
event.exceptions?.first?.mechanism?.handled = false
sut.capture(SentryEnvelope(event: event))

let endSession = sut.session
XCTAssertNotEqual(beginSession, endSession)

//Check whether session was finished as crashed
let envelope = fixture.client.captureEnvelopeInvocations.first
let sessionEnvelopeItem = envelope?.items.first(where: { $0.header.type == "session" })

let json = (try! JSONSerialization.jsonObject(with: sessionEnvelopeItem!.data)) as! [String: Any]

XCTAssertNotNil(json["timestamp"])
XCTAssertEqual(json["status"] as? String, "crashed")
}

func testCaptureEnvelope_WithHandledException() {
sut.startSession()

let beginSession = sut.session

let event = TestData.event
event.level = .error
event.exceptions = [TestData.exception]
sut.capture(SentryEnvelope(event: event))

let endSession = sut.session

XCTAssertEqual(beginSession, endSession)
}

#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst)
func test_reportFullyDisplayed_enableTimeToFullDisplay_YES() {
fixture.options.enableTimeToFullDisplay = true
Expand Down

0 comments on commit 80127d9

Please sign in to comment.