From addc11b32769b20761cb20290e17a7a6cacfbfa7 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Wed, 19 Oct 2022 14:40:53 +0200 Subject: [PATCH 1/4] RUMM-2290 Set `rulePsr` in RUM resource event for enhancing APM ingestion control and add support to `_dd.rule_psr` passed from cross-platform SDKs --- .../Datadog/Core/Attributes/Attributes.swift | 6 ++ .../Datadog/Core/FeaturesConfiguration.swift | 4 +- .../URLSessionRUMResourcesHandler.swift | 8 +- .../Datadog/RUM/RUMMonitor/RUMCommand.swift | 4 + .../RUMMonitor/Scopes/RUMResourceScope.swift | 3 +- .../URLSessionTracingHandler.swift | 2 +- .../Interception/URLSessionInterceptor.swift | 3 +- .../Datadog/Mocks/RUMFeatureMocks.swift | 32 +++++++- .../SystemFrameworks/FoundationMocks.swift | 6 +- .../URLSessionRUMResourcesHandlerTests.swift | 13 ++- .../Scopes/RUMResourceScopeTests.swift | 82 ++++++++++++++----- 11 files changed, 130 insertions(+), 33 deletions(-) diff --git a/Sources/Datadog/Core/Attributes/Attributes.swift b/Sources/Datadog/Core/Attributes/Attributes.swift index 0db0afe1f5..4b8c15190c 100644 --- a/Sources/Datadog/Core/Attributes/Attributes.swift +++ b/Sources/Datadog/Core/Attributes/Attributes.swift @@ -113,4 +113,10 @@ internal struct CrossPlatformAttributes { /// and send it within the RUM resource, so the RUM backend can issue corresponding APM span on behalf of the mobile app. /// Expects `String` value. static let spanID = "_dd.span_id" + + /// Trace sample rate applied to RUM resources created by cross platform SDK. + /// We send cross-platform SDK's sample rate within RUM resource in order to provide accurate visibility into what settings are + /// configured at the SDK level. This gets displayed on APM's traffic ingestion control page. + /// Expects `Double` value between `0.0` and `1.0`. + static let rulePSR = "_dd.rule_psr" } diff --git a/Sources/Datadog/Core/FeaturesConfiguration.swift b/Sources/Datadog/Core/FeaturesConfiguration.swift index bcc4337ac9..cac8ed5cb1 100644 --- a/Sources/Datadog/Core/FeaturesConfiguration.swift +++ b/Sources/Datadog/Core/FeaturesConfiguration.swift @@ -80,7 +80,9 @@ internal struct FeaturesConfiguration { let instrumentTracing: Bool /// If the RUM instrumentation should be enabled. let instrumentRUM: Bool - // Tracing sampler + /// Tracing sampler to decide if trace should be generated for certain network request: + /// - if RUM instrumentation is enabled, it is used to sample traces generated by RUM BE, + /// - if RUM is disabled, it is used to sample traces generated by the SDK. let tracingSampler: Sampler } diff --git a/Sources/Datadog/RUM/Instrumentation/Resources/URLSessionRUMResourcesHandler.swift b/Sources/Datadog/RUM/Instrumentation/Resources/URLSessionRUMResourcesHandler.swift index 50108853bc..943bdcd246 100644 --- a/Sources/Datadog/RUM/Instrumentation/Resources/URLSessionRUMResourcesHandler.swift +++ b/Sources/Datadog/RUM/Instrumentation/Resources/URLSessionRUMResourcesHandler.swift @@ -10,14 +10,17 @@ internal typealias URLSessionRUMAttributesProvider = (URLRequest, URLResponse?, internal class URLSessionRUMResourcesHandler: URLSessionInterceptionHandler, RUMCommandPublisher { private let dateProvider: DateProvider + /// Tracing sampler used to sample traces generated by RUM BE. + let tracingSampler: Sampler /// Attributes-providing callback. /// It is configured by the user and should be used to associate additional RUM attributes with intercepted RUM Resource. let rumAttributesProvider: (URLSessionRUMAttributesProvider)? // MARK: - Initialization - init(dateProvider: DateProvider, rumAttributesProvider: (URLSessionRUMAttributesProvider)?) { + init(dateProvider: DateProvider, tracingSampler: Sampler, rumAttributesProvider: URLSessionRUMAttributesProvider?) { self.dateProvider = dateProvider + self.tracingSampler = tracingSampler self.rumAttributesProvider = rumAttributesProvider } @@ -45,7 +48,8 @@ internal class URLSessionRUMResourcesHandler: URLSessionInterceptionHandler, RUM spanContext: interception.spanContext.map { .init( traceID: String($0.traceID.rawValue), - spanID: String($0.spanID.rawValue) + spanID: String($0.spanID.rawValue), + samplingRate: Double(tracingSampler.samplingRate) / 100.0 ) } ) diff --git a/Sources/Datadog/RUM/RUMMonitor/RUMCommand.swift b/Sources/Datadog/RUM/RUMMonitor/RUMCommand.swift index 76eb07f059..7a3ce7dae6 100644 --- a/Sources/Datadog/RUM/RUMMonitor/RUMCommand.swift +++ b/Sources/Datadog/RUM/RUMMonitor/RUMCommand.swift @@ -146,8 +146,12 @@ internal protocol RUMResourceCommand: RUMCommand { /// Tracing information propagated by Tracing to the underlying `URLRequest`. It is passed to the RUM backend /// in order to create the APM span. The actual `Span` is not send by the SDK. internal struct RUMSpanContext { + /// The trace ID injected to `URLRequest` that issues RUM resource. let traceID: String + /// The span ID injected to `URLRequest` that issues RUM resource. let spanID: String + /// The sampling rate applied to the trace (a value between `0.0` and `1.0`). + let samplingRate: Double } internal struct RUMStartResourceCommand: RUMResourceCommand { diff --git a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScope.swift b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScope.swift index 3dbafb8b05..d6b6420869 100644 --- a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScope.swift +++ b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScope.swift @@ -118,6 +118,7 @@ internal class RUMResourceScope: RUMScope { // Check trace attributes let traceId = (attributes.removeValue(forKey: CrossPlatformAttributes.traceID) as? String) ?? spanContext?.traceID let spanId = (attributes.removeValue(forKey: CrossPlatformAttributes.spanID) as? String) ?? spanContext?.spanID + let traceSamplingRate = (attributes.removeValue(forKey: CrossPlatformAttributes.rulePSR) as? Double) ?? spanContext?.samplingRate /// Metrics values take precedence over other values. if let metrics = resourceMetrics { @@ -135,7 +136,7 @@ internal class RUMResourceScope: RUMScope { dd: .init( browserSdkVersion: nil, discarded: nil, - rulePsr: nil, + rulePsr: traceSamplingRate, session: .init(plan: .plan1), spanId: spanId, traceId: traceId diff --git a/Sources/Datadog/Tracing/AutoInstrumentation/URLSessionTracingHandler.swift b/Sources/Datadog/Tracing/AutoInstrumentation/URLSessionTracingHandler.swift index 023d49f3c3..579934b66b 100644 --- a/Sources/Datadog/Tracing/AutoInstrumentation/URLSessionTracingHandler.swift +++ b/Sources/Datadog/Tracing/AutoInstrumentation/URLSessionTracingHandler.swift @@ -9,7 +9,7 @@ import Foundation internal class URLSessionTracingHandler: URLSessionInterceptionHandler { /// Listening to app state changes and use it to report `foreground_duration` let appStateListener: AppStateListening - /// The Tracing sampler. + /// Tracing sampler used to sample traces generated by the SDK. let tracingSampler: Sampler init(appStateListener: AppStateListening, tracingSampler: Sampler) { diff --git a/Sources/Datadog/URLSessionAutoInstrumentation/Interception/URLSessionInterceptor.swift b/Sources/Datadog/URLSessionAutoInstrumentation/Interception/URLSessionInterceptor.swift index c113919fca..1cdf06eb37 100644 --- a/Sources/Datadog/URLSessionAutoInstrumentation/Interception/URLSessionInterceptor.swift +++ b/Sources/Datadog/URLSessionAutoInstrumentation/Interception/URLSessionInterceptor.swift @@ -45,7 +45,7 @@ public class URLSessionInterceptor: URLSessionInterceptorType { /// Additional header injected to intercepted 1st party requests. /// Set to `x-datadog-origin: rum` if both RUM and Tracing instrumentations are enabled and `nil` in all other cases. internal let additionalHeadersForFirstPartyRequests: [String: String]? - /// Tracing sampler + /// Tracing sampler used to sample traces generated by the SDK or RUM BE. internal let tracingSampler: Sampler // MARK: - Initialization @@ -60,6 +60,7 @@ public class URLSessionInterceptor: URLSessionInterceptorType { if configuration.instrumentRUM { handler = URLSessionRUMResourcesHandler( dateProvider: dateProvider, + tracingSampler: configuration.tracingSampler, rumAttributesProvider: configuration.rumAttributesProvider ) } else { diff --git a/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift b/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift index d6edea080d..94ab83d96a 100644 --- a/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift @@ -289,6 +289,32 @@ extension RUMAddViewTimingCommand: AnyMockable, RandomMockable { } } +extension RUMSpanContext: AnyMockable, RandomMockable { + static func mockAny() -> RUMSpanContext { + return .mockWith() + } + + static func mockRandom() -> RUMSpanContext { + return RUMSpanContext( + traceID: .mockRandom(), + spanID: .mockRandom(), + samplingRate: .mockRandom() + ) + } + + static func mockWith( + traceID: String = .mockAny(), + spanID: String = .mockAny(), + samplingRate: Double = .mockAny() + ) -> RUMSpanContext { + return RUMSpanContext( + traceID: traceID, + spanID: spanID, + samplingRate: samplingRate + ) + } +} + extension RUMStartResourceCommand: AnyMockable, RandomMockable { static func mockAny() -> RUMStartResourceCommand { mockWith() } @@ -301,7 +327,7 @@ extension RUMStartResourceCommand: AnyMockable, RandomMockable { httpMethod: .mockRandom(), kind: .mockAny(), isFirstPartyRequest: .mockRandom(), - spanContext: .init(traceID: .mockRandom(), spanID: .mockRandom()) + spanContext: .init(traceID: .mockRandom(), spanID: .mockRandom(), samplingRate: .mockAny()) ) } @@ -313,7 +339,7 @@ extension RUMStartResourceCommand: AnyMockable, RandomMockable { httpMethod: RUMMethod = .mockAny(), kind: RUMResourceType = .mockAny(), isFirstPartyRequest: Bool = .mockAny(), - spanContext: RUMSpanContext? = nil + spanContext: RUMSpanContext? = .mockAny() ) -> RUMStartResourceCommand { return RUMStartResourceCommand( resourceKey: resourceKey, @@ -784,7 +810,7 @@ extension RUMResourceScope { httpMethod: RUMMethod = .mockAny(), isFirstPartyResource: Bool? = nil, resourceKindBasedOnRequest: RUMResourceType? = nil, - spanContext: RUMSpanContext? = nil, + spanContext: RUMSpanContext? = .mockAny(), onResourceEventSent: @escaping () -> Void = {}, onErrorEventSent: @escaping () -> Void = {} ) -> RUMResourceScope { diff --git a/Tests/DatadogTests/Datadog/Mocks/SystemFrameworks/FoundationMocks.swift b/Tests/DatadogTests/Datadog/Mocks/SystemFrameworks/FoundationMocks.swift index 9fab3bff47..64916ff5fc 100644 --- a/Tests/DatadogTests/Datadog/Mocks/SystemFrameworks/FoundationMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/SystemFrameworks/FoundationMocks.swift @@ -370,10 +370,14 @@ extension Bool: AnyMockable { } } -extension Float: AnyMockable { +extension Float: AnyMockable, RandomMockable { static func mockAny() -> Float { return 0 } + + static func mockRandom() -> Float { + return .random(in: -Float(Int.min)...Float(Int.max)) + } } extension Double: AnyMockable, RandomMockable { diff --git a/Tests/DatadogTests/Datadog/RUM/Instrumentation/Resources/URLSessionRUMResourcesHandlerTests.swift b/Tests/DatadogTests/Datadog/RUM/Instrumentation/Resources/URLSessionRUMResourcesHandlerTests.swift index cbb3356741..b170aab998 100644 --- a/Tests/DatadogTests/Datadog/RUM/Instrumentation/Resources/URLSessionRUMResourcesHandlerTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/Instrumentation/Resources/URLSessionRUMResourcesHandlerTests.swift @@ -11,10 +11,15 @@ extension ResourceMetrics: EquatableInTests {} class URLSessionRUMResourcesHandlerTests: XCTestCase { private let dateProvider = RelativeDateProvider(using: .mockDecember15th2019At10AMUTC()) + private let traceSamplingRate: Double = .mockRandom(min: 0, max: 1) private let commandSubscriber = RUMCommandSubscriberMock() private func createHandler(rumAttributesProvider: URLSessionRUMAttributesProvider? = nil) -> URLSessionRUMResourcesHandler { - let handler = URLSessionRUMResourcesHandler(dateProvider: dateProvider, rumAttributesProvider: rumAttributesProvider) + let handler = URLSessionRUMResourcesHandler( + dateProvider: dateProvider, + tracingSampler: Sampler(samplingRate: Float(traceSamplingRate * 100)), + rumAttributesProvider: rumAttributesProvider + ) handler.publish(to: commandSubscriber) return handler } @@ -61,8 +66,10 @@ class URLSessionRUMResourcesHandlerTests: XCTestCase { waitForExpectations(timeout: 0.5, handler: nil) let resourceStartCommand = try XCTUnwrap(commandSubscriber.lastReceivedCommand as? RUMStartResourceCommand) - XCTAssertEqual(resourceStartCommand.spanContext?.traceID, "1") - XCTAssertEqual(resourceStartCommand.spanContext?.spanID, "2") + let spanContext = try XCTUnwrap(resourceStartCommand.spanContext) + XCTAssertEqual(spanContext.traceID, "1") + XCTAssertEqual(spanContext.spanID, "2") + XCTAssertEqual(spanContext.samplingRate, traceSamplingRate, accuracy: 0.01) } func testGivenTaskInterceptionWithMetricsAndResponse_whenInterceptionCompletes_itStopsRUMResourceWithMetrics() throws { diff --git a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScopeTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScopeTests.swift index c3b2f1f54b..70a467c5e9 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScopeTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScopeTests.swift @@ -61,7 +61,7 @@ class RUMResourceScopeTests: XCTestCase { url: "https://foo.com/resource/1", httpMethod: .post, resourceKindBasedOnRequest: nil, - spanContext: .init(traceID: "100", spanID: "200") + spanContext: .init(traceID: "100", spanID: "200", samplingRate: 0.42) ) currentTime.addTimeInterval(2) @@ -110,12 +110,63 @@ class RUMResourceScopeTests: XCTestCase { XCTAssertEqual(event.context?.contextInfo as? [String: String], ["foo": "bar"]) XCTAssertEqual(event.dd.traceId, "100") XCTAssertEqual(event.dd.spanId, "200") + XCTAssertEqual(event.dd.rulePsr, 0.42) XCTAssertEqual(event.dd.session?.plan, .plan1, "All RUM events should use RUM Lite plan") XCTAssertEqual(event.service, "test-service") XCTAssertEqual(event.device?.name, "device-name") XCTAssertEqual(event.os?.name, "device-os") } + func testGivenStartedResourceWithSpanContext_whenResourceLoadingEnds_itSendsResourceEvent() throws { + // Given + let scope = RUMResourceScope.mockWith( + context: rumContext, + dependencies: dependencies, + resourceKey: "/resource/1", + spanContext: .init(traceID: "100", spanID: "200", samplingRate: 0.42) + ) + + // When + XCTAssertFalse( + scope.process( + command: RUMStopResourceCommand.mockWith(resourceKey: "/resource/1"), + context: datadogContext, + writer: writer + ) + ) + + // Then + let event = try XCTUnwrap(writer.events(ofType: RUMResourceEvent.self).first) + XCTAssertEqual(event.dd.traceId, "100") + XCTAssertEqual(event.dd.spanId, "200") + XCTAssertEqual(event.dd.rulePsr, 0.42) + } + + func testGivenStartedResourceWithoutSpanContext_whenResourceLoadingEnds_itSendsResourceEvent() throws { + // Given + let scope = RUMResourceScope.mockWith( + context: rumContext, + dependencies: dependencies, + resourceKey: "/resource/1", + spanContext: nil + ) + + // When + XCTAssertFalse( + scope.process( + command: RUMStopResourceCommand.mockWith(resourceKey: "/resource/1"), + context: datadogContext, + writer: writer + ) + ) + + // Then + let event = try XCTUnwrap(writer.events(ofType: RUMResourceEvent.self).first) + XCTAssertNil(event.dd.traceId) + XCTAssertNil(event.dd.spanId) + XCTAssertNil(event.dd.rulePsr) + } + func testGivenConfiguredSoruce_whenResourceLoadingEnds_itSendsResourceEventWithCorrecSource() throws { var currentTime: Date = .mockDecember15th2019At10AMUTC() @@ -129,9 +180,7 @@ class RUMResourceScopeTests: XCTestCase { resourceKey: "/resource/1", startTime: currentTime, url: "https://foo.com/resource/1", - httpMethod: .post, - resourceKindBasedOnRequest: nil, - spanContext: .init(traceID: "100", spanID: "200") + httpMethod: .post ) currentTime.addTimeInterval(2) @@ -168,9 +217,7 @@ class RUMResourceScopeTests: XCTestCase { attributes: [:], startTime: currentTime, url: "https://foo.com/resource/1", - httpMethod: .post, - resourceKindBasedOnRequest: nil, - spanContext: .init(traceID: "100", spanID: "200") + httpMethod: .post ) currentTime.addTimeInterval(-1) @@ -217,8 +264,6 @@ class RUMResourceScopeTests: XCTestCase { XCTAssertNil(event.resource.download) XCTAssertEqual(try XCTUnwrap(event.action?.id.stringValue), rumContext.activeUserActionID?.toRUMDataFormat) XCTAssertEqual(event.context?.contextInfo as? [String: String], ["foo": "bar"]) - XCTAssertEqual(event.dd.traceId, "100") - XCTAssertEqual(event.dd.spanId, "200") XCTAssertEqual(event.dd.session?.plan, .plan1, "All RUM events should use RUM Lite plan") XCTAssertEqual(event.service, "test-service") XCTAssertEqual(event.device?.name, "device-name") @@ -233,7 +278,11 @@ class RUMResourceScopeTests: XCTestCase { context: rumContext, dependencies: dependencies, resourceKey: "/resource/1", - attributes: [CrossPlatformAttributes.traceID: "100", CrossPlatformAttributes.spanID: "200"], + attributes: [ + CrossPlatformAttributes.traceID: "100", + CrossPlatformAttributes.spanID: "200", + CrossPlatformAttributes.rulePSR: 0.12, + ], startTime: currentTime, url: "https://foo.com/resource/1", httpMethod: .post @@ -284,6 +333,7 @@ class RUMResourceScopeTests: XCTestCase { XCTAssertEqual(event.context?.contextInfo as? [String: String], ["foo": "bar"]) XCTAssertEqual(event.dd.traceId, "100") XCTAssertEqual(event.dd.spanId, "200") + XCTAssertEqual(event.dd.rulePsr, 0.12) XCTAssertEqual(event.source, .ios) XCTAssertEqual(event.service, "test-service") XCTAssertEqual(event.device?.name, "device-name") @@ -494,8 +544,6 @@ class RUMResourceScopeTests: XCTestCase { XCTAssertEqual(event.resource.download?.duration, 1_000_000_000) XCTAssertEqual(try XCTUnwrap(event.action?.id.stringValue), rumContext.activeUserActionID?.toRUMDataFormat) XCTAssertEqual(event.context?.contextInfo as? [String: String], ["foo": "bar"]) - XCTAssertNil(event.dd.traceId) - XCTAssertNil(event.dd.spanId) XCTAssertEqual(event.source, .ios) XCTAssertEqual(event.service, "test-service") XCTAssertEqual(event.device?.name, "device-name") @@ -580,8 +628,7 @@ class RUMResourceScopeTests: XCTestCase { startTime: currentTime, url: "https://firstparty.com/resource/1", httpMethod: .post, - isFirstPartyResource: true, - spanContext: .init(traceID: "100", spanID: "200") + isFirstPartyResource: true ) currentTime.addTimeInterval(2) @@ -614,8 +661,7 @@ class RUMResourceScopeTests: XCTestCase { startTime: currentTime, url: "https://foo.com/resource/1", httpMethod: .post, - isFirstPartyResource: false, - spanContext: .init(traceID: "100", spanID: "200") + isFirstPartyResource: false ) currentTime.addTimeInterval(2) @@ -747,7 +793,6 @@ class RUMResourceScopeTests: XCTestCase { startTime: currentTime, url: "https://foo.com/resource/1", httpMethod: .post, - spanContext: .init(traceID: "100", spanID: "200"), onResourceEventSent: { onResourceEventSentCalled = true } @@ -760,7 +805,6 @@ class RUMResourceScopeTests: XCTestCase { startTime: currentTime, url: "https://foo.com/resource/2", httpMethod: .post, - spanContext: .init(traceID: "100", spanID: "200"), onErrorEventSent: { onErrorEventSentCalled = true } @@ -824,7 +868,6 @@ class RUMResourceScopeTests: XCTestCase { startTime: currentTime, url: "https://foo.com/resource/1", httpMethod: .post, - spanContext: .init(traceID: "100", spanID: "200"), onResourceEventSent: { onResourceEventSentCalled = true } @@ -837,7 +880,6 @@ class RUMResourceScopeTests: XCTestCase { startTime: currentTime, url: "https://foo.com/resource/2", httpMethod: .post, - spanContext: .init(traceID: "100", spanID: "200"), onErrorEventSent: { onErrorEventSentCalled = true } From d1b7fbb4aae4c09db398c0bc8c91bb4543dc6eba Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Wed, 19 Oct 2022 21:07:38 +0200 Subject: [PATCH 2/4] RUMM-2290 Add checks to `rulePsr` in integration tests --- .../Scenarios/RUM/RUMResourcesScenarioTests.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Tests/DatadogIntegrationTests/Scenarios/RUM/RUMResourcesScenarioTests.swift b/Tests/DatadogIntegrationTests/Scenarios/RUM/RUMResourcesScenarioTests.swift index 0142657e0b..c927a50bac 100644 --- a/Tests/DatadogIntegrationTests/Scenarios/RUM/RUMResourcesScenarioTests.swift +++ b/Tests/DatadogIntegrationTests/Scenarios/RUM/RUMResourcesScenarioTests.swift @@ -134,6 +134,7 @@ class RUMResourcesScenarioTests: IntegrationTests, RUMCommonAsserts { XCTAssertNil(firstPartyResource1.dd.traceId, "`firstPartyGETResourceURL` should not be traced") XCTAssertNil(firstPartyResource1.dd.spanId, "`firstPartyGETResourceURL` should not be traced") + XCTAssertNil(firstPartyResource1.dd.rulePsr, "Not traced resource should not send sample rate") let firstPartyResource2 = try XCTUnwrap( session.viewVisits[0].resourceEvents.first { $0.resource.url == firstPartyPOSTResourceURL.absoluteString }, @@ -151,6 +152,8 @@ class RUMResourcesScenarioTests: IntegrationTests, RUMCommonAsserts { firstPartyPOSTRequestSpanID, "Tracing information should be propagated to `firstPartyPOSTResourceURL`" ) + let firstPartyResource2SampleRate = try XCTUnwrap(firstPartyResource2.dd.rulePsr, "Traced resource should send sample rate") + XCTAssertTrue(isValid(sampleRate: firstPartyResource2SampleRate), "\(firstPartyResource2SampleRate) is not valid sample rate") let firstPartyResourceError1 = try XCTUnwrap( session.viewVisits[0].errorEvents.first { $0.error.resource?.url == firstPartyBadResourceURL.absoluteString }, @@ -172,6 +175,7 @@ class RUMResourcesScenarioTests: IntegrationTests, RUMCommonAsserts { XCTAssertGreaterThan(thirdPartyResource1.resource.duration, 0) XCTAssertNil(thirdPartyResource1.dd.traceId, "3rd party RUM Resources should not be traced") XCTAssertNil(thirdPartyResource1.dd.spanId, "3rd party RUM Resources should not be traced") + XCTAssertNil(thirdPartyResource1.dd.rulePsr, "Not traced resource should not send sample rate") let thirdPartyResource2 = try XCTUnwrap( session.viewVisits[1].resourceEvents.first { $0.resource.url == thirdPartyPOSTResourceURL.absoluteString }, @@ -181,6 +185,7 @@ class RUMResourcesScenarioTests: IntegrationTests, RUMCommonAsserts { XCTAssertGreaterThan(thirdPartyResource2.resource.duration, 0) XCTAssertNil(thirdPartyResource2.dd.traceId, "3rd party RUM Resources should not be traced") XCTAssertNil(thirdPartyResource2.dd.spanId, "3rd party RUM Resources should not be traced") + XCTAssertNil(thirdPartyResource2.dd.rulePsr, "Not traced resource should not send sample rate") XCTAssertTrue( thirdPartyResource1.resource.dns != nil || thirdPartyResource2.resource.dns != nil, @@ -237,4 +242,8 @@ class RUMResourcesScenarioTests: IntegrationTests, RUMCommonAsserts { header?.removeFirst(prefix.count) return header } + + private func isValid(sampleRate: Double) -> Bool { + return sampleRate >= 0 && sampleRate <= 1 + } } From 487140fb3e012f7acd3ad653577de474d607b778 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Thu, 20 Oct 2022 10:00:42 +0200 Subject: [PATCH 3/4] RUMM-2290 Add `CHANGELOG.md` entry --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9164710de..fb2ec3eb15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +- [IMPROVEMENT] Send trace sample rate (`dd.rulePsr`) for APM's traffic ingestion control page. See [#1029][] + # 1.12.1 / 18-10-2022 - [IMPROVEMENT] Upgrade to PLCrashReporter 1.11.0 to fix Xcode 14 support. @@ -402,6 +404,7 @@ [@leffelmania]: https://github.com/LeffelMania [@simpleapp]: https://github.com/SimpleApp [@tsvetelinvladimirov]: https://github.com/TsvetelinVladimirov +[#1029]: https://github.com/DataDog/dd-sdk-ios/issues/1029 [@arnauddorgans]: https://github.com/arnauddorgans [@ben-yolabs]: https://github.com/ben-yolabs [@earltedly]: https://github.com/earltedly From d099988339f89ac37dc8fdbc5cadf0fc371cf8cd Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Mon, 24 Oct 2022 12:11:09 +0200 Subject: [PATCH 4/4] RUMM-2290 CR feedback - conventions --- CHANGELOG.md | 2 +- .../Resources/URLSessionRUMResourcesHandler.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb2ec3eb15..48ca68bde5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -398,13 +398,13 @@ [#964]: https://github.com/DataDog/dd-sdk-ios/issues/964 [#973]: https://github.com/DataDog/dd-sdk-ios/issues/973 [#997]: https://github.com/DataDog/dd-sdk-ios/issues/997 +[#1029]: https://github.com/DataDog/dd-sdk-ios/issues/1029 [@00fa9a]: https://github.com/00FA9A [@britton-earnin]: https://github.com/Britton-Earnin [@hengyu]: https://github.com/Hengyu [@leffelmania]: https://github.com/LeffelMania [@simpleapp]: https://github.com/SimpleApp [@tsvetelinvladimirov]: https://github.com/TsvetelinVladimirov -[#1029]: https://github.com/DataDog/dd-sdk-ios/issues/1029 [@arnauddorgans]: https://github.com/arnauddorgans [@ben-yolabs]: https://github.com/ben-yolabs [@earltedly]: https://github.com/earltedly diff --git a/Sources/Datadog/RUM/Instrumentation/Resources/URLSessionRUMResourcesHandler.swift b/Sources/Datadog/RUM/Instrumentation/Resources/URLSessionRUMResourcesHandler.swift index 943bdcd246..1f68db2cbe 100644 --- a/Sources/Datadog/RUM/Instrumentation/Resources/URLSessionRUMResourcesHandler.swift +++ b/Sources/Datadog/RUM/Instrumentation/Resources/URLSessionRUMResourcesHandler.swift @@ -14,7 +14,7 @@ internal class URLSessionRUMResourcesHandler: URLSessionInterceptionHandler, RUM let tracingSampler: Sampler /// Attributes-providing callback. /// It is configured by the user and should be used to associate additional RUM attributes with intercepted RUM Resource. - let rumAttributesProvider: (URLSessionRUMAttributesProvider)? + let rumAttributesProvider: URLSessionRUMAttributesProvider? // MARK: - Initialization