diff --git a/Sources/Datadog/Core/FeaturesConfiguration.swift b/Sources/Datadog/Core/FeaturesConfiguration.swift index 0e9ac177cc..65a9ff9205 100644 --- a/Sources/Datadog/Core/FeaturesConfiguration.swift +++ b/Sources/Datadog/Core/FeaturesConfiguration.swift @@ -53,6 +53,7 @@ internal struct FeaturesConfiguration { /// RUM auto instrumentation configuration, `nil` if not enabled. let autoInstrumentation: AutoInstrumentation? let backgroundEventTrackingEnabled: Bool + let onSessionStart: RUMSessionListener? } struct URLSessionAutoInstrumentation { @@ -200,7 +201,8 @@ extension FeaturesConfiguration { actionEventMapper: configuration.rumActionEventMapper, errorEventMapper: configuration.rumErrorEventMapper, autoInstrumentation: autoInstrumentation, - backgroundEventTrackingEnabled: configuration.rumBackgroundEventTrackingEnabled + backgroundEventTrackingEnabled: configuration.rumBackgroundEventTrackingEnabled, + onSessionStart: configuration.rumSessionsListener ) } else { let error = ProgrammerError( diff --git a/Sources/Datadog/DatadogConfiguration.swift b/Sources/Datadog/DatadogConfiguration.swift index 1b8c954c8f..e2d596f682 100644 --- a/Sources/Datadog/DatadogConfiguration.swift +++ b/Sources/Datadog/DatadogConfiguration.swift @@ -233,6 +233,7 @@ extension Datadog { private(set) var firstPartyHosts: Set? private(set) var spanEventMapper: SpanEventMapper? private(set) var rumSessionsSamplingRate: Float + private(set) var rumSessionsListener: RUMSessionListener? private(set) var rumUIKitViewsPredicate: UIKitRUMViewsPredicate? private(set) var rumUIKitUserActionsPredicate: UIKitRUMUserActionsPredicate? private(set) var rumViewEventMapper: RUMViewEventMapper? @@ -306,6 +307,7 @@ extension Datadog { firstPartyHosts: nil, spanEventMapper: nil, rumSessionsSamplingRate: 100.0, + rumSessionsListener: nil, rumUIKitViewsPredicate: nil, rumUIKitUserActionsPredicate: nil, rumViewEventMapper: nil, @@ -505,6 +507,17 @@ extension Datadog { return self } + /// Sets the RUM Session start callback. + /// + /// The callback takes 2 arguments: the newly started Session ID and a boolean indicating whether or not the session is discarded by the sampling rate + /// (when `true` it means no event in this session will be kept). + /// + /// - Parameter handler: the callback handler to notify whenever a new Session starts. + public func onRUMSessionStart(_ handler: @escaping (String, Bool) -> Void) -> Builder { + configuration.rumSessionsListener = handler + return self + } + /// Sets the predicate for automatically tracking `UIViewControllers` as RUM Views. /// /// When the app is running, the SDK will ask provided `predicate` if any noticed `UIViewController` should be considered diff --git a/Sources/Datadog/RUM/RUMFeature.swift b/Sources/Datadog/RUM/RUMFeature.swift index 0da8e5b1f4..ea504091f5 100644 --- a/Sources/Datadog/RUM/RUMFeature.swift +++ b/Sources/Datadog/RUM/RUMFeature.swift @@ -41,6 +41,8 @@ internal final class RUMFeature { let vitalMemoryReader: SamplingBasedVitalReader // VitalMemoryReader let vitalRefreshRateReader: ContinuousVitalReader // VitalRefreshRateReader + let onSessionStart: RUMSessionListener? + // MARK: - Components static let featureName = "rum" @@ -143,7 +145,8 @@ internal final class RUMFeature { commonDependencies: commonDependencies, vitalCPUReader: VitalCPUReader(), vitalMemoryReader: VitalMemoryReader(), - vitalRefreshRateReader: VitalRefreshRateReader() + vitalRefreshRateReader: VitalRefreshRateReader(), + onSessionStart: configuration.onSessionStart ) } @@ -155,7 +158,8 @@ internal final class RUMFeature { commonDependencies: FeaturesCommonDependencies, vitalCPUReader: SamplingBasedVitalReader, vitalMemoryReader: SamplingBasedVitalReader, - vitalRefreshRateReader: ContinuousVitalReader + vitalRefreshRateReader: ContinuousVitalReader, + onSessionStart: RUMSessionListener? = nil ) { // Configuration self.configuration = configuration @@ -176,6 +180,8 @@ internal final class RUMFeature { self.vitalCPUReader = vitalCPUReader self.vitalMemoryReader = vitalMemoryReader self.vitalRefreshRateReader = vitalRefreshRateReader + + self.onSessionStart = onSessionStart } #if DD_SDK_COMPILED_FOR_TESTING diff --git a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScope.swift b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScope.swift index 6bc69d0574..ea4a35826e 100644 --- a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScope.swift +++ b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScope.swift @@ -6,6 +6,8 @@ import Foundation +internal typealias RUMSessionListener = (String, Bool) -> Void + /// Injection container for common dependencies used by all `RUMScopes`. internal struct RUMScopeDependencies { let userInfoProvider: RUMUserInfoProvider @@ -20,6 +22,8 @@ internal struct RUMScopeDependencies { let vitalCPUReader: SamplingBasedVitalReader let vitalMemoryReader: SamplingBasedVitalReader let vitalRefreshRateReader: ContinuousVitalReader + + let onSessionStart: RUMSessionListener? } internal class RUMApplicationScope: RUMScope, RUMContextProvider { @@ -28,6 +32,7 @@ internal class RUMApplicationScope: RUMScope, RUMContextProvider { /// Session scope. It gets created with the first `.startView` event. /// Might be re-created later according to session duration constraints. private(set) var sessionScope: RUMSessionScope? + /// RUM Sessions sampling rate. internal let samplingRate: Float @@ -87,6 +92,7 @@ internal class RUMApplicationScope: RUMScope, RUMContextProvider { private func refresh(expiredSession: RUMSessionScope, on command: RUMCommand) { let refreshedSession = RUMSessionScope(from: expiredSession, startTime: command.time) sessionScope = refreshedSession + sessionScopeDidUpdate(refreshedSession) _ = refreshedSession.process(command: command) } @@ -103,6 +109,12 @@ internal class RUMApplicationScope: RUMScope, RUMContextProvider { ) sessionScope = initialSession + sessionScopeDidUpdate(initialSession) _ = initialSession.process(command: startInitialViewCommand) } + + private func sessionScopeDidUpdate(_ sessionScope: RUMSessionScope) { + let sessionID = sessionScope.sessionUUID.rawValue.uuidString + dependencies.onSessionStart?(sessionID, sessionScope.shouldBeSampledOut) + } } diff --git a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMSessionScope.swift b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMSessionScope.swift index c163ddc74e..fe71d769d9 100644 --- a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMSessionScope.swift +++ b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMSessionScope.swift @@ -30,10 +30,10 @@ internal class RUMSessionScope: RUMScope, RUMContextProvider { /// This Session UUID. Equals `.nullUUID` if the Session is sampled. let sessionUUID: RUMUUID + /// Tells if events from this Session should be sampled-out (not send). + let shouldBeSampledOut: Bool /// RUM Session sampling rate. private let samplingRate: Float - /// Tells if events from this Session should be sampled-out (not send). - private let shouldBeSampledOut: Bool /// The start time of this Session. private let sessionStartTime: Date /// Time of the last RUM interaction noticed by this Session. diff --git a/Sources/Datadog/RUMMonitor.swift b/Sources/Datadog/RUMMonitor.swift index 9f12da6eca..6fa5331837 100644 --- a/Sources/Datadog/RUMMonitor.swift +++ b/Sources/Datadog/RUMMonitor.swift @@ -189,7 +189,8 @@ public class RUMMonitor: DDRUMMonitor, RUMCommandSubscriber { dateCorrector: rumFeature.dateCorrector, vitalCPUReader: rumFeature.vitalCPUReader, vitalMemoryReader: rumFeature.vitalMemoryReader, - vitalRefreshRateReader: rumFeature.vitalRefreshRateReader + vitalRefreshRateReader: rumFeature.vitalRefreshRateReader, + onSessionStart: rumFeature.onSessionStart ), samplingRate: rumFeature.configuration.sessionSamplingRate, backgroundEventTrackingEnabled: rumFeature.configuration.backgroundEventTrackingEnabled diff --git a/Sources/DatadogObjc/DatadogConfiguration+objc.swift b/Sources/DatadogObjc/DatadogConfiguration+objc.swift index 0cb0aea277..d43e33a334 100644 --- a/Sources/DatadogObjc/DatadogConfiguration+objc.swift +++ b/Sources/DatadogObjc/DatadogConfiguration+objc.swift @@ -254,6 +254,11 @@ public class DDConfigurationBuilder: NSObject { _ = sdkBuilder.set(rumSessionsSamplingRate: rumSessionsSamplingRate) } + @objc + public func set(onRUMSessionStart handler: @escaping (String, Bool) -> Void) { + _ = sdkBuilder.onRUMSessionStart(handler) + } + @objc public func trackUIKitRUMViews() { let defaultPredicate = DefaultUIKitRUMViewsPredicate() diff --git a/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift b/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift index fbb3527d68..6754ced971 100644 --- a/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift +++ b/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift @@ -45,6 +45,7 @@ class DatadogConfigurationBuilderTests: XCTestCase { XCTAssertNil(configuration.firstPartyHosts) XCTAssertNil(configuration.spanEventMapper) XCTAssertEqual(configuration.rumSessionsSamplingRate, 100.0) + XCTAssertNil(configuration.rumSessionsListener) XCTAssertNil(configuration.rumUIKitViewsPredicate) XCTAssertNil(configuration.rumUIKitUserActionsPredicate) XCTAssertNil(configuration.rumViewEventMapper) @@ -78,6 +79,7 @@ class DatadogConfigurationBuilderTests: XCTestCase { .set(customTracesEndpoint: URL(string: "https://api.custom.traces/")!) .set(customRUMEndpoint: URL(string: "https://api.custom.rum/")!) .set(rumSessionsSamplingRate: 42.5) + .onRUMSessionStart { _, _ in } .setSpanEventMapper { _ in mockSpanEvent } .trackURLSession(firstPartyHosts: ["example.com"]) .trackUIKitRUMViews(using: UIKitRUMViewsPredicateMock()) @@ -133,6 +135,7 @@ class DatadogConfigurationBuilderTests: XCTestCase { XCTAssertEqual(configuration.customRUMEndpoint, URL(string: "https://api.custom.rum/")!) XCTAssertEqual(configuration.firstPartyHosts, ["example.com"]) XCTAssertEqual(configuration.rumSessionsSamplingRate, 42.5) + XCTAssertNotNil(configuration.rumSessionsListener) XCTAssertTrue(configuration.rumUIKitViewsPredicate is UIKitRUMViewsPredicateMock) XCTAssertTrue(configuration.rumUIKitUserActionsPredicate is UIKitRUMUserActionsPredicateMock) XCTAssertEqual(configuration.spanEventMapper?(.mockRandom()), mockSpanEvent) diff --git a/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift b/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift index e448fa6584..513bc6168e 100644 --- a/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift @@ -233,7 +233,8 @@ extension FeaturesConfiguration.RUM { actionEventMapper: RUMActionEventMapper? = nil, errorEventMapper: RUMErrorEventMapper? = nil, autoInstrumentation: FeaturesConfiguration.RUM.AutoInstrumentation? = nil, - backgroundEventTrackingEnabled: Bool = false + backgroundEventTrackingEnabled: Bool = false, + onSessionStart: @escaping RUMSessionListener = mockNoOpSessionListerner() ) -> Self { return .init( common: common, @@ -246,7 +247,8 @@ extension FeaturesConfiguration.RUM { actionEventMapper: actionEventMapper, errorEventMapper: errorEventMapper, autoInstrumentation: autoInstrumentation, - backgroundEventTrackingEnabled: backgroundEventTrackingEnabled + backgroundEventTrackingEnabled: backgroundEventTrackingEnabled, + onSessionStart: onSessionStart ) } } diff --git a/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift b/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift index cb6667481f..bc78473959 100644 --- a/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift @@ -381,6 +381,10 @@ extension RUMContext { // MARK: - RUMScope Mocks +func mockNoOpSessionListerner() -> RUMSessionListener { + return { _, _ in } +} + extension RUMScopeDependencies { static func mockAny() -> RUMScopeDependencies { return mockWith() @@ -396,7 +400,8 @@ extension RUMScopeDependencies { eventBuilder: RUMEventBuilder = RUMEventBuilder(eventsMapper: .mockNoOp()), eventOutput: RUMEventOutput = RUMEventOutputMock(), rumUUIDGenerator: RUMUUIDGenerator = DefaultRUMUUIDGenerator(), - dateCorrector: DateCorrectorType = DateCorrectorMock() + dateCorrector: DateCorrectorType = DateCorrectorMock(), + onSessionStart: @escaping RUMSessionListener = mockNoOpSessionListerner() ) -> RUMScopeDependencies { return RUMScopeDependencies( userInfoProvider: userInfoProvider, @@ -408,7 +413,8 @@ extension RUMScopeDependencies { dateCorrector: dateCorrector, vitalCPUReader: SamplingBasedVitalReaderMock(), vitalMemoryReader: SamplingBasedVitalReaderMock(), - vitalRefreshRateReader: ContinuousVitalReaderMock() + vitalRefreshRateReader: ContinuousVitalReaderMock(), + onSessionStart: onSessionStart ) } @@ -420,7 +426,8 @@ extension RUMScopeDependencies { eventBuilder: RUMEventBuilder? = nil, eventOutput: RUMEventOutput? = nil, rumUUIDGenerator: RUMUUIDGenerator? = nil, - dateCorrector: DateCorrectorType? = nil + dateCorrector: DateCorrectorType? = nil, + onSessionStart: @escaping RUMSessionListener = mockNoOpSessionListerner() ) -> RUMScopeDependencies { return RUMScopeDependencies( userInfoProvider: userInfoProvider ?? self.userInfoProvider, @@ -432,7 +439,8 @@ extension RUMScopeDependencies { dateCorrector: dateCorrector ?? self.dateCorrector, vitalCPUReader: SamplingBasedVitalReaderMock(), vitalMemoryReader: SamplingBasedVitalReaderMock(), - vitalRefreshRateReader: ContinuousVitalReaderMock() + vitalRefreshRateReader: ContinuousVitalReaderMock(), + onSessionStart: onSessionStart ) } } diff --git a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScopeTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScopeTests.swift index 73c80190fd..b73e906a30 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScopeTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScopeTests.swift @@ -24,16 +24,48 @@ class RUMApplicationScopeTests: XCTestCase { } func testWhenFirstViewIsStarted_itStartsNewSession() { - let scope = RUMApplicationScope(rumApplicationID: .mockAny(), dependencies: .mockAny(), samplingRate: 100, backgroundEventTrackingEnabled: .mockAny()) + let expectation = self.expectation(description: "onSessionStart is called") + let onSessionStart: RUMSessionListener = { sessionId, isDiscarded in + XCTAssertTrue(sessionId.matches(regex: .uuidRegex)) + XCTAssertTrue(isDiscarded) + expectation.fulfill() + } + + let scope = RUMApplicationScope( + rumApplicationID: .mockAny(), + dependencies: .mockWith( + onSessionStart: onSessionStart + ), + samplingRate: 0, + backgroundEventTrackingEnabled: .mockAny() + ) XCTAssertNil(scope.sessionScope) XCTAssertTrue(scope.process(command: RUMStartViewCommand.mockAny())) + waitForExpectations(timeout: 0.5) XCTAssertNotNil(scope.sessionScope) XCTAssertEqual(scope.sessionScope?.backgroundEventTrackingEnabled, scope.backgroundEventTrackingEnabled) } func testWhenSessionExpires_itStartsANewOneAndTransfersActiveViews() throws { - let scope = RUMApplicationScope(rumApplicationID: .mockAny(), dependencies: .mockAny(), samplingRate: 100, backgroundEventTrackingEnabled: .mockAny()) + let expectation = self.expectation(description: "onSessionStart is called twice") + expectation.expectedFulfillmentCount = 2 + + let onSessionStart: RUMSessionListener = { sessionId, isDiscarded in + XCTAssertTrue(sessionId.matches(regex: .uuidRegex)) + XCTAssertFalse(isDiscarded) + expectation.fulfill() + } + + let scope = RUMApplicationScope( + rumApplicationID: .mockAny(), + dependencies: .mockWith( + onSessionStart: onSessionStart + ), + samplingRate: 100, + backgroundEventTrackingEnabled: .mockAny() + ) + var currentTime = Date() let view = createMockViewInWindow() @@ -49,6 +81,7 @@ class RUMApplicationScopeTests: XCTestCase { let secondSessionViewScopes = try XCTUnwrap(scope.sessionScope?.viewScopes) let secondSessionViewScope = try XCTUnwrap(secondSessionViewScopes.first) + waitForExpectations(timeout: 0.5) XCTAssertNotEqual(firstSessionUUID, secondSessionUUID) XCTAssertEqual(firstsSessionViewScopes.count, secondSessionViewScopes.count) XCTAssertTrue(secondSessionViewScope.identity.equals(view)) diff --git a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift index dc032d3627..7b4a43f522 100644 --- a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift +++ b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift @@ -789,6 +789,33 @@ class RUMMonitorTests: XCTestCase { XCTAssertEqual(try lastViewUpdate.timing(named: "timing3_.@$-______"), 3_000_000_000) } + // MARK: - RUM New Session + + func testStartingViewCreatesNewSession() { + let keepAllSessions: Bool = .random() + + let expectation = self.expectation(description: "onSessionStart is called") + let onSessionStart: RUMSessionListener = { sessionId, isDiscarded in + XCTAssertTrue(sessionId.matches(regex: .uuidRegex)) + XCTAssertEqual(isDiscarded, !keepAllSessions) + expectation.fulfill() + } + + RUMFeature.instance = .mockWith( + directories: temporaryFeatureDirectories, + configuration: .mockWith( + sessionSamplingRate: keepAllSessions ? 100 : 0, + onSessionStart: onSessionStart + ) + ) + + defer { RUMFeature.instance?.deinitialize() } + + let monitor = RUMMonitor.initialize() + monitor.startView(viewController: mockView) + waitForExpectations(timeout: 0.5) + } + // MARK: - RUM Events Dates Correction func testGivenTimeDifferenceBetweenDeviceAndServer_whenCollectingRUMEvents_thenEventsDateUseServerTime() throws { diff --git a/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m index 5b0d63dd20..e167a738b7 100644 --- a/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m +++ b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m @@ -82,6 +82,7 @@ - (void)testDDConfigurationBuilderAPI { [builder trackURLSessionWithFirstPartyHosts:[NSSet setWithArray:@[]]]; [builder setWithServiceName:@""]; [builder setWithRumSessionsSamplingRate:50]; + [builder setOnRUMSessionStart:^(NSString * _Nonnull sessionId, BOOL isDiscarded) {}]; [builder trackUIKitRUMViews]; [builder trackUIKitRUMViewsUsing:[CustomDDUIKitRUMViewsPredicate new]]; [builder trackUIKitRUMActions]; diff --git a/api-surface-objc b/api-surface-objc index dadb7a954c..774afd99c3 100644 --- a/api-surface-objc +++ b/api-surface-objc @@ -15,6 +15,7 @@ public class DDDatadog: NSObject public static func verbosityLevel() -> DDSDKVerbosityLevel public static func setUserInfo(id: String? = nil, name: String? = nil, email: String? = nil, extraInfo: [String: Any] = [:]) public static func setTrackingConsent(consent: DDTrackingConsent) + public static func flushAndDeinitialize() public class DDEndpoint: NSObject public static func us1() -> DDEndpoint public static func us3() -> DDEndpoint @@ -68,6 +69,7 @@ public class DDConfigurationBuilder: NSObject public func trackURLSession(firstPartyHosts: Set) public func set(serviceName: String) public func set(rumSessionsSamplingRate: Float) + public func set(onRUMSessionStart handler: @escaping (String, Bool) -> Void) public func trackUIKitRUMViews() public func trackUIKitRUMViews(using predicate: DDUIKitRUMViewsPredicate) public func trackUIKitActions() @@ -80,6 +82,7 @@ public class DDConfigurationBuilder: NSObject public func set(batchSize: DDBatchSize) public func set(uploadFrequency: DDUploadFrequency) public func set(additionalConfiguration: [String: Any]) + public func set(proxyConfiguration: [AnyHashable: Any]) public func build() -> DDConfiguration public class DDGlobal: NSObject @objc public static var sharedTracer = DatadogObjc.DDTracer(swiftTracer: Datadog.Global.sharedTracer) @@ -167,6 +170,12 @@ public class DDRUMViewEvent: NSObject public class DDRUMViewEventDD: NSObject @objc public var documentVersion: NSNumber @objc public var formatVersion: NSNumber + @objc public var session: DDRUMViewEventDDSession? +public class DDRUMViewEventDDSession: NSObject + @objc public var plan: DDRUMViewEventDDSessionPlan +public enum DDRUMViewEventDDSessionPlan: Int + case plan1 + case plan2 public class DDRUMViewEventApplication: NSObject @objc public var id: String public class DDRUMViewEventRUMConnectivity: NSObject @@ -273,8 +282,14 @@ public class DDRUMResourceEvent: NSObject @objc public var view: DDRUMResourceEventView public class DDRUMResourceEventDD: NSObject @objc public var formatVersion: NSNumber + @objc public var session: DDRUMResourceEventDDSession? @objc public var spanId: String? @objc public var traceId: String? +public class DDRUMResourceEventDDSession: NSObject + @objc public var plan: DDRUMResourceEventDDSessionPlan +public enum DDRUMResourceEventDDSessionPlan: Int + case plan1 + case plan2 public class DDRUMResourceEventAction: NSObject @objc public var id: String public class DDRUMResourceEventApplication: NSObject @@ -405,6 +420,12 @@ public class DDRUMActionEvent: NSObject @objc public var view: DDRUMActionEventView public class DDRUMActionEventDD: NSObject @objc public var formatVersion: NSNumber + @objc public var session: DDRUMActionEventDDSession? +public class DDRUMActionEventDDSession: NSObject + @objc public var plan: DDRUMActionEventDDSessionPlan +public enum DDRUMActionEventDDSessionPlan: Int + case plan1 + case plan2 public class DDRUMActionEventAction: NSObject @objc public var crash: DDRUMActionEventActionCrash? @objc public var error: DDRUMActionEventActionError? @@ -490,6 +511,12 @@ public class DDRUMErrorEvent: NSObject @objc public var view: DDRUMErrorEventView public class DDRUMErrorEventDD: NSObject @objc public var formatVersion: NSNumber + @objc public var session: DDRUMErrorEventDDSession? +public class DDRUMErrorEventDDSession: NSObject + @objc public var plan: DDRUMErrorEventDDSessionPlan +public enum DDRUMErrorEventDDSessionPlan: Int + case plan1 + case plan2 public class DDRUMErrorEventAction: NSObject @objc public var id: String public class DDRUMErrorEventApplication: NSObject @@ -518,6 +545,8 @@ public enum DDRUMErrorEventRUMConnectivityStatus: Int public class DDRUMErrorEventRUMEventAttributes: NSObject @objc public var contextInfo: [String: Any] public class DDRUMErrorEventError: NSObject + @objc public var handling: DDRUMErrorEventErrorHandling + @objc public var handlingStack: String? @objc public var id: String? @objc public var isCrash: NSNumber? @objc public var message: String @@ -525,6 +554,10 @@ public class DDRUMErrorEventError: NSObject @objc public var source: DDRUMErrorEventErrorSource @objc public var stack: String? @objc public var type: String? +public enum DDRUMErrorEventErrorHandling: Int + case none + case handled + case unhandled public class DDRUMErrorEventErrorResource: NSObject @objc public var method: DDRUMErrorEventErrorResourceRUMMethod @objc public var provider: DDRUMErrorEventErrorResourceProvider? diff --git a/api-surface-swift b/api-surface-swift index 008962891a..66cf573903 100644 --- a/api-surface-swift +++ b/api-surface-swift @@ -81,6 +81,7 @@ public class Datadog public static func initialize(appContext: AppContext,trackingConsent: TrackingConsent,configuration: Configuration) public static var verbosityLevel: LogLevel? = nil public static var debugRUM = false + public static var isInitialized: Bool public static func setUserInfo(id: String? = nil,name: String? = nil,email: String? = nil,extraInfo: [AttributeKey: AttributeValue] = [:]) public static func set(trackingConsent: TrackingConsent) public static func flushAndDeinitialize() @@ -146,6 +147,7 @@ public class Datadog public func enableRUM(_ enabled: Bool) -> Builder public func set(rumEndpoint: RUMEndpoint) -> Builder public func set(rumSessionsSamplingRate: Float) -> Builder + public func onRUMSessionStart(_ handler: @escaping (String, Bool) -> Void) -> Builder public func trackUIKitRUMViews(using predicate: UIKitRUMViewsPredicate = DefaultUIKitRUMViewsPredicate()) -> Builder public func trackUIKitActions(_ enabled: Bool = true) -> Builder public func trackUIKitRUMActions(using predicate: UIKitRUMUserActionsPredicate = DefaultUIKitRUMUserActionsPredicate()) -> Builder @@ -160,6 +162,7 @@ public class Datadog public func set(serviceName: String) -> Builder public func set(batchSize: BatchSize) -> Builder public func set(uploadFrequency: UploadFrequency) -> Builder + public func set(proxyConfiguration: [AnyHashable: Any]?) -> Builder public func set(additionalConfiguration: [String: Any]) -> Builder public func build() -> Configuration public typealias DDGlobal = Global @@ -295,16 +298,22 @@ public struct RUMViewEvent: RUMDataModel public let dd: DD public let application: Application public let connectivity: RUMConnectivity? - public let context: RUMEventAttributes? + public internal(set) var context: RUMEventAttributes? public let date: Int64 public let service: String? public let session: Session public let type: String = "view" - public let usr: RUMUser? + public internal(set) var usr: RUMUser? public var view: View public struct DD: Codable public let documentVersion: Int64 public let formatVersion: Int64 = 2 + public let session: Session? + public struct Session: Codable + public let plan: Plan + public enum Plan: Int, Codable + case plan1 = 1 + case plan2 = 2 public struct Application: Codable public let id: String public struct Session: Codable @@ -372,18 +381,24 @@ public struct RUMResourceEvent: RUMDataModel public let action: Action? public let application: Application public let connectivity: RUMConnectivity? - public let context: RUMEventAttributes? + public internal(set) var context: RUMEventAttributes? public let date: Int64 public var resource: Resource public let service: String? public let session: Session public let type: String = "resource" - public let usr: RUMUser? + public internal(set) var usr: RUMUser? public var view: View public struct DD: Codable public let formatVersion: Int64 = 2 + public let session: Session? public let spanId: String? public let traceId: String? + public struct Session: Codable + public let plan: Plan + public enum Plan: Int, Codable + case plan1 = 1 + case plan2 = 2 public struct Action: Codable public let id: String public struct Application: Codable @@ -468,15 +483,21 @@ public struct RUMActionEvent: RUMDataModel public var action: Action public let application: Application public let connectivity: RUMConnectivity? - public let context: RUMEventAttributes? + public internal(set) var context: RUMEventAttributes? public let date: Int64 public let service: String? public let session: Session public let type: String = "action" - public let usr: RUMUser? + public internal(set) var usr: RUMUser? public var view: View public struct DD: Codable public let formatVersion: Int64 = 2 + public let session: Session? + public struct Session: Codable + public let plan: Plan + public enum Plan: Int, Codable + case plan1 = 1 + case plan2 = 2 public struct Action: Codable public let crash: Crash? public let error: Error? @@ -524,21 +545,29 @@ public struct RUMErrorEvent: RUMDataModel public let action: Action? public let application: Application public let connectivity: RUMConnectivity? - public let context: RUMEventAttributes? + public internal(set) var context: RUMEventAttributes? public let date: Int64 public var error: Error public let service: String? public let session: Session public let type: String = "error" - public let usr: RUMUser? + public internal(set) var usr: RUMUser? public var view: View public struct DD: Codable public let formatVersion: Int64 = 2 + public let session: Session? + public struct Session: Codable + public let plan: Plan + public enum Plan: Int, Codable + case plan1 = 1 + case plan2 = 2 public struct Action: Codable public let id: String public struct Application: Codable public let id: String public struct Error: Codable + public let handling: Handling? + public let handlingStack: String? public let id: String? public let isCrash: Bool? public var message: String @@ -546,6 +575,9 @@ public struct RUMErrorEvent: RUMDataModel public let source: Source public var stack: String? public let type: String? + public enum Handling: String, Codable + case handled = "handled" + case unhandled = "unhandled" public struct Resource: Codable public let method: RUMMethod public let provider: Provider? @@ -613,14 +645,14 @@ public struct RUMConnectivity: Codable case notConnected = "not_connected" case maybe = "maybe" public struct RUMEventAttributes: Codable - public let contextInfo: [String: Codable] + public internal(set) var contextInfo: [String: Encodable] public func encode(to encoder: Encoder) throws public init(from decoder: Decoder) throws public struct RUMUser: Codable public let email: String? public let id: String? public let name: String? - public let usrInfo: [String: Codable] + public internal(set) var usrInfo: [String: Encodable] public func encode(to encoder: Encoder) throws public init(from decoder: Decoder) throws public enum RUMMethod: String, Codable @@ -684,8 +716,8 @@ public class Tracer: OTTracer public var bundleWithRUM: Bool public init(serviceName: String? = nil,sendNetworkInfo: Bool = false,bundleWithRUM: Bool = true,globalTags: [String: Encodable]? = nil) public class HTTPHeadersWriter: OTHTTPHeadersWriter - public init() public private(set) var tracePropagationHTTPHeaders: [String: String] = [:] + public init() public func inject(spanContext: OTSpanContext) public struct SpanEvent: Encodable public var operationName: String