Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: HTTP Crumb level based on response status #4779

Merged
merged 5 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Features

- HTTP Breadcrumb level based on response status code (#4779) 4xx is warning, 5xx is error.

### Improvements

- Add more debug logs for SentryViewHierarchy (#4780)
Expand Down
24 changes: 22 additions & 2 deletions Sources/Sentry/SentryNetworkTracker.m
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,10 @@ - (void)addBreadcrumbForSessionTask:(NSURLSessionTask *)sessionTask
NSDate *requestStart
= objc_getAssociatedObject(sessionTask, &SENTRY_NETWORK_REQUEST_START_DATE);

SentryLevel breadcrumbLevel = sessionTask.error != nil ? kSentryLevelError : kSentryLevelInfo;
NSInteger responseStatusCode = [self urlResponseStatusCode:sessionTask.response];
SentryLevel breadcrumbLevel = [self getBreadcrumbLevel:sessionTask
responseStatusCode:responseStatusCode];

SentryBreadcrumb *breadcrumb = [[SentryBreadcrumb alloc] initWithLevel:breadcrumbLevel
category:@"http"];

Expand All @@ -507,7 +510,7 @@ - (void)addBreadcrumbForSessionTask:(NSURLSessionTask *)sessionTask
[NSNumber numberWithLongLong:sessionTask.countOfBytesSent];
breadcrumbData[@"response_body_size"] =
[NSNumber numberWithLongLong:sessionTask.countOfBytesReceived];
NSInteger responseStatusCode = [self urlResponseStatusCode:sessionTask.response];

if (responseStatusCode != -1) {
NSNumber *statusCode = [NSNumber numberWithInteger:responseStatusCode];
breadcrumbData[@"status_code"] = statusCode;
Expand Down Expand Up @@ -601,4 +604,21 @@ - (SentrySpanStatus)spanStatusForHttpResponseStatusCode:(NSInteger)statusCode
return kSentrySpanStatusUndefined;
}

- (SentryLevel)getBreadcrumbLevel:(NSURLSessionTask *)sessionTask
responseStatusCode:(NSInteger)responseStatusCode
{
SentryLevel breadcrumbLevel = kSentryLevelInfo;
if (responseStatusCode >= 400 && responseStatusCode < 500) {
breadcrumbLevel = kSentryLevelWarning;
} else if (responseStatusCode >= 500 && responseStatusCode < 600) {
breadcrumbLevel = kSentryLevelError;
}

if (sessionTask.error != nil) {
breadcrumbLevel = kSentryLevelError;
}

return breadcrumbLevel;
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,147 @@ class SentryNetworkTrackerTests: XCTestCase {
let breadcrumbs = Dynamic(fixture.scope).breadcrumbArray as [Breadcrumb]?
XCTAssertEqual(breadcrumbs?.count, 0)
}

func test_Breadcrumb_HTTP400_HasLevelWarning() throws {
// Arrange
fixture.options.enableAutoPerformanceTracing = false

let task = createDataTask()
task.setResponse(createResponse(code: 400))
let _ = spanForTask(task: task)!

//Act
try setTaskState(task, state: .completed)

//Assert
let breadcrumbsDynamic = Dynamic(fixture.scope).breadcrumbArray as [Breadcrumb]?
let breadcrumbs = try XCTUnwrap(breadcrumbsDynamic)
XCTAssertEqual(breadcrumbs.count, 1)
let breadcrumb = try XCTUnwrap(breadcrumbs.first)

XCTAssertEqual(breadcrumb.category, "http")
XCTAssertEqual(breadcrumb.level, .warning)
XCTAssertEqual(breadcrumb.type, "http")

let data = try XCTUnwrap(breadcrumb.data)
XCTAssertEqual(SentryNetworkTrackerTests.testUrl, data["url"] as? String)
XCTAssertEqual("GET", data["method"] as? String)
XCTAssertEqual(400, data["status_code"] as? Int)
XCTAssertEqual("bad request", data["reason"] as? String)
}

func test_Breadcrumb_HTTP499_HasLevelWarning() throws {
// Arrange
fixture.options.enableAutoPerformanceTracing = false

let task = createDataTask()
task.setResponse(createResponse(code: 499))
let _ = spanForTask(task: task)!

//Act
try setTaskState(task, state: .completed)

//Assert
let breadcrumbsDynamic = Dynamic(fixture.scope).breadcrumbArray as [Breadcrumb]?
let breadcrumbs = try XCTUnwrap(breadcrumbsDynamic)
XCTAssertEqual(breadcrumbs.count, 1)
let breadcrumb = try XCTUnwrap(breadcrumbs.first)

XCTAssertEqual(breadcrumb.category, "http")
XCTAssertEqual(breadcrumb.level, .warning)
XCTAssertEqual(breadcrumb.type, "http")

let data = try XCTUnwrap(breadcrumb.data)
XCTAssertEqual(SentryNetworkTrackerTests.testUrl, data["url"] as? String)
XCTAssertEqual("GET", data["method"] as? String)
XCTAssertEqual(499, data["status_code"] as? Int)
XCTAssertEqual("client error", data["reason"] as? String)
}

func testBreadcrumb_SessionTaskError_HTTP400_HasLevelError() throws {
// Arrange
fixture.options.enableAutoPerformanceTracing = false

let task = createDataTask()
task.setResponse(createResponse(code: 400))
task.setError(NSError(domain: "Some Error", code: 1, userInfo: nil))
let _ = spanForTask(task: task)!

//Act
try setTaskState(task, state: .completed)

//Assert
let breadcrumbsDynamic = Dynamic(fixture.scope).breadcrumbArray as [Breadcrumb]?
let breadcrumbs = try XCTUnwrap(breadcrumbsDynamic)
XCTAssertEqual(breadcrumbs.count, 1)
let breadcrumb = try XCTUnwrap(breadcrumbs.first)

XCTAssertEqual(breadcrumb.category, "http")
XCTAssertEqual(breadcrumb.level, .error)
XCTAssertEqual(breadcrumb.type, "http")

let data = try XCTUnwrap(breadcrumb.data)
XCTAssertEqual(SentryNetworkTrackerTests.testUrl, data["url"] as? String)
XCTAssertEqual("GET", data["method"] as? String)
XCTAssertEqual(400, data["status_code"] as? Int)
XCTAssertEqual("bad request", data["reason"] as? String)
}

func test_Breadcrumb_HTTP500_HasLevelError() throws {
// Arrange
fixture.options.enableAutoPerformanceTracing = false

let task = createDataTask()
task.setResponse(createResponse(code: 500))
let _ = spanForTask(task: task)!

//Act
try setTaskState(task, state: .completed)

//Assert
let breadcrumbsDynamic = Dynamic(fixture.scope).breadcrumbArray as [Breadcrumb]?
let breadcrumbs = try XCTUnwrap(breadcrumbsDynamic)
XCTAssertEqual(breadcrumbs.count, 1)
let breadcrumb = try XCTUnwrap(breadcrumbs.first)

XCTAssertEqual(breadcrumb.category, "http")
XCTAssertEqual(breadcrumb.level, .error)
XCTAssertEqual(breadcrumb.type, "http")

let data = try XCTUnwrap(breadcrumb.data)
XCTAssertEqual(SentryNetworkTrackerTests.testUrl, data["url"] as? String)
XCTAssertEqual("GET", data["method"] as? String)
XCTAssertEqual(500, data["status_code"] as? Int)
XCTAssertEqual("internal server error", data["reason"] as? String)
}

func test_Breadcrumb_HTTP599_HasLevelError() throws {
// Arrange
fixture.options.enableAutoPerformanceTracing = false

let task = createDataTask()
task.setResponse(createResponse(code: 599))
let _ = spanForTask(task: task)!

//Act
try setTaskState(task, state: .completed)

//Assert
let breadcrumbsDynamic = Dynamic(fixture.scope).breadcrumbArray as [Breadcrumb]?
let breadcrumbs = try XCTUnwrap(breadcrumbsDynamic)
XCTAssertEqual(breadcrumbs.count, 1)
let breadcrumb = try XCTUnwrap(breadcrumbs.first)

XCTAssertEqual(breadcrumb.category, "http")
XCTAssertEqual(breadcrumb.level, .error)
XCTAssertEqual(breadcrumb.type, "http")

let data = try XCTUnwrap(breadcrumb.data)
XCTAssertEqual(SentryNetworkTrackerTests.testUrl, data["url"] as? String)
XCTAssertEqual("GET", data["method"] as? String)
XCTAssertEqual(599, data["status_code"] as? Int)
XCTAssertEqual("server error", data["reason"] as? String)
}

func testResumeAfterCompleted_OnlyOneSpanCreated() throws {
let task = createDataTask()
Expand Down
Loading