-
-
Notifications
You must be signed in to change notification settings - Fork 319
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Session Replay): Add breadcrumbs to session replay (#4002)
Converting session breadcrumbs to replay breadcrumbs
- Loading branch information
Showing
23 changed files
with
612 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#import "SentryLevelHelper.h" | ||
#import "SentryBreadcrumb+Private.h" | ||
|
||
@implementation SentryLevelHelper | ||
|
||
+ (NSUInteger)breadcrumbLevel:(SentryBreadcrumb *)breadcrumb | ||
{ | ||
return breadcrumb.level; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
#import <Foundation/Foundation.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@class SentryBreadcrumb; | ||
|
||
/** | ||
* This is a workaround to access SentryLevel value from swift | ||
*/ | ||
@interface SentryLevelHelper : NSObject | ||
|
||
+ (NSUInteger)breadcrumbLevel:(SentryBreadcrumb *)breadcrumb; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 3 additions & 3 deletions
6
Sources/Swift/Integrations/SessionReplay/RRWeb/SentryRRWebBreadcrumbEvent.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
Sources/Swift/Integrations/SessionReplay/RRWeb/SentryRRWebSpanEvent.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
@_implementationOnly import _SentryPrivate | ||
import Foundation | ||
|
||
@objc class SentryRRWebSpanEvent: SentryRRWebCustomEvent { | ||
|
||
init(timestamp: Date, endTimestamp: Date, operation: String, description: String, data: [String: Any]) { | ||
super.init(timestamp: timestamp, tag: "performanceSpan", payload: | ||
[ | ||
"op": operation, | ||
"description": description, | ||
"startTimestamp": timestamp.timeIntervalSince1970, | ||
"endTimestamp": endTimestamp.timeIntervalSince1970, | ||
"data": data | ||
] | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
92 changes: 92 additions & 0 deletions
92
Sources/Swift/Integrations/SessionReplay/SentryReplayBreadcrumbConverter.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
@_implementationOnly import _SentryPrivate | ||
import Foundation | ||
|
||
@objcMembers | ||
class SentryReplayBreadcrumbConverter: NSObject { | ||
|
||
private let supportedNetworkData = Set<String>([ | ||
"status_code", | ||
"method", | ||
"response_body_size", | ||
"request_body_size", | ||
"http.query", | ||
"http.fragment"] | ||
) | ||
|
||
func convert(breadcrumbs: [Breadcrumb], from: Date, until: Date) -> [SentryRRWebEvent] { | ||
breadcrumbs.filter { | ||
guard let timestamp = $0.timestamp else { return false } | ||
return timestamp >= from && timestamp <= until | ||
} | ||
.compactMap { convert(from: $0) } | ||
} | ||
|
||
/** | ||
* This function will convert the SDK breadcrumbs to session replay breadcrumbs in a format that the front-end understands. | ||
* Any deviation in the information will cause the breadcrumb or the information itself to be discarded | ||
* in order to avoid unknown behavior in the front-end. | ||
*/ | ||
private func convert(from breadcrumb: Breadcrumb) -> SentryRRWebEvent? { | ||
guard let timestamp = breadcrumb.timestamp else { return nil } | ||
if breadcrumb.category == "http" { | ||
return networkSpan(breadcrumb) | ||
} else if breadcrumb.type == "navigation" { | ||
return navigationBreadcrumb(breadcrumb) | ||
} else if breadcrumb.category == "touch" { | ||
return SentryRRWebBreadcrumbEvent(timestamp: timestamp, category: "ui.tap", message: breadcrumb.message) | ||
} else if breadcrumb.type == "connectivity" && breadcrumb.category == "device.connectivity" { | ||
guard let networkType = breadcrumb.data?["connectivity"] as? String, !networkType.isEmpty else { return nil } | ||
return SentryRRWebBreadcrumbEvent(timestamp: timestamp, category: "device.connectivity", data: ["state": networkType]) | ||
} else if let action = breadcrumb.data?["action"] as? String, action == "BATTERY_STATE_CHANGE" { | ||
var data = breadcrumb.data?.filter({ item in item.key == "level" || item.key == "plugged" }) ?? [:] | ||
|
||
data["charging"] = data["plugged"] | ||
data["plugged"] = nil | ||
|
||
return SentryRRWebBreadcrumbEvent(timestamp: timestamp, | ||
category: "device.battery", | ||
data: data) | ||
} | ||
|
||
let level = getLevel(breadcrumb: breadcrumb) | ||
return SentryRRWebBreadcrumbEvent(timestamp: timestamp, category: breadcrumb.category, message: breadcrumb.message, level: level, data: breadcrumb.data) | ||
} | ||
|
||
private func navigationBreadcrumb(_ breadcrumb: Breadcrumb) -> SentryRRWebBreadcrumbEvent? { | ||
guard let timestamp = breadcrumb.timestamp else { return nil } | ||
|
||
if breadcrumb.category == "app.lifecycle" { | ||
guard let state = breadcrumb.data?["state"] else { return nil } | ||
return SentryRRWebBreadcrumbEvent(timestamp: timestamp, category: "app.\(state)") | ||
} else if let position = breadcrumb.data?["position"] as? String, breadcrumb.category == "device.orientation" { | ||
return SentryRRWebBreadcrumbEvent(timestamp: timestamp, category: "device.orientation", data: ["position": position]) | ||
} else { | ||
if let to = breadcrumb.data?["screen"] as? String { | ||
return SentryRRWebBreadcrumbEvent(timestamp: timestamp, category: "navigation", message: to, data: ["to": to]) | ||
} else { | ||
return nil | ||
} | ||
} | ||
} | ||
|
||
private func networkSpan(_ breadcrumb: Breadcrumb) -> SentryRRWebSpanEvent? { | ||
guard let timestamp = breadcrumb.timestamp, | ||
let description = breadcrumb.data?["url"] as? String, | ||
let startTimestamp = breadcrumb.data?["request_start"] as? Date | ||
else { return nil } | ||
var data = [String: Any]() | ||
|
||
breadcrumb.data?.forEach({ (key, value) in | ||
guard supportedNetworkData.contains(key) else { return } | ||
let newKey = key.replacingOccurrences(of: "http.", with: "") | ||
data[newKey.snakeToCamelCase()] = value | ||
}) | ||
|
||
//We dont have end of the request in the breadcrumb. | ||
return SentryRRWebSpanEvent(timestamp: startTimestamp, endTimestamp: timestamp, operation: "resource.http", description: description, data: data) | ||
} | ||
|
||
private func getLevel(breadcrumb: Breadcrumb) -> SentryLevel { | ||
return SentryLevel(rawValue: SentryLevelHelper.breadcrumbLevel(breadcrumb)) ?? .none | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import Foundation | ||
@testable import Sentry | ||
import XCTest | ||
|
||
class StringExtensionsTests: XCTestCase { | ||
func testSnakeToCamelCase() { | ||
XCTAssertEqual("name_something".snakeToCamelCase(), "nameSomething") | ||
XCTAssertEqual("name_something_else".snakeToCamelCase(), "nameSomethingElse") | ||
XCTAssertEqual("KEEP_CASE".snakeToCamelCase(), "KEEPCASE") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.