Skip to content

Commit 016277f

Browse files
Merge af2b170 into 939d583
2 parents 939d583 + af2b170 commit 016277f

File tree

10 files changed

+169
-65
lines changed

10 files changed

+169
-65
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
- Change `value` and `type` of `SentryException` to be nullable (#6563)
3232
- Change the default trace context status to "ok" instead of "undefined" (#6611)
3333
- Remove `getHash` from SentryDsn (#6605)
34+
- [HTTP Client errors](https://docs.sentry.io/platforms/apple/guides/ios/configuration/http-client-errors/) now mark sessions as errored (#6633)
3435
- The precompiled XCFramework is now built with Xcode 16. To submit to the App Store, [Apple now requires Xcode 16](https://developer.apple.com/news/upcoming-requirements/?id=02212025a).
3536
If you need a precompiled XCFramework built with Xcode 15, continue using Sentry SDK 8.x.x.
3637
- Set `SentryException.type` to `nil` when `NSException` has no `reason` (#6653). The backend then can provide a proper message when there is no reason.

SentryTestUtils/Sources/TestClient.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,13 @@ public class TestClient: SentryClientInternal {
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/Sources/TestHub.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ public class TestHub: SentryHubInternal {
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
@@ -151,14 +151,7 @@ - (SentryId *)captureException:(NSException *)exception
151151
incrementSessionErrors:(SentrySession * (^)(void))sessionBlock
152152
{
153153
SentryEvent *event = [self buildExceptionEvent:exception];
154-
event = [self prepareEvent:event withScope:scope alwaysAttachStacktrace:YES];
155-
156-
if (event != nil) {
157-
SentrySession *session = sessionBlock();
158-
return [self sendEvent:event withSession:session withScope:scope];
159-
}
160-
161-
return SentryId.empty;
154+
return [self captureErrorEvent:event withScope:scope incrementSessionErrors:sessionBlock];
162155
}
163156

164157
- (SentryEvent *)buildExceptionEvent:(NSException *)exception
@@ -197,14 +190,7 @@ - (SentryId *)captureError:(NSError *)error
197190
incrementSessionErrors:(SentrySession * (^)(void))sessionBlock
198191
{
199192
SentryEvent *event = [self buildErrorEvent:error];
200-
event = [self prepareEvent:event withScope:scope alwaysAttachStacktrace:YES];
201-
202-
if (event != nil) {
203-
SentrySession *session = sessionBlock();
204-
return [self sendEvent:event withSession:session withScope:scope];
205-
}
206-
207-
return SentryId.empty;
193+
return [self captureErrorEvent:event withScope:scope incrementSessionErrors:sessionBlock];
208194
}
209195

210196
- (SentryEvent *)buildErrorEvent:(NSError *)error
@@ -353,6 +339,22 @@ - (SentryId *)captureEvent:(SentryEvent *)event
353339
additionalEnvelopeItems:additionalEnvelopeItems];
354340
}
355341

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

Sources/Sentry/SentryHub.m

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

490490
- (SentryId *)captureError:(NSError *)error withScope:(SentryScope *)scope
491491
{
492-
SentrySession *currentSession = _session;
493-
SentryClientInternal *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)(SentryClientInternal *)
493+
= ^SentryId *(SentryClientInternal *clientParam) {
494+
return [clientParam captureError:error
495+
withScope:scope
496+
incrementSessionErrors:^(void) { return [self incrementSessionErrors]; }];
497+
};
498+
499+
SentryId * (^captureClientBlockSessionNil)(SentryClientInternal *)
500+
= ^SentryId *(SentryClientInternal *clientParam) {
501+
return [clientParam captureError:error withScope:scope];
502+
};
503+
504+
return [self captureEventIncrementingSessionErrors:scope
505+
captureClientBlock:captureClientBlock
506+
captureClientSessionNilBlock:captureClientBlockSessionNil];
507+
}
508+
509+
- (SentryId *)captureErrorEvent:(SentryEvent *)event
510+
{
511+
SentryScope *scope = self.scope;
512+
513+
SentryId * (^captureClientBlock)(SentryClientInternal *) = ^SentryId *(
514+
SentryClientInternal *clientParam) {
515+
return [clientParam captureErrorEvent:event
516+
withScope:scope
517+
incrementSessionErrors:^(void) { return [self incrementSessionErrors]; }];
518+
};
519+
520+
SentryId * (^captureClientBlockSessionNil)(SentryClientInternal *)
521+
= ^SentryId *(SentryClientInternal *clientParam) {
522+
return [clientParam captureEvent:event withScope:scope];
523+
};
524+
525+
return [self captureEventIncrementingSessionErrors:scope
526+
captureClientBlock:captureClientBlock
527+
captureClientSessionNilBlock:captureClientBlockSessionNil];
505528
}
506529

507530
- (SentryId *)captureException:(NSException *)exception
@@ -511,16 +534,38 @@ - (SentryId *)captureException:(NSException *)exception
511534

512535
- (SentryId *)captureException:(NSException *)exception withScope:(SentryScope *)scope
513536
{
537+
SentryId * (^captureClientBlock)(SentryClientInternal *) = ^SentryId *(
538+
SentryClientInternal *clientParam) {
539+
return [clientParam captureException:exception
540+
withScope:scope
541+
incrementSessionErrors:^(void) { return [self incrementSessionErrors]; }];
542+
};
543+
544+
SentryId * (^captureClientBlockSessionNil)(SentryClientInternal *)
545+
= ^SentryId *(SentryClientInternal *clientParam) {
546+
return [clientParam captureException:exception withScope:scope];
547+
};
548+
549+
return [self captureEventIncrementingSessionErrors:scope
550+
captureClientBlock:captureClientBlock
551+
captureClientSessionNilBlock:captureClientBlockSessionNil];
552+
}
553+
554+
- (SentryId *)captureEventIncrementingSessionErrors:(SentryScope *)scope
555+
captureClientBlock:(SentryId * (^)(
556+
SentryClientInternal *))captureClientBlock
557+
captureClientSessionNilBlock:
558+
(SentryId * (^)(SentryClientInternal *))captureClientSessionNilBlock
559+
{
560+
514561
SentrySession *currentSession = _session;
515562
SentryClientInternal *client = self.client;
516563
if (client != nil) {
517564
if (currentSession != nil) {
518-
return [client captureException:exception
519-
withScope:scope
520-
incrementSessionErrors:^(void) { return [self incrementSessionErrors]; }];
565+
return captureClientBlock(client);
521566
} else {
522567
_errorsBeforeSession++;
523-
return [client captureException:exception withScope:scope];
568+
return captureClientSessionNilBlock(client);
524569
}
525570
}
526571
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)