Skip to content

Commit 23a141f

Browse files
Merge 721cbf3 into 11853e6
2 parents 11853e6 + 721cbf3 commit 23a141f

File tree

10 files changed

+162
-65
lines changed

10 files changed

+162
-65
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
- Change `value` and `type` of `SentryException` to be nullable (#6563)
2727
- Change the default trace context status to "ok" instead of "undefined" (#6611)
2828
- Remove `getHash` from SentryDsn (#6605)
29+
- [HTTP Client errors](https://docs.sentry.io/platforms/apple/guides/ios/configuration/http-client-errors/) now mark sessions as errored (#6631)
2930

3031
### Features
3132

SentryTestUtils/TestClient.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,13 @@ public class TestClient: SentryClient {
106106
captureExceptionWithSessionInvocations.record((exception, callSessionBlockWithIncrementSessionErrors ? sessionBlock() : nil, scope))
107107
return SentryId()
108108
}
109-
109+
110+
@_spi(Private) public var captureErrorEventWithSessionInvocations = Invocations<(event: Event, session: SentrySession?, scope: Scope)>()
111+
@_spi(Private) public override func captureErrorEvent(event: Event, scope: Scope, incrementSessionErrors sessionBlock: @escaping () -> SentrySession) -> SentryId {
112+
captureErrorEventWithSessionInvocations.record((event, callSessionBlockWithIncrementSessionErrors ? sessionBlock() : nil, scope))
113+
return SentryId()
114+
}
115+
110116
public var captureFatalEventInvocations = Invocations<(event: Event, scope: Scope)>()
111117
public override func captureFatalEvent(_ event: Event, with scope: Scope) -> SentryId {
112118
captureFatalEventInvocations.record((event, scope))

SentryTestUtils/TestHub.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ public class TestHub: SentryHub {
4444
return event.eventId
4545
}
4646

47+
@_spi(Private) public var capturedErrorEvents = Invocations<Event>()
48+
public override func captureErrorEvent(event: Event) -> SentryId {
49+
self.capturedErrorEvents.record((event))
50+
51+
return event.eventId
52+
}
53+
4754
public var capturedTransactionsWithScope = Invocations<(transaction: [String: Any], scope: Scope)>()
4855
public override func capture(_ transaction: Transaction, with scope: Scope) {
4956
capturedTransactionsWithScope.record((transaction.serialize(), scope))

Sources/Sentry/SentryClient.m

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -153,14 +153,7 @@ - (SentryId *)captureException:(NSException *)exception
153153
incrementSessionErrors:(SentrySession * (^)(void))sessionBlock
154154
{
155155
SentryEvent *event = [self buildExceptionEvent:exception];
156-
event = [self prepareEvent:event withScope:scope alwaysAttachStacktrace:YES];
157-
158-
if (event != nil) {
159-
SentrySession *session = sessionBlock();
160-
return [self sendEvent:event withSession:session withScope:scope];
161-
}
162-
163-
return SentryId.empty;
156+
return [self captureErrorEvent:event withScope:scope incrementSessionErrors:sessionBlock];
164157
}
165158

166159
- (SentryEvent *)buildExceptionEvent:(NSException *)exception
@@ -199,14 +192,7 @@ - (SentryId *)captureError:(NSError *)error
199192
incrementSessionErrors:(SentrySession * (^)(void))sessionBlock
200193
{
201194
SentryEvent *event = [self buildErrorEvent:error];
202-
event = [self prepareEvent:event withScope:scope alwaysAttachStacktrace:YES];
203-
204-
if (event != nil) {
205-
SentrySession *session = sessionBlock();
206-
return [self sendEvent:event withSession:session withScope:scope];
207-
}
208-
209-
return SentryId.empty;
195+
return [self captureErrorEvent:event withScope:scope incrementSessionErrors:sessionBlock];
210196
}
211197

212198
- (SentryEvent *)buildErrorEvent:(NSError *)error
@@ -355,6 +341,22 @@ - (SentryId *)captureEvent:(SentryEvent *)event
355341
additionalEnvelopeItems:additionalEnvelopeItems];
356342
}
357343

344+
- (SentryId *)captureErrorEvent:(SentryEvent *)event
345+
withScope:(SentryScope *)scope
346+
incrementSessionErrors:(SentrySession * (^)(void))sessionBlock
347+
{
348+
SentryEvent *preparedEvent = [self prepareEvent:event
349+
withScope:scope
350+
alwaysAttachStacktrace:YES];
351+
352+
if (preparedEvent != nil) {
353+
SentrySession *session = sessionBlock();
354+
return [self sendEvent:preparedEvent withSession:session withScope:scope];
355+
}
356+
357+
return SentryId.empty;
358+
}
359+
358360
- (SentryId *)sendEvent:(SentryEvent *)event
359361
withScope:(SentryScope *)scope
360362
alwaysAttachStacktrace:(BOOL)alwaysAttachStacktrace

Sources/Sentry/SentryHub.m

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -489,19 +489,36 @@ - (SentryId *)captureError:(NSError *)error
489489

490490
- (SentryId *)captureError:(NSError *)error withScope:(SentryScope *)scope
491491
{
492-
SentrySession *currentSession = _session;
493-
SentryClient *client = self.client;
494-
if (client != nil) {
495-
if (currentSession != nil) {
496-
return [client captureError:error
497-
withScope:scope
498-
incrementSessionErrors:^(void) { return [self incrementSessionErrors]; }];
499-
} else {
500-
_errorsBeforeSession++;
501-
return [client captureError:error withScope:scope];
502-
}
503-
}
504-
return SentryId.empty;
492+
SentryId * (^captureClientBlock)(SentryClient *) = ^SentryId *(SentryClient *clientParam) {
493+
return [clientParam captureError:error
494+
withScope:scope
495+
incrementSessionErrors:^(void) { return [self incrementSessionErrors]; }];
496+
};
497+
498+
SentryId * (^captureClientBlockSessionNil)(SentryClient *) = ^SentryId *(
499+
SentryClient *clientParam) { return [clientParam captureError:error withScope:scope]; };
500+
501+
return [self captureEventIncrementingSessionErrors:scope
502+
captureClientBlock:captureClientBlock
503+
captureClientSessionNilBlock:captureClientBlockSessionNil];
504+
}
505+
506+
- (SentryId *)captureErrorEvent:(SentryEvent *)event
507+
{
508+
SentryScope *scope = self.scope;
509+
510+
SentryId * (^captureClientBlock)(SentryClient *) = ^SentryId *(SentryClient *clientParam) {
511+
return [clientParam captureErrorEvent:event
512+
withScope:scope
513+
incrementSessionErrors:^(void) { return [self incrementSessionErrors]; }];
514+
};
515+
516+
SentryId * (^captureClientBlockSessionNil)(SentryClient *) = ^SentryId *(
517+
SentryClient *clientParam) { return [clientParam captureEvent:event withScope:scope]; };
518+
519+
return [self captureEventIncrementingSessionErrors:scope
520+
captureClientBlock:captureClientBlock
521+
captureClientSessionNilBlock:captureClientBlockSessionNil];
505522
}
506523

507524
- (SentryId *)captureException:(NSException *)exception
@@ -511,16 +528,37 @@ - (SentryId *)captureException:(NSException *)exception
511528

512529
- (SentryId *)captureException:(NSException *)exception withScope:(SentryScope *)scope
513530
{
531+
SentryId * (^captureClientBlock)(SentryClient *) = ^SentryId *(SentryClient *clientParam) {
532+
return [clientParam captureException:exception
533+
withScope:scope
534+
incrementSessionErrors:^(void) { return [self incrementSessionErrors]; }];
535+
};
536+
537+
SentryId * (^captureClientBlockSessionNil)(SentryClient *)
538+
= ^SentryId *(SentryClient *clientParam) {
539+
return [clientParam captureException:exception withScope:scope];
540+
};
541+
542+
return [self captureEventIncrementingSessionErrors:scope
543+
captureClientBlock:captureClientBlock
544+
captureClientSessionNilBlock:captureClientBlockSessionNil];
545+
}
546+
547+
- (SentryId *)captureEventIncrementingSessionErrors:(SentryScope *)scope
548+
captureClientBlock:(SentryId * (^)(
549+
SentryClient *))captureClientBlock
550+
captureClientSessionNilBlock:
551+
(SentryId * (^)(SentryClient *))captureClientSessionNilBlock
552+
{
553+
514554
SentrySession *currentSession = _session;
515555
SentryClient *client = self.client;
516556
if (client != nil) {
517557
if (currentSession != nil) {
518-
return [client captureException:exception
519-
withScope:scope
520-
incrementSessionErrors:^(void) { return [self incrementSessionErrors]; }];
558+
return captureClientBlock(client);
521559
} else {
522560
_errorsBeforeSession++;
523-
return [client captureException:exception withScope:scope];
561+
return captureClientSessionNilBlock(client);
524562
}
525563
}
526564
return SentryId.empty;

Sources/Sentry/SentryNetworkTracker.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ - (void)captureFailedRequests:(NSURLSessionTask *)sessionTask
395395

396396
event.context = context;
397397

398-
[SentrySDK captureEvent:event];
398+
[SentrySDKInternal.currentHub captureErrorEvent:event];
399399
}
400400

401401
- (BOOL)containsStatusCode:(NSInteger)statusCode

Sources/Sentry/include/SentryClient+Private.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ NS_ASSUME_NONNULL_BEGIN
5656
additionalEnvelopeItems:(NSArray<SentryEnvelopeItem *> *)additionalEnvelopeItems
5757
NS_SWIFT_NAME(capture(event:scope:additionalEnvelopeItems:));
5858

59+
- (SentryId *)captureErrorEvent:(SentryEvent *)event
60+
withScope:(SentryScope *)scope
61+
incrementSessionErrors:(SentrySession * (^)(void))sessionBlock
62+
NS_SWIFT_NAME(captureErrorEvent(event:scope:incrementSessionErrors:));
63+
5964
- (void)captureReplayEvent:(SentryReplayEvent *)replayEvent
6065
replayRecording:(SentryReplayRecording *)replayRecording
6166
video:(NSURL *)videoURL

Sources/Sentry/include/SentryHub+Private.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ NS_ASSUME_NONNULL_BEGIN
6464
additionalEnvelopeItems:(NSArray<SentryEnvelopeItem *> *)additionalEnvelopeItems
6565
NS_SWIFT_NAME(capture(event:scope:additionalEnvelopeItems:));
6666

67+
- (SentryId *)captureErrorEvent:(SentryEvent *)event NS_SWIFT_NAME(captureErrorEvent(event:));
68+
6769
- (void)captureSerializedFeedback:(NSDictionary *)serializedFeedback
6870
withEventId:(NSString *)feedbackEventId
6971
attachments:(NSArray<SentryAttachment *> *)feedbackAttachments;

Tests/SentryTests/Integrations/Performance/Network/SentryNetworkTrackerTests.swift

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,11 +1013,9 @@ class SentryNetworkTrackerTests: XCTestCase {
10131013

10141014
sut.urlSessionTask(task, setState: .completed)
10151015

1016-
guard let envelope = self.fixture.hub.capturedEventsWithScopes.first else {
1017-
XCTFail("Expected to capture 1 event")
1018-
return
1019-
}
1020-
let sentryRequest = try XCTUnwrap(envelope.event.request)
1016+
XCTAssertEqual(self.fixture.hub.capturedErrorEvents.count, 1, "Expected only one error event to be captured")
1017+
let capturedErrorEvent = try XCTUnwrap(self.fixture.hub.capturedErrorEvents.first)
1018+
let sentryRequest = try XCTUnwrap(capturedErrorEvent.request)
10211019

10221020
XCTAssertEqual(sentryRequest.url, "https://www.domain.com/api")
10231021
XCTAssertEqual(sentryRequest.method, "GET")
@@ -1050,13 +1048,11 @@ class SentryNetworkTrackerTests: XCTestCase {
10501048

10511049
sut.urlSessionTask(task, setState: .completed)
10521050

1053-
let envelope = try XCTUnwrap(
1054-
fixture.hub.capturedEventsWithScopes.first,
1055-
"Expected to capture 1 event"
1056-
)
1057-
1051+
XCTAssertEqual(self.fixture.hub.capturedErrorEvents.count, 1, "Expected only one error event to be captured")
1052+
let capturedErrorEvent = try XCTUnwrap(fixture.hub.capturedErrorEvents.first)
1053+
10581054
let graphQLContext = try XCTUnwrap(
1059-
envelope.event.context?["graphql"],
1055+
capturedErrorEvent.context?["graphql"],
10601056
"Expected 'graphql' object in context"
10611057
)
10621058

@@ -1080,11 +1076,10 @@ class SentryNetworkTrackerTests: XCTestCase {
10801076
task.setResponse(try createResponse(code: 500))
10811077
sut.urlSessionTask(task, setState: .completed)
10821078

1083-
guard let envelope = self.fixture.hub.capturedEventsWithScopes.first else {
1084-
XCTFail("Expected to capture 1 event")
1085-
return
1086-
}
1087-
let sentryRequest = try XCTUnwrap(envelope.event.request)
1079+
XCTAssertEqual(self.fixture.hub.capturedErrorEvents.count, 1, "Expected only one error event to be captured")
1080+
let capturedErrorEvent = try XCTUnwrap(self.fixture.hub.capturedErrorEvents.first)
1081+
1082+
let sentryRequest = try XCTUnwrap(capturedErrorEvent.request)
10881083

10891084
XCTAssertEqual(sentryRequest.url, "https://[Filtered]:[Filtered]@www.domain.com/api")
10901085
XCTAssertEqual(sentryRequest.headers, ["VALID_HEADER": "value"])
@@ -1104,11 +1099,11 @@ class SentryNetworkTrackerTests: XCTestCase {
11041099

11051100
sut.urlSessionTask(task, setState: .completed)
11061101

1107-
guard let envelope = self.fixture.hub.capturedEventsWithScopes.first else {
1108-
XCTFail("Expected to capture 1 event")
1109-
return
1110-
}
1111-
let sentryResponse = try XCTUnwrap(envelope.event.context?["response"])
1102+
XCTAssertEqual(self.fixture.hub.capturedErrorEvents.count, 1, "Expected only one error event to be captured")
1103+
1104+
let capturedErrorEvent = try XCTUnwrap(self.fixture.hub.capturedErrorEvents.first)
1105+
1106+
let sentryResponse = try XCTUnwrap(capturedErrorEvent.context?["response"])
11121107

11131108
XCTAssertEqual(sentryResponse["status_code"] as? NSNumber, 500)
11141109
XCTAssertEqual(sentryResponse["headers"] as? [String: String], ["test": "test"])
@@ -1129,11 +1124,10 @@ class SentryNetworkTrackerTests: XCTestCase {
11291124
task.setResponse(response)
11301125
sut.urlSessionTask(task, setState: .completed)
11311126

1132-
guard let envelope = self.fixture.hub.capturedEventsWithScopes.first else {
1133-
XCTFail("Expected to capture 1 event")
1134-
return
1135-
}
1136-
let sentryResponse = try XCTUnwrap(envelope.event.context?["response"])
1127+
XCTAssertEqual(self.fixture.hub.capturedErrorEvents.count, 1, "Expected only one error event to be captured")
1128+
let capturedErrorEvent = try XCTUnwrap(self.fixture.hub.capturedErrorEvents.first)
1129+
1130+
let sentryResponse = try XCTUnwrap(capturedErrorEvent.context?["response"])
11371131

11381132
XCTAssertEqual(sentryResponse["headers"] as? [String: String], ["VALID_HEADER": "value"])
11391133
}
@@ -1145,9 +1139,10 @@ class SentryNetworkTrackerTests: XCTestCase {
11451139

11461140
sut.urlSessionTask(task, setState: .completed)
11471141

1148-
let envelope = try XCTUnwrap(self.fixture.hub.capturedEventsWithScopes.first)
1149-
1150-
let exceptions = try XCTUnwrap(envelope.event.exceptions)
1142+
XCTAssertEqual(self.fixture.hub.capturedErrorEvents.count, 1, "Expected only one error event to be captured")
1143+
let capturedErrorEvent = try XCTUnwrap(self.fixture.hub.capturedErrorEvents.first)
1144+
1145+
let exceptions = try XCTUnwrap(capturedErrorEvent.exceptions)
11511146
XCTAssertEqual(exceptions.count, 1)
11521147
let exception = try XCTUnwrap(exceptions.first)
11531148

Tests/SentryTests/SentryHubTests.swift

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -861,7 +861,48 @@ class SentryHubTests: XCTestCase {
861861
)
862862
}
863863
}
864-
864+
865+
func testCaptureErrorEvent_WithSession() {
866+
let sut = fixture.getSut()
867+
sut.startSession()
868+
869+
let event = fixture.event
870+
sut.captureErrorEvent(event: event).assertIsNotEmpty()
871+
872+
XCTAssertEqual(1, fixture.client.captureErrorEventWithSessionInvocations.count)
873+
if let eventArguments = fixture.client.captureErrorEventWithSessionInvocations.first {
874+
XCTAssertEqual(event, eventArguments.event)
875+
876+
XCTAssertEqual(1, eventArguments.session?.errors)
877+
XCTAssertEqual(SentrySessionStatus.ok, eventArguments.session?.status)
878+
879+
XCTAssertEqual(sut.scope, eventArguments.scope)
880+
}
881+
882+
// only session init is sent
883+
XCTAssertEqual(1, fixture.client.captureSessionInvocations.count)
884+
}
885+
886+
func testCaptureErrorEvent_WithoutIncreasingErrorCount() {
887+
let sut = fixture.getSut()
888+
sut.startSession()
889+
890+
fixture.client.callSessionBlockWithIncrementSessionErrors = false
891+
892+
let event = fixture.event
893+
sut.captureErrorEvent(event: event).assertIsNotEmpty()
894+
895+
XCTAssertEqual(1, fixture.client.captureErrorEventWithSessionInvocations.count)
896+
if let eventArguments = fixture.client.captureErrorEventWithSessionInvocations.first {
897+
XCTAssertEqual(event, eventArguments.event)
898+
XCTAssertNil(eventArguments.session)
899+
XCTAssertEqual(sut.scope, eventArguments.scope)
900+
}
901+
902+
// only session init is sent
903+
XCTAssertEqual(1, fixture.client.captureSessionInvocations.count)
904+
}
905+
865906
func testCaptureClientIsNil_ReturnsEmptySentryId() {
866907
sut.bindClient(nil)
867908

0 commit comments

Comments
 (0)