diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 5b013b97d6..0000000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.7.6 \ No newline at end of file diff --git a/BenchmarkTests/DataUpload/DataUploaderBenchmarkTests.swift b/BenchmarkTests/DataUpload/DataUploaderBenchmarkTests.swift index 70be721bc2..0671d36e4b 100644 --- a/BenchmarkTests/DataUpload/DataUploaderBenchmarkTests.swift +++ b/BenchmarkTests/DataUpload/DataUploaderBenchmarkTests.swift @@ -27,7 +27,7 @@ class DataUploaderBenchmarkTests: BenchmarkTests { /// `DataUploader` leaves no memory footprint (the memory peak after upload is less or equal `0kB`). func testUploadingDataToServer_leavesNoMemoryFootprint() throws { let dataUploader = DataUploader( - httpClient: HTTPClient(), + httpClient: URLSessionClient(), requestBuilder: FeatureRequestBuilderMock() ) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57348a9d54..5deb9ecce1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Unreleased +- [BUGFIX] Manual trace injection APIs are not available in DatadogTrace. See [#1415][]. +- [BUGFIX] Fix session replay uploads to AP1 site. See [#1418][]. +- [BUGFIX] Allow instantiating custom instance of the SDK after default one. See [#1413][]. +- [BUGFIX] Do not propagate attributes from Errors and LongTasks to Views. +- [IMPROVEMENT] Upgrade to PLCrashReporter 1.11.1. +- [FEATURE] Report session sample rate to the backend with RUM events. See [#1410][] +- [IMPROVEMENT] Expose Session Replay to Objective-C. see [#1419][] + # 2.0.0 / 31-07-2023 Release `2.0` introduces breaking changes. Follow the [Migration Guide](MIGRATION.md) to upgrade from `1.x` versions. @@ -488,6 +496,11 @@ Release `2.0` introduces breaking changes. Follow the [Migration Guide](MIGRATIO [#1331]: https://github.com/DataDog/dd-sdk-ios/pull/1331 [#1328]: https://github.com/DataDog/dd-sdk-ios/pull/1328 [#1355]: https://github.com/DataDog/dd-sdk-ios/pull/1355 +[#1410]: https://github.com/DataDog/dd-sdk-ios/pull/1410 +[#1413]: https://github.com/DataDog/dd-sdk-ios/pull/1413 +[#1415]: https://github.com/DataDog/dd-sdk-ios/pull/1415 +[#1418]: https://github.com/DataDog/dd-sdk-ios/pull/1418 +[#1419]: https://github.com/DataDog/dd-sdk-ios/pull/1419 [@00fa9a]: https://github.com/00FA9A [@britton-earnin]: https://github.com/Britton-Earnin [@hengyu]: https://github.com/Hengyu diff --git a/Cartfile b/Cartfile index db6a3815d4..0299765af6 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "microsoft/plcrashreporter" ~> 1.11.0 +github "microsoft/plcrashreporter" ~> 1.11.1 diff --git a/Cartfile.resolved b/Cartfile.resolved index aa7d854d9d..8e3025f36c 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1 +1 @@ -github "microsoft/plcrashreporter" "1.11.0" +github "microsoft/plcrashreporter" "1.11.1" diff --git a/Datadog/Datadog.xcodeproj/project.pbxproj b/Datadog/Datadog.xcodeproj/project.pbxproj index e2f866d277..0467eafa3e 100644 --- a/Datadog/Datadog.xcodeproj/project.pbxproj +++ b/Datadog/Datadog.xcodeproj/project.pbxproj @@ -180,12 +180,12 @@ 61133BD52423979B00786299 /* DataUploadConditions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133BAF2423979B00786299 /* DataUploadConditions.swift */; }; 61133BD62423979B00786299 /* DataUploader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133BB02423979B00786299 /* DataUploader.swift */; }; 61133BD72423979B00786299 /* DataUploadWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133BB12423979B00786299 /* DataUploadWorker.swift */; }; - 61133BD82423979B00786299 /* HTTPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133BB22423979B00786299 /* HTTPClient.swift */; }; + 61133BD82423979B00786299 /* URLSessionClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133BB22423979B00786299 /* URLSessionClient.swift */; }; 61133BD92423979B00786299 /* DataUploadDelay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133BB32423979B00786299 /* DataUploadDelay.swift */; }; 61133C00242397DA00786299 /* DatadogObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 61133BF2242397DA00786299 /* DatadogObjc.h */; settings = {ATTRIBUTES = (Public, ); }; }; 61133C0E2423983800786299 /* Datadog+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C092423983800786299 /* Datadog+objc.swift */; }; 61133C0F2423983800786299 /* ObjcIntercompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C0B2423983800786299 /* ObjcIntercompatibility.swift */; }; - 61133C102423983800786299 /* DDLogs+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C0C2423983800786299 /* DDLogs+objc.swift */; }; + 61133C102423983800786299 /* Logs+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C0C2423983800786299 /* Logs+objc.swift */; }; 61133C112423983800786299 /* DatadogConfiguration+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C0D2423983800786299 /* DatadogConfiguration+objc.swift */; }; 61133C482423990D00786299 /* DDDatadogTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C142423990D00786299 /* DDDatadogTests.swift */; }; 61133C4A2423990D00786299 /* DDConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C162423990D00786299 /* DDConfigurationTests.swift */; }; @@ -202,7 +202,7 @@ 61133C5E2423990D00786299 /* DataUploadDelayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C312423990D00786299 /* DataUploadDelayTests.swift */; }; 61133C5F2423990D00786299 /* DataUploaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C322423990D00786299 /* DataUploaderTests.swift */; }; 61133C602423990D00786299 /* RequestBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C332423990D00786299 /* RequestBuilderTests.swift */; }; - 61133C612423990D00786299 /* HTTPClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C342423990D00786299 /* HTTPClientTests.swift */; }; + 61133C612423990D00786299 /* URLSessionClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C342423990D00786299 /* URLSessionClientTests.swift */; }; 61133C642423990D00786299 /* LoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C382423990D00786299 /* LoggerTests.swift */; }; 61133C6A2423990D00786299 /* DatadogTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C412423990D00786299 /* DatadogTests.swift */; }; 61133C6B2423990D00786299 /* LogMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C432423990D00786299 /* LogMatcher.swift */; }; @@ -262,9 +262,7 @@ 6147989C2A459E2B0095CB02 /* DDTrace+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6147989B2A459E2B0095CB02 /* DDTrace+apiTests.m */; }; 6147989D2A459E2B0095CB02 /* DDTrace+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6147989B2A459E2B0095CB02 /* DDTrace+apiTests.m */; }; 6147989E2A45A42C0095CB02 /* DatadogTrace.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D25EE93429C4C3C300CE3839 /* DatadogTrace.framework */; }; - 6147989F2A45A4500095CB02 /* DatadogTrace.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D25EE93429C4C3C300CE3839 /* DatadogTrace.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 614798A02A45A46B0095CB02 /* DatadogTrace.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2C1A55A29C4F2DF00946C31 /* DatadogTrace.framework */; }; - 614798A12A45A4790095CB02 /* DatadogTrace.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D2C1A55A29C4F2DF00946C31 /* DatadogTrace.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 614798A22A45A48F0095CB02 /* DatadogTrace.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2C1A55A29C4F2DF00946C31 /* DatadogTrace.framework */; }; 614798A32A45A4980095CB02 /* DatadogTrace.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D25EE93429C4C3C300CE3839 /* DatadogTrace.framework */; }; 6147E3B3270486920092BC9F /* TraceConfigurationE2ETests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6147E3B2270486920092BC9F /* TraceConfigurationE2ETests.swift */; }; @@ -295,6 +293,13 @@ 617247B825DAB0E2007085B3 /* DDCrashReportBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617247B725DAB0E2007085B3 /* DDCrashReportBuilder.swift */; }; 6175922B2A6FA8EE0073F431 /* DatadogSessionReplay.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6133D1F52A6ED9E100384BEF /* DatadogSessionReplay.framework */; }; 6175922D2A6FADDD0073F431 /* DatadogSessionReplay.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6133D1F52A6ED9E100384BEF /* DatadogSessionReplay.framework */; }; + 617699182A860D9D0030022B /* HTTPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617699172A860D9D0030022B /* HTTPClient.swift */; }; + 617699192A860D9D0030022B /* HTTPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617699172A860D9D0030022B /* HTTPClient.swift */; }; + 6176991B2A86121B0030022B /* HTTPClientMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6176991A2A86121B0030022B /* HTTPClientMock.swift */; }; + 6176991C2A86121B0030022B /* HTTPClientMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6176991A2A86121B0030022B /* HTTPClientMock.swift */; }; + 6176991E2A8791880030022B /* Datadog+MultipleInstancesIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6176991D2A8791880030022B /* Datadog+MultipleInstancesIntegrationTests.swift */; }; + 6176991F2A8791880030022B /* Datadog+MultipleInstancesIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6176991D2A8791880030022B /* Datadog+MultipleInstancesIntegrationTests.swift */; }; + 617699212A8A7DF50030022B /* DebugManualTraceInjectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617699202A8A7DF50030022B /* DebugManualTraceInjectionViewController.swift */; }; 61776CED273BEA5500F93802 /* DebugRUMSessionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61776CEC273BEA5500F93802 /* DebugRUMSessionViewController.swift */; }; 61776D4E273E6D9F00F93802 /* SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61776D4D273E6D9F00F93802 /* SwiftUI.swift */; }; 6179FFD3254ADB1700556A0B /* ObjcAppLaunchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 6179FFD2254ADB1100556A0B /* ObjcAppLaunchHandler.m */; }; @@ -332,9 +337,7 @@ 61A2CC242A44454D0000FF25 /* DDRUMTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61A2CC232A44454D0000FF25 /* DDRUMTests.swift */; }; 61A2CC252A44454D0000FF25 /* DDRUMTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61A2CC232A44454D0000FF25 /* DDRUMTests.swift */; }; 61A2CC262A4449210000FF25 /* DatadogRUM.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D29A9F3429DD84AA005C54A4 /* DatadogRUM.framework */; }; - 61A2CC272A4449210000FF25 /* DatadogRUM.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D29A9F3429DD84AA005C54A4 /* DatadogRUM.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 61A2CC2B2A4449300000FF25 /* DatadogRUM.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D23F8E9929DDCD28001CFAE8 /* DatadogRUM.framework */; }; - 61A2CC2C2A4449300000FF25 /* DatadogRUM.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D23F8E9929DDCD28001CFAE8 /* DatadogRUM.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 61A2CC302A4449CB0000FF25 /* DatadogRUM.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D29A9F3429DD84AA005C54A4 /* DatadogRUM.framework */; }; 61A2CC312A4449D70000FF25 /* DatadogRUM.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D23F8E9929DDCD28001CFAE8 /* DatadogRUM.framework */; }; 61A2CC322A445D8A0000FF25 /* DatadogRUM.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D29A9F3429DD84AA005C54A4 /* DatadogRUM.framework */; }; @@ -451,10 +454,10 @@ A728ADAC2934EA2100397996 /* W3CHTTPHeadersWriter+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = A728ADAA2934EA2100397996 /* W3CHTTPHeadersWriter+objc.swift */; }; A728ADB02934EB0900397996 /* DDW3CHTTPHeadersWriter+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A728ADAD2934EB0300397996 /* DDW3CHTTPHeadersWriter+apiTests.m */; }; A728ADB12934EB0C00397996 /* DDW3CHTTPHeadersWriter+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A728ADAD2934EB0300397996 /* DDW3CHTTPHeadersWriter+apiTests.m */; }; - A79B0F64292BD074008742B3 /* DDOTelHTTPHeadersWriter+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F63292BD074008742B3 /* DDOTelHTTPHeadersWriter+apiTests.m */; }; - A79B0F65292BD074008742B3 /* DDOTelHTTPHeadersWriter+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F63292BD074008742B3 /* DDOTelHTTPHeadersWriter+apiTests.m */; }; - A79B0F66292BD7CA008742B3 /* OTelHTTPHeadersWriter+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F5E292BA435008742B3 /* OTelHTTPHeadersWriter+objc.swift */; }; - A79B0F67292BD7CC008742B3 /* OTelHTTPHeadersWriter+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F5E292BA435008742B3 /* OTelHTTPHeadersWriter+objc.swift */; }; + A79B0F64292BD074008742B3 /* DDB3HTTPHeadersWriter+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F63292BD074008742B3 /* DDB3HTTPHeadersWriter+apiTests.m */; }; + A79B0F65292BD074008742B3 /* DDB3HTTPHeadersWriter+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F63292BD074008742B3 /* DDB3HTTPHeadersWriter+apiTests.m */; }; + A79B0F66292BD7CA008742B3 /* B3HTTPHeadersWriter+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F5E292BA435008742B3 /* B3HTTPHeadersWriter+objc.swift */; }; + A79B0F67292BD7CC008742B3 /* B3HTTPHeadersWriter+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F5E292BA435008742B3 /* B3HTTPHeadersWriter+objc.swift */; }; D20605A3287464F40047275C /* ContextValuePublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20605A2287464F40047275C /* ContextValuePublisher.swift */; }; D20605A4287464F40047275C /* ContextValuePublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20605A2287464F40047275C /* ContextValuePublisher.swift */; }; D20605A6287476230047275C /* ServerOffsetPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20605A5287476230047275C /* ServerOffsetPublisher.swift */; }; @@ -472,9 +475,7 @@ D20605CA2875A83D0047275C /* ContextValueReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20605C62875A77D0047275C /* ContextValueReader.swift */; }; D20605CB2875A83F0047275C /* ContextValueReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20605C62875A77D0047275C /* ContextValueReader.swift */; }; D206BB852A41CA6800F43BA2 /* DatadogLogs.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D207317C29A5226A00ECBF94 /* DatadogLogs.framework */; }; - D206BB862A41CA6800F43BA2 /* DatadogLogs.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D207317C29A5226A00ECBF94 /* DatadogLogs.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D206BB8A2A41CA7000F43BA2 /* DatadogLogs.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D20731B429A5279D00ECBF94 /* DatadogLogs.framework */; }; - D206BB8B2A41CA7000F43BA2 /* DatadogLogs.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D20731B429A5279D00ECBF94 /* DatadogLogs.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D207318429A5226B00ECBF94 /* DatadogLogs.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D207317C29A5226A00ECBF94 /* DatadogLogs.framework */; platformFilter = ios; }; D207319529A522F600ECBF94 /* LogsFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 616F1FAF283E227100651A3A /* LogsFeature.swift */; }; D207319629A522F600ECBF94 /* ConsoleLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6194E4BB2878AF7600EB6307 /* ConsoleLogger.swift */; }; @@ -995,6 +996,10 @@ D2A1EE3F2885D7EC00D28DFB /* LaunchTimePublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A1EE3D2885D7EC00D28DFB /* LaunchTimePublisherTests.swift */; }; D2A1EE442886B8B400D28DFB /* UserInfoPublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A1EE432886B8B400D28DFB /* UserInfoPublisherTests.swift */; }; D2A1EE452886B8B400D28DFB /* UserInfoPublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A1EE432886B8B400D28DFB /* UserInfoPublisherTests.swift */; }; + D2A434A22A8E3F900028E329 /* DatadogSessionReplay.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6133D1F52A6ED9E100384BEF /* DatadogSessionReplay.framework */; }; + D2A434AA2A8E40A20028E329 /* SessionReplay+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A434A82A8E402B0028E329 /* SessionReplay+objc.swift */; }; + D2A434AC2A8E416F0028E329 /* DDSessionReplay+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D2A434AB2A8E416F0028E329 /* DDSessionReplay+apiTests.m */; }; + D2A434AE2A8E426C0028E329 /* DDSessionReplayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A434AD2A8E426C0028E329 /* DDSessionReplayTests.swift */; }; D2A783D429A5309F003B03BB /* SwiftExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133BBA2423979B00786299 /* SwiftExtensions.swift */; }; D2A783D529A530A0003B03BB /* SwiftExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133BBA2423979B00786299 /* SwiftExtensions.swift */; }; D2A783D929A530EF003B03BB /* SwiftExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E36D92124373EA700BFBDB7 /* SwiftExtensionsTests.swift */; }; @@ -1136,7 +1141,7 @@ D2CB6E9A27C50EAE00A62B57 /* KronosTimeStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CA277B23F0008BE766 /* KronosTimeStorage.swift */; }; D2CB6E9B27C50EAE00A62B57 /* FilesOrchestrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133BA92423979B00786299 /* FilesOrchestrator.swift */; }; D2CB6EA727C50EAE00A62B57 /* Versioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1D5AEA624B4D45A007F194B /* Versioning.swift */; }; - D2CB6EA827C50EAE00A62B57 /* HTTPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133BB22423979B00786299 /* HTTPClient.swift */; }; + D2CB6EA827C50EAE00A62B57 /* URLSessionClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133BB22423979B00786299 /* URLSessionClient.swift */; }; D2CB6EAF27C50EAE00A62B57 /* BundleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 614E9EB2244719FA007EE3E1 /* BundleType.swift */; }; D2CB6EB327C50EAE00A62B57 /* KronosNTPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D3E0CE277B23F0008BE766 /* KronosNTPClient.swift */; }; D2CB6EBA27C50EAE00A62B57 /* DataUploadConditions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133BAF2423979B00786299 /* DataUploadConditions.swift */; }; @@ -1169,7 +1174,7 @@ D2CB6F1A27C520D400A62B57 /* FileReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C282423990D00786299 /* FileReaderTests.swift */; }; D2CB6F1D27C520D400A62B57 /* DataUploaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C322423990D00786299 /* DataUploaderTests.swift */; }; D2CB6F2027C520D400A62B57 /* DatadogConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61BBD19624ED50040023E65F /* DatadogConfigurationTests.swift */; }; - D2CB6F2127C520D400A62B57 /* HTTPClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C342423990D00786299 /* HTTPClientTests.swift */; }; + D2CB6F2127C520D400A62B57 /* URLSessionClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C342423990D00786299 /* URLSessionClientTests.swift */; }; D2CB6F2227C520D400A62B57 /* DatadogTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C412423990D00786299 /* DatadogTests.swift */; }; D2CB6F2627C520D400A62B57 /* DataUploadDelayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C312423990D00786299 /* DataUploadDelayTests.swift */; }; D2CB6F2827C520D400A62B57 /* DataUploadWorkerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C2F2423990D00786299 /* DataUploadWorkerTests.swift */; }; @@ -1210,7 +1215,7 @@ D2CB6F9B27C5217A00A62B57 /* DDSpanContext+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6132BF4824A49B6800D7BD17 /* DDSpanContext+objc.swift */; }; D2CB6F9C27C5217A00A62B57 /* OTTracer+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6132BF4124A38D2400D7BD17 /* OTTracer+objc.swift */; }; D2CB6F9E27C5217A00A62B57 /* Datadog+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C092423983800786299 /* Datadog+objc.swift */; }; - D2CB6F9F27C5217A00A62B57 /* DDLogs+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C0C2423983800786299 /* DDLogs+objc.swift */; }; + D2CB6F9F27C5217A00A62B57 /* Logs+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C0C2423983800786299 /* Logs+objc.swift */; }; D2CB6FA027C5217A00A62B57 /* Trace+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 615A4A8224A3431600233986 /* Trace+objc.swift */; }; D2CB6FA127C5217A00A62B57 /* HTTPHeadersWriter+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6132BF4B24A49C8F00D7BD17 /* HTTPHeadersWriter+objc.swift */; }; D2CB6FA227C5217A00A62B57 /* DDSpan+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6132BF4624A498D800D7BD17 /* DDSpan+objc.swift */; }; @@ -1328,11 +1333,11 @@ D2EBEE2029BA160F00B15732 /* TracePropagationHeadersWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EBEDCF29B8A02100B15732 /* TracePropagationHeadersWriter.swift */; }; D2EBEE2129BA160F00B15732 /* W3CHTTPHeaders.swift in Sources */ = {isa = PBXBuildFile; fileRef = A728AD9C2934CE4400397996 /* W3CHTTPHeaders.swift */; }; D2EBEE2229BA160F00B15732 /* TracePropagationHeadersReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EBEDD229B8A58E00B15732 /* TracePropagationHeadersReader.swift */; }; - D2EBEE2329BA160F00B15732 /* OTelHTTPHeadersReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F773DC29253F8B00AC1A62 /* OTelHTTPHeadersReader.swift */; }; + D2EBEE2329BA160F00B15732 /* B3HTTPHeadersReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F773DC29253F8B00AC1A62 /* B3HTTPHeadersReader.swift */; }; D2EBEE2429BA160F00B15732 /* W3CHTTPHeadersReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = A728ADA02934CE5D00397996 /* W3CHTTPHeadersReader.swift */; }; D2EBEE2529BA160F00B15732 /* TraceID.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EBEDCC29B893D800B15732 /* TraceID.swift */; }; - D2EBEE2629BA160F00B15732 /* OTelHTTPHeaders.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F773D32924EA2D00AC1A62 /* OTelHTTPHeaders.swift */; }; - D2EBEE2729BA160F00B15732 /* OTelHTTPHeadersWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F773DB29253F8B00AC1A62 /* OTelHTTPHeadersWriter.swift */; }; + D2EBEE2629BA160F00B15732 /* B3HTTPHeaders.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F773D32924EA2D00AC1A62 /* B3HTTPHeaders.swift */; }; + D2EBEE2729BA160F00B15732 /* B3HTTPHeadersWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F773DB29253F8B00AC1A62 /* B3HTTPHeadersWriter.swift */; }; D2EBEE2829BA160F00B15732 /* W3CHTTPHeadersWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A728AD9E2934CE5000397996 /* W3CHTTPHeadersWriter.swift */; }; D2EBEE2929BA160F00B15732 /* HTTPHeadersWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C5A88324509A0C00DA608C /* HTTPHeadersWriter.swift */; }; D2EBEE2A29BA160F00B15732 /* TracingHTTPHeaders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 618E13B02524B8F80098C6B0 /* TracingHTTPHeaders.swift */; }; @@ -1340,20 +1345,20 @@ D2EBEE2E29BA161100B15732 /* TracePropagationHeadersWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EBEDCF29B8A02100B15732 /* TracePropagationHeadersWriter.swift */; }; D2EBEE2F29BA161100B15732 /* W3CHTTPHeaders.swift in Sources */ = {isa = PBXBuildFile; fileRef = A728AD9C2934CE4400397996 /* W3CHTTPHeaders.swift */; }; D2EBEE3029BA161100B15732 /* TracePropagationHeadersReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EBEDD229B8A58E00B15732 /* TracePropagationHeadersReader.swift */; }; - D2EBEE3129BA161100B15732 /* OTelHTTPHeadersReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F773DC29253F8B00AC1A62 /* OTelHTTPHeadersReader.swift */; }; + D2EBEE3129BA161100B15732 /* B3HTTPHeadersReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F773DC29253F8B00AC1A62 /* B3HTTPHeadersReader.swift */; }; D2EBEE3229BA161100B15732 /* W3CHTTPHeadersReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = A728ADA02934CE5D00397996 /* W3CHTTPHeadersReader.swift */; }; D2EBEE3329BA161100B15732 /* TraceID.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EBEDCC29B893D800B15732 /* TraceID.swift */; }; - D2EBEE3429BA161100B15732 /* OTelHTTPHeaders.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F773D32924EA2D00AC1A62 /* OTelHTTPHeaders.swift */; }; - D2EBEE3529BA161100B15732 /* OTelHTTPHeadersWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F773DB29253F8B00AC1A62 /* OTelHTTPHeadersWriter.swift */; }; + D2EBEE3429BA161100B15732 /* B3HTTPHeaders.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F773D32924EA2D00AC1A62 /* B3HTTPHeaders.swift */; }; + D2EBEE3529BA161100B15732 /* B3HTTPHeadersWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F773DB29253F8B00AC1A62 /* B3HTTPHeadersWriter.swift */; }; D2EBEE3629BA161100B15732 /* W3CHTTPHeadersWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A728AD9E2934CE5000397996 /* W3CHTTPHeadersWriter.swift */; }; D2EBEE3729BA161100B15732 /* HTTPHeadersWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C5A88324509A0C00DA608C /* HTTPHeadersWriter.swift */; }; D2EBEE3829BA161100B15732 /* TracingHTTPHeaders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 618E13B02524B8F80098C6B0 /* TracingHTTPHeaders.swift */; }; - D2EBEE3B29BA163E00B15732 /* OTelHTTPHeadersReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F60292BB071008742B3 /* OTelHTTPHeadersReaderTests.swift */; }; - D2EBEE3C29BA163E00B15732 /* OTelHTTPHeadersWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F5A292B7C06008742B3 /* OTelHTTPHeadersWriterTests.swift */; }; + D2EBEE3B29BA163E00B15732 /* B3HTTPHeadersReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F60292BB071008742B3 /* B3HTTPHeadersReaderTests.swift */; }; + D2EBEE3C29BA163E00B15732 /* B3HTTPHeadersWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F5A292B7C06008742B3 /* B3HTTPHeadersWriterTests.swift */; }; D2EBEE3D29BA163E00B15732 /* W3CHTTPHeadersWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A728ADA22934DB5000397996 /* W3CHTTPHeadersWriterTests.swift */; }; D2EBEE3E29BA163E00B15732 /* W3CHTTPHeadersReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A728ADA52934DF2400397996 /* W3CHTTPHeadersReaderTests.swift */; }; - D2EBEE3F29BA163F00B15732 /* OTelHTTPHeadersReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F60292BB071008742B3 /* OTelHTTPHeadersReaderTests.swift */; }; - D2EBEE4029BA163F00B15732 /* OTelHTTPHeadersWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F5A292B7C06008742B3 /* OTelHTTPHeadersWriterTests.swift */; }; + D2EBEE3F29BA163F00B15732 /* B3HTTPHeadersReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F60292BB071008742B3 /* B3HTTPHeadersReaderTests.swift */; }; + D2EBEE4029BA163F00B15732 /* B3HTTPHeadersWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F5A292B7C06008742B3 /* B3HTTPHeadersWriterTests.swift */; }; D2EBEE4129BA163F00B15732 /* W3CHTTPHeadersWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A728ADA22934DB5000397996 /* W3CHTTPHeadersWriterTests.swift */; }; D2EBEE4229BA163F00B15732 /* W3CHTTPHeadersReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A728ADA52934DF2400397996 /* W3CHTTPHeadersReaderTests.swift */; }; D2EBEE4329BA168200B15732 /* TraceIDGeneratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B558D32469CDD8001460D3 /* TraceIDGeneratorTests.swift */; }; @@ -1368,8 +1373,8 @@ D2EFA876286E011900F1FAA6 /* DatadogContextProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EFA874286E011900F1FAA6 /* DatadogContextProviderTests.swift */; }; D2F44FB8299AA1DA0074B0D9 /* DataCompressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213532F270CA722000315AD /* DataCompressionTests.swift */; }; D2F44FB9299AA1DB0074B0D9 /* DataCompressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213532F270CA722000315AD /* DataCompressionTests.swift */; }; - D2F44FBC299AA36D0074B0D9 /* zlib.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2F44FBB299AA36D0074B0D9 /* zlib.swift */; }; - D2F44FBD299AA36D0074B0D9 /* zlib.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2F44FBB299AA36D0074B0D9 /* zlib.swift */; }; + D2F44FBC299AA36D0074B0D9 /* Decompression.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2F44FBB299AA36D0074B0D9 /* Decompression.swift */; }; + D2F44FBD299AA36D0074B0D9 /* Decompression.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2F44FBB299AA36D0074B0D9 /* Decompression.swift */; }; D2F44FC2299BD5600074B0D9 /* UIViewController+KeyboardControlling.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2F44FC1299BD5600074B0D9 /* UIViewController+KeyboardControlling.swift */; }; D2F44FC3299BD5600074B0D9 /* UIViewController+KeyboardControlling.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2F44FC1299BD5600074B0D9 /* UIViewController+KeyboardControlling.swift */; }; D2F8235329915E12003C7E99 /* DatadogSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2F8235229915E12003C7E99 /* DatadogSite.swift */; }; @@ -1681,6 +1686,13 @@ remoteGlobalIDString = D23039A4298D513C001A1FA3; remoteInfo = "DatadogInternal iOS"; }; + D2A434A42A8E3F900028E329 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 61133B79242393DE00786299 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 6133D1E52A6ED9E100384BEF; + remoteInfo = "DatadogSessionReplay iOS"; + }; D2A783E229A53414003B03BB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 61133B79242393DE00786299 /* Project object */; @@ -1745,32 +1757,6 @@ name = "⚙️ Embed Framework Dependencies"; runOnlyForDeploymentPostprocessing = 0; }; - 61A2CC2A2A4449210000FF25 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 6147989F2A45A4500095CB02 /* DatadogTrace.framework in Embed Frameworks */, - 61A2CC272A4449210000FF25 /* DatadogRUM.framework in Embed Frameworks */, - D206BB862A41CA6800F43BA2 /* DatadogLogs.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; - 61A2CC2F2A4449300000FF25 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 614798A12A45A4790095CB02 /* DatadogTrace.framework in Embed Frameworks */, - 61A2CC2C2A4449300000FF25 /* DatadogRUM.framework in Embed Frameworks */, - D206BB8B2A41CA7000F43BA2 /* DatadogLogs.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; D240684527CE6C9E00C04F44 /* ⚙️ Embed Framework Dependencies */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -1994,7 +1980,7 @@ 61133BAF2423979B00786299 /* DataUploadConditions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataUploadConditions.swift; sourceTree = ""; }; 61133BB02423979B00786299 /* DataUploader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataUploader.swift; sourceTree = ""; }; 61133BB12423979B00786299 /* DataUploadWorker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataUploadWorker.swift; sourceTree = ""; }; - 61133BB22423979B00786299 /* HTTPClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPClient.swift; sourceTree = ""; }; + 61133BB22423979B00786299 /* URLSessionClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionClient.swift; sourceTree = ""; }; 61133BB32423979B00786299 /* DataUploadDelay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataUploadDelay.swift; sourceTree = ""; }; 61133BBA2423979B00786299 /* SwiftExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftExtensions.swift; sourceTree = ""; }; 61133BC22423979B00786299 /* LogEventEncoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogEventEncoder.swift; sourceTree = ""; }; @@ -2007,7 +1993,7 @@ 61133BF3242397DA00786299 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 61133C092423983800786299 /* Datadog+objc.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Datadog+objc.swift"; sourceTree = ""; }; 61133C0B2423983800786299 /* ObjcIntercompatibility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjcIntercompatibility.swift; sourceTree = ""; }; - 61133C0C2423983800786299 /* DDLogs+objc.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DDLogs+objc.swift"; sourceTree = ""; }; + 61133C0C2423983800786299 /* Logs+objc.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Logs+objc.swift"; sourceTree = ""; }; 61133C0D2423983800786299 /* DatadogConfiguration+objc.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DatadogConfiguration+objc.swift"; sourceTree = ""; }; 61133C142423990D00786299 /* DDDatadogTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DDDatadogTests.swift; sourceTree = ""; }; 61133C162423990D00786299 /* DDConfigurationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DDConfigurationTests.swift; sourceTree = ""; }; @@ -2024,7 +2010,7 @@ 61133C312423990D00786299 /* DataUploadDelayTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataUploadDelayTests.swift; sourceTree = ""; }; 61133C322423990D00786299 /* DataUploaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataUploaderTests.swift; sourceTree = ""; }; 61133C332423990D00786299 /* RequestBuilderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestBuilderTests.swift; sourceTree = ""; }; - 61133C342423990D00786299 /* HTTPClientTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPClientTests.swift; sourceTree = ""; }; + 61133C342423990D00786299 /* URLSessionClientTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionClientTests.swift; sourceTree = ""; }; 61133C382423990D00786299 /* LoggerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggerTests.swift; sourceTree = ""; }; 61133C3B2423990D00786299 /* LogEventBuilderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogEventBuilderTests.swift; sourceTree = ""; }; 61133C3C2423990D00786299 /* LogSanitizerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogSanitizerTests.swift; sourceTree = ""; }; @@ -2145,6 +2131,10 @@ 617247AD25DA9BEA007085B3 /* CrashReportingObjcHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CrashReportingObjcHelpers.h; sourceTree = ""; }; 617247AE25DA9BEA007085B3 /* CrashReportingObjcHelpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CrashReportingObjcHelpers.m; sourceTree = ""; }; 617247B725DAB0E2007085B3 /* DDCrashReportBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DDCrashReportBuilder.swift; sourceTree = ""; }; + 617699172A860D9D0030022B /* HTTPClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPClient.swift; sourceTree = ""; }; + 6176991A2A86121B0030022B /* HTTPClientMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPClientMock.swift; sourceTree = ""; }; + 6176991D2A8791880030022B /* Datadog+MultipleInstancesIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Datadog+MultipleInstancesIntegrationTests.swift"; sourceTree = ""; }; + 617699202A8A7DF50030022B /* DebugManualTraceInjectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugManualTraceInjectionViewController.swift; sourceTree = ""; }; 61776CEC273BEA5500F93802 /* DebugRUMSessionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugRUMSessionViewController.swift; sourceTree = ""; }; 61776D4D273E6D9F00F93802 /* SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUI.swift; sourceTree = ""; }; 61786F7624FCDE04009E6BAB /* RUMDebuggingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMDebuggingTests.swift; sourceTree = ""; }; @@ -2358,13 +2348,13 @@ A728ADA52934DF2400397996 /* W3CHTTPHeadersReaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = W3CHTTPHeadersReaderTests.swift; sourceTree = ""; }; A728ADAA2934EA2100397996 /* W3CHTTPHeadersWriter+objc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "W3CHTTPHeadersWriter+objc.swift"; sourceTree = ""; }; A728ADAD2934EB0300397996 /* DDW3CHTTPHeadersWriter+apiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "DDW3CHTTPHeadersWriter+apiTests.m"; sourceTree = ""; }; - A79B0F5A292B7C06008742B3 /* OTelHTTPHeadersWriterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OTelHTTPHeadersWriterTests.swift; sourceTree = ""; }; - A79B0F5E292BA435008742B3 /* OTelHTTPHeadersWriter+objc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OTelHTTPHeadersWriter+objc.swift"; sourceTree = ""; }; - A79B0F60292BB071008742B3 /* OTelHTTPHeadersReaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OTelHTTPHeadersReaderTests.swift; sourceTree = ""; }; - A79B0F63292BD074008742B3 /* DDOTelHTTPHeadersWriter+apiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "DDOTelHTTPHeadersWriter+apiTests.m"; sourceTree = ""; }; - A7F773D32924EA2D00AC1A62 /* OTelHTTPHeaders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OTelHTTPHeaders.swift; sourceTree = ""; }; - A7F773DB29253F8B00AC1A62 /* OTelHTTPHeadersWriter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OTelHTTPHeadersWriter.swift; sourceTree = ""; }; - A7F773DC29253F8B00AC1A62 /* OTelHTTPHeadersReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OTelHTTPHeadersReader.swift; sourceTree = ""; }; + A79B0F5A292B7C06008742B3 /* B3HTTPHeadersWriterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = B3HTTPHeadersWriterTests.swift; sourceTree = ""; }; + A79B0F5E292BA435008742B3 /* B3HTTPHeadersWriter+objc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "B3HTTPHeadersWriter+objc.swift"; sourceTree = ""; }; + A79B0F60292BB071008742B3 /* B3HTTPHeadersReaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = B3HTTPHeadersReaderTests.swift; sourceTree = ""; }; + A79B0F63292BD074008742B3 /* DDB3HTTPHeadersWriter+apiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "DDB3HTTPHeadersWriter+apiTests.m"; sourceTree = ""; }; + A7F773D32924EA2D00AC1A62 /* B3HTTPHeaders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = B3HTTPHeaders.swift; sourceTree = ""; }; + A7F773DB29253F8B00AC1A62 /* B3HTTPHeadersWriter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = B3HTTPHeadersWriter.swift; sourceTree = ""; }; + A7F773DC29253F8B00AC1A62 /* B3HTTPHeadersReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = B3HTTPHeadersReader.swift; sourceTree = ""; }; B3BBBCB0265E71C600943419 /* VitalMemoryReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VitalMemoryReader.swift; sourceTree = ""; }; B3BBBCBB265E71D100943419 /* VitalMemoryReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VitalMemoryReaderTests.swift; sourceTree = ""; }; B3FC3C0626526EFF00DEED9E /* VitalInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VitalInfo.swift; sourceTree = ""; }; @@ -2546,6 +2536,9 @@ D2A1EE3D2885D7EC00D28DFB /* LaunchTimePublisherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchTimePublisherTests.swift; sourceTree = ""; }; D2A1EE432886B8B400D28DFB /* UserInfoPublisherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInfoPublisherTests.swift; sourceTree = ""; }; D2A38DDA29C37E1B007C6900 /* TracingURLSessionHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracingURLSessionHandlerTests.swift; sourceTree = ""; }; + D2A434A82A8E402B0028E329 /* SessionReplay+objc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionReplay+objc.swift"; sourceTree = ""; }; + D2A434AB2A8E416F0028E329 /* DDSessionReplay+apiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "DDSessionReplay+apiTests.m"; sourceTree = ""; }; + D2A434AD2A8E426C0028E329 /* DDSessionReplayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DDSessionReplayTests.swift; sourceTree = ""; }; D2A7840129A534F9003B03BB /* DatadogLogsTests tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "DatadogLogsTests tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; D2A7840229A536AD003B03BB /* PrintFunctionMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrintFunctionMock.swift; sourceTree = ""; }; D2B249932A4598FE00DD4F9F /* LoggerProtocol+Internal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LoggerProtocol+Internal.swift"; sourceTree = ""; }; @@ -2597,7 +2590,7 @@ D2EFA874286E011900F1FAA6 /* DatadogContextProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatadogContextProviderTests.swift; sourceTree = ""; }; D2EFF3D22731822A00D09F33 /* RUMViewsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMViewsHandler.swift; sourceTree = ""; }; D2F1B81426D8E5FF009F3293 /* DDNoopTracerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DDNoopTracerTests.swift; sourceTree = ""; }; - D2F44FBB299AA36D0074B0D9 /* zlib.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = zlib.swift; sourceTree = ""; }; + D2F44FBB299AA36D0074B0D9 /* Decompression.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Decompression.swift; sourceTree = ""; }; D2F44FC1299BD5600074B0D9 /* UIViewController+KeyboardControlling.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+KeyboardControlling.swift"; sourceTree = ""; }; D2F8235229915E12003C7E99 /* DatadogSite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatadogSite.swift; sourceTree = ""; }; D2FB1253292E0E92005B13F8 /* TrackingConsentPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackingConsentPublisher.swift; sourceTree = ""; }; @@ -2656,6 +2649,7 @@ 6147989E2A45A42C0095CB02 /* DatadogTrace.framework in Frameworks */, 61A2CC262A4449210000FF25 /* DatadogRUM.framework in Frameworks */, D206BB852A41CA6800F43BA2 /* DatadogLogs.framework in Frameworks */, + D2A434A22A8E3F900028E329 /* DatadogSessionReplay.framework in Frameworks */, 61133C702423993200786299 /* DatadogCore.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3023,7 +3017,6 @@ isa = PBXGroup; children = ( 61054E3B2A6EE10A00AAA894 /* Feature */, - 61054E0A2A6EE10A00AAA894 /* Metrics */, 61054E482A6EE10A00AAA894 /* Processor */, 61054E0D2A6EE10A00AAA894 /* Recorder */, 61054E0C2A6EE10A00AAA894 /* SessionReplay.swift */, @@ -3042,7 +3035,6 @@ 61054F922A6EE1BA00AAA894 /* Helpers */, 61054F7D2A6EE1BA00AAA894 /* Mocks */, 61054F4E2A6EE1BA00AAA894 /* Processor */, - 61054F942A6EE1BA00AAA894 /* Public */, 61054F592A6EE1BA00AAA894 /* Recorder */, 61054F3D2A6EE1B900AAA894 /* SessionReplayConfigurationTests.swift */, 61054F482A6EE1B900AAA894 /* SessionReplayTests.swift */, @@ -3073,13 +3065,6 @@ path = Models; sourceTree = ""; }; - 61054E0A2A6EE10A00AAA894 /* Metrics */ = { - isa = PBXGroup; - children = ( - ); - path = Metrics; - sourceTree = ""; - }; 61054E0D2A6EE10A00AAA894 /* Recorder */ = { isa = PBXGroup; children = ( @@ -3521,13 +3506,6 @@ path = Helpers; sourceTree = ""; }; - 61054F942A6EE1BA00AAA894 /* Public */ = { - isa = PBXGroup; - children = ( - ); - path = Public; - sourceTree = ""; - }; 610ABD492A69309900AFEA34 /* IntegrationUnitTests */ = { isa = PBXGroup; children = ( @@ -3540,6 +3518,7 @@ 610ABD4A2A6930AB00AFEA34 /* Public */ = { isa = PBXGroup; children = ( + 6176991D2A8791880030022B /* Datadog+MultipleInstancesIntegrationTests.swift */, 610ABD4B2A6930CA00AFEA34 /* TelemetryCoreIntegrationTests.swift */, ); path = Public; @@ -3557,6 +3536,7 @@ 6111C58025C0080C00F5C4A2 /* RUM */ = { isa = PBXGroup; children = ( + 9E55407B25812D1C00F6E3AD /* RUM+objc.swift */, 6111C58125C0081F00F5C4A2 /* RUMDataModels+objc.swift */, ); path = RUM; @@ -3768,13 +3748,12 @@ children = ( 61133C092423983800786299 /* Datadog+objc.swift */, 61133C0D2423983800786299 /* DatadogConfiguration+objc.swift */, - 61133C0C2423983800786299 /* DDLogs+objc.swift */, - 615A4A8224A3431600233986 /* Trace+objc.swift */, 611720D42524D9FB00634D9E /* DDURLSessionDelegate+objc.swift */, - 9E55407B25812D1C00F6E3AD /* RUM+objc.swift */, + D2A434A62A8E3FEA0028E329 /* Logs */, 6132BF4524A498B400D7BD17 /* Tracing */, 6111C58025C0080C00F5C4A2 /* RUM */, 6132BF4024A38D0600D7BD17 /* OpenTracing */, + D2A434A72A8E3FFB0028E329 /* SessionReplay */, 61133C0A2423983800786299 /* ObjcIntercompatibility */, ); name = DatadogObjc; @@ -3817,6 +3796,7 @@ 61A2CC202A443D330000FF25 /* DDRUMConfigurationTests.swift */, 61A2CC232A44454D0000FF25 /* DDRUMTests.swift */, 9EE5AD8126205B82001E699E /* DDNSURLSessionDelegateTests.swift */, + D2A434AD2A8E426C0028E329 /* DDSessionReplayTests.swift */, 61D03BDE273404BB00367DE0 /* RUM */, ); path = DatadogObjc; @@ -3851,6 +3831,7 @@ D20605B4287572270047275C /* DatadogCore */, D26F59312851E30B0097C455 /* DatadogInternal */, 61F1A6192498A51700075390 /* CoreMocks.swift */, + 6176991A2A86121B0030022B /* HTTPClientMock.swift */, D24C9C7029A7D57A002057CF /* DirectoriesMock.swift */, D24C9C4C29A7B9CA002057CF /* LogsMocks.swift */, D25CFA9E29C85FA400E3A43D /* TracingFeatureMocks.swift */, @@ -3911,7 +3892,7 @@ 61133C312423990D00786299 /* DataUploadDelayTests.swift */, 61DA20EF26C40121004AFE6D /* DataUploadStatusTests.swift */, 61133C322423990D00786299 /* DataUploaderTests.swift */, - 61133C342423990D00786299 /* HTTPClientTests.swift */, + 61133C342423990D00786299 /* URLSessionClientTests.swift */, 61133C332423990D00786299 /* RequestBuilderTests.swift */, ); path = Upload; @@ -4021,6 +4002,7 @@ 6132BF4524A498B400D7BD17 /* Tracing */ = { isa = PBXGroup; children = ( + 615A4A8224A3431600233986 /* Trace+objc.swift */, 6132BF4624A498D800D7BD17 /* DDSpan+objc.swift */, 6132BF4824A49B6800D7BD17 /* DDSpanContext+objc.swift */, 6132BF4A24A49C7200D7BD17 /* Propagation */, @@ -4033,7 +4015,7 @@ isa = PBXGroup; children = ( 6132BF4B24A49C8F00D7BD17 /* HTTPHeadersWriter+objc.swift */, - A79B0F5E292BA435008742B3 /* OTelHTTPHeadersWriter+objc.swift */, + A79B0F5E292BA435008742B3 /* B3HTTPHeadersWriter+objc.swift */, A728ADAA2934EA2100397996 /* W3CHTTPHeadersWriter+objc.swift */, ); path = Propagation; @@ -4203,6 +4185,7 @@ children = ( 61441C942461A649003D8BB8 /* DebugLoggingViewController.swift */, 61441C932461A649003D8BB8 /* DebugTracingViewController.swift */, + 617699202A8A7DF50030022B /* DebugManualTraceInjectionViewController.swift */, 61E5333724B84EE2003D6C4E /* DebugRUMViewController.swift */, 61F74AF326F20E4600E5F5ED /* DebugCrashReportingWithRUMViewController.swift */, 618236882710560900125326 /* DebugWebviewViewController.swift */, @@ -4360,6 +4343,25 @@ path = ../DatadogCrashReporting/Tests; sourceTree = ""; }; + 617699162A8608C20030022B /* Context */ = { + isa = PBXGroup; + children = ( + 614B78EC296D7B63009C6B92 /* LowPowerModePublisherTests.swift */, + D2EFA874286E011900F1FAA6 /* DatadogContextProviderTests.swift */, + D29294E2291D652900F8EFF9 /* ApplicationVersionPublisherTests.swift */, + D2A1EE34287EB8DB00D28DFB /* ServerOffsetPublisherTests.swift */, + D2A1EE3A287EECA800D28DFB /* CarrierInfoPublisherTests.swift */, + D2FB1256292E0F0B005B13F8 /* TrackingConsentPublisherTests.swift */, + D2A1EE37287EBE4200D28DFB /* NetworkConnectionInfoPublisherTests.swift */, + D2A1EE3D2885D7EC00D28DFB /* LaunchTimePublisherTests.swift */, + D2A1EE432886B8B400D28DFB /* UserInfoPublisherTests.swift */, + D26C49AE2886DC7B00802B2D /* ApplicationStatePublisherTests.swift */, + D2C7E3AA28F97DCF0023B2CC /* BatteryStatusPublisherTests.swift */, + D234613028B7712F00055D4C /* FeatureContextTests.swift */, + ); + path = Context; + sourceTree = ""; + }; 61776D4C273E6D8100F93802 /* Helpers */ = { isa = PBXGroup; children = ( @@ -4560,8 +4562,9 @@ 6147989B2A459E2B0095CB02 /* DDTrace+apiTests.m */, 61B5E42A26DFC433000B0A5F /* DDNSURLSessionDelegate+apiTests.m */, D2B3F051282E826A00C2B5EE /* DDHTTPHeadersWriter+apiTests.m */, - A79B0F63292BD074008742B3 /* DDOTelHTTPHeadersWriter+apiTests.m */, + A79B0F63292BD074008742B3 /* DDB3HTTPHeadersWriter+apiTests.m */, A728ADAD2934EB0300397996 /* DDW3CHTTPHeadersWriter+apiTests.m */, + D2A434AB2A8E416F0028E329 /* DDSessionReplay+apiTests.m */, ); path = ObjcAPITests; sourceTree = ""; @@ -4941,24 +4944,24 @@ path = W3C; sourceTree = ""; }; - A7F773D929253F5900AC1A62 /* OpenTracing */ = { + A7F773D929253F5900AC1A62 /* Datadog */ = { isa = PBXGroup; children = ( 618E13B02524B8F80098C6B0 /* TracingHTTPHeaders.swift */, 61C5A88324509A0C00DA608C /* HTTPHeadersWriter.swift */, 618E13A92524B8700098C6B0 /* HTTPHeadersReader.swift */, ); - path = OpenTracing; + path = Datadog; sourceTree = ""; }; - A7F773DA29253F6200AC1A62 /* OpenTelemetry */ = { + A7F773DA29253F6200AC1A62 /* B3 */ = { isa = PBXGroup; children = ( - A7F773D32924EA2D00AC1A62 /* OTelHTTPHeaders.swift */, - A7F773DB29253F8B00AC1A62 /* OTelHTTPHeadersWriter.swift */, - A7F773DC29253F8B00AC1A62 /* OTelHTTPHeadersReader.swift */, + A7F773D32924EA2D00AC1A62 /* B3HTTPHeaders.swift */, + A7F773DB29253F8B00AC1A62 /* B3HTTPHeadersWriter.swift */, + A7F773DC29253F8B00AC1A62 /* B3HTTPHeadersReader.swift */, ); - path = OpenTelemetry; + path = B3; sourceTree = ""; }; B3FC3C0426526EE900DEED9E /* RUMVitals */ = { @@ -5323,7 +5326,7 @@ D257954F298ABB04008A1BE5 /* DDAssert.swift */, D2579550298ABB04008A1BE5 /* SwiftExtensions.swift */, D2579551298ABB04008A1BE5 /* XCTestCase.swift */, - D2F44FBB299AA36D0074B0D9 /* zlib.swift */, + D2F44FBB299AA36D0074B0D9 /* Decompression.swift */, 61133C462423990D00786299 /* TestsDirectory.swift */, 61C713D22A3DFB4900FA735A /* FuzzyHelpers.swift */, ); @@ -5399,7 +5402,8 @@ 61133BB12423979B00786299 /* DataUploadWorker.swift */, 61133BB02423979B00786299 /* DataUploader.swift */, 61133BAF2423979B00786299 /* DataUploadConditions.swift */, - 61133BB22423979B00786299 /* HTTPClient.swift */, + 61133BB22423979B00786299 /* URLSessionClient.swift */, + 617699172A860D9D0030022B /* HTTPClient.swift */, ); path = Upload; sourceTree = ""; @@ -5510,6 +5514,22 @@ path = SwiftUI; sourceTree = ""; }; + D2A434A62A8E3FEA0028E329 /* Logs */ = { + isa = PBXGroup; + children = ( + 61133C0C2423983800786299 /* Logs+objc.swift */, + ); + path = Logs; + sourceTree = ""; + }; + D2A434A72A8E3FFB0028E329 /* SessionReplay */ = { + isa = PBXGroup; + children = ( + D2A434A82A8E402B0028E329 /* SessionReplay+objc.swift */, + ); + path = SessionReplay; + sourceTree = ""; + }; D2A783D329A53049003B03BB /* Utils */ = { isa = PBXGroup; children = ( @@ -5617,8 +5637,8 @@ D2EBEDD229B8A58E00B15732 /* TracePropagationHeadersReader.swift */, D2160CC029C0DED100FAA9A5 /* URLSession */, A728AD992934CE2800397996 /* W3C */, - A7F773DA29253F6200AC1A62 /* OpenTelemetry */, - A7F773D929253F5900AC1A62 /* OpenTracing */, + A7F773DA29253F6200AC1A62 /* B3 */, + A7F773D929253F5900AC1A62 /* Datadog */, ); path = NetworkInstrumentation; sourceTree = ""; @@ -5634,8 +5654,8 @@ D2160CD229C0DF6700FAA9A5 /* URLSessionTaskInterceptionTests.swift */, 61E45BCE2450A6EC00F2C652 /* TraceIDTests.swift */, 61B558D32469CDD8001460D3 /* TraceIDGeneratorTests.swift */, - A79B0F5A292B7C06008742B3 /* OTelHTTPHeadersWriterTests.swift */, - A79B0F60292BB071008742B3 /* OTelHTTPHeadersReaderTests.swift */, + A79B0F5A292B7C06008742B3 /* B3HTTPHeadersWriterTests.swift */, + A79B0F60292BB071008742B3 /* B3HTTPHeadersReaderTests.swift */, A728ADA22934DB5000397996 /* W3CHTTPHeadersWriterTests.swift */, A728ADA52934DF2400397996 /* W3CHTTPHeadersReaderTests.swift */, ); @@ -5665,20 +5685,9 @@ D2EFA873286E010100F1FAA6 /* DatadogCore */ = { isa = PBXGroup; children = ( + 617699162A8608C20030022B /* Context */, 614B78EA296D7B63009C6B92 /* DatadogCoreTests.swift */, - 614B78EC296D7B63009C6B92 /* LowPowerModePublisherTests.swift */, - D2EFA874286E011900F1FAA6 /* DatadogContextProviderTests.swift */, - D29294E2291D652900F8EFF9 /* ApplicationVersionPublisherTests.swift */, - D2A1EE34287EB8DB00D28DFB /* ServerOffsetPublisherTests.swift */, - D2A1EE3A287EECA800D28DFB /* CarrierInfoPublisherTests.swift */, - D2FB1256292E0F0B005B13F8 /* TrackingConsentPublisherTests.swift */, - D2A1EE37287EBE4200D28DFB /* NetworkConnectionInfoPublisherTests.swift */, - D2A1EE3D2885D7EC00D28DFB /* LaunchTimePublisherTests.swift */, - D2A1EE432886B8B400D28DFB /* UserInfoPublisherTests.swift */, - D26C49AE2886DC7B00802B2D /* ApplicationStatePublisherTests.swift */, - D2C7E3AA28F97DCF0023B2CC /* BatteryStatusPublisherTests.swift */, D21C26D028A64599005DD405 /* MessageBusTests.swift */, - D234613028B7712F00055D4C /* FeatureContextTests.swift */, ); path = DatadogCore; sourceTree = ""; @@ -5925,7 +5934,6 @@ 61133BEC242397DA00786299 /* Sources */, 61133BED242397DA00786299 /* Frameworks */, 61133BEE242397DA00786299 /* Resources */, - 61A2CC2A2A4449210000FF25 /* Embed Frameworks */, ); buildRules = ( ); @@ -5933,6 +5941,7 @@ 61133C732423993200786299 /* PBXTargetDependency */, 61A2CC292A4449210000FF25 /* PBXTargetDependency */, D206BB882A41CA6800F43BA2 /* PBXTargetDependency */, + D2A434A52A8E3F900028E329 /* PBXTargetDependency */, ); name = "DatadogObjc iOS"; productName = DatadogObjc; @@ -6468,7 +6477,6 @@ D2CB6F9727C5217A00A62B57 /* Sources */, D2CB6FA927C5217A00A62B57 /* Frameworks */, D2CB6FAB27C5217A00A62B57 /* Resources */, - 61A2CC2F2A4449300000FF25 /* Embed Frameworks */, ); buildRules = ( ); @@ -7260,6 +7268,7 @@ 613E793B2577B6EE00DFCC17 /* DataReader.swift in Sources */, D2B3F04D282A85FD00C2B5EE /* DatadogCore.swift in Sources */, 61133BD62423979B00786299 /* DataUploader.swift in Sources */, + 617699182A860D9D0030022B /* HTTPClient.swift in Sources */, D21C26C528A3B49C005DD405 /* FeatureStorage.swift in Sources */, 61133BD42423979B00786299 /* FileReader.swift in Sources */, D29294E0291D5ED100F8EFF9 /* ApplicationVersionPublisher.swift in Sources */, @@ -7273,7 +7282,7 @@ D20605A3287464F40047275C /* ContextValuePublisher.swift in Sources */, 61DA8CAF28620C760074A606 /* Cryptography.swift in Sources */, E1D5AEA724B4D45B007F194B /* Versioning.swift in Sources */, - 61133BD82423979B00786299 /* HTTPClient.swift in Sources */, + 61133BD82423979B00786299 /* URLSessionClient.swift in Sources */, 614E9EB3244719FA007EE3E1 /* BundleType.swift in Sources */, 61D3E0D8277B23F1008BE766 /* KronosNTPClient.swift in Sources */, D20605B22874E1660047275C /* CarrierInfoPublisher.swift in Sources */, @@ -7307,6 +7316,7 @@ 618C365F248E85B400520CDE /* DateFormattingTests.swift in Sources */, 61133C5A2423990D00786299 /* FileTests.swift in Sources */, 61133C6B2423990D00786299 /* LogMatcher.swift in Sources */, + D2A434AC2A8E416F0028E329 /* DDSessionReplay+apiTests.m in Sources */, 61DB33B225DEDFC200F7EA71 /* CustomObjcViewController.m in Sources */, D2EFA875286E011900F1FAA6 /* DatadogContextProviderTests.swift in Sources */, 61363D9F24D99BAA0084CD6F /* DDErrorTests.swift in Sources */, @@ -7314,6 +7324,7 @@ 61D3E0E7277B3D92008BE766 /* KronosTimeStorageTests.swift in Sources */, 61133C582423990D00786299 /* FileWriterTests.swift in Sources */, D22743DC29DEB8B4001A7EF9 /* VitalRefreshRateReaderTests.swift in Sources */, + 6176991E2A8791880030022B /* Datadog+MultipleInstancesIntegrationTests.swift in Sources */, 617B954224BF4E7600E6F443 /* RUMMonitorConfigurationTests.swift in Sources */, 61F9CABA2513A7F5000A5E61 /* RUMSessionMatcher.swift in Sources */, 3C0D5DE22A543DC400446CF9 /* EventGeneratorTests.swift in Sources */, @@ -7325,6 +7336,7 @@ 61DA8CB2286215DE0074A606 /* CryptographyTests.swift in Sources */, 615A4A8924A34FD700233986 /* DDTracerTests.swift in Sources */, 61A2CC212A443D330000FF25 /* DDRUMConfigurationTests.swift in Sources */, + D2A434AE2A8E426C0028E329 /* DDSessionReplayTests.swift in Sources */, 61D03BE0273404E700367DE0 /* RUMDataModels+objcTests.swift in Sources */, E143CCAF27D236F600F4018A /* CITestIntegrationTests.swift in Sources */, D224430D29E95D6700274EC7 /* CrashReportReceiverTests.swift in Sources */, @@ -7345,7 +7357,8 @@ 61133C5F2423990D00786299 /* DataUploaderTests.swift in Sources */, D2A1EE36287EB8DB00D28DFB /* ServerOffsetPublisherTests.swift in Sources */, 61BBD19724ED50040023E65F /* DatadogConfigurationTests.swift in Sources */, - 61133C612423990D00786299 /* HTTPClientTests.swift in Sources */, + 6176991B2A86121B0030022B /* HTTPClientMock.swift in Sources */, + 61133C612423990D00786299 /* URLSessionClientTests.swift in Sources */, 61133C6A2423990D00786299 /* DatadogTests.swift in Sources */, D22743DB29DEB8B4001A7EF9 /* VitalCPUReaderTests.swift in Sources */, 61DA8CAC2861C3720074A606 /* DirectoriesTests.swift in Sources */, @@ -7380,7 +7393,7 @@ D2DA23CD298D5EDD00C6C7E6 /* _InternalProxyTests.swift in Sources */, 61133C5B2423990D00786299 /* DirectoryTests.swift in Sources */, 610ABD4C2A6930CA00AFEA34 /* TelemetryCoreIntegrationTests.swift in Sources */, - A79B0F64292BD074008742B3 /* DDOTelHTTPHeadersWriter+apiTests.m in Sources */, + A79B0F64292BD074008742B3 /* DDB3HTTPHeadersWriter+apiTests.m in Sources */, D2B3F052282E827700C2B5EE /* DDHTTPHeadersWriter+apiTests.m in Sources */, D20605B92875729E0047275C /* ContextValuePublisherMock.swift in Sources */, D24C9C4D29A7BA3F002057CF /* LogsMocks.swift in Sources */, @@ -7433,15 +7446,16 @@ 6132BF4924A49B6800D7BD17 /* DDSpanContext+objc.swift in Sources */, 6132BF4224A38D2400D7BD17 /* OTTracer+objc.swift in Sources */, A728ADAB2934EA2100397996 /* W3CHTTPHeadersWriter+objc.swift in Sources */, - A79B0F66292BD7CA008742B3 /* OTelHTTPHeadersWriter+objc.swift in Sources */, + A79B0F66292BD7CA008742B3 /* B3HTTPHeadersWriter+objc.swift in Sources */, 61133C0E2423983800786299 /* Datadog+objc.swift in Sources */, - 61133C102423983800786299 /* DDLogs+objc.swift in Sources */, + 61133C102423983800786299 /* Logs+objc.swift in Sources */, 615A4A8324A3431600233986 /* Trace+objc.swift in Sources */, 6132BF4C24A49C8F00D7BD17 /* HTTPHeadersWriter+objc.swift in Sources */, 6132BF4724A498D800D7BD17 /* DDSpan+objc.swift in Sources */, 615A4A8B24A3568900233986 /* OTSpan+objc.swift in Sources */, 611720D52524D9FB00634D9E /* DDURLSessionDelegate+objc.swift in Sources */, 9E55407C25812D1C00F6E3AD /* RUM+objc.swift in Sources */, + D2A434AA2A8E40A20028E329 /* SessionReplay+objc.swift in Sources */, 615A4A8D24A356A000233986 /* OTSpanContext+objc.swift in Sources */, 61133C112423983800786299 /* DatadogConfiguration+objc.swift in Sources */, ); @@ -7608,6 +7622,7 @@ 61020C2C2758E853005EEAEA /* DebugBackgroundEventsViewController.swift in Sources */, D2F44FC2299BD5600074B0D9 /* UIViewController+KeyboardControlling.swift in Sources */, 61441C992461A649003D8BB8 /* DebugLoggingViewController.swift in Sources */, + 617699212A8A7DF50030022B /* DebugManualTraceInjectionViewController.swift in Sources */, 617247AF25DA9BEA007085B3 /* CrashReportingObjcHelpers.m in Sources */, 61776CED273BEA5500F93802 /* DebugRUMSessionViewController.swift in Sources */, 61776D4E273E6D9F00F93802 /* SwiftUI.swift in Sources */, @@ -7783,7 +7798,7 @@ D263BCAF29DAFFEB00FA0E21 /* PerformancePresetOverride.swift in Sources */, D23039E7298D5236001A1FA3 /* NetworkConnectionInfo.swift in Sources */, D23039E9298D5236001A1FA3 /* TrackingConsent.swift in Sources */, - D2EBEE2629BA160F00B15732 /* OTelHTTPHeaders.swift in Sources */, + D2EBEE2629BA160F00B15732 /* B3HTTPHeaders.swift in Sources */, D23354FC2A42E32000AFCAE2 /* InternalExtended.swift in Sources */, D23039F3298D5236001A1FA3 /* DynamicCodingKey.swift in Sources */, D23039FE298D5236001A1FA3 /* FeatureRequestBuilder.swift in Sources */, @@ -7817,7 +7832,7 @@ D2DE63532A30A7CA00441A54 /* CoreRegistry.swift in Sources */, D2EBEE2829BA160F00B15732 /* W3CHTTPHeadersWriter.swift in Sources */, D23039EA298D5236001A1FA3 /* DeviceInfo.swift in Sources */, - D2EBEE2329BA160F00B15732 /* OTelHTTPHeadersReader.swift in Sources */, + D2EBEE2329BA160F00B15732 /* B3HTTPHeadersReader.swift in Sources */, D23039F8298D5236001A1FA3 /* InternalLogger.swift in Sources */, D2303A01298D5236001A1FA3 /* DateFormatting.swift in Sources */, D23039F1298D5236001A1FA3 /* AnyDecodable.swift in Sources */, @@ -7831,7 +7846,7 @@ D23039FF298D5236001A1FA3 /* Foundation+Datadog.swift in Sources */, D2F8235329915E12003C7E99 /* DatadogSite.swift in Sources */, D2D3199A29E98D970004F169 /* DefaultJSONEncoder.swift in Sources */, - D2EBEE2729BA160F00B15732 /* OTelHTTPHeadersWriter.swift in Sources */, + D2EBEE2729BA160F00B15732 /* B3HTTPHeadersWriter.swift in Sources */, D23039E2298D5236001A1FA3 /* UserInfo.swift in Sources */, D23039FB298D5236001A1FA3 /* URLRequestBuilder.swift in Sources */, D23039F6298D5236001A1FA3 /* Attributes.swift in Sources */, @@ -8002,7 +8017,7 @@ D2579554298ABB04008A1BE5 /* FeatureBaggageMock.swift in Sources */, 3C85D42C29F7C87D00AFF894 /* HostsSanitizerMock.swift in Sources */, D2160CF729C0EE2B00FAA9A5 /* UploadMocks.swift in Sources */, - D2F44FBC299AA36D0074B0D9 /* zlib.swift in Sources */, + D2F44FBC299AA36D0074B0D9 /* Decompression.swift in Sources */, D24C9C5229A7BD12002057CF /* SamplerMock.swift in Sources */, D2579557298ABB04008A1BE5 /* AttributesMocks.swift in Sources */, D2DA23C7298D5AC000C6C7E6 /* TelemetryMocks.swift in Sources */, @@ -8036,7 +8051,7 @@ D257957E298ABB83008A1BE5 /* FeatureBaggageMock.swift in Sources */, 3C85D42D29F7C87D00AFF894 /* HostsSanitizerMock.swift in Sources */, D2160CF829C0EE2B00FAA9A5 /* UploadMocks.swift in Sources */, - D2F44FBD299AA36D0074B0D9 /* zlib.swift in Sources */, + D2F44FBD299AA36D0074B0D9 /* Decompression.swift in Sources */, D24C9C5329A7BD12002057CF /* SamplerMock.swift in Sources */, D257957F298ABB83008A1BE5 /* AttributesMocks.swift in Sources */, D2DA23C8298D5AC000C6C7E6 /* TelemetryMocks.swift in Sources */, @@ -8328,6 +8343,7 @@ D2A7841229A53B2F003B03BB /* File.swift in Sources */, D2CB6E7627C50EAE00A62B57 /* KronosClock.swift in Sources */, D2CB6E7727C50EAE00A62B57 /* DataReader.swift in Sources */, + 617699192A860D9D0030022B /* HTTPClient.swift in Sources */, D2FB1255292E0E99005B13F8 /* TrackingConsentPublisher.swift in Sources */, D26C49C0288982DA00802B2D /* FeatureUpload.swift in Sources */, D2CB6E8127C50EAE00A62B57 /* DataUploader.swift in Sources */, @@ -8344,7 +8360,7 @@ D2553827288F0B1A00727FAD /* BatteryStatusPublisher.swift in Sources */, D20605A4287464F40047275C /* ContextValuePublisher.swift in Sources */, D2CB6EA727C50EAE00A62B57 /* Versioning.swift in Sources */, - D2CB6EA827C50EAE00A62B57 /* HTTPClient.swift in Sources */, + D2CB6EA827C50EAE00A62B57 /* URLSessionClient.swift in Sources */, D2CB6EAF27C50EAE00A62B57 /* BundleType.swift in Sources */, D2CB6EB327C50EAE00A62B57 /* KronosNTPClient.swift in Sources */, D2CB6EBA27C50EAE00A62B57 /* DataUploadConditions.swift in Sources */, @@ -8415,7 +8431,7 @@ D2A1EE35287EB8DB00D28DFB /* ServerOffsetPublisherTests.swift in Sources */, D22743E129DEB8B5001A7EF9 /* VitalRefreshRateReaderTests.swift in Sources */, D2CB6F2027C520D400A62B57 /* DatadogConfigurationTests.swift in Sources */, - D2CB6F2127C520D400A62B57 /* HTTPClientTests.swift in Sources */, + D2CB6F2127C520D400A62B57 /* URLSessionClientTests.swift in Sources */, D2CB6F2227C520D400A62B57 /* DatadogTests.swift in Sources */, 61DA8CAD2861C3720074A606 /* DirectoriesTests.swift in Sources */, 614798972A459AA80095CB02 /* DDTraceTests.swift in Sources */, @@ -8436,8 +8452,9 @@ D2CB6F3B27C520D400A62B57 /* NSURLSessionBridge.m in Sources */, D2A1EE452886B8B400D28DFB /* UserInfoPublisherTests.swift in Sources */, 61A2CC222A443D330000FF25 /* DDRUMConfigurationTests.swift in Sources */, + 6176991C2A86121B0030022B /* HTTPClientMock.swift in Sources */, D29294E4291D652D00F8EFF9 /* ApplicationVersionPublisherTests.swift in Sources */, - A79B0F65292BD074008742B3 /* DDOTelHTTPHeadersWriter+apiTests.m in Sources */, + A79B0F65292BD074008742B3 /* DDB3HTTPHeadersWriter+apiTests.m in Sources */, D2CB6F4327C520D400A62B57 /* DDLogsTests.swift in Sources */, D2CB6F4527C520D400A62B57 /* TracerTests.swift in Sources */, D2CB6F4627C520D400A62B57 /* CoreMocks.swift in Sources */, @@ -8449,6 +8466,7 @@ D2B3F0452823EE8400C2B5EE /* DataBlockTests.swift in Sources */, D2CB6F5327C520D400A62B57 /* DirectoryTests.swift in Sources */, 618353BD2A69470A0085F84A /* CoreMetricsIntegrationTests.swift in Sources */, + 6176991F2A8791880030022B /* Datadog+MultipleInstancesIntegrationTests.swift in Sources */, D2B3F053282E827B00C2B5EE /* DDHTTPHeadersWriter+apiTests.m in Sources */, D20605BA2875729E0047275C /* ContextValuePublisherMock.swift in Sources */, D22743E729DEB953001A7EF9 /* UIApplicationSwizzlerTests.swift in Sources */, @@ -8500,9 +8518,9 @@ D2CB6F9B27C5217A00A62B57 /* DDSpanContext+objc.swift in Sources */, D2CB6F9C27C5217A00A62B57 /* OTTracer+objc.swift in Sources */, A728ADAC2934EA2100397996 /* W3CHTTPHeadersWriter+objc.swift in Sources */, - A79B0F67292BD7CC008742B3 /* OTelHTTPHeadersWriter+objc.swift in Sources */, + A79B0F67292BD7CC008742B3 /* B3HTTPHeadersWriter+objc.swift in Sources */, D2CB6F9E27C5217A00A62B57 /* Datadog+objc.swift in Sources */, - D2CB6F9F27C5217A00A62B57 /* DDLogs+objc.swift in Sources */, + D2CB6F9F27C5217A00A62B57 /* Logs+objc.swift in Sources */, D2CB6FA027C5217A00A62B57 /* Trace+objc.swift in Sources */, D2CB6FA127C5217A00A62B57 /* HTTPHeadersWriter+objc.swift in Sources */, D2CB6FA227C5217A00A62B57 /* DDSpan+objc.swift in Sources */, @@ -8560,7 +8578,7 @@ D263BCB029DAFFEB00FA0E21 /* PerformancePresetOverride.swift in Sources */, D2DA2359298D57AA00C6C7E6 /* NetworkConnectionInfo.swift in Sources */, D2DA235A298D57AA00C6C7E6 /* TrackingConsent.swift in Sources */, - D2EBEE3429BA161100B15732 /* OTelHTTPHeaders.swift in Sources */, + D2EBEE3429BA161100B15732 /* B3HTTPHeaders.swift in Sources */, D23354FD2A42E32000AFCAE2 /* InternalExtended.swift in Sources */, D2DA235B298D57AA00C6C7E6 /* DynamicCodingKey.swift in Sources */, D2DA235C298D57AA00C6C7E6 /* FeatureRequestBuilder.swift in Sources */, @@ -8594,7 +8612,7 @@ D2DE63542A30A7CA00441A54 /* CoreRegistry.swift in Sources */, D2EBEE3629BA161100B15732 /* W3CHTTPHeadersWriter.swift in Sources */, D2DA236D298D57AA00C6C7E6 /* DeviceInfo.swift in Sources */, - D2EBEE3129BA161100B15732 /* OTelHTTPHeadersReader.swift in Sources */, + D2EBEE3129BA161100B15732 /* B3HTTPHeadersReader.swift in Sources */, D2DA236E298D57AA00C6C7E6 /* InternalLogger.swift in Sources */, D2DA236F298D57AA00C6C7E6 /* DateFormatting.swift in Sources */, D2DA2370298D57AA00C6C7E6 /* AnyDecodable.swift in Sources */, @@ -8608,7 +8626,7 @@ D2DA2375298D57AA00C6C7E6 /* Foundation+Datadog.swift in Sources */, D2F8235429915E12003C7E99 /* DatadogSite.swift in Sources */, D2D3199B29E98D970004F169 /* DefaultJSONEncoder.swift in Sources */, - D2EBEE3529BA161100B15732 /* OTelHTTPHeadersWriter.swift in Sources */, + D2EBEE3529BA161100B15732 /* B3HTTPHeadersWriter.swift in Sources */, D2DA2376298D57AA00C6C7E6 /* UserInfo.swift in Sources */, D2DA2377298D57AA00C6C7E6 /* URLRequestBuilder.swift in Sources */, D2DA2378298D57AA00C6C7E6 /* Attributes.swift in Sources */, @@ -8636,7 +8654,7 @@ files = ( D2160CD629C0DF6700FAA9A5 /* URLSessionSwizzlerTests.swift in Sources */, D26416B62A30E84F00BCD9F7 /* CoreRegistryTest.swift in Sources */, - D2EBEE3C29BA163E00B15732 /* OTelHTTPHeadersWriterTests.swift in Sources */, + D2EBEE3C29BA163E00B15732 /* B3HTTPHeadersWriterTests.swift in Sources */, D21AE6BC29E5EDAF0064BF29 /* TelemetryTests.swift in Sources */, D2DA23A3298D58F400C6C7E6 /* AnyEncodableTests.swift in Sources */, D263BCB429DB014900FA0E21 /* FixedWidthInteger+ConvenienceTests.swift in Sources */, @@ -8657,7 +8675,7 @@ D2160CDC29C0DF6700FAA9A5 /* HostsSanitizerTests.swift in Sources */, D2F44FB8299AA1DA0074B0D9 /* DataCompressionTests.swift in Sources */, D2160CE029C0DF6700FAA9A5 /* URLSessionDelegateAsSuperclassTests.swift in Sources */, - D2EBEE3B29BA163E00B15732 /* OTelHTTPHeadersReaderTests.swift in Sources */, + D2EBEE3B29BA163E00B15732 /* B3HTTPHeadersReaderTests.swift in Sources */, D2DA23A9298D58F400C6C7E6 /* FeatureBaggageTests.swift in Sources */, D2A783DA29A530EF003B03BB /* SwiftExtensionsTests.swift in Sources */, D2160CD429C0DF6700FAA9A5 /* NetworkInstrumentationFeatureTests.swift in Sources */, @@ -8673,7 +8691,7 @@ files = ( D2160CD729C0DF6700FAA9A5 /* URLSessionSwizzlerTests.swift in Sources */, D26416B72A30E84F00BCD9F7 /* CoreRegistryTest.swift in Sources */, - D2EBEE4029BA163F00B15732 /* OTelHTTPHeadersWriterTests.swift in Sources */, + D2EBEE4029BA163F00B15732 /* B3HTTPHeadersWriterTests.swift in Sources */, D21AE6BD29E5EDAF0064BF29 /* TelemetryTests.swift in Sources */, D2DA23B1298D59DC00C6C7E6 /* AnyEncodableTests.swift in Sources */, D263BCB529DB014900FA0E21 /* FixedWidthInteger+ConvenienceTests.swift in Sources */, @@ -8694,7 +8712,7 @@ D2160CDD29C0DF6700FAA9A5 /* HostsSanitizerTests.swift in Sources */, D2F44FB9299AA1DB0074B0D9 /* DataCompressionTests.swift in Sources */, D2160CE129C0DF6700FAA9A5 /* URLSessionDelegateAsSuperclassTests.swift in Sources */, - D2EBEE3F29BA163F00B15732 /* OTelHTTPHeadersReaderTests.swift in Sources */, + D2EBEE3F29BA163F00B15732 /* B3HTTPHeadersReaderTests.swift in Sources */, D2DA23B7298D59DC00C6C7E6 /* FeatureBaggageTests.swift in Sources */, D2A783D929A530EF003B03BB /* SwiftExtensionsTests.swift in Sources */, D2160CD529C0DF6700FAA9A5 /* NetworkInstrumentationFeatureTests.swift in Sources */, @@ -8919,6 +8937,11 @@ target = D23039A4298D513C001A1FA3 /* DatadogInternal iOS */; targetProxy = D29A9F4D29DD8525005C54A4 /* PBXContainerItemProxy */; }; + D2A434A52A8E3F900028E329 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 6133D1E52A6ED9E100384BEF /* DatadogSessionReplay iOS */; + targetProxy = D2A434A42A8E3F900028E329 /* PBXContainerItemProxy */; + }; D2A783E329A53414003B03BB /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = D2DA2355298D57AA00C6C7E6 /* DatadogInternal tvOS */; diff --git a/Datadog/E2ETests/Tracing/TracerE2ETests.swift b/Datadog/E2ETests/Tracing/TracerE2ETests.swift index 4e272c1350..1e48e276b7 100644 --- a/Datadog/E2ETests/Tracing/TracerE2ETests.swift +++ b/Datadog/E2ETests/Tracing/TracerE2ETests.swift @@ -5,7 +5,6 @@ */ import DatadogCore -import DatadogInternal import DatadogTrace class TracerE2ETests: E2ETests { diff --git a/Datadog/Example/Base.lproj/Main iOS.storyboard b/Datadog/Example/Base.lproj/Main iOS.storyboard index d018ef6429..45f77d1e5e 100644 --- a/Datadog/Example/Base.lproj/Main iOS.storyboard +++ b/Datadog/Example/Base.lproj/Main iOS.storyboard @@ -1,9 +1,9 @@ - + - + @@ -65,8 +65,28 @@ + + + + + + + + + + + + + + - + @@ -1662,6 +1682,34 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Datadog/Example/Debugging/DebugManualTraceInjectionViewController.swift b/Datadog/Example/Debugging/DebugManualTraceInjectionViewController.swift new file mode 100644 index 0000000000..2d56e0d42f --- /dev/null +++ b/Datadog/Example/Debugging/DebugManualTraceInjectionViewController.swift @@ -0,0 +1,153 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-Present Datadog, Inc. + */ + +import SwiftUI +import DatadogTrace + +@available(iOS 14, *) +internal class DebugManualTraceInjectionViewController: UIHostingController { + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder, rootView: DebugManualTraceInjectionView()) + } +} + +private var currentSession: URLSession? = nil + +@available(iOS 14.0, *) +internal struct DebugManualTraceInjectionView: View { + enum TraceHeaderType: String, CaseIterable { + case datadog = "Datadog" + case w3c = "W3C" + case b3Single = "B3-Single" + case b3Multiple = "B3-Multiple" + } + + @State private var spanName = "network request" + @State private var requestURL = "http://127.0.0.1:8000" + @State private var selectedTraceHeaderType: TraceHeaderType = .datadog + @State private var sampleRate: Float = 100.0 + @State private var isRequestPending = false + + private let session: URLSession = URLSession( + configuration: .ephemeral, + delegate: DDURLSessionDelegate(), + delegateQueue: nil + ) + + var body: some View { + let isButtonDisabled = isRequestPending || spanName.isEmpty || requestURL.isEmpty + + VStack() { + VStack(spacing: 8) { + Text("Trace injection") + .font(.caption.weight(.bold)) + .frame(maxWidth: .infinity, alignment: .leading) + + Text("After tapping \"SEND REQUEST\", a POST request will be sent to the given URL. The request will be traced using the chosen tracing header type and sample rate. A span with specified name will be sent to Datadog.") + .font(.caption.weight(.light)) + .frame(maxWidth: .infinity, alignment: .leading) + } + .padding() + + Form { + Section(header: Text("Traced URL:")) { + TextField("", text: $requestURL) + } + Section(header: Text("Span name:")) { + TextField("", text: $spanName) + } + Picker("Trace header type:", selection: $selectedTraceHeaderType) { + ForEach(TraceHeaderType.allCases, id: \.self) { headerType in + Text(headerType.rawValue) + } + } + .pickerStyle(.inline) + Section(header: Text("Trace sample Rate")) { + Slider( + value: $sampleRate, + in: 0...100, step: 1, + minimumValueLabel: Text("0"), + maximumValueLabel: Text("100") + ) { + Text("Sample Rate") + } + } + } + + Spacer() + + Button(action: { prepareAndSendRequest() }) { + Text("SEND REQUEST") + .fontWeight(.bold) + .foregroundColor(.white) + } + .frame(maxWidth: .infinity) + .padding() + .background(isButtonDisabled ? Color.gray : Color.datadogPurple) + .cornerRadius(10) + .disabled(isButtonDisabled) + .padding(.horizontal, 8) + .padding(.bottom, 30) + } + } + + private func prepareAndSendRequest() { + guard let url = URL(string: requestURL) else { + print("🔥 POST Request not sent - invalid url: \(requestURL)") + return + } + + var request = URLRequest(url: url) + request.httpMethod = "POST" + + let span = Tracer.shared().startRootSpan(operationName: spanName) + + switch selectedTraceHeaderType { + case .datadog: + let writer = HTTPHeadersWriter(sampleRate: sampleRate) + Tracer.shared().inject(spanContext: span.context, writer: writer) + writer.traceHeaderFields.forEach { request.setValue($0.value, forHTTPHeaderField: $0.key) } + case .w3c: + let writer = W3CHTTPHeadersWriter(sampleRate: sampleRate) + Tracer.shared().inject(spanContext: span.context, writer: writer) + writer.traceHeaderFields.forEach { request.setValue($0.value, forHTTPHeaderField: $0.key) } + case .b3Single: + let writer = B3HTTPHeadersWriter(sampleRate: sampleRate, injectEncoding: .single) + Tracer.shared().inject(spanContext: span.context, writer: writer) + writer.traceHeaderFields.forEach { request.setValue($0.value, forHTTPHeaderField: $0.key) } + case .b3Multiple: + let writer = B3HTTPHeadersWriter(sampleRate: sampleRate, injectEncoding: .multiple) + Tracer.shared().inject(spanContext: span.context, writer: writer) + writer.traceHeaderFields.forEach { request.setValue($0.value, forHTTPHeaderField: $0.key) } + } + + send(request: request) { + span.finish() + print("✅ Request sent to \(requestURL)") + } + } + + private func send(request: URLRequest, completion: @escaping () -> Void) { + isRequestPending = true + let task = session.dataTask(with: request) { _, _, _ in + completion() + DispatchQueue.main.async { self.isRequestPending = false } + } + task.resume() + } +} + +// MARK - Preview + +@available(iOS 14.0, *) + +struct DebugTraceInjectionView_Previews: PreviewProvider { + static var previews: some View { + NavigationView { + DebugManualTraceInjectionView() + } + } +} diff --git a/Datadog/Example/Debugging/DebugWebviewViewController.swift b/Datadog/Example/Debugging/DebugWebviewViewController.swift index 9130c59ec3..507371a16c 100644 --- a/Datadog/Example/Debugging/DebugWebviewViewController.swift +++ b/Datadog/Example/Debugging/DebugWebviewViewController.swift @@ -6,7 +6,6 @@ import UIKit import WebKit -import DatadogInternal import DatadogRUM import DatadogWebViewTracking diff --git a/Datadog/Example/Utils/ConsoleOutputInterceptor.swift b/Datadog/Example/Utils/ConsoleOutputInterceptor.swift index f38797c4ff..ebf6937311 100644 --- a/Datadog/Example/Utils/ConsoleOutputInterceptor.swift +++ b/Datadog/Example/Utils/ConsoleOutputInterceptor.swift @@ -6,7 +6,6 @@ #if DEBUG -import DatadogInternal import UIKit class ConsoleOutputInterceptor { diff --git a/Datadog/IntegrationUnitTests/Public/Datadog+MultipleInstancesIntegrationTests.swift b/Datadog/IntegrationUnitTests/Public/Datadog+MultipleInstancesIntegrationTests.swift new file mode 100644 index 0000000000..181e8f172e --- /dev/null +++ b/Datadog/IntegrationUnitTests/Public/Datadog+MultipleInstancesIntegrationTests.swift @@ -0,0 +1,100 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-Present Datadog, Inc. + */ + +import XCTest +import TestUtilities +@testable import DatadogCore +import DatadogInternal +import DatadogLogs + +class Datadog_MultipleInstancesIntegrationTests: XCTestCase { + /// The configuraiton of default instance of SDK. + private var defaultInstanceConfig = Datadog.Configuration(clientToken: "main-token", env: "default-env") + /// The configuraiton of custom instance of SDK. + private var customInstanceConfig = Datadog.Configuration(clientToken: "custom-token", env: "custom-env") + + override func setUp() { + super.setUp() + CreateTemporaryDirectory() + + // Root system directory for both instances: + let systemDirectory = Directory(url: temporaryDirectory) + defaultInstanceConfig.systemDirectory = { systemDirectory } + customInstanceConfig.systemDirectory = { systemDirectory } + } + + override func tearDown() { + DeleteTemporaryDirectory() + super.tearDown() + } + + func testGivenTwoInstancesOfSDK_whenCollectingLogs_thenEachSDKUploadsItsOwnData() throws { + let customInstanceName = "custom" + let numberOfLogs = 10 + let defaultHTTPClient = HTTPClientMock(responseCode: 200) + let customHTTPClient = HTTPClientMock(responseCode: 200) + defaultInstanceConfig.httpClientFactory = { _ in defaultHTTPClient } + customInstanceConfig.httpClientFactory = { _ in customHTTPClient } + defaultInstanceConfig.bundle = .mockWith(bundleIdentifier: "com.bundle.default", CFBundleShortVersionString: "1.0-default") + customInstanceConfig.bundle = .mockWith(bundleIdentifier: "com.bundle.custom", CFBundleShortVersionString: "1.0-custom") + + // Given + Datadog.initialize(with: defaultInstanceConfig, trackingConsent: .granted) + Datadog.initialize(with: customInstanceConfig, trackingConsent: .granted, instanceName: customInstanceName) + + Logs.enable(with: .init()) + Logs.enable(with: .init(), in: Datadog.sdkInstance(named: customInstanceName)) + + let defaultLogger = Logger.create() + let customLogger = Logger.create(in: Datadog.sdkInstance(named: customInstanceName)) + + // When + for _ in 0..) -> Void) { - let task = session.dataTask(with: request) { data, response, error in - completion(httpClientResult(for: (data, response, error))) - } - task.resume() - } -} - -/// An error returned if `URLSession` response state is inconsistent (like no data, no response and no error). -/// The code execution in `URLSessionTransport` should never reach its initialization. -internal struct URLSessionTransportInconsistencyException: Error {} - -/// Returns a `Basic` `Authorization` header using the `username` and `password` provided. -/// -/// - Parameters: -/// - username: The username of the header. -/// - password: The password of the header. -/// - Returns: The HTTP Basic authentication header value -private func basicHTTPAuthentication(username: String, password: String) -> String { - let credential = Data("\(username):\(password)".utf8).base64EncodedString() - return "Basic \(credential)" -} - -/// As `URLSession` returns 3-values-tuple for request execution, this function applies consistency constraints and turns -/// it into only two possible states of `HTTPTransportResult`. -private func httpClientResult(for urlSessionTaskCompletion: (Data?, URLResponse?, Error?)) -> Result { - let (_, response, error) = urlSessionTaskCompletion - - if let error = error { - return .failure(error) - } - - if let httpResponse = response as? HTTPURLResponse { - return .success(httpResponse) - } - - return .failure(URLSessionTransportInconsistencyException()) +/// Defines a type responsible for sending HTTP requests. +internal protocol HTTPClient { + /// Sends the provided request using HTTP. + /// - Parameters: + /// - request: The request to be sent. + /// - completion: A closure that receives a Result containing either an HTTPURLResponse or an Error. + func send(request: URLRequest, completion: @escaping (Result) -> Void) } diff --git a/DatadogCore/Sources/Core/Upload/URLSessionClient.swift b/DatadogCore/Sources/Core/Upload/URLSessionClient.swift new file mode 100644 index 0000000000..8725d896eb --- /dev/null +++ b/DatadogCore/Sources/Core/Upload/URLSessionClient.swift @@ -0,0 +1,75 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-Present Datadog, Inc. + */ + +import Foundation + +/// Client for sending requests over HTTP. +internal class URLSessionClient: HTTPClient { + internal let session: URLSession + + convenience init(proxyConfiguration: [AnyHashable: Any]? = nil) { + let configuration: URLSessionConfiguration = .ephemeral + // NOTE: RUMM-610 Default behaviour of `.ephemeral` session is to cache requests. + // To not leak requests memory (including their `.httpBody` which may be significant) + // we explicitly opt-out from using cache. This cannot be achieved using `.requestCachePolicy`. + configuration.urlCache = nil + configuration.connectionProxyDictionary = proxyConfiguration + + // URLSession does not set the `Proxy-Authorization` header automatically when using a proxy + // configuration. We manually set the HTTP basic authentication header. + if + let user = proxyConfiguration?[kCFProxyUsernameKey] as? String, + let password = proxyConfiguration?[kCFProxyPasswordKey] as? String + { + let authorization = basicHTTPAuthentication(username: user, password: password) + configuration.httpAdditionalHeaders = ["Proxy-Authorization": authorization] + } + + self.init(session: URLSession(configuration: configuration)) + } + + init(session: URLSession) { + self.session = session + } + + func send(request: URLRequest, completion: @escaping (Result) -> Void) { + let task = session.dataTask(with: request) { data, response, error in + completion(httpClientResult(for: (data, response, error))) + } + task.resume() + } +} + +/// An error returned if `URLSession` response state is inconsistent (like no data, no response and no error). +/// The code execution in `URLSessionTransport` should never reach its initialization. +internal struct URLSessionTransportInconsistencyException: Error {} + +/// Returns a `Basic` `Authorization` header using the `username` and `password` provided. +/// +/// - Parameters: +/// - username: The username of the header. +/// - password: The password of the header. +/// - Returns: The HTTP Basic authentication header value +private func basicHTTPAuthentication(username: String, password: String) -> String { + let credential = Data("\(username):\(password)".utf8).base64EncodedString() + return "Basic \(credential)" +} + +/// As `URLSession` returns 3-values-tuple for request execution, this function applies consistency constraints and turns +/// it into only two possible states of `HTTPTransportResult`. +private func httpClientResult(for urlSessionTaskCompletion: (Data?, URLResponse?, Error?)) -> Result { + let (_, response, error) = urlSessionTaskCompletion + + if let error = error { + return .failure(error) + } + + if let httpResponse = response as? HTTPURLResponse { + return .success(httpResponse) + } + + return .failure(URLSessionTransportInconsistencyException()) +} diff --git a/DatadogCore/Sources/Datadog.swift b/DatadogCore/Sources/Datadog.swift index 209a4d7073..03cec4d4d2 100644 --- a/DatadogCore/Sources/Datadog.swift +++ b/DatadogCore/Sources/Datadog.swift @@ -7,8 +7,10 @@ import Foundation import DatadogInternal -//swiftlint:disable:next duplicate_imports +//swiftlint:disable duplicate_imports @_exported import enum DatadogInternal.TrackingConsent +@_exported import protocol DatadogInternal.DatadogCoreProtocol +//swiftlint:enable duplicate_imports /// An entry point to Datadog SDK. /// @@ -84,6 +86,8 @@ public struct Datadog { /// Proxy configuration attributes. /// This can be used to a enable a custom proxy for uploading tracked data to Datadog's intake. + /// + /// Ref.: https://developer.apple.com/documentation/foundation/urlsessionconfiguration/1411499-connectionproxydictionary public var proxyConfiguration: [AnyHashable: Any]? /// SeData encryption to use for on-disk data persistency by providing an object @@ -101,16 +105,6 @@ public struct Datadog { /// The bundle object that contains the current executable. public var bundle: Bundle - /// Overrides the default process information. - internal var processInfo: ProcessInfo = .processInfo - - /// Sets additional configuration attributes. - /// This can be used to tweak internal features of the SDK. - internal var additionalConfiguration: [String: Any] = [:] - - /// Overrides the date provider. - internal var dateProvider: DateProvider = SystemDateProvider() - /// Creates a Datadog SDK Configuration object. /// /// - Parameters: @@ -169,6 +163,27 @@ public struct Datadog { self.encryption = encryption self.serverDateProvider = serverDateProvider ?? DatadogNTPDateProvider() } + + // MARK: - Internal + + /// Obtains OS directory where SDK creates its root folder. + /// All instances of the SDK use the same root folder, but each creates its own subdirectory. + internal var systemDirectory: () throws -> Directory = { try Directory.cache() } + + /// Default process information. + internal var processInfo: ProcessInfo = .processInfo + + /// Sets additional configuration attributes. + /// This can be used to tweak internal features of the SDK. + internal var additionalConfiguration: [String: Any] = [:] + + /// Default date provider used by the SDK and all products. + internal var dateProvider: DateProvider = SystemDateProvider() + + /// Creates `HTTPClient` with given proxy configuration attributes. + internal var httpClientFactory: ([AnyHashable: Any]?) -> HTTPClient = { proxyConfiguration in + URLSessionClient(proxyConfiguration: proxyConfiguration) + } } /// Verbosity level of Datadog SDK. Can be used for debugging purposes. @@ -315,8 +330,8 @@ public struct Datadog { trackingConsent: TrackingConsent, instanceName: String ) throws -> DatadogCoreProtocol { - if CoreRegistry.default is DatadogCore { - throw ProgrammerError(description: "SDK is already initialized.") + guard !CoreRegistry.isRegistered(instanceName: instanceName) else { + throw ProgrammerError(description: "The '\(instanceName)' instance of SDK is already initialized.") } let debug = configuration.processInfo.arguments.contains(LaunchArguments.Debug) @@ -346,14 +361,14 @@ public struct Datadog { // Set default `DatadogCore`: let core = DatadogCore( directory: try CoreDirectory( - in: Directory.cache(), + in: configuration.systemDirectory(), instancenName: instanceName, site: configuration.site ), dateProvider: configuration.dateProvider, initialConsent: trackingConsent, performance: performance, - httpClient: HTTPClient(proxyConfiguration: configuration.proxyConfiguration), + httpClient: configuration.httpClientFactory(configuration.proxyConfiguration), encryption: configuration.encryption, contextProvider: DatadogContextProvider( site: configuration.site, diff --git a/DatadogCore/Sources/Versioning.swift b/DatadogCore/Sources/Versioning.swift index 09f1230bdd..6a2c045161 100644 --- a/DatadogCore/Sources/Versioning.swift +++ b/DatadogCore/Sources/Versioning.swift @@ -1,3 +1,3 @@ // GENERATED FILE: Do not edit directly -internal let __sdkVersion = "2.0.0" +internal let __sdkVersion = "2.1.0" diff --git a/DatadogCore/Tests/Datadog/Core/Upload/DataUploadWorkerTests.swift b/DatadogCore/Tests/Datadog/Core/Upload/DataUploadWorkerTests.swift index 0ceafdf3ca..8cf2ab13c6 100644 --- a/DatadogCore/Tests/Datadog/Core/Upload/DataUploadWorkerTests.swift +++ b/DatadogCore/Tests/Datadog/Core/Upload/DataUploadWorkerTests.swift @@ -41,11 +41,12 @@ class DataUploadWorkerTests: XCTestCase { // MARK: - Data Uploads func testItUploadsAllData() { - let server = ServerMock(delivery: .success(response: .mockResponseWith(statusCode: 200))) - let httpClient = HTTPClient(session: server.getInterceptedURLSession()) - let dataUploader = DataUploader( - httpClient: httpClient, - requestBuilder: FeatureRequestBuilderMock() + let uploadExpectation = self.expectation(description: "Make 3 uploads") + uploadExpectation.expectedFulfillmentCount = 3 + + let dataUploader = DataUploaderMock( + uploadStatus: DataUploadStatus(httpResponse: .mockResponseWith(statusCode: 200), ddRequestID: nil), + onUpload: uploadExpectation.fulfill ) // Given @@ -65,10 +66,10 @@ class DataUploadWorkerTests: XCTestCase { ) // Then - let recordedRequests = server.waitAndReturnRequests(count: 3) - XCTAssertTrue(recordedRequests.contains { $0.httpBody == #"[{"k1":"v1"}]"#.utf8Data }) - XCTAssertTrue(recordedRequests.contains { $0.httpBody == #"[{"k2":"v2"}]"#.utf8Data }) - XCTAssertTrue(recordedRequests.contains { $0.httpBody == #"[{"k3":"v3"}]"#.utf8Data }) + waitForExpectations(timeout: 1) + XCTAssertEqual(dataUploader.uploadedEvents[0], Event(data: #"{"k1":"v1"}"#.utf8Data)) + XCTAssertEqual(dataUploader.uploadedEvents[1], Event(data: #"{"k2":"v2"}"#.utf8Data)) + XCTAssertEqual(dataUploader.uploadedEvents[2], Event(data: #"{"k3":"v3"}"#.utf8Data)) worker.cancelSynchronously() XCTAssertEqual(try orchestrator.directory.files().count, 0) @@ -77,7 +78,7 @@ class DataUploadWorkerTests: XCTestCase { func testGivenDataToUpload_whenUploadFinishesAndDoesNotNeedToBeRetried_thenDataIsDeleted() { let startUploadExpectation = self.expectation(description: "Upload has started") - var mockDataUploader = DataUploaderMock(uploadStatus: .mockWith(needsRetry: false)) + let mockDataUploader = DataUploaderMock(uploadStatus: .mockWith(needsRetry: false)) mockDataUploader.onUpload = { startUploadExpectation.fulfill() } // Given @@ -105,7 +106,7 @@ class DataUploadWorkerTests: XCTestCase { func testGivenDataToUpload_whenUploadFailsToBeInitiated_thenDataIsDeleted() { let initiatingUploadExpectation = self.expectation(description: "Upload is being initiated") - var mockDataUploader = DataUploaderMock(uploadStatus: .mockRandom()) + let mockDataUploader = DataUploaderMock(uploadStatus: .mockRandom()) mockDataUploader.onUpload = { initiatingUploadExpectation.fulfill() throw ErrorMock("Failed to prepare upload") @@ -136,7 +137,7 @@ class DataUploadWorkerTests: XCTestCase { func testGivenDataToUpload_whenUploadFinishesAndNeedsToBeRetried_thenDataIsPreserved() { let startUploadExpectation = self.expectation(description: "Upload has started") - var mockDataUploader = DataUploaderMock(uploadStatus: .mockWith(needsRetry: true)) + let mockDataUploader = DataUploaderMock(uploadStatus: .mockWith(needsRetry: true)) mockDataUploader.onUpload = { startUploadExpectation.fulfill() } // Given @@ -177,7 +178,7 @@ class DataUploadWorkerTests: XCTestCase { XCTAssertEqual(try orchestrator.directory.files().count, 0) let server = ServerMock(delivery: .success(response: .mockResponseWith(statusCode: 200))) - let httpClient = HTTPClient(session: server.getInterceptedURLSession()) + let httpClient = URLSessionClient(session: server.getInterceptedURLSession()) let dataUploader = DataUploader( httpClient: httpClient, @@ -213,7 +214,7 @@ class DataUploadWorkerTests: XCTestCase { writer.write(value: ["k1": "v1"]) let server = ServerMock(delivery: .success(response: .mockResponseWith(statusCode: 500))) - let httpClient = HTTPClient(session: server.getInterceptedURLSession()) + let httpClient = URLSessionClient(session: server.getInterceptedURLSession()) let dataUploader = DataUploader( httpClient: httpClient, @@ -249,7 +250,7 @@ class DataUploadWorkerTests: XCTestCase { writer.write(value: ["k1": "v1"]) let server = ServerMock(delivery: .success(response: .mockResponseWith(statusCode: 200))) - let httpClient = HTTPClient(session: server.getInterceptedURLSession()) + let httpClient = URLSessionClient(session: server.getInterceptedURLSession()) let dataUploader = DataUploader( httpClient: httpClient, @@ -285,7 +286,7 @@ class DataUploadWorkerTests: XCTestCase { // When let startUploadExpectation = self.expectation(description: "Upload has started") - var mockDataUploader = DataUploaderMock(uploadStatus: randomUploadStatus) + let mockDataUploader = DataUploaderMock(uploadStatus: randomUploadStatus) mockDataUploader.onUpload = { startUploadExpectation.fulfill() } let worker = DataUploadWorker( @@ -329,7 +330,7 @@ class DataUploadWorkerTests: XCTestCase { // When let startUploadExpectation = self.expectation(description: "Upload has started") - var mockDataUploader = DataUploaderMock(uploadStatus: randomUploadStatus) + let mockDataUploader = DataUploaderMock(uploadStatus: randomUploadStatus) mockDataUploader.onUpload = { startUploadExpectation.fulfill() } let worker = DataUploadWorker( @@ -363,7 +364,7 @@ class DataUploadWorkerTests: XCTestCase { // When let startUploadExpectation = self.expectation(description: "Upload has started") - var mockDataUploader = DataUploaderMock(uploadStatus: randomUploadStatus) + let mockDataUploader = DataUploaderMock(uploadStatus: randomUploadStatus) mockDataUploader.onUpload = { startUploadExpectation.fulfill() } let worker = DataUploadWorker( @@ -399,7 +400,7 @@ class DataUploadWorkerTests: XCTestCase { // When let startUploadExpectation = self.expectation(description: "Upload has started") - var mockDataUploader = DataUploaderMock(uploadStatus: randomUploadStatus) + let mockDataUploader = DataUploaderMock(uploadStatus: randomUploadStatus) mockDataUploader.onUpload = { startUploadExpectation.fulfill() } let worker = DataUploadWorker( @@ -434,7 +435,7 @@ class DataUploadWorkerTests: XCTestCase { // When let initiatingUploadExpectation = self.expectation(description: "Upload is being initiated") - var mockDataUploader = DataUploaderMock(uploadStatus: .mockRandom()) + let mockDataUploader = DataUploaderMock(uploadStatus: .mockRandom()) mockDataUploader.onUpload = { initiatingUploadExpectation.fulfill() throw ErrorMock("Failed to prepare upload") @@ -468,7 +469,7 @@ class DataUploadWorkerTests: XCTestCase { func testWhenCancelled_itPerformsNoMoreUploads() { // Given let server = ServerMock(delivery: .success(response: .mockResponseWith(statusCode: 200))) - let httpClient = HTTPClient(session: server.getInterceptedURLSession()) + let httpClient = URLSessionClient(session: server.getInterceptedURLSession()) let dataUploader = DataUploader( httpClient: httpClient, @@ -494,12 +495,12 @@ class DataUploadWorkerTests: XCTestCase { } func testItFlushesAllData() { - let server = ServerMock(delivery: .success(response: .mockResponseWith(statusCode: 200))) - let httpClient = HTTPClient(session: server.getInterceptedURLSession()) + let uploadExpectation = self.expectation(description: "Make 3 uploads") + uploadExpectation.expectedFulfillmentCount = 3 - let dataUploader = DataUploader( - httpClient: httpClient, - requestBuilder: FeatureRequestBuilderMock() + let dataUploader = DataUploaderMock( + uploadStatus: .mockRandom(), + onUpload: uploadExpectation.fulfill ) let worker = DataUploadWorker( queue: uploaderQueue, @@ -522,10 +523,10 @@ class DataUploadWorkerTests: XCTestCase { // Then XCTAssertEqual(try orchestrator.directory.files().count, 0) - let recordedRequests = server.waitAndReturnRequests(count: 3) - XCTAssertTrue(recordedRequests.contains { $0.httpBody == #"[{"k1":"v1"}]"#.utf8Data }) - XCTAssertTrue(recordedRequests.contains { $0.httpBody == #"[{"k2":"v2"}]"#.utf8Data }) - XCTAssertTrue(recordedRequests.contains { $0.httpBody == #"[{"k3":"v3"}]"#.utf8Data }) + waitForExpectations(timeout: 1) + XCTAssertEqual(dataUploader.uploadedEvents[0], Event(data: #"{"k1":"v1"}"#.utf8Data)) + XCTAssertEqual(dataUploader.uploadedEvents[1], Event(data: #"{"k2":"v2"}"#.utf8Data)) + XCTAssertEqual(dataUploader.uploadedEvents[2], Event(data: #"{"k3":"v3"}"#.utf8Data)) worker.cancelSynchronously() } diff --git a/DatadogCore/Tests/Datadog/Core/Upload/DataUploaderTests.swift b/DatadogCore/Tests/Datadog/Core/Upload/DataUploaderTests.swift index 01a9a0b170..341f18aeab 100644 --- a/DatadogCore/Tests/Datadog/Core/Upload/DataUploaderTests.swift +++ b/DatadogCore/Tests/Datadog/Core/Upload/DataUploaderTests.swift @@ -10,20 +10,18 @@ import TestUtilities @testable import DatadogCore class DataUploaderTests: XCTestCase { - func testWhenUploadCompletesWithSuccess_itReturnsExpectedUploadStatus() throws { + // swiftlint:disable opening_brace + func testGivenValidRequest_whenUploadCompletesWithStatusCode_itReturnsUploadStatus() throws { // Given let randomResponse: HTTPURLResponse = .mockResponseWith(statusCode: (100...599).randomElement()!) - let randomRequestIDOrNil: String? = Bool.random() ? .mockRandom() : nil - let requestIDHeaderOrNil: URLRequestBuilder.HTTPHeader? = randomRequestIDOrNil.flatMap { randomRequestID in - .init(field: URLRequestBuilder.HTTPHeader.ddRequestIDHeaderField, value: { randomRequestID }) - } - - let server = ServerMock(delivery: .success(response: randomResponse)) - let httpClient = HTTPClient(session: server.getInterceptedURLSession()) + let randomRequest: URLRequest = oneOf([ + { .mockWith(headers: [:]) }, + { .mockWith(headers: ["DD-REQUEST-ID": String.mockRandom()]) } + ]) let uploader = DataUploader( - httpClient: httpClient, - requestBuilder: FeatureRequestBuilderMock(headers: requestIDHeaderOrNil.map { [$0] } ?? []) + httpClient: HTTPClientMock(response: randomResponse), + requestBuilder: FeatureRequestBuilderMock(request: randomRequest) ) // When @@ -33,23 +31,24 @@ class DataUploaderTests: XCTestCase { ) // Then - let expectedUploadStatus = DataUploadStatus(httpResponse: randomResponse, ddRequestID: randomRequestIDOrNil) + let expectedUploadStatus = DataUploadStatus( + httpResponse: randomResponse, + ddRequestID: randomRequest.value(forHTTPHeaderField: "DD-REQUEST-ID") + ) DDAssertReflectionEqual(uploadStatus, expectedUploadStatus) - server.waitFor(requestsCompletion: 1) } + // swiftlint:enable opening_brace - func testWhenUploadCompletesWithFailure_itReturnsExpectedUploadStatus() throws { + func testGivenValidRequest_whenUploadCompletesWithError_itReturnsUploadStatus() throws { // Given let randomErrorDescription: String = .mockRandom() let randomError = NSError(domain: .mockRandom(), code: .mockRandom(), userInfo: [NSLocalizedDescriptionKey: randomErrorDescription]) - - let server = ServerMock(delivery: .failure(error: randomError)) - let httpClient = HTTPClient(session: server.getInterceptedURLSession()) + let randomRequest: URLRequest = .mockAny() let uploader = DataUploader( - httpClient: httpClient, - requestBuilder: FeatureRequestBuilderMock() + httpClient: HTTPClientMock(error: randomError), + requestBuilder: FeatureRequestBuilderMock(request: randomRequest) ) // When @@ -62,15 +61,14 @@ class DataUploaderTests: XCTestCase { let expectedUploadStatus = DataUploadStatus(networkError: randomError) DDAssertReflectionEqual(uploadStatus, expectedUploadStatus) - server.waitFor(requestsCompletion: 1) } - func testWhenUploadCannotBeInitiated_itThrows() throws { + func testWhenRequestCannotBeCreated_itThrows() throws { // Given let error = ErrorMock() let uploader = DataUploader( - httpClient: .mockAny(), + httpClient: HTTPClientMock(), requestBuilder: FailingRequestBuilderMock(error: error) ) diff --git a/DatadogCore/Tests/Datadog/Core/Upload/HTTPClientTests.swift b/DatadogCore/Tests/Datadog/Core/Upload/URLSessionClientTests.swift similarity index 90% rename from DatadogCore/Tests/Datadog/Core/Upload/HTTPClientTests.swift rename to DatadogCore/Tests/Datadog/Core/Upload/URLSessionClientTests.swift index 9cbd72b213..f3fda0cd24 100644 --- a/DatadogCore/Tests/Datadog/Core/Upload/HTTPClientTests.swift +++ b/DatadogCore/Tests/Datadog/Core/Upload/URLSessionClientTests.swift @@ -8,11 +8,11 @@ import XCTest import TestUtilities @testable import DatadogCore -class HTTPClientTests: XCTestCase { +class URLSessionClientTests: XCTestCase { func testWhenRequestIsDelivered_itReturnsHTTPResponse() { let server = ServerMock(delivery: .success(response: .mockResponseWith(statusCode: 200))) let expectation = self.expectation(description: "receive response") - let client = HTTPClient(session: server.getInterceptedURLSession()) + let client = URLSessionClient(session: server.getInterceptedURLSession()) client.send(request: .mockAny()) { result in switch result { @@ -32,7 +32,7 @@ class HTTPClientTests: XCTestCase { let mockError = NSError(domain: "network", code: 999, userInfo: [NSLocalizedDescriptionKey: "no internet connection"]) let server = ServerMock(delivery: .failure(error: mockError)) let expectation = self.expectation(description: "receive response") - let client = HTTPClient(session: server.getInterceptedURLSession()) + let client = URLSessionClient(session: server.getInterceptedURLSession()) client.send(request: .mockAny()) { result in switch result { @@ -57,7 +57,7 @@ class HTTPClientTests: XCTestCase { kCFProxyPasswordKey: "proxypass", ] - let client = HTTPClient(proxyConfiguration: proxyConfiguration) + let client = URLSessionClient(proxyConfiguration: proxyConfiguration) let actualProxy: [AnyHashable: Any] = client.session.configuration.connectionProxyDictionary! XCTAssertEqual(actualProxy[kCFNetworkProxiesHTTPEnable] as? Bool, true) diff --git a/DatadogCore/Tests/Datadog/DatadogConfigurationTests.swift b/DatadogCore/Tests/Datadog/DatadogConfigurationTests.swift index 55712f2a57..23d319c391 100644 --- a/DatadogCore/Tests/Datadog/DatadogConfigurationTests.swift +++ b/DatadogCore/Tests/Datadog/DatadogConfigurationTests.swift @@ -53,8 +53,9 @@ class DatadogConfigurationTests: XCTestCase { defer { Datadog.flushAndDeinitialize() } let core = try XCTUnwrap(CoreRegistry.default as? DatadogCore) + let urlSessionClient = try XCTUnwrap(core.httpClient as? URLSessionClient) XCTAssertTrue(core.dateProvider is SystemDateProvider) - XCTAssertNil(core.httpClient.session.configuration.connectionProxyDictionary) + XCTAssertNil(urlSessionClient.session.configuration.connectionProxyDictionary) XCTAssertNil(core.encryption) let context = core.contextProvider.read() @@ -115,7 +116,8 @@ class DatadogConfigurationTests: XCTestCase { XCTAssertTrue(core.dateProvider is SystemDateProvider) XCTAssertTrue(core.encryption is DataEncryptionMock) - let connectionProxyDictionary = try XCTUnwrap(core.httpClient.session.configuration.connectionProxyDictionary) + let urlSessionClient = try XCTUnwrap(core.httpClient as? URLSessionClient) + let connectionProxyDictionary = try XCTUnwrap(urlSessionClient.session.configuration.connectionProxyDictionary) XCTAssertEqual(connectionProxyDictionary[kCFNetworkProxiesHTTPEnable] as? Bool, true) XCTAssertEqual(connectionProxyDictionary[kCFNetworkProxiesHTTPPort] as? Int, 123) XCTAssertEqual(connectionProxyDictionary[kCFNetworkProxiesHTTPProxy] as? String, "www.example.com") @@ -174,7 +176,7 @@ class DatadogConfigurationTests: XCTestCase { XCTAssertEqual( printFunction.printedMessage, - "🔥 Datadog SDK usage error: SDK is already initialized." + "🔥 Datadog SDK usage error: The 'main' instance of SDK is already initialized." ) Datadog.flushAndDeinitialize() diff --git a/DatadogCore/Tests/Datadog/DatadogCore/ApplicationStatePublisherTests.swift b/DatadogCore/Tests/Datadog/DatadogCore/Context/ApplicationStatePublisherTests.swift similarity index 100% rename from DatadogCore/Tests/Datadog/DatadogCore/ApplicationStatePublisherTests.swift rename to DatadogCore/Tests/Datadog/DatadogCore/Context/ApplicationStatePublisherTests.swift diff --git a/DatadogCore/Tests/Datadog/DatadogCore/ApplicationVersionPublisherTests.swift b/DatadogCore/Tests/Datadog/DatadogCore/Context/ApplicationVersionPublisherTests.swift similarity index 100% rename from DatadogCore/Tests/Datadog/DatadogCore/ApplicationVersionPublisherTests.swift rename to DatadogCore/Tests/Datadog/DatadogCore/Context/ApplicationVersionPublisherTests.swift diff --git a/DatadogCore/Tests/Datadog/DatadogCore/BatteryStatusPublisherTests.swift b/DatadogCore/Tests/Datadog/DatadogCore/Context/BatteryStatusPublisherTests.swift similarity index 100% rename from DatadogCore/Tests/Datadog/DatadogCore/BatteryStatusPublisherTests.swift rename to DatadogCore/Tests/Datadog/DatadogCore/Context/BatteryStatusPublisherTests.swift diff --git a/DatadogCore/Tests/Datadog/DatadogCore/CarrierInfoPublisherTests.swift b/DatadogCore/Tests/Datadog/DatadogCore/Context/CarrierInfoPublisherTests.swift similarity index 100% rename from DatadogCore/Tests/Datadog/DatadogCore/CarrierInfoPublisherTests.swift rename to DatadogCore/Tests/Datadog/DatadogCore/Context/CarrierInfoPublisherTests.swift diff --git a/DatadogCore/Tests/Datadog/DatadogCore/DatadogContextProviderTests.swift b/DatadogCore/Tests/Datadog/DatadogCore/Context/DatadogContextProviderTests.swift similarity index 100% rename from DatadogCore/Tests/Datadog/DatadogCore/DatadogContextProviderTests.swift rename to DatadogCore/Tests/Datadog/DatadogCore/Context/DatadogContextProviderTests.swift diff --git a/DatadogCore/Tests/Datadog/DatadogCore/FeatureContextTests.swift b/DatadogCore/Tests/Datadog/DatadogCore/Context/FeatureContextTests.swift similarity index 96% rename from DatadogCore/Tests/Datadog/DatadogCore/FeatureContextTests.swift rename to DatadogCore/Tests/Datadog/DatadogCore/Context/FeatureContextTests.swift index 980e27a8ff..8d4ff45a63 100644 --- a/DatadogCore/Tests/Datadog/DatadogCore/FeatureContextTests.swift +++ b/DatadogCore/Tests/Datadog/DatadogCore/Context/FeatureContextTests.swift @@ -17,7 +17,7 @@ class FeatureContextTests: XCTestCase { dateProvider: SystemDateProvider(), initialConsent: .granted, performance: .mockAny(), - httpClient: .mockAny(), + httpClient: HTTPClientMock(), encryption: nil, contextProvider: .mockAny(), applicationVersion: .mockAny() diff --git a/DatadogCore/Tests/Datadog/DatadogCore/LaunchTimePublisherTests.swift b/DatadogCore/Tests/Datadog/DatadogCore/Context/LaunchTimePublisherTests.swift similarity index 100% rename from DatadogCore/Tests/Datadog/DatadogCore/LaunchTimePublisherTests.swift rename to DatadogCore/Tests/Datadog/DatadogCore/Context/LaunchTimePublisherTests.swift diff --git a/DatadogCore/Tests/Datadog/DatadogCore/LowPowerModePublisherTests.swift b/DatadogCore/Tests/Datadog/DatadogCore/Context/LowPowerModePublisherTests.swift similarity index 100% rename from DatadogCore/Tests/Datadog/DatadogCore/LowPowerModePublisherTests.swift rename to DatadogCore/Tests/Datadog/DatadogCore/Context/LowPowerModePublisherTests.swift diff --git a/DatadogCore/Tests/Datadog/DatadogCore/NetworkConnectionInfoPublisherTests.swift b/DatadogCore/Tests/Datadog/DatadogCore/Context/NetworkConnectionInfoPublisherTests.swift similarity index 100% rename from DatadogCore/Tests/Datadog/DatadogCore/NetworkConnectionInfoPublisherTests.swift rename to DatadogCore/Tests/Datadog/DatadogCore/Context/NetworkConnectionInfoPublisherTests.swift diff --git a/DatadogCore/Tests/Datadog/DatadogCore/ServerOffsetPublisherTests.swift b/DatadogCore/Tests/Datadog/DatadogCore/Context/ServerOffsetPublisherTests.swift similarity index 100% rename from DatadogCore/Tests/Datadog/DatadogCore/ServerOffsetPublisherTests.swift rename to DatadogCore/Tests/Datadog/DatadogCore/Context/ServerOffsetPublisherTests.swift diff --git a/DatadogCore/Tests/Datadog/DatadogCore/TrackingConsentPublisherTests.swift b/DatadogCore/Tests/Datadog/DatadogCore/Context/TrackingConsentPublisherTests.swift similarity index 100% rename from DatadogCore/Tests/Datadog/DatadogCore/TrackingConsentPublisherTests.swift rename to DatadogCore/Tests/Datadog/DatadogCore/Context/TrackingConsentPublisherTests.swift diff --git a/DatadogCore/Tests/Datadog/DatadogCore/UserInfoPublisherTests.swift b/DatadogCore/Tests/Datadog/DatadogCore/Context/UserInfoPublisherTests.swift similarity index 100% rename from DatadogCore/Tests/Datadog/DatadogCore/UserInfoPublisherTests.swift rename to DatadogCore/Tests/Datadog/DatadogCore/Context/UserInfoPublisherTests.swift diff --git a/DatadogCore/Tests/Datadog/DatadogCore/DatadogCoreTests.swift b/DatadogCore/Tests/Datadog/DatadogCore/DatadogCoreTests.swift index 15347ab099..e8e30038bb 100644 --- a/DatadogCore/Tests/Datadog/DatadogCore/DatadogCoreTests.swift +++ b/DatadogCore/Tests/Datadog/DatadogCore/DatadogCoreTests.swift @@ -33,15 +33,13 @@ class DatadogCoreTests: XCTestCase { } func testWhenWritingEventsWithDifferentTrackingConsent_itOnlyUploadsAuthorizedEvents() throws { - let server = ServerMock(delivery: .success(response: .mockResponseWith(statusCode: 200))) - // Given let core = DatadogCore( directory: temporaryCoreDirectory, dateProvider: SystemDateProvider(), initialConsent: .mockRandom(), performance: .mockRandom(), - httpClient: HTTPClient(session: server.getInterceptedURLSession()), + httpClient: HTTPClientMock(), encryption: nil, contextProvider: .mockAny(), applicationVersion: .mockAny() @@ -70,7 +68,6 @@ class DatadogCoreTests: XCTestCase { // Then core.flushAndTearDown() - server.waitFor(requestsCompletion: 1) let uploadedEvents = requestBuilderSpy.requestParameters .flatMap { $0.events } @@ -81,15 +78,13 @@ class DatadogCoreTests: XCTestCase { } func testWhenWritingEventsWithBypassingConsent_itUploadsAllEvents() throws { - let server = ServerMock(delivery: .success(response: .mockResponseWith(statusCode: 200))) - // Given let core = DatadogCore( directory: temporaryCoreDirectory, dateProvider: SystemDateProvider(), initialConsent: .mockRandom(), performance: .mockRandom(), - httpClient: HTTPClient(session: server.getInterceptedURLSession()), + httpClient: HTTPClientMock(), encryption: nil, contextProvider: .mockAny(), applicationVersion: .mockAny() @@ -118,7 +113,6 @@ class DatadogCoreTests: XCTestCase { // Then core.flushAndTearDown() - server.waitFor(requestsCompletion: 1) let uploadedEvents = requestBuilderSpy.requestParameters .flatMap { $0.events } @@ -137,15 +131,13 @@ class DatadogCoreTests: XCTestCase { } func testWhenWritingEventsWithForcingNewBatch_itUploadsEachEventInSeparateRequest() throws { - let server = ServerMock(delivery: .success(response: .mockResponseWith(statusCode: 200))) - // Given let core = DatadogCore( directory: temporaryCoreDirectory, dateProvider: RelativeDateProvider(advancingBySeconds: 0.01), initialConsent: .granted, performance: .mockRandom(), - httpClient: HTTPClient(session: server.getInterceptedURLSession()), + httpClient: HTTPClientMock(), encryption: nil, contextProvider: .mockAny(), applicationVersion: .mockAny() @@ -154,7 +146,7 @@ class DatadogCoreTests: XCTestCase { let requestBuilderSpy = FeatureRequestBuilderSpy() try core.register(feature: FeatureMock(requestBuilder: requestBuilderSpy)) - let scope = try XCTUnwrap(core.scope(for: "mock")) + let scope = try XCTUnwrap(core.scope(for: FeatureMock.name)) // When scope.eventWriteContext(forceNewBatch: true) { context, writer in @@ -171,7 +163,6 @@ class DatadogCoreTests: XCTestCase { // Then core.flushAndTearDown() - server.waitFor(requestsCompletion: 3) let uploadedEvents = requestBuilderSpy.requestParameters .flatMap { $0.events } @@ -190,23 +181,37 @@ class DatadogCoreTests: XCTestCase { } func testWhenPerformancePresetOverrideIsProvided_itOverridesPresets() throws { - let core = DatadogCore( + // Given + let core1 = DatadogCore( directory: temporaryCoreDirectory, dateProvider: RelativeDateProvider(advancingBySeconds: 0.01), initialConsent: .granted, performance: .mockRandom(), - httpClient: .mockAny(), + httpClient: HTTPClientMock(), encryption: nil, contextProvider: .mockAny(), applicationVersion: .mockAny() ) - try core.register( + let core2 = DatadogCore( + directory: temporaryCoreDirectory, + dateProvider: RelativeDateProvider(advancingBySeconds: 0.01), + initialConsent: .granted, + performance: .mockRandom(), + httpClient: HTTPClientMock(), + encryption: nil, + contextProvider: .mockAny(), + applicationVersion: .mockAny() + ) + defer { + core1.flushAndTearDown() + core2.flushAndTearDown() + } + + // When + try core1.register( feature: FeatureMock(performanceOverride: nil) ) - let store = core.stores.values.first - XCTAssertEqual(store?.storage.authorizedFilesOrchestrator.performance.maxObjectSize, UInt64(512).KB) - XCTAssertEqual(store?.storage.authorizedFilesOrchestrator.performance.maxFileSize, UInt64(4).MB) - try core.register( + try core2.register( feature: FeatureMock( performanceOverride: PerformancePresetOverride( maxFileSize: 123, @@ -216,11 +221,17 @@ class DatadogCoreTests: XCTestCase { ) ) ) - let storage = core.stores.values.first?.storage - XCTAssertEqual(storage?.authorizedFilesOrchestrator.performance.maxObjectSize, 456) - XCTAssertEqual(storage?.authorizedFilesOrchestrator.performance.maxFileSize, 123) - XCTAssertEqual(storage?.authorizedFilesOrchestrator.performance.maxFileAgeForWrite, 95) - XCTAssertEqual(storage?.authorizedFilesOrchestrator.performance.minFileAgeForRead, 105) + + // Then + let storage1 = core1.stores.values.first?.storage + XCTAssertEqual(storage1?.authorizedFilesOrchestrator.performance.maxObjectSize, UInt64(512).KB) + XCTAssertEqual(storage1?.authorizedFilesOrchestrator.performance.maxFileSize, UInt64(4).MB) + + let storage2 = core2.stores.values.first?.storage + XCTAssertEqual(storage2?.authorizedFilesOrchestrator.performance.maxObjectSize, 456) + XCTAssertEqual(storage2?.authorizedFilesOrchestrator.performance.maxFileSize, 123) + XCTAssertEqual(storage2?.authorizedFilesOrchestrator.performance.maxFileAgeForWrite, 95) + XCTAssertEqual(storage2?.authorizedFilesOrchestrator.performance.minFileAgeForRead, 105) } func testItUpdatesTheFeatureBaggage() throws { @@ -231,7 +242,7 @@ class DatadogCoreTests: XCTestCase { dateProvider: SystemDateProvider(), initialConsent: .mockRandom(), performance: .mockRandom(), - httpClient: .mockAny(), + httpClient: HTTPClientMock(), encryption: nil, contextProvider: contextProvider, applicationVersion: .mockAny() @@ -240,16 +251,16 @@ class DatadogCoreTests: XCTestCase { try core.register(feature: FeatureMock()) // When - core.update(feature: "mock") { + core.update(feature: FeatureMock.name) { return ["foo": "bar"] } - core.update(feature: "mock") { + core.update(feature: FeatureMock.name) { return ["bizz": "bazz"] } // Then let context = contextProvider.read() - XCTAssertEqual(context.featuresAttributes["mock"]?.foo, "bar") - XCTAssertEqual(context.featuresAttributes["mock"]?.bizz, "bazz") + XCTAssertEqual(context.featuresAttributes[FeatureMock.name]?.foo, "bar") + XCTAssertEqual(context.featuresAttributes[FeatureMock.name]?.bizz, "bazz") } } diff --git a/DatadogCore/Tests/Datadog/DatadogTests.swift b/DatadogCore/Tests/Datadog/DatadogTests.swift index 785f51314d..749aa57d13 100644 --- a/DatadogCore/Tests/Datadog/DatadogTests.swift +++ b/DatadogCore/Tests/Datadog/DatadogTests.swift @@ -55,8 +55,9 @@ class DatadogTests: XCTestCase { defer { Datadog.flushAndDeinitialize() } let core = try XCTUnwrap(CoreRegistry.default as? DatadogCore) + let urlSessionClient = try XCTUnwrap(core.httpClient as? URLSessionClient) XCTAssertTrue(core.dateProvider is SystemDateProvider) - XCTAssertNil(core.httpClient.session.configuration.connectionProxyDictionary) + XCTAssertNil(urlSessionClient.session.configuration.connectionProxyDictionary) XCTAssertNil(core.encryption) let context = core.contextProvider.read() @@ -117,7 +118,8 @@ class DatadogTests: XCTestCase { XCTAssertTrue(core.dateProvider is SystemDateProvider) XCTAssertTrue(core.encryption is DataEncryptionMock) - let connectionProxyDictionary = try XCTUnwrap(core.httpClient.session.configuration.connectionProxyDictionary) + let urlSessionClient = try XCTUnwrap(core.httpClient as? URLSessionClient) + let connectionProxyDictionary = try XCTUnwrap(urlSessionClient.session.configuration.connectionProxyDictionary) XCTAssertEqual(connectionProxyDictionary[kCFNetworkProxiesHTTPEnable] as? Bool, true) XCTAssertEqual(connectionProxyDictionary[kCFNetworkProxiesHTTPPort] as? Int, 123) XCTAssertEqual(connectionProxyDictionary[kCFNetworkProxiesHTTPProxy] as? String, "www.example.com") @@ -175,7 +177,7 @@ class DatadogTests: XCTestCase { XCTAssertEqual( printFunction.printedMessage, - "🔥 Datadog SDK usage error: SDK is already initialized." + "🔥 Datadog SDK usage error: The 'main' instance of SDK is already initialized." ) Datadog.flushAndDeinitialize() @@ -415,4 +417,28 @@ class DatadogTests: XCTestCase { XCTAssertTrue(CoreRegistry.default is NOPDatadogCore) XCTAssertTrue(CoreRegistry.instance(named: "test") is DatadogCore) } + + func testGivenDefaultSDKInstanceInitialized_customOneCanBeInitializedAfterIt() throws { + let defaultConfig = Datadog.Configuration(clientToken: "abc-123", env: "default") + let customConfig = Datadog.Configuration(clientToken: "def-456", env: "custom") + + // Given + Datadog.initialize( + with: defaultConfig, + trackingConsent: .mockRandom() + ) + defer { Datadog.flushAndDeinitialize() } + + // When + Datadog.initialize( + with: customConfig, + trackingConsent: .mockRandom(), + instanceName: "custom-instance" + ) + defer { Datadog.flushAndDeinitialize(instanceName: "custom-instance") } + + // Then + XCTAssertTrue(CoreRegistry.default is DatadogCore) + XCTAssertTrue(CoreRegistry.instance(named: "custom-instance") is DatadogCore) + } } diff --git a/DatadogCore/Tests/Datadog/LoggerTests.swift b/DatadogCore/Tests/Datadog/LoggerTests.swift index d0b1593733..12e16304ec 100644 --- a/DatadogCore/Tests/Datadog/LoggerTests.swift +++ b/DatadogCore/Tests/Datadog/LoggerTests.swift @@ -99,7 +99,7 @@ class LoggerTests: XCTestCase { let logMatcher = try core.waitAndReturnLogMatchers()[0] - logMatcher.assertServiceName(equals: "custom-service-name") + logMatcher.assertService(equals: "custom-service-name") logMatcher.assertLoggerName(equals: "custom-logger-name") logMatcher.assertValue(forKeyPath: "network.client.sim_carrier.name", isTypeOf: String.self) logMatcher.assertValue(forKeyPath: "network.client.sim_carrier.iso_country", isTypeOf: String.self) diff --git a/DatadogCore/Tests/Datadog/Logs/DatadogLogsFeatureTests.swift b/DatadogCore/Tests/Datadog/Logs/DatadogLogsFeatureTests.swift index 0ef7f62773..1d43c9f3a6 100644 --- a/DatadogCore/Tests/Datadog/Logs/DatadogLogsFeatureTests.swift +++ b/DatadogCore/Tests/Datadog/Logs/DatadogLogsFeatureTests.swift @@ -38,7 +38,7 @@ class DatadogLogsFeatureTests: XCTestCase { let randomEncryption: DataEncryption? = Bool.random() ? DataEncryptionMock() : nil let server = ServerMock(delivery: .success(response: .mockResponseWith(statusCode: 200))) - let httpClient = HTTPClient(session: server.getInterceptedURLSession()) + let httpClient = URLSessionClient(session: server.getInterceptedURLSession()) let core = DatadogCore( directory: temporaryCoreDirectory, @@ -70,12 +70,7 @@ class DatadogLogsFeatureTests: XCTestCase { defer { core.flushAndTearDown() } // Given - let feature = LogsFeature( - logEventMapper: nil, - dateProvider: SystemDateProvider(), - customIntakeURL: randomUploadURL - ) - try core.register(feature: feature) + Logs.enable(with: .init(customEndpoint: randomUploadURL), in: core) // When let logger = Logger.create(in: core) @@ -105,7 +100,7 @@ class DatadogLogsFeatureTests: XCTestCase { func testItUsesExpectedPayloadFormatForUploads() throws { let server = ServerMock(delivery: .success(response: .mockResponseWith(statusCode: 200))) - let httpClient = HTTPClient(session: server.getInterceptedURLSession()) + let httpClient = URLSessionClient(session: server.getInterceptedURLSession()) let core = DatadogCore( directory: temporaryCoreDirectory, @@ -136,12 +131,7 @@ class DatadogLogsFeatureTests: XCTestCase { defer { core.flushAndTearDown() } // Given - let feature = LogsFeature( - logEventMapper: nil, - dateProvider: SystemDateProvider(), - customIntakeURL: .mockAny() - ) - try core.register(feature: feature) + Logs.enable(with: .init(), in: core) let logger = Logger.create(in: core) logger.debug("log 1") diff --git a/DatadogCore/Tests/Datadog/Mocks/CoreMocks.swift b/DatadogCore/Tests/Datadog/Mocks/CoreMocks.swift index 42f86c34b4..273985acf1 100644 --- a/DatadogCore/Tests/Datadog/Mocks/CoreMocks.swift +++ b/DatadogCore/Tests/Datadog/Mocks/CoreMocks.swift @@ -299,23 +299,27 @@ extension DataFormat { } } -extension HTTPClient { - static func mockAny() -> HTTPClient { - return HTTPClient(session: .mockAny()) - } -} - class NOPDataUploadWorker: DataUploadWorkerType { func flushSynchronously() {} func cancelSynchronously() {} } -struct DataUploaderMock: DataUploaderType { +internal class DataUploaderMock: DataUploaderType { let uploadStatus: DataUploadStatus - var onUpload: (() throws -> Void)? = nil + /// Notifies on each started upload. + var onUpload: (() throws -> Void)? + + /// Tracks uploaded events. + private(set) var uploadedEvents: [Event] = [] + + init(uploadStatus: DataUploadStatus, onUpload: (() -> Void)? = nil) { + self.uploadStatus = uploadStatus + self.onUpload = onUpload + } func upload(events: [Event], context: DatadogContext) throws -> DataUploadStatus { + uploadedEvents += events try onUpload?() return uploadStatus } diff --git a/DatadogCore/Tests/Datadog/Mocks/DatadogInternal/DatadogCoreProxy.swift b/DatadogCore/Tests/Datadog/Mocks/DatadogInternal/DatadogCoreProxy.swift index 72ceda95ce..05904ce94c 100644 --- a/DatadogCore/Tests/Datadog/Mocks/DatadogInternal/DatadogCoreProxy.swift +++ b/DatadogCore/Tests/Datadog/Mocks/DatadogInternal/DatadogCoreProxy.swift @@ -42,7 +42,7 @@ internal class DatadogCoreProxy: DatadogCoreProtocol { dateProvider: SystemDateProvider(), initialConsent: context.trackingConsent, performance: .mockAny(), - httpClient: .mockAny(), + httpClient: HTTPClientMock(), encryption: nil, contextProvider: DatadogContextProvider(context: context), applicationVersion: context.version diff --git a/DatadogCore/Tests/Datadog/Mocks/DatadogInternal/UploadMock.swift b/DatadogCore/Tests/Datadog/Mocks/DatadogInternal/UploadMock.swift index 9f4ed08172..5009e59f55 100644 --- a/DatadogCore/Tests/Datadog/Mocks/DatadogInternal/UploadMock.swift +++ b/DatadogCore/Tests/Datadog/Mocks/DatadogInternal/UploadMock.swift @@ -7,36 +7,26 @@ import Foundation import TestUtilities import DatadogInternal -@testable import DatadogCore -internal struct FeatureRequestBuilderMock: FeatureRequestBuilder { - let url: URL - let queryItems: [URLRequestBuilder.QueryItem] - let headers: [URLRequestBuilder.HTTPHeader] - let format: DataFormat +internal class FeatureRequestBuilderMock: FeatureRequestBuilder { + private let factory: (([Event], DatadogContext) throws -> URLRequest) - init( - url: URL = .mockAny(), - queryItems: [URLRequestBuilder.QueryItem] = [], - headers: [URLRequestBuilder.HTTPHeader] = [], - format: DataFormat = .mockWith(prefix: "[", suffix: "]", separator: ",") - ) { - self.url = url - self.queryItems = queryItems - self.headers = headers - self.format = format + init(factory: @escaping (([Event], DatadogContext) throws -> URLRequest) = { _, _ in .mockAny() }) { + self.factory = factory + } + + convenience init(request: URLRequest) { + self.init(factory: { _, _ in request }) } func request(for events: [Event], with context: DatadogContext) throws -> URLRequest { - let builder = URLRequestBuilder(url: url, queryItems: queryItems, headers: headers) - let data = format.format(events.map { $0.data }) - return builder.uploadRequest(with: data) + return try factory(events, context) } } internal class FeatureRequestBuilderSpy: FeatureRequestBuilder { /// Records parameters passed to `requet(for:with:)` - var requestParameters: [(events: [Event], context: DatadogContext)] = [] + private(set) var requestParameters: [(events: [Event], context: DatadogContext)] = [] func request(for events: [Event], with context: DatadogContext) throws -> URLRequest { requestParameters.append((events: events, context: context)) diff --git a/DatadogCore/Tests/Datadog/Mocks/HTTPClientMock.swift b/DatadogCore/Tests/Datadog/Mocks/HTTPClientMock.swift new file mode 100644 index 0000000000..c2546f2541 --- /dev/null +++ b/DatadogCore/Tests/Datadog/Mocks/HTTPClientMock.swift @@ -0,0 +1,62 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-Present Datadog, Inc. + */ + +import Foundation +import TestUtilities +@testable import DatadogCore + +internal class HTTPClientMock: HTTPClient { + /// The queue to synchronise access to tracked requests. + private let queue = DispatchQueue(label: "com.datadoghq.HTTPClientMock-\(UUID().uuidString)") + /// Keeps track of sent requests. + private var requests: [URLRequest] = [] + /// Closure providing the result for each request. + private let result: (URLRequest) -> Result + + /// Initializes the mock client with a result closure. + /// - Parameter result: Closure providing the completion result for each incoming request (default is a successful HTTP response with `202` code). + init(result: @escaping ((URLRequest) -> Result) = { _ in .success(.mockResponseWith(statusCode: 202)) }) { + self.result = result + } + + /// Convenience initializer for creating a mock client with a predefined response. + /// - Parameter response: `HTTPURLResponse` to be used as completion for all incoming requests. + convenience init(response: HTTPURLResponse) { + self.init(result: { _ in .success(response) }) + } + + /// Convenience initializer for creating a mock client with a predefined response code. + /// - Parameter responseCode: HTTP status code to be used as completion for all incoming requests. + convenience init(responseCode: Int) { + self.init(response: .mockResponseWith(statusCode: responseCode)) + } + + /// Convenience initializer for creating a mock client with a predefined error. + /// - Parameter error: Error to be used as completion for all incoming requests. + convenience init(error: Error) { + self.init(result: { _ in .failure(error) }) + } + + // MARK: - HTTPClient conformance + + func send(request: URLRequest, completion: @escaping (Result) -> Void) { + queue.async { + completion(self.result(request)) + self.requests.append(request) + } + } + + // MARK: - Tracked requests retrieval + + /// Retrieves the tracked requests. + /// - Returns: An array of tracked URLRequest instances. + /// - Throws: An error if decompression fails. + func requestsSent() -> [URLRequest] { + queue.sync { + self.requests + } + } +} diff --git a/DatadogCore/Tests/Datadog/Mocks/RUMDataModelMocks.swift b/DatadogCore/Tests/Datadog/Mocks/RUMDataModelMocks.swift index 0a266cf1cd..523eec3f19 100644 --- a/DatadogCore/Tests/Datadog/Mocks/RUMDataModelMocks.swift +++ b/DatadogCore/Tests/Datadog/Mocks/RUMDataModelMocks.swift @@ -94,6 +94,12 @@ extension RUMOperatingSystem: RandomMockable { } } +extension RUMViewEvent.DD.Configuration: RandomMockable { + public static func mockRandom() -> RUMViewEvent.DD.Configuration { + return .init(sessionReplaySampleRate: .mockRandom(min: 0, max: 100), sessionSampleRate: .mockRandom(min: 0, max: 100)) + } +} + extension RUMViewEvent: RandomMockable { public static func mockRandom() -> RUMViewEvent { return mockRandomWith() @@ -108,7 +114,7 @@ extension RUMViewEvent: RandomMockable { return RUMViewEvent( dd: .init( browserSdkVersion: nil, - configuration: nil, + configuration: .mockRandom(), documentVersion: .mockRandom(), pageStates: nil, replayStats: nil, @@ -162,6 +168,7 @@ extension RUMViewEvent: RandomMockable { start: .mockRandom() ) ], + interactionToNextPaint: nil, isActive: viewIsActive, isSlowRendered: .mockRandom(), jsRefreshRate: nil, @@ -184,12 +191,18 @@ extension RUMViewEvent: RandomMockable { } } +extension RUMResourceEvent.DD.Configuration: RandomMockable { + public static func mockRandom() -> RUMResourceEvent.DD.Configuration { + .init(sessionReplaySampleRate: .mockRandom(min: 0, max: 100), sessionSampleRate: .mockRandom(min: 0, max: 100)) + } +} + extension RUMResourceEvent: RandomMockable { public static func mockRandom() -> RUMResourceEvent { return RUMResourceEvent( dd: .init( browserSdkVersion: nil, - configuration: nil, + configuration: .mockRandom(), discarded: nil, rulePsr: nil, session: .init(plan: .plan1), @@ -244,6 +257,12 @@ extension RUMResourceEvent: RandomMockable { } } +extension RUMActionEvent.DD.Configuration: RandomMockable { + public static func mockRandom() -> RUMActionEvent.DD.Configuration { + .init(sessionReplaySampleRate: .mockRandom(min: 0, max: 100), sessionSampleRate: .mockRandom(min: 0, max: 100)) + } +} + extension RUMActionEvent: RandomMockable { public static func mockRandom() -> RUMActionEvent { return RUMActionEvent( @@ -257,7 +276,7 @@ extension RUMActionEvent: RandomMockable { ) ), browserSdkVersion: nil, - configuration: nil, + configuration: .mockRandom(), session: .init(plan: .plan1) ), action: .init( @@ -305,12 +324,18 @@ extension RUMErrorEvent.Error.SourceType: RandomMockable { } } +extension RUMErrorEvent.DD.Configuration: RandomMockable { + public static func mockRandom() -> RUMErrorEvent.DD.Configuration { + .init(sessionReplaySampleRate: .mockRandom(min: 0, max: 100), sessionSampleRate: .mockRandom(min: 0, max: 100)) + } +} + extension RUMErrorEvent: RandomMockable { public static func mockRandom() -> RUMErrorEvent { return RUMErrorEvent( dd: .init( browserSdkVersion: nil, - configuration: nil, + configuration: .mockRandom(), session: .init(plan: .plan1) ), action: .init(id: .mockRandom()), @@ -376,12 +401,18 @@ extension RUMCrashEvent: RandomMockable { } } +extension RUMLongTaskEvent.DD.Configuration: RandomMockable { + public static func mockRandom() -> RUMLongTaskEvent.DD.Configuration { + return .init(sessionReplaySampleRate: .mockRandom(min: 0, max: 100), sessionSampleRate: .mockRandom(min: 0, max: 100)) + } +} + extension RUMLongTaskEvent: RandomMockable { public static func mockRandom() -> RUMLongTaskEvent { return RUMLongTaskEvent( dd: .init( browserSdkVersion: nil, - configuration: nil, + configuration: .mockRandom(), discarded: nil, session: .init(plan: .plan1) ), @@ -424,6 +455,8 @@ extension TelemetryConfigurationEvent: RandomMockable { telemetry: .init( configuration: .init( actionNameAttribute: nil, + allowFallbackToLocalStorage: nil, + allowUntrustedEvents: nil, batchSize: .mockAny(), batchUploadFrequency: .mockAny(), defaultPrivacyLevel: .mockAny(), @@ -467,6 +500,7 @@ extension TelemetryConfigurationEvent: RandomMockable { useProxy: .mockRandom(), useSecureSessionCookie: nil, useTracing: .mockRandom(), + useWorkerUrl: nil, viewTrackingStrategy: nil ) ), diff --git a/DatadogCore/Tests/Datadog/RUM/RUMFeatureTests.swift b/DatadogCore/Tests/Datadog/RUM/RUMFeatureTests.swift index 5757115960..cd3ab7791a 100644 --- a/DatadogCore/Tests/Datadog/RUM/RUMFeatureTests.swift +++ b/DatadogCore/Tests/Datadog/RUM/RUMFeatureTests.swift @@ -40,7 +40,7 @@ class RUMFeatureTests: XCTestCase { let randomEncryption: DataEncryption? = Bool.random() ? DataEncryptionMock() : nil let server = ServerMock(delivery: .success(response: .mockResponseWith(statusCode: 200))) - let httpClient = HTTPClient(session: server.getInterceptedURLSession()) + let httpClient = URLSessionClient(session: server.getInterceptedURLSession()) let core = DatadogCore( directory: temporaryCoreDirectory, @@ -109,7 +109,7 @@ class RUMFeatureTests: XCTestCase { func testItUsesExpectedPayloadFormatForUploads() throws { let server = ServerMock(delivery: .success(response: .mockResponseWith(statusCode: 200))) - let httpClient = HTTPClient(session: server.getInterceptedURLSession()) + let httpClient = URLSessionClient(session: server.getInterceptedURLSession()) let core = DatadogCore( directory: temporaryCoreDirectory, diff --git a/DatadogCore/Tests/Datadog/RUM/RUMMonitorTests.swift b/DatadogCore/Tests/Datadog/RUM/RUMMonitorTests.swift index 356f5c8877..1909e61161 100644 --- a/DatadogCore/Tests/Datadog/RUM/RUMMonitorTests.swift +++ b/DatadogCore/Tests/Datadog/RUM/RUMMonitorTests.swift @@ -1250,6 +1250,60 @@ class RUMMonitorTests: XCTestCase { XCTAssertEqual(try resourceEvents[0].attribute(forKeyPath: "context.def"), "789") } + // MARK: - Configuration + + func testRUMEvents_containSessionSampleRate() throws { + // Given + RUM.enable(with: config, in: core) + + let monitor = RUMMonitor.shared(in: core) + + // When + monitor.startView(viewController: mockView) + monitor.startAction(type: .scroll, name: .mockAny()) + monitor.startResource(resourceKey: "/resource/1", request: .mockAny()) + monitor.startResource(resourceKey: "/resource/2", request: .mockAny()) + monitor.stopAction(type: .scroll) + monitor.stopResource(resourceKey: "/resource/1", response: .mockAny()) + monitor.stopResourceWithError(resourceKey: "/resource/2", message: .mockAny()) + monitor.addError(message: .mockAny(), source: .source) + monitor._internal?.addLongTask(at: Date(), duration: 1.0) + monitor.stopView(viewController: mockView) + + let rumEventMatchers = try core.waitAndReturnRUMEventMatchers() + + // Then + let viewEvents = rumEventMatchers.filterRUMEvents(ofType: RUMViewEvent.self) + XCTAssertNotEqual(viewEvents.count, 0) + for event in viewEvents { + XCTAssertEqual(try event.attribute(forKeyPath: "_dd.configuration.session_sample_rate"), config.sessionSampleRate) + } + + let actionEvents = rumEventMatchers.filterRUMEvents(ofType: RUMActionEvent.self) + XCTAssertNotEqual(actionEvents.count, 0) + for event in actionEvents { + XCTAssertEqual(try event.attribute(forKeyPath: "_dd.configuration.session_sample_rate"), config.sessionSampleRate) + } + + let resourceEvents = rumEventMatchers.filterRUMEvents(ofType: RUMResourceEvent.self) + XCTAssertNotEqual(resourceEvents.count, 0) + for event in resourceEvents { + XCTAssertEqual(try event.attribute(forKeyPath: "_dd.configuration.session_sample_rate"), config.sessionSampleRate) + } + + let errorEvents = rumEventMatchers.filterRUMEvents(ofType: RUMErrorEvent.self) + XCTAssertNotEqual(errorEvents.count, 0) + for event in errorEvents { + XCTAssertEqual(try event.attribute(forKeyPath: "_dd.configuration.session_sample_rate"), config.sessionSampleRate) + } + + let longTaskEvents = rumEventMatchers.filterRUMEvents(ofType: RUMLongTaskEvent.self) + XCTAssertNotEqual(longTaskEvents.count, 0) + for event in longTaskEvents { + XCTAssertEqual(try event.attribute(forKeyPath: "_dd.configuration.session_sample_rate"), config.sessionSampleRate) + } + } + // MARK: - Internal attributes func testHandlingInternalTimestampAttribute() throws { diff --git a/DatadogCore/Tests/Datadog/TracerTests.swift b/DatadogCore/Tests/Datadog/TracerTests.swift index 997441a563..d56f931d0f 100644 --- a/DatadogCore/Tests/Datadog/TracerTests.swift +++ b/DatadogCore/Tests/Datadog/TracerTests.swift @@ -744,14 +744,14 @@ class TracerTests: XCTestCase { XCTAssertEqual(httpHeadersWriter.traceHeaderFields, expectedHTTPHeaders2) } - func testItInjectsSpanContextWithOTelHTTPHeadersWriter_usingMultipleHeaders() { + func testItInjectsSpanContextWithB3HTTPHeadersWriter_usingMultipleHeaders() { Trace.enable(with: config, in: core) let tracer = Tracer.shared(in: core) let spanContext1 = DDSpanContext(traceID: 1, spanID: 2, parentSpanID: 3, baggageItems: .mockAny()) let spanContext2 = DDSpanContext(traceID: 4, spanID: 5, parentSpanID: 6, baggageItems: .mockAny()) let spanContext3 = DDSpanContext(traceID: 77, spanID: 88, parentSpanID: nil, baggageItems: .mockAny()) - let httpHeadersWriter = OTelHTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .multiple) + let httpHeadersWriter = B3HTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .multiple) XCTAssertEqual(httpHeadersWriter.traceHeaderFields, [:]) // When @@ -790,14 +790,14 @@ class TracerTests: XCTestCase { XCTAssertEqual(httpHeadersWriter.traceHeaderFields, expectedHTTPHeaders3) } - func testItInjectsSpanContextWithOTelHTTPHeadersWriter_usingSingleHeader() { + func testItInjectsSpanContextWithB3HTTPHeadersWriter_usingSingleHeader() { Trace.enable(with: config, in: core) let tracer = Tracer.shared(in: core) let spanContext1 = DDSpanContext(traceID: 1, spanID: 2, parentSpanID: 3, baggageItems: .mockAny()) let spanContext2 = DDSpanContext(traceID: 4, spanID: 5, parentSpanID: 6, baggageItems: .mockAny()) let spanContext3 = DDSpanContext(traceID: 77, spanID: 88, parentSpanID: nil, baggageItems: .mockAny()) - let httpHeadersWriter = OTelHTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .single) + let httpHeadersWriter = B3HTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .single) XCTAssertEqual(httpHeadersWriter.traceHeaderFields, [:]) // When @@ -828,12 +828,12 @@ class TracerTests: XCTestCase { XCTAssertEqual(httpHeadersWriter.traceHeaderFields, expectedHTTPHeaders3) } - func testItInjectsRejectedSpanContextWithOTelHTTPHeadersWriter_usingSingleHeader() { + func testItInjectsRejectedSpanContextWithB3HTTPHeadersWriter_usingSingleHeader() { Trace.enable(with: config, in: core) let tracer = Tracer.shared(in: core) let spanContext = DDSpanContext(traceID: 1, spanID: 2, parentSpanID: .mockAny(), baggageItems: .mockAny()) - let httpHeadersWriter = OTelHTTPHeadersWriter(sampler: .mockRejectAll()) + let httpHeadersWriter = B3HTTPHeadersWriter(sampler: .mockRejectAll()) XCTAssertEqual(httpHeadersWriter.traceHeaderFields, [:]) // When @@ -846,12 +846,12 @@ class TracerTests: XCTestCase { XCTAssertEqual(httpHeadersWriter.traceHeaderFields, expectedHTTPHeaders) } - func testItInjectsRejectedSpanContextWithOTelHTTPHeadersWriter_usingMultipleHeader() { + func testItInjectsRejectedSpanContextWithB3HTTPHeadersWriter_usingMultipleHeader() { Trace.enable(with: config, in: core) let tracer = Tracer.shared(in: core) let spanContext = DDSpanContext(traceID: 1, spanID: 2, parentSpanID: .mockAny(), baggageItems: .mockAny()) - let httpHeadersWriter = OTelHTTPHeadersWriter(sampler: .mockRejectAll(), injectEncoding: .multiple) + let httpHeadersWriter = B3HTTPHeadersWriter(sampler: .mockRejectAll(), injectEncoding: .multiple) XCTAssertEqual(httpHeadersWriter.traceHeaderFields, [:]) // When @@ -956,15 +956,15 @@ class TracerTests: XCTestCase { XCTAssertNil(extractedSpanContext?.dd.parentSpanID) } - func testItExtractsSpanContextWithOTelHTTPHeadersReader_forMultipleHeaders() { + func testItExtractsSpanContextWithB3HTTPHeadersReader_forMultipleHeaders() { Trace.enable(with: config, in: core) let tracer = Tracer.shared(in: core) let injectedSpanContext = DDSpanContext(traceID: 1, spanID: 2, parentSpanID: 3, baggageItems: .mockAny()) - let httpHeadersWriter = OTelHTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .multiple) + let httpHeadersWriter = B3HTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .multiple) tracer.inject(spanContext: injectedSpanContext, writer: httpHeadersWriter) - let httpHeadersReader = OTelHTTPHeadersReader( + let httpHeadersReader = B3HTTPHeadersReader( httpHeaderFields: httpHeadersWriter.traceHeaderFields ) let extractedSpanContext = tracer.extract(reader: httpHeadersReader) @@ -974,15 +974,15 @@ class TracerTests: XCTestCase { XCTAssertEqual(extractedSpanContext?.dd.parentSpanID, injectedSpanContext.dd.parentSpanID) } - func testItExtractsSpanContextWithOTelHTTPHeadersReader_forSingleHeader() { + func testItExtractsSpanContextWithB3HTTPHeadersReader_forSingleHeader() { Trace.enable(with: config, in: core) let tracer = Tracer.shared(in: core) let injectedSpanContext = DDSpanContext(traceID: 1, spanID: 2, parentSpanID: 3, baggageItems: .mockAny()) - let httpHeadersWriter = OTelHTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .single) + let httpHeadersWriter = B3HTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .single) tracer.inject(spanContext: injectedSpanContext, writer: httpHeadersWriter) - let httpHeadersReader = OTelHTTPHeadersReader( + let httpHeadersReader = B3HTTPHeadersReader( httpHeaderFields: httpHeadersWriter.traceHeaderFields ) let extractedSpanContext = tracer.extract(reader: httpHeadersReader) diff --git a/DatadogCore/Tests/Datadog/Tracing/DatadogTraceFeatureTests.swift b/DatadogCore/Tests/Datadog/Tracing/DatadogTraceFeatureTests.swift index 201c7def40..0e0f5f65ea 100644 --- a/DatadogCore/Tests/Datadog/Tracing/DatadogTraceFeatureTests.swift +++ b/DatadogCore/Tests/Datadog/Tracing/DatadogTraceFeatureTests.swift @@ -38,7 +38,7 @@ class DatadogTraceFeatureTests: XCTestCase { let randomEncryption: DataEncryption? = Bool.random() ? DataEncryptionMock() : nil let server = ServerMock(delivery: .success(response: .mockResponseWith(statusCode: 200))) - let httpClient = HTTPClient(session: server.getInterceptedURLSession()) + let httpClient = URLSessionClient(session: server.getInterceptedURLSession()) let core = DatadogCore( directory: temporaryCoreDirectory, @@ -101,7 +101,7 @@ class DatadogTraceFeatureTests: XCTestCase { func testItUsesExpectedPayloadFormatForUploads() throws { let server = ServerMock(delivery: .success(response: .mockResponseWith(statusCode: 200))) - let httpClient = HTTPClient(session: server.getInterceptedURLSession()) + let httpClient = URLSessionClient(session: server.getInterceptedURLSession()) let core = DatadogCore( directory: temporaryCoreDirectory, diff --git a/DatadogCore/Tests/DatadogObjc/DDSessionReplayTests.swift b/DatadogCore/Tests/DatadogObjc/DDSessionReplayTests.swift new file mode 100644 index 0000000000..735b50ab3d --- /dev/null +++ b/DatadogCore/Tests/DatadogObjc/DDSessionReplayTests.swift @@ -0,0 +1,78 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-Present Datadog, Inc. + */ + +#if os(iOS) + +import XCTest +import TestUtilities +import DatadogInternal + +@testable import DatadogObjc +@testable import DatadogSessionReplay + +class DDSessionReplayTests: XCTestCase { + func testDefaultConfiguration() { + // Given + let sampleRate: Float = .mockRandom(min: 0, max: 100) + + // When + let config = DDSessionReplayConfiguration(replaySampleRate: sampleRate) + + // Then + XCTAssertEqual(config._swift.replaySampleRate, sampleRate) + XCTAssertEqual(config._swift.defaultPrivacyLevel, .mask) + XCTAssertNil(config._swift.customEndpoint) + } + + func testConfigurationOverrides() { + // Given + let sampleRate: Float = .mockRandom(min: 0, max: 100) + let privacy: DDSessionReplayConfigurationPrivacyLevel = [.allow, .mask, .maskUserInput].randomElement()! + let url: URL = .mockRandom() + + // When + let config = DDSessionReplayConfiguration(replaySampleRate: 100) + config.replaySampleRate = sampleRate + config.defaultPrivacyLevel = privacy + config.customEndpoint = url + + // Then + XCTAssertEqual(config._swift.replaySampleRate, sampleRate) + XCTAssertEqual(config._swift.defaultPrivacyLevel, privacy._swift) + XCTAssertEqual(config._swift.customEndpoint, url) + } + + func testPrivacyLevelsInterop() { + XCTAssertEqual(DDSessionReplayConfigurationPrivacyLevel.allow._swift, .allow) + XCTAssertEqual(DDSessionReplayConfigurationPrivacyLevel.mask._swift, .mask) + XCTAssertEqual(DDSessionReplayConfigurationPrivacyLevel.maskUserInput._swift, .maskUserInput) + + XCTAssertEqual(DDSessionReplayConfigurationPrivacyLevel(.allow), .allow) + XCTAssertEqual(DDSessionReplayConfigurationPrivacyLevel(.mask), .mask) + XCTAssertEqual(DDSessionReplayConfigurationPrivacyLevel(.maskUserInput), .maskUserInput) + } + + func testWhenEnabled() throws { + // Given + let core = FeatureRegistrationCoreMock() + CoreRegistry.register(default: core) + defer { CoreRegistry.unregisterDefault() } + + let config = DDSessionReplayConfiguration(replaySampleRate: 42) + + // When + DDSessionReplay.enable(with: config) + + // Then + let sr = try XCTUnwrap(core.get(feature: SessionReplayFeature.self)) + let requestBuilder = try XCTUnwrap(sr.requestBuilder as? DatadogSessionReplay.RequestBuilder) + XCTAssertEqual(sr.recordingCoordinator.sampler.samplingRate, 42) + XCTAssertEqual(sr.recordingCoordinator.privacy, .mask) + XCTAssertNil(requestBuilder.customUploadURL) + } +} + +#endif diff --git a/DatadogCore/Tests/DatadogObjc/DDTracerTests.swift b/DatadogCore/Tests/DatadogObjc/DDTracerTests.swift index bf1c0a5b1b..b633963c90 100644 --- a/DatadogCore/Tests/DatadogObjc/DDTracerTests.swift +++ b/DatadogCore/Tests/DatadogObjc/DDTracerTests.swift @@ -203,7 +203,7 @@ class DDTracerTests: XCTestCase { swiftSpanContext: DDSpanContext.mockWith(traceID: 1, spanID: 2) ) - let objcWriter = DDHTTPHeadersWriter(samplingRate: 100) + let objcWriter = DDHTTPHeadersWriter(sampleRate: 100) try objcTracer.inject(objcSpanContext, format: OT.formatTextMap, carrier: objcWriter) let expectedHTTPHeaders = [ @@ -221,7 +221,7 @@ class DDTracerTests: XCTestCase { swiftSpanContext: DDSpanContext.mockWith(traceID: 1, spanID: 2) ) - let objcWriter = DDHTTPHeadersWriter(samplingRate: 0) + let objcWriter = DDHTTPHeadersWriter(sampleRate: 0) try objcTracer.inject(objcSpanContext, format: OT.formatTextMap, carrier: objcWriter) let expectedHTTPHeaders = [ @@ -235,7 +235,7 @@ class DDTracerTests: XCTestCase { let objcTracer = DDTracer.shared() let objcSpanContext = DDSpanContextObjc(swiftSpanContext: DDSpanContext.mockWith(traceID: 1, spanID: 2)) - let objcValidWriter = DDHTTPHeadersWriter(samplingRate: 100) + let objcValidWriter = DDHTTPHeadersWriter(sampleRate: 100) let objcInvalidFormat = "foo" XCTAssertThrowsError( try objcTracer.inject(objcSpanContext, format: objcInvalidFormat, carrier: objcValidWriter) @@ -248,14 +248,14 @@ class DDTracerTests: XCTestCase { ) } - func testInjectingSpanContextToValidCarrierAndFormatForOTel() throws { + func testInjectingSpanContextToValidCarrierAndFormatForB3() throws { Trace.enable(with: config) let objcTracer = DDTracer.shared() let objcSpanContext = DDSpanContextObjc( swiftSpanContext: DDSpanContext.mockWith(traceID: 1, spanID: 2) ) - let objcWriter = DDOTelHTTPHeadersWriter(samplingRate: 100) + let objcWriter = DDB3HTTPHeadersWriter(sampleRate: 100) try objcTracer.inject(objcSpanContext, format: OT.formatTextMap, carrier: objcWriter) let expectedHTTPHeaders = [ @@ -264,14 +264,14 @@ class DDTracerTests: XCTestCase { XCTAssertEqual(objcWriter.traceHeaderFields, expectedHTTPHeaders) } - func testInjectingRejectedSpanContextToValidCarrierAndFormatForOTel() throws { + func testInjectingRejectedSpanContextToValidCarrierAndFormatForB3() throws { Trace.enable(with: config) let objcTracer = DDTracer.shared() let objcSpanContext = DDSpanContextObjc( swiftSpanContext: DDSpanContext.mockWith(traceID: 1, spanID: 2) ) - let objcWriter = DDOTelHTTPHeadersWriter(samplingRate: 0) + let objcWriter = DDB3HTTPHeadersWriter(sampleRate: 0) try objcTracer.inject(objcSpanContext, format: OT.formatTextMap, carrier: objcWriter) let expectedHTTPHeaders = [ @@ -280,12 +280,12 @@ class DDTracerTests: XCTestCase { XCTAssertEqual(objcWriter.traceHeaderFields, expectedHTTPHeaders) } - func testInjectingSpanContextToInvalidCarrierOrFormatForOTel() throws { + func testInjectingSpanContextToInvalidCarrierOrFormatForB3() throws { Trace.enable(with: config) let objcTracer = DDTracer.shared() let objcSpanContext = DDSpanContextObjc(swiftSpanContext: DDSpanContext.mockWith(traceID: 1, spanID: 2)) - let objcValidWriter = DDOTelHTTPHeadersWriter(samplingRate: 100) + let objcValidWriter = DDB3HTTPHeadersWriter(sampleRate: 100) let objcInvalidFormat = "foo" XCTAssertThrowsError( try objcTracer.inject(objcSpanContext, format: objcInvalidFormat, carrier: objcValidWriter) @@ -305,7 +305,7 @@ class DDTracerTests: XCTestCase { swiftSpanContext: DDSpanContext.mockWith(traceID: 1, spanID: 2) ) - let objcWriter = DDW3CHTTPHeadersWriter(samplingRate: 100) + let objcWriter = DDW3CHTTPHeadersWriter(sampleRate: 100) try objcTracer.inject(objcSpanContext, format: OT.formatTextMap, carrier: objcWriter) let expectedHTTPHeaders = [ @@ -321,7 +321,7 @@ class DDTracerTests: XCTestCase { swiftSpanContext: DDSpanContext.mockWith(traceID: 1, spanID: 2) ) - let objcWriter = DDW3CHTTPHeadersWriter(samplingRate: 0) + let objcWriter = DDW3CHTTPHeadersWriter(sampleRate: 0) try objcTracer.inject(objcSpanContext, format: OT.formatTextMap, carrier: objcWriter) let expectedHTTPHeaders = [ @@ -335,7 +335,7 @@ class DDTracerTests: XCTestCase { let objcTracer = DDTracer.shared() let objcSpanContext = DDSpanContextObjc(swiftSpanContext: DDSpanContext.mockWith(traceID: 1, spanID: 2)) - let objcValidWriter = DDW3CHTTPHeadersWriter(samplingRate: 100) + let objcValidWriter = DDW3CHTTPHeadersWriter(sampleRate: 100) let objcInvalidFormat = "foo" XCTAssertThrowsError( try objcTracer.inject(objcSpanContext, format: objcInvalidFormat, carrier: objcValidWriter) diff --git a/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDOTelHTTPHeadersWriter+apiTests.m b/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDB3HTTPHeadersWriter+apiTests.m similarity index 72% rename from DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDOTelHTTPHeadersWriter+apiTests.m rename to DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDB3HTTPHeadersWriter+apiTests.m index a2ec4b62d5..57beff0c08 100644 --- a/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDOTelHTTPHeadersWriter+apiTests.m +++ b/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDB3HTTPHeadersWriter+apiTests.m @@ -7,19 +7,19 @@ #import @import DatadogObjc; -@interface DDOTelHTTPHeadersWriter_apiTests : XCTestCase +@interface DDB3HTTPHeadersWriter_apiTests : XCTestCase @end /* * `DatadogObjc` APIs smoke tests - only check if the interface is available to Objc. */ -@implementation DDOTelHTTPHeadersWriter_apiTests +@implementation DDB3HTTPHeadersWriter_apiTests #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-value" - (void)testInitWithSamplingRate { - [[DDOTelHTTPHeadersWriter alloc] initWithSamplingRate:100 injectEncoding:DDInjectEncodingSingle]; + [[DDB3HTTPHeadersWriter alloc] initWithSampleRate:100 injectEncoding:DDInjectEncodingSingle]; } #pragma clang diagnostic pop diff --git a/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDHTTPHeadersWriter+apiTests.m b/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDHTTPHeadersWriter+apiTests.m index 4d39edf4b2..cab2afaeb6 100644 --- a/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDHTTPHeadersWriter+apiTests.m +++ b/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDHTTPHeadersWriter+apiTests.m @@ -19,7 +19,7 @@ @implementation DDHTTPHeadersWriter_apiTests #pragma clang diagnostic ignored "-Wunused-value" - (void)testInitWithSamplingRate { - [[DDHTTPHeadersWriter alloc] initWithSamplingRate:50]; + [[DDHTTPHeadersWriter alloc] initWithSampleRate:50]; } #pragma clang diagnostic pop diff --git a/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDSessionReplay+apiTests.m b/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDSessionReplay+apiTests.m new file mode 100644 index 0000000000..19c187969d --- /dev/null +++ b/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDSessionReplay+apiTests.m @@ -0,0 +1,28 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-Present Datadog, Inc. + */ + +#import + +#if TARGET_OS_IOS + +@import DatadogObjc; + +@interface DDSessionReplay_apiTests : XCTestCase +@end + +@implementation DDSessionReplay_apiTests + +- (void)testConfiguration { + DDSessionReplayConfiguration *configuration = [[DDSessionReplayConfiguration alloc] initWithReplaySampleRate:100]; + configuration.defaultPrivacyLevel = DDSessionReplayConfigurationPrivacyLevelAllow; + configuration.customEndpoint = [NSURL new]; + + [DDSessionReplay enableWith:configuration]; +} + +@end + +#endif diff --git a/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDW3CHTTPHeadersWriter+apiTests.m b/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDW3CHTTPHeadersWriter+apiTests.m index 1ad07d22a2..f75ea8ce27 100644 --- a/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDW3CHTTPHeadersWriter+apiTests.m +++ b/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDW3CHTTPHeadersWriter+apiTests.m @@ -19,7 +19,7 @@ @implementation DDW3CHTTPHeadersWriter_apiTests #pragma clang diagnostic ignored "-Wunused-value" - (void)testInitWithSamplingRate { - [[DDW3CHTTPHeadersWriter alloc] initWithSamplingRate:50]; + [[DDW3CHTTPHeadersWriter alloc] initWithSampleRate:50]; } #pragma clang diagnostic pop diff --git a/DatadogCore/Tests/Matchers/LogMatcher.swift b/DatadogCore/Tests/Matchers/LogMatcher.swift index 423b8ee219..09e21f9a16 100644 --- a/DatadogCore/Tests/Matchers/LogMatcher.swift +++ b/DatadogCore/Tests/Matchers/LogMatcher.swift @@ -5,6 +5,7 @@ */ import XCTest +import TestUtilities /// Provides set of assertions for single `Log` JSON object and collection of `[Log]`. /// Note: this file is individually referenced by integration tests target, so no dependency on other source files should be introduced. @@ -14,7 +15,7 @@ internal class LogMatcher: JSONDataMatcher { static let date = "date" static let status = "status" static let message = "message" - static let serviceName = "service" + static let service = "service" static let tags = "ddtags" // MARK: - Application info @@ -77,6 +78,14 @@ internal class LogMatcher: JSONDataMatcher { .map { LogMatcher(from: $0) } } + class func fromLogsRequest(_ request: URLRequest, file: StaticString = #file, line: UInt = #line) throws -> [LogMatcher] { + guard let body = try request.decompressed().httpBody else { + XCTFail("Request has no body", file: file, line: line) + return [] + } + return try fromArrayOfJSONObjectsData(body) + } + override private init(from jsonObject: [String: Any]) { super.init(from: jsonObject) } @@ -95,8 +104,8 @@ internal class LogMatcher: JSONDataMatcher { XCTAssertTrue(datePredicate(date), file: file, line: line) } - func assertServiceName(equals serviceName: String, file: StaticString = #file, line: UInt = #line) { - assertValue(forKey: JSONKey.serviceName, equals: serviceName, file: file, line: line) + func assertService(equals serviceName: String, file: StaticString = #file, line: UInt = #line) { + assertValue(forKey: JSONKey.service, equals: serviceName, file: file, line: line) } func assertThreadName(equals threadName: String, file: StaticString = #file, line: UInt = #line) { diff --git a/DatadogCore/Tests/TestsObserver/DatadogTestsObserver.swift b/DatadogCore/Tests/TestsObserver/DatadogTestsObserver.swift index 23a25ccbd0..f0b924f3c0 100644 --- a/DatadogCore/Tests/TestsObserver/DatadogTestsObserver.swift +++ b/DatadogCore/Tests/TestsObserver/DatadogTestsObserver.swift @@ -23,19 +23,13 @@ internal class DatadogTestsObserver: NSObject, XCTestObservation { /// A list of checks ensuring global state integrity before and after each tests. private let checks: [TestIntegrityCheck] = [ .init( - assert: { !Datadog.isInitialized() }, - problem: "`Datadog` must not be initialized.", + assert: { CoreRegistry.instances.isEmpty }, + problem: "No instance of `DatadogCore` must be left initialized after test completion.", solution: """ - Make sure `Datadog.flushAndDeinitialize()` is called before the end of test that uses `Datadog.initialize()`. - """ - ), - .init( - assert: { - CoreRegistry.default is NOPDatadogCore - }, - problem: "`CoreRegistry.default` must be reset after each test.", - solution: """ - Make sure `CoreRegistry.unregisterDefault()` is called after the end of test that register a default core. + Make sure deinitialization APIs are called before the end of test that registers `DatadogCore`. + If registering directly to `CoreRegistry`, make sure the test cleans it up properly. + + `DatadogTestsObserver` found following instances still being registered: \(CoreRegistry.instances.map({ "'\($0.key)'" })) """ ), .init( diff --git a/DatadogCrashReporting.podspec b/DatadogCrashReporting.podspec index 8c0502fadf..ca61389458 100644 --- a/DatadogCrashReporting.podspec +++ b/DatadogCrashReporting.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "DatadogCrashReporting" - s.version = "2.0.0" + s.version = "2.1.0" s.summary = "Official Datadog Crash Reporting SDK for iOS." s.homepage = "https://www.datadoghq.com" @@ -23,5 +23,5 @@ Pod::Spec.new do |s| s.source_files = "DatadogCrashReporting/Sources/**/*.swift" s.dependency 'DatadogInternal', s.version.to_s - s.dependency 'PLCrashReporter', '~> 1.11.0' + s.dependency 'PLCrashReporter', '~> 1.11.1' end diff --git a/DatadogInternal.podspec b/DatadogInternal.podspec index c484fbd58c..93f512221c 100644 --- a/DatadogInternal.podspec +++ b/DatadogInternal.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "DatadogInternal" - s.version = "2.0.0" + s.version = "2.1.0" s.summary = "Datadog Internal Package. This module is not for public use." s.homepage = "https://www.datadoghq.com" diff --git a/DatadogInternal/Sources/CoreRegistry.swift b/DatadogInternal/Sources/CoreRegistry.swift index 36f5bf82b1..f3c917038a 100644 --- a/DatadogInternal/Sources/CoreRegistry.swift +++ b/DatadogInternal/Sources/CoreRegistry.swift @@ -20,7 +20,7 @@ public final class CoreRegistry { public static let defaultInstanceName = "main" @ReadWriteLock - private static var instances: [String: DatadogCoreProtocol] = [:] + internal private(set) static var instances: [String: DatadogCoreProtocol] = [:] private init() { } @@ -37,11 +37,19 @@ public final class CoreRegistry { /// - instance: The core instance /// - name: The name of the given instance. public static func register(_ instance: DatadogCoreProtocol, named name: String) { - if instances[name] == nil { - instances[name] = instance - } else { + guard !isRegistered(instanceName: name) else { DD.logger.warn("A core instance with name \(name) has already been registered.") + return } + instances[name] = instance + } + + /// Checks if a core instance with the specified name is currently registered. + /// + /// - Parameter instanceName: The name of the core instance to check. + /// - Returns: `true` if an instance with the given name is registered, otherwise `false`. + public static func isRegistered(instanceName: String) -> Bool { + return instances[instanceName] != nil } /// Unregisters the instance for the given name. diff --git a/DatadogInternal/Sources/NetworkInstrumentation/OpenTelemetry/OTelHTTPHeaders.swift b/DatadogInternal/Sources/NetworkInstrumentation/B3/B3HTTPHeaders.swift similarity index 93% rename from DatadogInternal/Sources/NetworkInstrumentation/OpenTelemetry/OTelHTTPHeaders.swift rename to DatadogInternal/Sources/NetworkInstrumentation/B3/B3HTTPHeaders.swift index fbc628e038..0d2886a2ab 100644 --- a/DatadogInternal/Sources/NetworkInstrumentation/OpenTelemetry/OTelHTTPHeaders.swift +++ b/DatadogInternal/Sources/NetworkInstrumentation/B3/B3HTTPHeaders.swift @@ -6,9 +6,12 @@ import Foundation -/// Open Telemetry propagation headers as explained in +@available(*, deprecated, renamed: "B3HTTPHeaders") +public typealias OTelHTTPHeaders = B3HTTPHeaders + +/// B3 propagation headers as explained in /// https://github.com/openzipkin/b3-propagation/blob/master/RATIONALE.md -public enum OTelHTTPHeaders { +public enum B3HTTPHeaders { public enum Multiple { /// The `X-B3-TraceId` header is encoded as 32 or 16 lower-hex characters. /// For example, a 128-bit TraceId header might look like: `X-B3-TraceId: 463ac35c9f6413ad48485a3953bb6124`. diff --git a/DatadogInternal/Sources/NetworkInstrumentation/OpenTelemetry/OTelHTTPHeadersReader.swift b/DatadogInternal/Sources/NetworkInstrumentation/B3/B3HTTPHeadersReader.swift similarity index 69% rename from DatadogInternal/Sources/NetworkInstrumentation/OpenTelemetry/OTelHTTPHeadersReader.swift rename to DatadogInternal/Sources/NetworkInstrumentation/B3/B3HTTPHeadersReader.swift index 3ca0604b48..528943d0a7 100644 --- a/DatadogInternal/Sources/NetworkInstrumentation/OpenTelemetry/OTelHTTPHeadersReader.swift +++ b/DatadogInternal/Sources/NetworkInstrumentation/B3/B3HTTPHeadersReader.swift @@ -6,7 +6,10 @@ import Foundation -public class OTelHTTPHeadersReader: TracePropagationHeadersReader { +@available(*, deprecated, renamed: "B3HTTPHeadersReader") +public typealias OTelHTTPHeadersReader = B3HTTPHeadersReader + +public class B3HTTPHeadersReader: TracePropagationHeadersReader { private let httpHeaderFields: [String: String] public init(httpHeaderFields: [String: String]) { @@ -14,20 +17,20 @@ public class OTelHTTPHeadersReader: TracePropagationHeadersReader { } public func read() -> (traceID: TraceID, spanID: SpanID, parentSpanID: SpanID?)? { - if let traceIDValue = httpHeaderFields[OTelHTTPHeaders.Multiple.traceIDField], - let spanIDValue = httpHeaderFields[OTelHTTPHeaders.Multiple.spanIDField], + if let traceIDValue = httpHeaderFields[B3HTTPHeaders.Multiple.traceIDField], + let spanIDValue = httpHeaderFields[B3HTTPHeaders.Multiple.spanIDField], let traceID = TraceID(traceIDValue, representation: .hexadecimal), let spanID = TraceID(spanIDValue, representation: .hexadecimal) { return ( traceID: traceID, spanID: spanID, - parentSpanID: httpHeaderFields[OTelHTTPHeaders.Multiple.parentSpanIDField] + parentSpanID: httpHeaderFields[B3HTTPHeaders.Multiple.parentSpanIDField] .flatMap { TraceID($0, representation: .hexadecimal) } ) } - let b3Value = httpHeaderFields[OTelHTTPHeaders.Single.b3Field]? - .components(separatedBy: OTelHTTPHeaders.Constants.b3Separator) + let b3Value = httpHeaderFields[B3HTTPHeaders.Single.b3Field]? + .components(separatedBy: B3HTTPHeaders.Constants.b3Separator) if let traceIDValue = b3Value?[safe: 0], let spanIDValue = b3Value?[safe: 1], diff --git a/DatadogInternal/Sources/NetworkInstrumentation/B3/B3HTTPHeadersWriter.swift b/DatadogInternal/Sources/NetworkInstrumentation/B3/B3HTTPHeadersWriter.swift new file mode 100644 index 0000000000..b6b219d9e6 --- /dev/null +++ b/DatadogInternal/Sources/NetworkInstrumentation/B3/B3HTTPHeadersWriter.swift @@ -0,0 +1,143 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-Present Datadog, Inc. + */ + +import Foundation + +@available(*, deprecated, renamed: "B3HTTPHeadersWriter") +public typealias OTelHTTPHeadersWriter = B3HTTPHeadersWriter + +/// The `B3HTTPHeadersWriter` class facilitates the injection of trace propagation headers into network requests +/// targeted at a backend expecting [B3 propagation format](https://github.com/openzipkin/b3-propagation). +/// +/// Usage: +/// +/// var request = URLRequest(...) +/// +/// let writer = B3HTTPHeadersWriter(injectEncoding: .single) +/// let span = Tracer.shared().startRootSpan(operationName: "network request") +/// Tracer.shared().inject(spanContext: span.context, writer: writer) +/// +/// writer.traceHeaderFields.forEach { (field, value) in +/// request.setValue(value, forHTTPHeaderField: field) +/// } +/// +/// // call span.finish() when the request completes +/// +public class B3HTTPHeadersWriter: TracePropagationHeadersWriter { + /// Enumerates B3 header encoding options. + /// + /// There are two encodings of B3 propagation: + /// [Single Header](https://github.com/openzipkin/b3-propagation#single-header) + /// and [Multiple Header](https://github.com/openzipkin/b3-propagation#multiple-headers). + /// + /// Multiple header encoding employs an `X-B3-` prefixed header per item in the trace context. + /// Single header delimits the context into a single entry named `B3`. + /// The single-header variant takes precedence over the multiple header one when extracting fields. + public enum InjectEncoding { + /// Encoding that employs `X-B3-*` prefixed headers per item in the trace context. + /// + /// See: [Multiple Header](https://github.com/openzipkin/b3-propagation#multiple-headers). + case multiple + /// Encoding that uses a single `B3` header to transport the trace context. + /// + /// See: [Single Header](https://github.com/openzipkin/b3-propagation#single-header) + case single + } + + /// A dictionary containing the required HTTP Headers for propagating trace information. + /// + /// Usage: + /// + /// writer.traceHeaderFields.forEach { (field, value) in + /// request.setValue(value, forHTTPHeaderField: field) + /// } + /// + public private(set) var traceHeaderFields: [String: String] = [:] + + /// The tracing sampler. + /// + /// The sample rate determines the `X-B3-Sampled` header field value + /// and whether `X-B3-TraceId`, `X-B3-SpanId`, and `X-B3-ParentSpanId` are propagated. + private let sampler: Sampler + + /// The telemetry header encoding used by the writer. + private let injectEncoding: InjectEncoding + + /// Initializes the headers writer. + /// + /// - Parameter samplingRate: The sampling rate applied for headers injection. + /// - Parameter injectEncoding: The B3 header encoding type, with `.single` as the default. + @available(*, deprecated, message: "This will be removed in future versions of the SDK. Use `init(sampleRate:injectEncoding:)` instead.") + public convenience init( + samplingRate: Float, + injectEncoding: InjectEncoding = .single + ) { + self.init(sampleRate: samplingRate, injectEncoding: injectEncoding) + } + + /// Initializes the headers writer. + /// + /// - Parameter sampleRate: The sampling rate applied for headers injection, with 20% as the default. + /// - Parameter injectEncoding: The B3 header encoding type, with `.single` as the default. + public convenience init( + sampleRate: Float = 20, + injectEncoding: InjectEncoding = .single + ) { + self.init( + sampler: Sampler(samplingRate: sampleRate), + injectEncoding: injectEncoding + ) + } + + /// Initializes the headers writer. + /// + /// - Parameter sampler: The sampler used for headers injection. + /// - Parameter injectEncoding: The B3 header encoding type, with `.single` as the default. + public init( + sampler: Sampler, + injectEncoding: InjectEncoding = .single + ) { + self.sampler = sampler + self.injectEncoding = injectEncoding + } + + /// Writes the trace ID, span ID, and optional parent span ID into the trace propagation headers. + /// + /// - Parameter traceID: The trace ID. + /// - Parameter spanID: The span ID. + /// - Parameter parentSpanID: The parent span ID, if applicable. + public func write(traceID: TraceID, spanID: SpanID, parentSpanID: SpanID?) { + let samplingPriority = sampler.sample() + + typealias Constants = B3HTTPHeaders.Constants + + switch injectEncoding { + case .multiple: + traceHeaderFields = [ + B3HTTPHeaders.Multiple.sampledField: samplingPriority ? Constants.sampledValue : Constants.unsampledValue + ] + + if samplingPriority { + traceHeaderFields[B3HTTPHeaders.Multiple.traceIDField] = String(traceID, representation: .hexadecimal32Chars) + traceHeaderFields[B3HTTPHeaders.Multiple.spanIDField] = String(spanID, representation: .hexadecimal16Chars) + traceHeaderFields[B3HTTPHeaders.Multiple.parentSpanIDField] = parentSpanID.map { String($0, representation: .hexadecimal16Chars) } + } + case .single: + if samplingPriority { + traceHeaderFields[B3HTTPHeaders.Single.b3Field] = [ + String(traceID, representation: .hexadecimal32Chars), + String(spanID, representation: .hexadecimal16Chars), + samplingPriority ? Constants.sampledValue : Constants.unsampledValue, + parentSpanID.map { String($0, representation: .hexadecimal16Chars) } + ] + .compactMap { $0 } + .joined(separator: Constants.b3Separator) + } else { + traceHeaderFields[B3HTTPHeaders.Single.b3Field] = Constants.unsampledValue + } + } + } +} diff --git a/DatadogInternal/Sources/NetworkInstrumentation/OpenTracing/HTTPHeadersReader.swift b/DatadogInternal/Sources/NetworkInstrumentation/Datadog/HTTPHeadersReader.swift similarity index 100% rename from DatadogInternal/Sources/NetworkInstrumentation/OpenTracing/HTTPHeadersReader.swift rename to DatadogInternal/Sources/NetworkInstrumentation/Datadog/HTTPHeadersReader.swift diff --git a/DatadogInternal/Sources/NetworkInstrumentation/OpenTracing/HTTPHeadersWriter.swift b/DatadogInternal/Sources/NetworkInstrumentation/Datadog/HTTPHeadersWriter.swift similarity index 54% rename from DatadogInternal/Sources/NetworkInstrumentation/OpenTracing/HTTPHeadersWriter.swift rename to DatadogInternal/Sources/NetworkInstrumentation/Datadog/HTTPHeadersWriter.swift index 3cdd7646cf..6a23257f14 100644 --- a/DatadogInternal/Sources/NetworkInstrumentation/OpenTracing/HTTPHeadersWriter.swift +++ b/DatadogInternal/Sources/NetworkInstrumentation/Datadog/HTTPHeadersWriter.swift @@ -6,16 +6,16 @@ import Foundation -/// The `HTTPHeadersWriter` should be used to inject trace propagation headers to -/// the network requests send to the backend instrumented with Datadog APM. +/// The `HTTPHeadersWriter` class facilitates the injection of trace propagation headers into network requests +/// targeted at a backend instrumented with Datadog and expecting `x-datadog-*` headers. /// /// Usage: /// /// var request = URLRequest(...) /// /// let writer = HTTPHeadersWriter() -/// let span = DatadogTracer.shared().startSpan("network request") -/// writer.inject(spanContext: span.context) +/// let span = Tracer.shared().startRootSpan(operationName: "network request") +/// Tracer.shared().inject(spanContext: span.context, writer: writer) /// /// writer.traceHeaderFields.forEach { (field, value) in /// request.setValue(value, forHTTPHeaderField: field) @@ -23,10 +23,8 @@ import Foundation /// /// // call span.finish() when the request completes /// -/// public class HTTPHeadersWriter: TracePropagationHeadersWriter { - /// A dictionary with HTTP Headers required to propagate the trace started in the mobile app - /// to the backend instrumented with Datadog APM. + /// A dictionary containing the required HTTP Headers for propagating trace information. /// /// Usage: /// @@ -42,22 +40,33 @@ public class HTTPHeadersWriter: TracePropagationHeadersWriter { /// and if `x-datadog-trace-id` and `x-datadog-parent-id` are propagated. private let sampler: Sampler - /// Creates a `HTTPHeadersWriter` to inject traces propagation headers - /// to network request. + /// Initializes the headers writer. /// - /// - Parameter samplingRate: Tracing sampling rate. 20% by default. - public init(samplingRate: Float = 20) { - self.sampler = Sampler(samplingRate: samplingRate) + /// - Parameter samplingRate: The sampling rate applied for headers injection. + @available(*, deprecated, message: "This will be removed in future versions of the SDK. Use `init(sampleRate:)` instead.") + public convenience init(samplingRate: Float) { + self.init(sampleRate: samplingRate) } - /// Creates a `HTTPHeadersWriter` to inject traces propagation headers - /// to network request. + /// Initializes the headers writer. /// - /// - Parameter sampler: Tracing sampler responsible for randomizing the sample. + /// - Parameter sampleRate: The sampling rate applied for headers injection, with 20% as the default. + public convenience init(sampleRate: Float = 20) { + self.init(sampler: Sampler(samplingRate: sampleRate)) + } + + /// Initializes the headers writer. + /// + /// - Parameter sampler: The sampler used for headers injection. public init(sampler: Sampler) { self.sampler = sampler } + /// Writes the trace ID, span ID, and optional parent span ID into the trace propagation headers. + /// + /// - Parameter traceID: The trace ID. + /// - Parameter spanID: The span ID. + /// - Parameter parentSpanID: The parent span ID, if applicable. public func write(traceID: TraceID, spanID: SpanID, parentSpanID: SpanID?) { let samplingPriority = sampler.sample() diff --git a/DatadogInternal/Sources/NetworkInstrumentation/OpenTracing/TracingHTTPHeaders.swift b/DatadogInternal/Sources/NetworkInstrumentation/Datadog/TracingHTTPHeaders.swift similarity index 100% rename from DatadogInternal/Sources/NetworkInstrumentation/OpenTracing/TracingHTTPHeaders.swift rename to DatadogInternal/Sources/NetworkInstrumentation/Datadog/TracingHTTPHeaders.swift diff --git a/DatadogInternal/Sources/NetworkInstrumentation/NetworkInstrumentationFeature.swift b/DatadogInternal/Sources/NetworkInstrumentation/NetworkInstrumentationFeature.swift index 722f886db8..5309d351fc 100644 --- a/DatadogInternal/Sources/NetworkInstrumentation/NetworkInstrumentationFeature.swift +++ b/DatadogInternal/Sources/NetworkInstrumentation/NetworkInstrumentationFeature.swift @@ -175,7 +175,7 @@ extension NetworkInstrumentationFeature { if tracingHeaderTypes.contains(.datadog) { reader = HTTPHeadersReader(httpHeaderFields: headers) } else if tracingHeaderTypes.contains(.b3) || tracingHeaderTypes.contains(.b3multi) { - reader = OTelHTTPHeadersReader(httpHeaderFields: headers) + reader = B3HTTPHeadersReader(httpHeaderFields: headers) } else { reader = W3CHTTPHeadersReader(httpHeaderFields: headers) } diff --git a/DatadogInternal/Sources/NetworkInstrumentation/OpenTelemetry/OTelHTTPHeadersWriter.swift b/DatadogInternal/Sources/NetworkInstrumentation/OpenTelemetry/OTelHTTPHeadersWriter.swift deleted file mode 100644 index 886081515d..0000000000 --- a/DatadogInternal/Sources/NetworkInstrumentation/OpenTelemetry/OTelHTTPHeadersWriter.swift +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2019-Present Datadog, Inc. - */ - -import Foundation - -/// The `OTelHTTPHeadersWriter` should be used to inject trace propagation headers to -/// the network requests send to the backend instrumented with Open Telemetry. -/// The injected headers conform to [Open Telemetry](https://github.com/openzipkin/b3-propagation) standard. -/// -/// Usage: -/// -/// var request = URLRequest(...) -/// -/// let writer = OTelHTTPHeadersWriter(injectEncoding: .single) -/// let span = DatadogTracer.shared().startSpan("network request") -/// writer.inject(spanContext: span.context) -/// -/// writer.traceHeaderFields.forEach { (field, value) in -/// request.setValue(value, forHTTPHeaderField: field) -/// } -/// -/// // call span.finish() when the request completes -/// -/// -public class OTelHTTPHeadersWriter: TracePropagationHeadersWriter { - /// Open Telemetry header encoding. - /// - /// There are two encodings of B3: - /// [Single Header](https://github.com/openzipkin/b3-propagation#single-header) - /// and [Multiple Header](https://github.com/openzipkin/b3-propagation#multiple-headers). - /// - /// Multiple header encoding uses an `X-B3-` prefixed header per item in the trace context. - /// Single header delimits the context into into a single entry named b3. - /// The single-header variant takes precedence over the multiple header one when extracting fields. - public enum InjectEncoding { - case multiple, single - } - - /// A dictionary with HTTP Headers required to propagate the trace started in the mobile app - /// to the backend instrumented with Open Telemetry. - /// - /// Usage: - /// - /// writer.traceHeaderFields.forEach { (field, value) in - /// request.setValue(value, forHTTPHeaderField: field) - /// } - /// - public private(set) var traceHeaderFields: [String: String] = [:] - - /// The tracing sampler. - /// - /// This value will decide of the `X-B3-Sampled` header field value - /// and if `X-B3-TraceId`, `X-B3-SpanId` and `X-B3-ParentSpanId` are propagated. - private let sampler: Sampler - - /// Determines the type of telemetry header type used by the writer. - private let injectEncoding: InjectEncoding - - /// Creates a `OTelHTTPHeadersWriter` to inject traces propagation headers - /// to network request. - /// - /// - Parameter samplingRate: Tracing sampling rate. 20% by default. - /// - Parameter injectEncoding: Determines the type of telemetry header type used by the writer. - public init( - samplingRate: Float = 20, - injectEncoding: InjectEncoding = .single - ) { - self.sampler = Sampler(samplingRate: samplingRate) - self.injectEncoding = injectEncoding - } - - /// Creates a `OTelHTTPHeadersWriter` to inject traces propagation headers - /// to network request. - /// - /// - Parameter sampler: Tracing sampler responsible for randomizing the sample. - /// - Parameter injectEncoding: Determines the type of telemetry header type used by the writer. - public init( - sampler: Sampler, - injectEncoding: InjectEncoding = .single - ) { - self.sampler = sampler - self.injectEncoding = injectEncoding - } - - public func write(traceID: TraceID, spanID: SpanID, parentSpanID: SpanID?) { - let samplingPriority = sampler.sample() - - typealias Constants = OTelHTTPHeaders.Constants - - switch injectEncoding { - case .multiple: - traceHeaderFields = [ - OTelHTTPHeaders.Multiple.sampledField: samplingPriority ? Constants.sampledValue : Constants.unsampledValue - ] - - if samplingPriority { - traceHeaderFields[OTelHTTPHeaders.Multiple.traceIDField] = String(traceID, representation: .hexadecimal32Chars) - traceHeaderFields[OTelHTTPHeaders.Multiple.spanIDField] = String(spanID, representation: .hexadecimal16Chars) - traceHeaderFields[OTelHTTPHeaders.Multiple.parentSpanIDField] = parentSpanID.map { String($0, representation: .hexadecimal16Chars) } - } - case .single: - if samplingPriority { - traceHeaderFields[OTelHTTPHeaders.Single.b3Field] = [ - String(traceID, representation: .hexadecimal32Chars), - String(spanID, representation: .hexadecimal16Chars), - samplingPriority ? Constants.sampledValue : Constants.unsampledValue, - parentSpanID.map { String($0, representation: .hexadecimal16Chars) } - ] - .compactMap { $0 } - .joined(separator: Constants.b3Separator) - } else { - traceHeaderFields[OTelHTTPHeaders.Single.b3Field] = Constants.unsampledValue - } - } - } -} diff --git a/DatadogInternal/Sources/NetworkInstrumentation/W3C/W3CHTTPHeadersWriter.swift b/DatadogInternal/Sources/NetworkInstrumentation/W3C/W3CHTTPHeadersWriter.swift index c8b71c50c5..bd97c9136d 100644 --- a/DatadogInternal/Sources/NetworkInstrumentation/W3C/W3CHTTPHeadersWriter.swift +++ b/DatadogInternal/Sources/NetworkInstrumentation/W3C/W3CHTTPHeadersWriter.swift @@ -6,17 +6,16 @@ import Foundation -/// The `W3CHTTPHeadersWriter` should be used to inject trace propagation headers to -/// the network requests send to the backend instrumented with W3C trace context. -/// The injected headers conform to [W3C](https://www.w3.org/TR/trace-context/) standard. +/// The `W3CHTTPHeadersWriter` class facilitates the injection of trace propagation headers into network requests +/// targeted at a backend expecting [W3C propagation format](https://github.com/openzipkin/b3-propagation). /// /// Usage: /// /// var request = URLRequest(...) /// /// let writer = W3CHTTPHeadersWriter() -/// let span = DatadogTracer.shared().startSpan("network request") -/// writer.inject(spanContext: span.context) +/// let span = Tracer.shared().startRootSpan(operationName: "network request") +/// Tracer.shared().inject(spanContext: span.context, writer: writer) /// /// writer.traceHeaderFields.forEach { (field, value) in /// request.setValue(value, forHTTPHeaderField: field) @@ -24,10 +23,8 @@ import Foundation /// /// // call span.finish() when the request completes /// -/// public class W3CHTTPHeadersWriter: TracePropagationHeadersWriter { - /// A dictionary with HTTP Headers required to propagate the trace started in the mobile app - /// to the backend instrumented with W3C trace context. + /// A dictionary containing the required HTTP Headers for propagating trace information. /// /// Usage: /// @@ -43,22 +40,33 @@ public class W3CHTTPHeadersWriter: TracePropagationHeadersWriter { /// and if `trace-id`, `span-id` are propagated. private let sampler: Sampler - /// Creates a `W3CHTTPHeadersWriter` to inject traces propagation headers - /// to network request. + /// Initializes the headers writer. /// - /// - Parameter samplingRate: Tracing sampling rate. 20% by default. - public init(samplingRate: Float = 20) { - self.sampler = Sampler(samplingRate: samplingRate) + /// - Parameter samplingRate: The sampling rate applied for headers injection. + @available(*, deprecated, message: "This will be removed in future versions of the SDK. Use `init(sampleRate:)` instead.") + public convenience init(samplingRate: Float) { + self.init(sampleRate: samplingRate) } - /// Creates a `W3CHTTPHeadersWriter` to inject traces propagation headers - /// to network request. + /// Initializes the headers writer. /// - /// - Parameter sampler: Tracing sampler responsible for randomizing the sample. + /// - Parameter sampleRate: The sampling rate applied for headers injection, with 20% as the default. + public convenience init(sampleRate: Float = 20) { + self.init(sampler: Sampler(samplingRate: sampleRate)) + } + + /// Initializes the headers writer. + /// + /// - Parameter sampler: The sampler used for headers injection. public init(sampler: Sampler) { self.sampler = sampler } + /// Writes the trace ID, span ID, and optional parent span ID into the trace propagation headers. + /// + /// - Parameter traceID: The trace ID. + /// - Parameter spanID: The span ID. + /// - Parameter parentSpanID: The parent span ID, if applicable. public func write(traceID: TraceID, spanID: SpanID, parentSpanID: SpanID?) { typealias Constants = W3CHTTPHeaders.Constants diff --git a/DatadogInternal/Tests/CoreRegistryTest.swift b/DatadogInternal/Tests/CoreRegistryTest.swift index 4165afbdf6..fc57718202 100644 --- a/DatadogInternal/Tests/CoreRegistryTest.swift +++ b/DatadogInternal/Tests/CoreRegistryTest.swift @@ -10,14 +10,6 @@ import TestUtilities @testable import DatadogInternal class CoreRegistryTest: XCTestCase { - override func tearDown() { - // make sure to clean the registry: - // conccurency test can leve the registry unclean - CoreRegistry.unregisterDefault() - CoreRegistry.unregisterInstance(named: "test") - super.tearDown() - } - func testRegistration() { let core = PassthroughCoreMock() CoreRegistry.register(default: core) @@ -26,11 +18,15 @@ class CoreRegistryTest: XCTestCase { let name: String = .mockRandom() CoreRegistry.register(core, named: name) XCTAssertTrue(CoreRegistry.instance(named: name) === core) + XCTAssertTrue(CoreRegistry.isRegistered(instanceName: CoreRegistry.defaultInstanceName)) + XCTAssertTrue(CoreRegistry.isRegistered(instanceName: name)) CoreRegistry.unregisterDefault() - XCTAssertTrue(CoreRegistry.default is NOPDatadogCore) CoreRegistry.unregisterInstance(named: name) + XCTAssertTrue(CoreRegistry.default is NOPDatadogCore) XCTAssertTrue(CoreRegistry.instance(named: name) is NOPDatadogCore) + XCTAssertFalse(CoreRegistry.isRegistered(instanceName: CoreRegistry.defaultInstanceName)) + XCTAssertFalse(CoreRegistry.isRegistered(instanceName: name)) } func testConcurrency() { @@ -46,5 +42,8 @@ class CoreRegistryTest: XCTestCase { { CoreRegistry.unregisterInstance(named: "test") } ) // swiftlint:enable opening_brace + + CoreRegistry.unregisterDefault() + CoreRegistry.unregisterInstance(named: "test") } } diff --git a/DatadogInternal/Tests/NetworkInstrumentation/OTelHTTPHeadersReaderTests.swift b/DatadogInternal/Tests/NetworkInstrumentation/B3HTTPHeadersReaderTests.swift similarity index 52% rename from DatadogInternal/Tests/NetworkInstrumentation/OTelHTTPHeadersReaderTests.swift rename to DatadogInternal/Tests/NetworkInstrumentation/B3HTTPHeadersReaderTests.swift index 93774b0e7d..7c4b5331ee 100644 --- a/DatadogInternal/Tests/NetworkInstrumentation/OTelHTTPHeadersReaderTests.swift +++ b/DatadogInternal/Tests/NetworkInstrumentation/B3HTTPHeadersReaderTests.swift @@ -7,71 +7,71 @@ import XCTest import DatadogInternal -class OTelHTTPHeadersReaderTests: XCTestCase { - func testOTelHTTPHeadersReaderreadsSingleHeader() { - let oTelHTTPHeadersReader = OTelHTTPHeadersReader(httpHeaderFields: ["b3": "4d2-929-1-162e"]) +class B3HTTPHeadersReaderTests: XCTestCase { + func testItReadsSingleHeader() { + let reader = B3HTTPHeadersReader(httpHeaderFields: ["b3": "4d2-929-1-162e"]) - let ids = oTelHTTPHeadersReader.read() + let ids = reader.read() XCTAssertEqual(ids?.traceID, 1_234) XCTAssertEqual(ids?.spanID, 2_345) XCTAssertEqual(ids?.parentSpanID, 5_678) } - func testOTelHTTPHeadersReaderreadsSingleHeaderWithSampling() { - let oTelHTTPHeadersReader = OTelHTTPHeadersReader(httpHeaderFields: ["b3": "0"]) + func testItReadsSingleHeaderWithSampling() { + let reader = B3HTTPHeadersReader(httpHeaderFields: ["b3": "0"]) - let ids = oTelHTTPHeadersReader.read() + let ids = reader.read() XCTAssertNil(ids?.traceID) XCTAssertNil(ids?.spanID) XCTAssertNil(ids?.parentSpanID) } - func testOTelHTTPHeadersReaderreadsSingleHeaderWithoutOptionalValues() { - let oTelHTTPHeadersReader = OTelHTTPHeadersReader(httpHeaderFields: ["b3": "4d2-929"]) + func testItReadsSingleHeaderWithoutOptionalValues() { + let reader = B3HTTPHeadersReader(httpHeaderFields: ["b3": "4d2-929"]) - let ids = oTelHTTPHeadersReader.read() + let ids = reader.read() XCTAssertEqual(ids?.traceID, 1_234) XCTAssertEqual(ids?.spanID, 2_345) XCTAssertNil(ids?.parentSpanID) } - func testOTelHTTPHeadersReaderreadsMultipleHeader() { - let oTelHTTPHeadersReader = OTelHTTPHeadersReader(httpHeaderFields: [ + func testItReadsMultipleHeader() { + let reader = B3HTTPHeadersReader(httpHeaderFields: [ "X-B3-TraceId": "4d2", "X-B3-SpanId": "929", "X-B3-Sampled": "1", "X-B3-ParentSpanId": "162e" ]) - let ids = oTelHTTPHeadersReader.read() + let ids = reader.read() XCTAssertEqual(ids?.traceID, 1_234) XCTAssertEqual(ids?.spanID, 2_345) XCTAssertEqual(ids?.parentSpanID, 5_678) } - func testOTelHTTPHeadersReaderreadsMultipleHeaderWithSampling() { - let oTelHTTPHeadersReader = OTelHTTPHeadersReader(httpHeaderFields: [ + func testItReadsMultipleHeaderWithSampling() { + let reader = B3HTTPHeadersReader(httpHeaderFields: [ "X-B3-Sampled": "0" ]) - let ids = oTelHTTPHeadersReader.read() + let ids = reader.read() XCTAssertNil(ids?.traceID) XCTAssertNil(ids?.spanID) XCTAssertNil(ids?.parentSpanID) } - func testOTelHTTPHeadersReaderreadsMultipleHeaderWithoutOptionalValues() { - let oTelHTTPHeadersReader = OTelHTTPHeadersReader(httpHeaderFields: [ + func testItReadsMultipleHeaderWithoutOptionalValues() { + let reader = B3HTTPHeadersReader(httpHeaderFields: [ "X-B3-TraceId": "4d2", "X-B3-SpanId": "929" ]) - let ids = oTelHTTPHeadersReader.read() + let ids = reader.read() XCTAssertEqual(ids?.traceID, 1_234) XCTAssertEqual(ids?.spanID, 2_345) diff --git a/DatadogInternal/Tests/NetworkInstrumentation/B3HTTPHeadersWriterTests.swift b/DatadogInternal/Tests/NetworkInstrumentation/B3HTTPHeadersWriterTests.swift new file mode 100644 index 0000000000..40a3a02ac9 --- /dev/null +++ b/DatadogInternal/Tests/NetworkInstrumentation/B3HTTPHeadersWriterTests.swift @@ -0,0 +1,109 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-Present Datadog, Inc. + */ + +import XCTest +@testable import DatadogInternal + +class B3HTTPHeadersWriterTests: XCTestCase { + func testItWritesSingleHeader() { + let sampler: Sampler = .mockKeepAll() + let writer = B3HTTPHeadersWriter( + sampler: sampler, + injectEncoding: .single + ) + + writer.write( + traceID: 1_234, + spanID: 2_345, + parentSpanID: 5_678 + ) + + let headers = writer.traceHeaderFields + XCTAssertEqual(headers[B3HTTPHeaders.Single.b3Field], "000000000000000000000000000004d2-0000000000000929-1-000000000000162e") + } + + func testItWritesSingleHeaderWithSampling() { + let sampler: Sampler = .mockRejectAll() + let writer = B3HTTPHeadersWriter( + sampler: sampler, + injectEncoding: .single + ) + + writer.write( + traceID: 1_234, + spanID: 2_345, + parentSpanID: 5_678 + ) + + let headers = writer.traceHeaderFields + XCTAssertEqual(headers[B3HTTPHeaders.Single.b3Field], "0") + } + + func testItWritesSingleHeaderWithoutOptionalValues() { + let sampler: Sampler = .mockKeepAll() + let writer = B3HTTPHeadersWriter( + sampler: sampler, + injectEncoding: .single + ) + writer.write(traceID: 1_234, spanID: 2_345) + + let headers = writer.traceHeaderFields + XCTAssertEqual(headers[B3HTTPHeaders.Single.b3Field], "000000000000000000000000000004d2-0000000000000929-1") + } + + func testItWritesMultipleHeader() { + let sampler: Sampler = .mockKeepAll() + let writer = B3HTTPHeadersWriter( + sampler: sampler, + injectEncoding: .multiple + ) + writer.write( + traceID: 1_234, + spanID: 2_345, + parentSpanID: 5_678 + ) + + let headers = writer.traceHeaderFields + XCTAssertEqual(headers[B3HTTPHeaders.Multiple.traceIDField], "000000000000000000000000000004d2") + XCTAssertEqual(headers[B3HTTPHeaders.Multiple.spanIDField], "0000000000000929") + XCTAssertEqual(headers[B3HTTPHeaders.Multiple.sampledField], "1") + XCTAssertEqual(headers[B3HTTPHeaders.Multiple.parentSpanIDField], "000000000000162e") + } + + func testItWritesMultipleHeaderWithSampling() { + let sampler: Sampler = .mockRejectAll() + let writer = B3HTTPHeadersWriter( + sampler: sampler, + injectEncoding: .multiple + ) + writer.write( + traceID: 1_234, + spanID: 2_345, + parentSpanID: 5_678 + ) + + let headers = writer.traceHeaderFields + XCTAssertNil(headers[B3HTTPHeaders.Multiple.traceIDField]) + XCTAssertNil(headers[B3HTTPHeaders.Multiple.spanIDField]) + XCTAssertEqual(headers[B3HTTPHeaders.Multiple.sampledField], "0") + XCTAssertNil(headers[B3HTTPHeaders.Multiple.parentSpanIDField]) + } + + func testItWritesMultipleHeaderWithoutOptionalValues() { + let sampler: Sampler = .mockKeepAll() + let writer = B3HTTPHeadersWriter( + sampler: sampler, + injectEncoding: .multiple + ) + writer.write(traceID: 1_234, spanID: 2_345) + + let headers = writer.traceHeaderFields + XCTAssertEqual(headers[B3HTTPHeaders.Multiple.traceIDField], "000000000000000000000000000004d2") + XCTAssertEqual(headers[B3HTTPHeaders.Multiple.spanIDField], "0000000000000929") + XCTAssertEqual(headers[B3HTTPHeaders.Multiple.sampledField], "1") + XCTAssertNil(headers[B3HTTPHeaders.Multiple.parentSpanIDField]) + } +} diff --git a/DatadogInternal/Tests/NetworkInstrumentation/NetworkInstrumentationFeatureTests.swift b/DatadogInternal/Tests/NetworkInstrumentation/NetworkInstrumentationFeatureTests.swift index 20861aea5c..24684b92d0 100644 --- a/DatadogInternal/Tests/NetworkInstrumentation/NetworkInstrumentationFeatureTests.swift +++ b/DatadogInternal/Tests/NetworkInstrumentation/NetworkInstrumentationFeatureTests.swift @@ -263,7 +263,7 @@ class NetworkInstrumentationFeatureTests: XCTestCase { func testGivenOpenTelemetry_b3single_whenInterceptingRequests_itInjectsTrace() throws { // Given var request: URLRequest = .mockWith(url: "https://test.com") - let writer = OTelHTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .single) + let writer = B3HTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .single) handler.firstPartyHosts = .init(["test.com": [.b3]]) // When @@ -285,7 +285,7 @@ class NetworkInstrumentationFeatureTests: XCTestCase { func testGivenOpenTelemetry_b3multi_whenInterceptingRequests_itInjectsTrace() throws { // Given var request: URLRequest = .mockWith(url: "https://test.com") - let writer = OTelHTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .multiple) + let writer = B3HTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .multiple) handler.firstPartyHosts = .init(["test.com": [.b3multi]]) // When diff --git a/DatadogInternal/Tests/NetworkInstrumentation/OTelHTTPHeadersWriterTests.swift b/DatadogInternal/Tests/NetworkInstrumentation/OTelHTTPHeadersWriterTests.swift deleted file mode 100644 index d57334cda4..0000000000 --- a/DatadogInternal/Tests/NetworkInstrumentation/OTelHTTPHeadersWriterTests.swift +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2019-Present Datadog, Inc. - */ - -import XCTest -@testable import DatadogInternal - -class OTelHTTPHeadersWriterTests: XCTestCase { - func testOTelHTTPHeadersWriterwritesSingleHeader() { - let sampler: Sampler = .mockKeepAll() - let oTelHTTPHeadersWriter = OTelHTTPHeadersWriter( - sampler: sampler, - injectEncoding: .single - ) - - oTelHTTPHeadersWriter.write( - traceID: 1_234, - spanID: 2_345, - parentSpanID: 5_678 - ) - - let headers = oTelHTTPHeadersWriter.traceHeaderFields - XCTAssertEqual(headers[OTelHTTPHeaders.Single.b3Field], "000000000000000000000000000004d2-0000000000000929-1-000000000000162e") - } - - func testOTelHTTPHeadersWriterwritesSingleHeaderWithSampling() { - let sampler: Sampler = .mockRejectAll() - let oTelHTTPHeadersWriter = OTelHTTPHeadersWriter( - sampler: sampler, - injectEncoding: .single - ) - - oTelHTTPHeadersWriter.write( - traceID: 1_234, - spanID: 2_345, - parentSpanID: 5_678 - ) - - let headers = oTelHTTPHeadersWriter.traceHeaderFields - XCTAssertEqual(headers[OTelHTTPHeaders.Single.b3Field], "0") - } - - func testOTelHTTPHeadersWriterwritesSingleHeaderWithoutOptionalValues() { - let sampler: Sampler = .mockKeepAll() - let oTelHTTPHeadersWriter = OTelHTTPHeadersWriter( - sampler: sampler, - injectEncoding: .single - ) - oTelHTTPHeadersWriter.write(traceID: 1_234, spanID: 2_345) - - let headers = oTelHTTPHeadersWriter.traceHeaderFields - XCTAssertEqual(headers[OTelHTTPHeaders.Single.b3Field], "000000000000000000000000000004d2-0000000000000929-1") - } - - func testOTelHTTPHeadersWriterwritesMultipleHeader() { - let sampler: Sampler = .mockKeepAll() - let oTelHTTPHeadersWriter = OTelHTTPHeadersWriter( - sampler: sampler, - injectEncoding: .multiple - ) - oTelHTTPHeadersWriter.write( - traceID: 1_234, - spanID: 2_345, - parentSpanID: 5_678 - ) - - let headers = oTelHTTPHeadersWriter.traceHeaderFields - XCTAssertEqual(headers[OTelHTTPHeaders.Multiple.traceIDField], "000000000000000000000000000004d2") - XCTAssertEqual(headers[OTelHTTPHeaders.Multiple.spanIDField], "0000000000000929") - XCTAssertEqual(headers[OTelHTTPHeaders.Multiple.sampledField], "1") - XCTAssertEqual(headers[OTelHTTPHeaders.Multiple.parentSpanIDField], "000000000000162e") - } - - func testOTelHTTPHeadersWriterwritesMultipleHeaderWithSampling() { - let sampler: Sampler = .mockRejectAll() - let oTelHTTPHeadersWriter = OTelHTTPHeadersWriter( - sampler: sampler, - injectEncoding: .multiple - ) - oTelHTTPHeadersWriter.write( - traceID: 1_234, - spanID: 2_345, - parentSpanID: 5_678 - ) - - let headers = oTelHTTPHeadersWriter.traceHeaderFields - XCTAssertNil(headers[OTelHTTPHeaders.Multiple.traceIDField]) - XCTAssertNil(headers[OTelHTTPHeaders.Multiple.spanIDField]) - XCTAssertEqual(headers[OTelHTTPHeaders.Multiple.sampledField], "0") - XCTAssertNil(headers[OTelHTTPHeaders.Multiple.parentSpanIDField]) - } - - func testOTelHTTPHeadersWriterwritesMultipleHeaderWithoutOptionalValues() { - let sampler: Sampler = .mockKeepAll() - let oTelHTTPHeadersWriter = OTelHTTPHeadersWriter( - sampler: sampler, - injectEncoding: .multiple - ) - oTelHTTPHeadersWriter.write(traceID: 1_234, spanID: 2_345) - - let headers = oTelHTTPHeadersWriter.traceHeaderFields - XCTAssertEqual(headers[OTelHTTPHeaders.Multiple.traceIDField], "000000000000000000000000000004d2") - XCTAssertEqual(headers[OTelHTTPHeaders.Multiple.spanIDField], "0000000000000929") - XCTAssertEqual(headers[OTelHTTPHeaders.Multiple.sampledField], "1") - XCTAssertNil(headers[OTelHTTPHeaders.Multiple.parentSpanIDField]) - } -} diff --git a/DatadogLogs.podspec b/DatadogLogs.podspec index b54c6ab62b..9a175ef8aa 100644 --- a/DatadogLogs.podspec +++ b/DatadogLogs.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "DatadogLogs" - s.version = "2.0.0" + s.version = "2.1.0" s.summary = "Datadog Logs Module." s.homepage = "https://www.datadoghq.com" diff --git a/DatadogObjc.podspec b/DatadogObjc.podspec index e28a4056e8..ed7e0a826e 100644 --- a/DatadogObjc.podspec +++ b/DatadogObjc.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "DatadogObjc" - s.version = "2.0.0" + s.version = "2.1.0" s.summary = "Official Datadog Objective-C SDK for iOS." s.homepage = "https://www.datadoghq.com" @@ -25,4 +25,5 @@ Pod::Spec.new do |s| s.dependency 'DatadogRUM', s.version.to_s s.dependency 'DatadogLogs', s.version.to_s s.dependency 'DatadogTrace', s.version.to_s + s.dependency 'DatadogSessionReplay', s.version.to_s end diff --git a/DatadogObjc/Sources/DDLogs+objc.swift b/DatadogObjc/Sources/Logs/Logs+objc.swift similarity index 100% rename from DatadogObjc/Sources/DDLogs+objc.swift rename to DatadogObjc/Sources/Logs/Logs+objc.swift diff --git a/DatadogObjc/Sources/RUM+objc.swift b/DatadogObjc/Sources/RUM/RUM+objc.swift similarity index 100% rename from DatadogObjc/Sources/RUM+objc.swift rename to DatadogObjc/Sources/RUM/RUM+objc.swift diff --git a/DatadogObjc/Sources/RUM/RUMDataModels+objc.swift b/DatadogObjc/Sources/RUM/RUMDataModels+objc.swift index f064777ee0..9c896d0353 100644 --- a/DatadogObjc/Sources/RUM/RUMDataModels+objc.swift +++ b/DatadogObjc/Sources/RUM/RUMDataModels+objc.swift @@ -185,8 +185,8 @@ public class DDRUMActionEventDDConfiguration: NSObject { self.root = root } - @objc public var sessionReplaySampleRate: NSNumber { - root.swiftModel.dd.configuration!.sessionReplaySampleRate as NSNumber + @objc public var sessionReplaySampleRate: NSNumber? { + root.swiftModel.dd.configuration!.sessionReplaySampleRate as NSNumber? } @objc public var sessionSampleRate: NSNumber { @@ -958,8 +958,8 @@ public class DDRUMErrorEventDDConfiguration: NSObject { self.root = root } - @objc public var sessionReplaySampleRate: NSNumber { - root.swiftModel.dd.configuration!.sessionReplaySampleRate as NSNumber + @objc public var sessionReplaySampleRate: NSNumber? { + root.swiftModel.dd.configuration!.sessionReplaySampleRate as NSNumber? } @objc public var sessionSampleRate: NSNumber { @@ -1953,8 +1953,8 @@ public class DDRUMLongTaskEventDDConfiguration: NSObject { self.root = root } - @objc public var sessionReplaySampleRate: NSNumber { - root.swiftModel.dd.configuration!.sessionReplaySampleRate as NSNumber + @objc public var sessionReplaySampleRate: NSNumber? { + root.swiftModel.dd.configuration!.sessionReplaySampleRate as NSNumber? } @objc public var sessionSampleRate: NSNumber { @@ -2603,8 +2603,8 @@ public class DDRUMResourceEventDDConfiguration: NSObject { self.root = root } - @objc public var sessionReplaySampleRate: NSNumber { - root.swiftModel.dd.configuration!.sessionReplaySampleRate as NSNumber + @objc public var sessionReplaySampleRate: NSNumber? { + root.swiftModel.dd.configuration!.sessionReplaySampleRate as NSNumber? } @objc public var sessionSampleRate: NSNumber { @@ -3558,8 +3558,8 @@ public class DDRUMViewEventDDConfiguration: NSObject { self.root = root } - @objc public var sessionReplaySampleRate: NSNumber { - root.swiftModel.dd.configuration!.sessionReplaySampleRate as NSNumber + @objc public var sessionReplaySampleRate: NSNumber? { + root.swiftModel.dd.configuration!.sessionReplaySampleRate as NSNumber? } @objc public var sessionSampleRate: NSNumber { @@ -4266,6 +4266,10 @@ public class DDRUMViewEventView: NSObject { root.swiftModel.view.inForegroundPeriods?.map { DDRUMViewEventViewInForegroundPeriods(swiftModel: $0) } } + @objc public var interactionToNextPaint: NSNumber? { + root.swiftModel.view.interactionToNextPaint as NSNumber? + } + @objc public var isActive: NSNumber? { root.swiftModel.view.isActive as NSNumber? } @@ -5102,6 +5106,14 @@ public class DDTelemetryConfigurationEventTelemetryConfiguration: NSObject { root.swiftModel.telemetry.configuration.actionNameAttribute } + @objc public var allowFallbackToLocalStorage: NSNumber? { + root.swiftModel.telemetry.configuration.allowFallbackToLocalStorage as NSNumber? + } + + @objc public var allowUntrustedEvents: NSNumber? { + root.swiftModel.telemetry.configuration.allowUntrustedEvents as NSNumber? + } + @objc public var batchSize: NSNumber? { root.swiftModel.telemetry.configuration.batchSize as NSNumber? } @@ -5310,6 +5322,10 @@ public class DDTelemetryConfigurationEventTelemetryConfiguration: NSObject { root.swiftModel.telemetry.configuration.useTracing as NSNumber? } + @objc public var useWorkerUrl: NSNumber? { + root.swiftModel.telemetry.configuration.useWorkerUrl as NSNumber? + } + @objc public var viewTrackingStrategy: DDTelemetryConfigurationEventTelemetryConfigurationViewTrackingStrategy { .init(swift: root.swiftModel.telemetry.configuration.viewTrackingStrategy) } @@ -5434,4 +5450,4 @@ public class DDTelemetryConfigurationEventView: NSObject { // swiftlint:enable force_unwrapping -// Generated from https://github.com/DataDog/rum-events-format/tree/2b1615693d269368ed91f061103ee98bfecafb00 +// Generated from https://github.com/DataDog/rum-events-format/tree/f21e8badee23a4d3204440d55a5ac7b5d9fadc81 diff --git a/DatadogObjc/Sources/SessionReplay/SessionReplay+objc.swift b/DatadogObjc/Sources/SessionReplay/SessionReplay+objc.swift new file mode 100644 index 0000000000..65c2c1f535 --- /dev/null +++ b/DatadogObjc/Sources/SessionReplay/SessionReplay+objc.swift @@ -0,0 +1,109 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-Present Datadog, Inc. + */ + +import Foundation +#if os(iOS) +import DatadogSessionReplay + +/// An entry point to Datadog Session Replay feature. +@objc +public final class DDSessionReplay: NSObject { + override private init() { } + + /// Enables Datadog Session Replay feature. + /// + /// Recording will start automatically after enabling Session Replay. + /// + /// Note: Session Replay requires the RUM feature to be enabled. + /// + /// - Parameters: + /// - configuration: Configuration of the feature. + @objc + public static func enable(with configuration: DDSessionReplayConfiguration) { + SessionReplay.enable(with: configuration._swift) + } +} + +/// Session Replay feature configuration. +@objc +public final class DDSessionReplayConfiguration: NSObject { + internal var _swift: SessionReplay.Configuration = .init(replaySampleRate: 0) + + /// The sampling rate for Session Replay. It is applied in addition to the RUM session sample rate. + /// + /// It must be a number between 0.0 and 100.0, where 0 means no replays will be recorded + /// and 100 means all RUM sessions will contain replay. + /// + /// Note: This sample rate is applied in addition to the RUM sample rate. For example, if RUM uses a sample rate of 80% + /// and Session Replay uses a sample rate of 20%, it means that out of all user sessions, 80% will be included in RUM, + /// and within those sessions, only 20% will have replays. + @objc public var replaySampleRate: Float { + set { _swift.replaySampleRate = newValue } + get { _swift.replaySampleRate } + } + + /// Defines the way sensitive content (e.g. text) should be masked. + /// + /// Default: `.mask`. + @objc public var defaultPrivacyLevel: DDSessionReplayConfigurationPrivacyLevel { + set { _swift.defaultPrivacyLevel = newValue._swift } + get { .init(_swift.defaultPrivacyLevel) } + } + + /// Custom server url for sending replay data. + /// + /// Default: `nil`. + @objc public var customEndpoint: URL? { + set { _swift.customEndpoint = newValue } + get { _swift.customEndpoint } + } + + /// Creates Session Replay configuration. + /// + /// - Parameters: + /// - replaySampleRate: The sampling rate for Session Replay. It is applied in addition to the RUM session sample rate. + @objc + public required init( + replaySampleRate: Float + ) { + _swift = SessionReplay.Configuration( + replaySampleRate: replaySampleRate + ) + super.init() + } +} + +/// Available privacy levels for content masking. +@objc +public enum DDSessionReplayConfigurationPrivacyLevel: Int { + /// Record all content. + case allow + + /// Mask all content. + case mask + + /// Mask input elements, but record all other content. + case maskUserInput + + internal var _swift: SessionReplay.Configuration.PrivacyLevel { + switch self { + case .allow: return .allow + case .mask: return .mask + case .maskUserInput: return .maskUserInput + default: return .mask + } + } + + internal init(_ swift: SessionReplay.Configuration.PrivacyLevel) { + switch swift { + case .allow: self = .allow + case .mask: self = .mask + case .maskUserInput: self = .maskUserInput + } + } +} + +#endif diff --git a/DatadogObjc/Sources/Tracing/Propagation/B3HTTPHeadersWriter+objc.swift b/DatadogObjc/Sources/Tracing/Propagation/B3HTTPHeadersWriter+objc.swift new file mode 100644 index 0000000000..2bb596ecf3 --- /dev/null +++ b/DatadogObjc/Sources/Tracing/Propagation/B3HTTPHeadersWriter+objc.swift @@ -0,0 +1,58 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-Present Datadog, Inc. + */ + +import Foundation +import class DatadogInternal.B3HTTPHeadersWriter + +@objc +public enum DDInjectEncoding: Int { + case multiple = 0 + case single = 1 +} + +private extension B3HTTPHeadersWriter.InjectEncoding { + init(_ value: DDInjectEncoding) { + switch value { + case .single: + self = .single + case .multiple: + self = .multiple + } + } +} + +@objc +@available(*, deprecated, renamed: "DDB3HTTPHeadersWriter") +public class DDOTelHTTPHeadersWriter: DDB3HTTPHeadersWriter {} + +@objc +public class DDB3HTTPHeadersWriter: NSObject { + let swiftB3HTTPHeadersWriter: B3HTTPHeadersWriter + + @objc public var traceHeaderFields: [String: String] { + swiftB3HTTPHeadersWriter.traceHeaderFields + } + + @objc + @available(*, deprecated, message: "This will be removed in future versions of the SDK. Use `init(sampleRate:injectEncoding:)` instead.") + public convenience init( + samplingRate: Float, + injectEncoding: DDInjectEncoding = .single + ) { + self.init(sampleRate: samplingRate, injectEncoding: injectEncoding) + } + + @objc + public init( + sampleRate: Float = 20, + injectEncoding: DDInjectEncoding = .single + ) { + swiftB3HTTPHeadersWriter = B3HTTPHeadersWriter( + sampleRate: sampleRate, + injectEncoding: .init(injectEncoding) + ) + } +} diff --git a/DatadogObjc/Sources/Tracing/Propagation/HTTPHeadersWriter+objc.swift b/DatadogObjc/Sources/Tracing/Propagation/HTTPHeadersWriter+objc.swift index 15b2bcd0a4..46e4b00ad0 100644 --- a/DatadogObjc/Sources/Tracing/Propagation/HTTPHeadersWriter+objc.swift +++ b/DatadogObjc/Sources/Tracing/Propagation/HTTPHeadersWriter+objc.swift @@ -16,7 +16,13 @@ public class DDHTTPHeadersWriter: NSObject { } @objc - public init(samplingRate: Float = 20) { - swiftHTTPHeadersWriter = HTTPHeadersWriter(samplingRate: samplingRate) + @available(*, deprecated, message: "This will be removed in future versions of the SDK. Use `init(sampleRate:)` instead.") + public convenience init(samplingRate: Float) { + self.init(sampleRate: samplingRate) + } + + @objc + public init(sampleRate: Float = 20) { + swiftHTTPHeadersWriter = HTTPHeadersWriter(sampleRate: sampleRate) } } diff --git a/DatadogObjc/Sources/Tracing/Propagation/OTelHTTPHeadersWriter+objc.swift b/DatadogObjc/Sources/Tracing/Propagation/OTelHTTPHeadersWriter+objc.swift deleted file mode 100644 index 6ee9941158..0000000000 --- a/DatadogObjc/Sources/Tracing/Propagation/OTelHTTPHeadersWriter+objc.swift +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2019-Present Datadog, Inc. - */ - -import Foundation -import class DatadogInternal.OTelHTTPHeadersWriter - -@objc -public enum DDInjectEncoding: Int { - case multiple = 0 - case single = 1 -} - -private extension OTelHTTPHeadersWriter.InjectEncoding { - init(_ value: DDInjectEncoding) { - switch value { - case .single: - self = .single - case .multiple: - self = .multiple - } - } -} - -@objc -public class DDOTelHTTPHeadersWriter: NSObject { - let swiftOTelHTTPHeadersWriter: OTelHTTPHeadersWriter - - @objc public var traceHeaderFields: [String: String] { - swiftOTelHTTPHeadersWriter.traceHeaderFields - } - - @objc - public init( - samplingRate: Float = 20, - injectEncoding: DDInjectEncoding = .single - ) { - swiftOTelHTTPHeadersWriter = OTelHTTPHeadersWriter( - samplingRate: samplingRate, - injectEncoding: .init(injectEncoding) - ) - } -} diff --git a/DatadogObjc/Sources/Tracing/Propagation/W3CHTTPHeadersWriter+objc.swift b/DatadogObjc/Sources/Tracing/Propagation/W3CHTTPHeadersWriter+objc.swift index a11b4c0c58..4aa06aa565 100644 --- a/DatadogObjc/Sources/Tracing/Propagation/W3CHTTPHeadersWriter+objc.swift +++ b/DatadogObjc/Sources/Tracing/Propagation/W3CHTTPHeadersWriter+objc.swift @@ -16,7 +16,13 @@ public class DDW3CHTTPHeadersWriter: NSObject { } @objc - public init(samplingRate: Float = 20) { - swiftW3CHTTPHeadersWriter = W3CHTTPHeadersWriter(samplingRate: samplingRate) + @available(*, deprecated, message: "This will be removed in future versions of the SDK. Use `init(sampleRate:)` instead.") + public convenience init(samplingRate: Float) { + self.init(sampleRate: samplingRate) + } + + @objc + public init(sampleRate: Float = 20) { + swiftW3CHTTPHeadersWriter = W3CHTTPHeadersWriter(sampleRate: sampleRate) } } diff --git a/DatadogObjc/Sources/Trace+objc.swift b/DatadogObjc/Sources/Tracing/Trace+objc.swift similarity index 98% rename from DatadogObjc/Sources/Trace+objc.swift rename to DatadogObjc/Sources/Tracing/Trace+objc.swift index 88d96e7101..df3922f091 100644 --- a/DatadogObjc/Sources/Trace+objc.swift +++ b/DatadogObjc/Sources/Tracing/Trace+objc.swift @@ -197,13 +197,13 @@ public class DDTracer: NSObject, DatadogObjc.OTTracer { spanContext: ddspanContext.swiftSpanContext, writer: objcWriter.swiftHTTPHeadersWriter ) - } else if let objcWriter = carrier as? DDOTelHTTPHeadersWriter, format == OT.formatTextMap { + } else if let objcWriter = carrier as? DDB3HTTPHeadersWriter, format == OT.formatTextMap { guard let ddspanContext = spanContext.dd else { return } swiftTracer.inject( spanContext: ddspanContext.swiftSpanContext, - writer: objcWriter.swiftOTelHTTPHeadersWriter + writer: objcWriter.swiftB3HTTPHeadersWriter ) } else if let objcWriter = carrier as? DDW3CHTTPHeadersWriter, format == OT.formatTextMap { guard let ddspanContext = spanContext.dd else { diff --git a/DatadogRUM.podspec b/DatadogRUM.podspec index 78970820f3..fc5703f35d 100644 --- a/DatadogRUM.podspec +++ b/DatadogRUM.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "DatadogRUM" - s.version = "2.0.0" + s.version = "2.1.0" s.summary = "Datadog Real User Monitoring Module." s.homepage = "https://www.datadoghq.com" diff --git a/DatadogRUM/Sources/DataModels/RUMDataModels.swift b/DatadogRUM/Sources/DataModels/RUMDataModels.swift index c5f7fa14fc..4fa0e26a0f 100644 --- a/DatadogRUM/Sources/DataModels/RUMDataModels.swift +++ b/DatadogRUM/Sources/DataModels/RUMDataModels.swift @@ -161,7 +161,7 @@ public struct RUMActionEvent: RUMDataModel { /// Subset of the SDK configuration options in use during its execution public struct Configuration: Codable { /// The percentage of sessions with RUM & Session Replay pricing tracked - public let sessionReplaySampleRate: Double + public let sessionReplaySampleRate: Double? /// The percentage of sessions tracked public let sessionSampleRate: Double @@ -533,7 +533,7 @@ public struct RUMErrorEvent: RUMDataModel { /// Subset of the SDK configuration options in use during its execution public struct Configuration: Codable { /// The percentage of sessions with RUM & Session Replay pricing tracked - public let sessionReplaySampleRate: Double + public let sessionReplaySampleRate: Double? /// The percentage of sessions tracked public let sessionSampleRate: Double @@ -997,7 +997,7 @@ public struct RUMLongTaskEvent: RUMDataModel { /// Subset of the SDK configuration options in use during its execution public struct Configuration: Codable { /// The percentage of sessions with RUM & Session Replay pricing tracked - public let sessionReplaySampleRate: Double + public let sessionReplaySampleRate: Double? /// The percentage of sessions tracked public let sessionSampleRate: Double @@ -1284,7 +1284,7 @@ public struct RUMResourceEvent: RUMDataModel { /// Subset of the SDK configuration options in use during its execution public struct Configuration: Codable { /// The percentage of sessions with RUM & Session Replay pricing tracked - public let sessionReplaySampleRate: Double + public let sessionReplaySampleRate: Double? /// The percentage of sessions tracked public let sessionSampleRate: Double @@ -1746,7 +1746,7 @@ public struct RUMViewEvent: RUMDataModel { /// Subset of the SDK configuration options in use during its execution public struct Configuration: Codable { /// The percentage of sessions with RUM & Session Replay pricing tracked - public let sessionReplaySampleRate: Double + public let sessionReplaySampleRate: Double? /// The percentage of sessions tracked public let sessionSampleRate: Double @@ -2033,6 +2033,9 @@ public struct RUMViewEvent: RUMDataModel { /// List of the periods of time the user had the view in foreground (focused in the browser) public let inForegroundPeriods: [InForegroundPeriods]? + /// Longest duration in ns between an interaction and the next paint + public let interactionToNextPaint: Int64? + /// Whether the View corresponding to this event is considered active public let isActive: Bool? @@ -2105,6 +2108,7 @@ public struct RUMViewEvent: RUMDataModel { case frustration = "frustration" case id = "id" case inForegroundPeriods = "in_foreground_periods" + case interactionToNextPaint = "interaction_to_next_paint" case isActive = "is_active" case isSlowRendered = "is_slow_rendered" case jsRefreshRate = "js_refresh_rate" @@ -2747,6 +2751,12 @@ public struct TelemetryConfigurationEvent: RUMDataModel { /// Attribute to be used to name actions public let actionNameAttribute: String? + /// Whether it is allowed to use LocalStorage when cookies are not available + public let allowFallbackToLocalStorage: Bool? + + /// Whether untrusted events are allowed + public let allowUntrustedEvents: Bool? + /// The window duration for batches sent by the SDK (in milliseconds) public let batchSize: Int64? @@ -2876,7 +2886,7 @@ public struct TelemetryConfigurationEvent: RUMDataModel { /// Whether local encryption is used public let useLocalEncryption: Bool? - /// Whether a proxy configured is used + /// Whether a proxy is used public var useProxy: Bool? /// Whether a secure session cookie is used @@ -2885,11 +2895,16 @@ public struct TelemetryConfigurationEvent: RUMDataModel { /// Whether tracing features are enabled public let useTracing: Bool? + /// Whether the Worker is loaded from an external URL + public let useWorkerUrl: Bool? + /// View tracking strategy public let viewTrackingStrategy: ViewTrackingStrategy? enum CodingKeys: String, CodingKey { case actionNameAttribute = "action_name_attribute" + case allowFallbackToLocalStorage = "allow_fallback_to_local_storage" + case allowUntrustedEvents = "allow_untrusted_events" case batchSize = "batch_size" case batchUploadFrequency = "batch_upload_frequency" case dartVersion = "dart_version" @@ -2936,6 +2951,7 @@ public struct TelemetryConfigurationEvent: RUMDataModel { case useProxy = "use_proxy" case useSecureSessionCookie = "use_secure_session_cookie" case useTracing = "use_tracing" + case useWorkerUrl = "use_worker_url" case viewTrackingStrategy = "view_tracking_strategy" } @@ -3309,4 +3325,4 @@ public enum RUMMethod: String, Codable { case patch = "PATCH" } -// Generated from https://github.com/DataDog/rum-events-format/tree/2b1615693d269368ed91f061103ee98bfecafb00 +// Generated from https://github.com/DataDog/rum-events-format/tree/f21e8badee23a4d3204440d55a5ac7b5d9fadc81 diff --git a/DatadogRUM/Sources/Feature/RequestBuilder.swift b/DatadogRUM/Sources/Feature/RequestBuilder.swift index 39ac03de96..b6570ab33a 100644 --- a/DatadogRUM/Sources/Feature/RequestBuilder.swift +++ b/DatadogRUM/Sources/Feature/RequestBuilder.swift @@ -56,7 +56,7 @@ internal struct RequestBuilder: FeatureRequestBuilder { return builder.uploadRequest(with: data) } - func url(with context: DatadogContext) -> URL { + private func url(with context: DatadogContext) -> URL { customIntakeURL ?? context.site.endpoint.appendingPathComponent("api/v2/rum") } } diff --git a/DatadogRUM/Sources/Instrumentation/Resources/URLSessionRUMResourcesHandler.swift b/DatadogRUM/Sources/Instrumentation/Resources/URLSessionRUMResourcesHandler.swift index 0e4f78761c..10a92419a5 100644 --- a/DatadogRUM/Sources/Instrumentation/Resources/URLSessionRUMResourcesHandler.swift +++ b/DatadogRUM/Sources/Instrumentation/Resources/URLSessionRUMResourcesHandler.swift @@ -154,12 +154,12 @@ extension DistributedTracing { // To make sure the generated traces from RUM don’t affect APM Index Spans counts. request.setValue("rum", forHTTPHeaderField: TracingHTTPHeaders.originField) case .b3: - writer = OTelHTTPHeadersWriter( + writer = B3HTTPHeadersWriter( sampler: sampler, injectEncoding: .single ) case .b3multi: - writer = OTelHTTPHeadersWriter( + writer = B3HTTPHeadersWriter( sampler: sampler, injectEncoding: .multiple ) diff --git a/DatadogRUM/Sources/Integrations/CrashReportReceiver.swift b/DatadogRUM/Sources/Integrations/CrashReportReceiver.swift index 82d6ad5712..497ac4166a 100644 --- a/DatadogRUM/Sources/Integrations/CrashReportReceiver.swift +++ b/DatadogRUM/Sources/Integrations/CrashReportReceiver.swift @@ -325,7 +325,7 @@ internal struct CrashReportReceiver: FeatureMessageReceiver { let event = RUMErrorEvent( dd: .init( browserSdkVersion: nil, - configuration: nil, + configuration: .init(sessionReplaySampleRate: nil, sessionSampleRate: Double(self.sessionSampler.samplingRate)), session: .init(plan: .plan1) ), action: nil, @@ -378,7 +378,7 @@ internal struct CrashReportReceiver: FeatureMessageReceiver { return RUMViewEvent( dd: .init( browserSdkVersion: nil, - configuration: nil, + configuration: .init(sessionReplaySampleRate: nil, sessionSampleRate: Double(self.sessionSampler.samplingRate)), documentVersion: original.dd.documentVersion + 1, pageStates: nil, replayStats: nil, @@ -420,6 +420,7 @@ internal struct CrashReportReceiver: FeatureMessageReceiver { frustration: .init(count: 0), id: original.view.id, inForegroundPeriods: original.view.inForegroundPeriods, + interactionToNextPaint: nil, isActive: false, isSlowRendered: false, jsRefreshRate: nil, @@ -455,7 +456,7 @@ internal struct CrashReportReceiver: FeatureMessageReceiver { return RUMViewEvent( dd: .init( browserSdkVersion: nil, - configuration: nil, + configuration: .init(sessionReplaySampleRate: nil, sessionSampleRate: Double(self.sessionSampler.samplingRate)), documentVersion: 1, pageStates: nil, replayStats: nil, @@ -513,6 +514,7 @@ internal struct CrashReportReceiver: FeatureMessageReceiver { frustration: .init(count: 0), id: viewUUID.toRUMDataFormat, inForegroundPeriods: nil, + interactionToNextPaint: nil, isActive: false, // we know it won't receive updates isSlowRendered: false, jsRefreshRate: nil, diff --git a/DatadogRUM/Sources/Integrations/TelemetryReceiver.swift b/DatadogRUM/Sources/Integrations/TelemetryReceiver.swift index b7c63e756f..f25796b7a4 100644 --- a/DatadogRUM/Sources/Integrations/TelemetryReceiver.swift +++ b/DatadogRUM/Sources/Integrations/TelemetryReceiver.swift @@ -280,6 +280,8 @@ private extension TelemetryConfigurationEvent.Telemetry.Configuration { init(_ configuration: DatadogInternal.ConfigurationTelemetry) { self.init( actionNameAttribute: nil, + allowFallbackToLocalStorage: nil, + allowUntrustedEvents: nil, batchSize: configuration.batchSize, batchUploadFrequency: configuration.batchUploadFrequency, dartVersion: configuration.dartVersion, @@ -324,6 +326,7 @@ private extension TelemetryConfigurationEvent.Telemetry.Configuration { useProxy: configuration.useProxy, useSecureSessionCookie: nil, useTracing: configuration.useTracing, + useWorkerUrl: nil, viewTrackingStrategy: nil ) } diff --git a/DatadogRUM/Sources/RUMMonitor/RUMCommand.swift b/DatadogRUM/Sources/RUMMonitor/RUMCommand.swift index e6040cc3bf..4c9475952f 100644 --- a/DatadogRUM/Sources/RUMMonitor/RUMCommand.swift +++ b/DatadogRUM/Sources/RUMMonitor/RUMCommand.swift @@ -87,7 +87,7 @@ internal struct RUMStopViewCommand: RUMCommand, RUMViewScopePropagatableAttribut let identity: RUMViewIdentifiable } -internal struct RUMAddCurrentViewErrorCommand: RUMCommand, RUMViewScopePropagatableAttributes { +internal struct RUMAddCurrentViewErrorCommand: RUMCommand { var time: Date var attributes: [AttributeKey: AttributeValue] let canStartBackgroundView = true // yes, we want to track errors in "Background" view diff --git a/DatadogRUM/Sources/RUMMonitor/Scopes/RUMResourceScope.swift b/DatadogRUM/Sources/RUMMonitor/Scopes/RUMResourceScope.swift index 4a72cbfd61..5e4a7f810a 100644 --- a/DatadogRUM/Sources/RUMMonitor/Scopes/RUMResourceScope.swift +++ b/DatadogRUM/Sources/RUMMonitor/Scopes/RUMResourceScope.swift @@ -136,7 +136,7 @@ internal class RUMResourceScope: RUMScope { let resourceEvent = RUMResourceEvent( dd: .init( browserSdkVersion: nil, - configuration: nil, + configuration: .init(sessionReplaySampleRate: nil, sessionSampleRate: Double(dependencies.sessionSampler.samplingRate)), discarded: nil, rulePsr: traceSamplingRate, session: .init(plan: .plan1), @@ -230,7 +230,7 @@ internal class RUMResourceScope: RUMScope { let errorEvent = RUMErrorEvent( dd: .init( browserSdkVersion: nil, - configuration: nil, + configuration: .init(sessionReplaySampleRate: nil, sessionSampleRate: Double(dependencies.sessionSampler.samplingRate)), session: .init(plan: .plan1) ), action: self.context.activeUserActionID.map { rumUUID in diff --git a/DatadogRUM/Sources/RUMMonitor/Scopes/RUMUserActionScope.swift b/DatadogRUM/Sources/RUMMonitor/Scopes/RUMUserActionScope.swift index 806d2df6e2..f13d61aea5 100644 --- a/DatadogRUM/Sources/RUMMonitor/Scopes/RUMUserActionScope.swift +++ b/DatadogRUM/Sources/RUMMonitor/Scopes/RUMUserActionScope.swift @@ -141,7 +141,7 @@ internal class RUMUserActionScope: RUMScope, RUMContextProvider { dd: .init( action: nil, browserSdkVersion: nil, - configuration: nil, + configuration: .init(sessionReplaySampleRate: nil, sessionSampleRate: Double(dependencies.sessionSampler.samplingRate)), session: .init(plan: .plan1) ), action: .init( diff --git a/DatadogRUM/Sources/RUMMonitor/Scopes/RUMViewScope.swift b/DatadogRUM/Sources/RUMMonitor/Scopes/RUMViewScope.swift index ceb31779ac..13ebb00bd4 100644 --- a/DatadogRUM/Sources/RUMMonitor/Scopes/RUMViewScope.swift +++ b/DatadogRUM/Sources/RUMMonitor/Scopes/RUMViewScope.swift @@ -368,7 +368,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { dd: .init( action: nil, browserSdkVersion: nil, - configuration: nil, + configuration: .init(sessionReplaySampleRate: nil, sessionSampleRate: Double(dependencies.sessionSampler.samplingRate)), session: .init(plan: .plan1) ), action: .init( @@ -438,7 +438,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { let viewEvent = RUMViewEvent( dd: .init( browserSdkVersion: nil, - configuration: nil, + configuration: .init(sessionReplaySampleRate: nil, sessionSampleRate: Double(dependencies.sessionSampler.samplingRate)), documentVersion: version.toInt64, pageStates: nil, replayStats: .init( @@ -494,6 +494,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { frustration: .init(count: frustrationCount), id: viewUUID.toRUMDataFormat, inForegroundPeriods: nil, + interactionToNextPaint: nil, isActive: isActive, isSlowRendered: isSlowRendered ?? false, jsRefreshRate: viewPerformanceMetrics[.jsFrameTimeSeconds]?.asJsRefreshRate(), @@ -532,12 +533,11 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { private func sendErrorEvent(on command: RUMAddCurrentViewErrorCommand, context: DatadogContext, writer: Writer) { errorsCount += 1 - attributes.merge(rumCommandAttributes: command.attributes) let errorEvent = RUMErrorEvent( dd: .init( browserSdkVersion: nil, - configuration: nil, + configuration: .init(sessionReplaySampleRate: nil, sessionSampleRate: Double(dependencies.sessionSampler.samplingRate)), session: .init(plan: .plan1) ), action: self.context.activeUserActionID.map { rumUUID in @@ -546,7 +546,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { application: .init(id: self.context.rumApplicationID), ciTest: dependencies.ciTest, connectivity: .init(context: context), - context: .init(contextInfo: attributes), + context: .init(contextInfo: command.attributes), date: command.time.addingTimeInterval(serverTimeOffset).timeIntervalSince1970.toInt64Milliseconds, device: .init(context: context), display: nil, @@ -593,15 +593,13 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { } private func sendLongTaskEvent(on command: RUMAddLongTaskCommand, context: DatadogContext, writer: Writer) { - attributes.merge(rumCommandAttributes: command.attributes) - let taskDurationInNs = command.duration.toInt64Nanoseconds let isFrozenFrame = taskDurationInNs > Constants.frozenFrameThresholdInNs let longTaskEvent = RUMLongTaskEvent( dd: .init( browserSdkVersion: nil, - configuration: nil, + configuration: .init(sessionReplaySampleRate: nil, sessionSampleRate: Double(dependencies.sessionSampler.samplingRate)), discarded: nil, session: .init(plan: .plan1) ), @@ -611,7 +609,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { application: .init(id: self.context.rumApplicationID), ciTest: dependencies.ciTest, connectivity: .init(context: context), - context: .init(contextInfo: attributes), + context: .init(contextInfo: command.attributes), date: (command.time - command.duration).addingTimeInterval(serverTimeOffset).timeIntervalSince1970.toInt64Milliseconds, device: .init(context: context), display: nil, diff --git a/DatadogRUM/Tests/Instrumentation/Resources/URLSessionRUMResourcesHandlerTests.swift b/DatadogRUM/Tests/Instrumentation/Resources/URLSessionRUMResourcesHandlerTests.swift index f7d1686a08..c6e967cdd5 100644 --- a/DatadogRUM/Tests/Instrumentation/Resources/URLSessionRUMResourcesHandlerTests.swift +++ b/DatadogRUM/Tests/Instrumentation/Resources/URLSessionRUMResourcesHandlerTests.swift @@ -67,7 +67,7 @@ class URLSessionRUMResourcesHandlerTests: XCTestCase { ) XCTAssertNil(request.value(forHTTPHeaderField: TracingHTTPHeaders.originField)) - XCTAssertEqual(request.value(forHTTPHeaderField: OTelHTTPHeaders.Single.b3Field), "00000000000000000000000000000001-0000000000000001-1") + XCTAssertEqual(request.value(forHTTPHeaderField: B3HTTPHeaders.Single.b3Field), "00000000000000000000000000000001-0000000000000001-1") } func testGivenFirstPartyInterception_withSampledTrace_itInjectB3MultiTraceHeaders() throws { @@ -87,10 +87,10 @@ class URLSessionRUMResourcesHandlerTests: XCTestCase { ) XCTAssertNil(request.value(forHTTPHeaderField: TracingHTTPHeaders.originField)) - XCTAssertEqual(request.value(forHTTPHeaderField: OTelHTTPHeaders.Multiple.traceIDField), "00000000000000000000000000000001") - XCTAssertEqual(request.value(forHTTPHeaderField: OTelHTTPHeaders.Multiple.spanIDField), "0000000000000001") - XCTAssertNil(request.value(forHTTPHeaderField: OTelHTTPHeaders.Multiple.parentSpanIDField)) - XCTAssertEqual(request.value(forHTTPHeaderField: OTelHTTPHeaders.Multiple.sampledField), "1") + XCTAssertEqual(request.value(forHTTPHeaderField: B3HTTPHeaders.Multiple.traceIDField), "00000000000000000000000000000001") + XCTAssertEqual(request.value(forHTTPHeaderField: B3HTTPHeaders.Multiple.spanIDField), "0000000000000001") + XCTAssertNil(request.value(forHTTPHeaderField: B3HTTPHeaders.Multiple.parentSpanIDField)) + XCTAssertEqual(request.value(forHTTPHeaderField: B3HTTPHeaders.Multiple.sampledField), "1") } func testGivenFirstPartyInterception_withSampledTrace_itInjectW3CTraceHeaders() throws { @@ -152,7 +152,7 @@ class URLSessionRUMResourcesHandlerTests: XCTestCase { ) XCTAssertNil(request.value(forHTTPHeaderField: TracingHTTPHeaders.originField)) - XCTAssertEqual(request.value(forHTTPHeaderField: OTelHTTPHeaders.Single.b3Field), "0") + XCTAssertEqual(request.value(forHTTPHeaderField: B3HTTPHeaders.Single.b3Field), "0") } func testGivenFirstPartyInterception_withRejectedTrace_itDoesNotInjectB3MultiTraceHeaders() throws { @@ -172,10 +172,10 @@ class URLSessionRUMResourcesHandlerTests: XCTestCase { ) XCTAssertNil(request.value(forHTTPHeaderField: TracingHTTPHeaders.originField)) - XCTAssertNil(request.value(forHTTPHeaderField: OTelHTTPHeaders.Multiple.traceIDField)) - XCTAssertNil(request.value(forHTTPHeaderField: OTelHTTPHeaders.Multiple.spanIDField)) - XCTAssertNil(request.value(forHTTPHeaderField: OTelHTTPHeaders.Multiple.parentSpanIDField)) - XCTAssertEqual(request.value(forHTTPHeaderField: OTelHTTPHeaders.Multiple.sampledField), "0") + XCTAssertNil(request.value(forHTTPHeaderField: B3HTTPHeaders.Multiple.traceIDField)) + XCTAssertNil(request.value(forHTTPHeaderField: B3HTTPHeaders.Multiple.spanIDField)) + XCTAssertNil(request.value(forHTTPHeaderField: B3HTTPHeaders.Multiple.parentSpanIDField)) + XCTAssertEqual(request.value(forHTTPHeaderField: B3HTTPHeaders.Multiple.sampledField), "0") } func testGivenFirstPartyInterception_withRejectedTrace_itDoesNotInjectW3CTraceHeaders() throws { diff --git a/DatadogRUM/Tests/Mocks/RUMDataModelMocks.swift b/DatadogRUM/Tests/Mocks/RUMDataModelMocks.swift index 004f3d9448..dd5fed5d39 100644 --- a/DatadogRUM/Tests/Mocks/RUMDataModelMocks.swift +++ b/DatadogRUM/Tests/Mocks/RUMDataModelMocks.swift @@ -94,6 +94,12 @@ extension RUMOperatingSystem: RandomMockable { } } +extension RUMViewEvent.DD.Configuration: RandomMockable { + public static func mockRandom() -> RUMViewEvent.DD.Configuration { + return .init(sessionReplaySampleRate: .mockRandom(min: 0, max: 100), sessionSampleRate: .mockRandom(min: 0, max: 100)) + } +} + extension RUMViewEvent: RandomMockable { public static func mockRandom() -> RUMViewEvent { return mockRandomWith() @@ -108,7 +114,7 @@ extension RUMViewEvent: RandomMockable { return RUMViewEvent( dd: .init( browserSdkVersion: nil, - configuration: nil, + configuration: .mockRandom(), documentVersion: .mockRandom(), pageStates: nil, replayStats: nil, @@ -162,6 +168,7 @@ extension RUMViewEvent: RandomMockable { start: .mockRandom() ) ], + interactionToNextPaint: nil, isActive: viewIsActive, isSlowRendered: .mockRandom(), jsRefreshRate: nil, @@ -184,12 +191,18 @@ extension RUMViewEvent: RandomMockable { } } +extension RUMResourceEvent.DD.Configuration: RandomMockable { + public static func mockRandom() -> RUMResourceEvent.DD.Configuration { + return .init(sessionReplaySampleRate: .mockRandom(min: 0, max: 100), sessionSampleRate: .mockRandom(min: 0, max: 100)) + } +} + extension RUMResourceEvent: RandomMockable { public static func mockRandom() -> RUMResourceEvent { return RUMResourceEvent( dd: .init( browserSdkVersion: nil, - configuration: nil, + configuration: .mockRandom(), discarded: nil, rulePsr: nil, session: .init(plan: .plan1), @@ -244,6 +257,12 @@ extension RUMResourceEvent: RandomMockable { } } +extension RUMActionEvent.DD.Configuration: RandomMockable { + public static func mockRandom() -> RUMActionEvent.DD.Configuration { + return .init(sessionReplaySampleRate: .mockRandom(min: 0, max: 100), sessionSampleRate: .mockRandom(min: 0, max: 100)) + } +} + extension RUMActionEvent: RandomMockable { public static func mockRandom() -> RUMActionEvent { return RUMActionEvent( @@ -257,7 +276,7 @@ extension RUMActionEvent: RandomMockable { ) ), browserSdkVersion: nil, - configuration: nil, + configuration: .mockRandom(), session: .init(plan: .plan1) ), action: .init( @@ -305,12 +324,18 @@ extension RUMErrorEvent.Error.SourceType: RandomMockable { } } +extension RUMErrorEvent.DD.Configuration: RandomMockable { + public static func mockRandom() -> RUMErrorEvent.DD.Configuration { + return .init(sessionReplaySampleRate: .mockRandom(min: 0, max: 100), sessionSampleRate: .mockRandom(min: 0, max: 100)) + } +} + extension RUMErrorEvent: RandomMockable { public static func mockRandom() -> RUMErrorEvent { return RUMErrorEvent( dd: .init( browserSdkVersion: nil, - configuration: nil, + configuration: .mockRandom(), session: .init(plan: .plan1) ), action: .init(id: .mockRandom()), @@ -376,12 +401,18 @@ extension RUMCrashEvent: RandomMockable { } } +extension RUMLongTaskEvent.DD.Configuration: RandomMockable { + public static func mockRandom() -> RUMLongTaskEvent.DD.Configuration { + return .init(sessionReplaySampleRate: .mockRandom(min: 0, max: 100), sessionSampleRate: .mockRandom(min: 0, max: 100)) + } +} + extension RUMLongTaskEvent: RandomMockable { public static func mockRandom() -> RUMLongTaskEvent { return RUMLongTaskEvent( dd: .init( browserSdkVersion: nil, - configuration: nil, + configuration: .mockRandom(), discarded: nil, session: .init(plan: .plan1) ), @@ -424,6 +455,8 @@ extension TelemetryConfigurationEvent: RandomMockable { telemetry: .init( configuration: .init( actionNameAttribute: nil, + allowFallbackToLocalStorage: nil, + allowUntrustedEvents: nil, batchSize: .mockAny(), batchUploadFrequency: .mockAny(), defaultPrivacyLevel: .mockAny(), @@ -467,6 +500,7 @@ extension TelemetryConfigurationEvent: RandomMockable { useProxy: .mockRandom(), useSecureSessionCookie: nil, useTracing: .mockRandom(), + useWorkerUrl: nil, viewTrackingStrategy: nil ) ), diff --git a/DatadogRUM/Tests/RUMMonitor/Scopes/RUMViewScopeTests.swift b/DatadogRUM/Tests/RUMMonitor/Scopes/RUMViewScopeTests.swift index 8dd047af88..a09ae2a0b1 100644 --- a/DatadogRUM/Tests/RUMMonitor/Scopes/RUMViewScopeTests.swift +++ b/DatadogRUM/Tests/RUMMonitor/Scopes/RUMViewScopeTests.swift @@ -1045,7 +1045,7 @@ class RUMViewScopeTests: XCTestCase { XCTAssertTrue(error.error.isCrash == false) XCTAssertNil(error.error.resource) XCTAssertNil(error.action) - XCTAssertEqual(error.context?.contextInfo as? [String: String], ["foo": "bar"]) + XCTAssertEqual(error.context?.contextInfo as? [String: String], [:]) XCTAssertEqual(error.dd.session?.plan, .plan1, "All RUM events should use RUM Lite plan") XCTAssertEqual(error.source, .ios) XCTAssertEqual(error.service, "test-service") @@ -1153,6 +1153,60 @@ class RUMViewScopeTests: XCTestCase { XCTAssertEqual(viewUpdate.service, "test-service") } + func testGivenStartedView_whenErrorWithAttributesIsAdded_itDoesNotUpdateViewAttributes() throws { + let hasReplay: Bool = .mockRandom() + var context = self.context + context.featuresAttributes = .mockSessionReplayAttributes(hasReplay: hasReplay) + + var currentTime: Date = .mockDecember15th2019At10AMUTC() + let scope = RUMViewScope( + isInitialView: .mockRandom(), + parent: parent, + dependencies: .mockAny(), + identity: mockView, + path: "UIViewController", + name: "ViewName", + attributes: [ + "test_attribute": "abc", + "other_attribute": "my attribute" + ], + customTimings: [:], + startTime: currentTime, + serverTimeOffset: .zero + ) + + XCTAssertTrue( + scope.process( + command: RUMStartViewCommand.mockWith(time: currentTime, attributes: [:], identity: mockView), + context: context, + writer: writer + ) + ) + + currentTime.addTimeInterval(1) + + XCTAssertTrue( + scope.process( + command: RUMAddCurrentViewErrorCommand.mockWithErrorMessage( + time: currentTime, + message: "view error", + source: .source, + stack: nil, + attributes: ["other_attribute": "overwritten", "foo": "bar"] + ), + context: context, + writer: writer + ) + ) + + let error = try XCTUnwrap(writer.events(ofType: RUMErrorEvent.self).last) + DDAssertDictionariesEqual(error.context!.contextInfo, ["other_attribute": "overwritten", "foo": "bar"]) + + XCTAssertEqual(scope.attributes["test_attribute"] as? String, "abc") + XCTAssertEqual(scope.attributes["other_attribute"] as? String, "my attribute") + XCTAssertNil(scope.attributes["foo"]) + } + func testWhenResourceIsFinishedWithError_itSendsViewUpdateEvent() throws { let scope = RUMViewScope( isInitialView: .mockRandom(), @@ -1264,6 +1318,56 @@ class RUMViewScopeTests: XCTestCase { XCTAssertEqual(viewUpdate.view.longTask?.count, 1) } + func testGivenStartedView_whenLongTaskWithAttributesIsAdded_itDoesNotUpdateViewAttributes() throws { + let hasReplay: Bool = .mockRandom() + var context = self.context + context.featuresAttributes = .mockSessionReplayAttributes(hasReplay: hasReplay) + + var currentTime: Date = .mockDecember15th2019At10AMUTC() + let scope = RUMViewScope( + isInitialView: .mockRandom(), + parent: parent, + dependencies: .mockAny(), + identity: mockView, + path: "UIViewController", + name: "ViewName", + attributes: [ + "test_attribute": "abc", + "other_attribute": "my attribute" + ], + customTimings: [:], + startTime: currentTime, + serverTimeOffset: .zero + ) + + XCTAssertTrue( + scope.process( + command: RUMStartViewCommand.mockWith(time: currentTime, attributes: [:], identity: mockView), + context: context, + writer: writer + ) + ) + + currentTime.addTimeInterval(1) + let duration: TimeInterval = 1.0 + + XCTAssertTrue( + scope.process( + command: RUMAddLongTaskCommand( + time: currentTime, + attributes: ["foo": "bar", "test_attribute": "overwritten"], + duration: duration + ), + context: context, + writer: writer + ) + ) + + let event = try XCTUnwrap(writer.events(ofType: RUMLongTaskEvent.self).last) + DDAssertDictionariesEqual(event.context!.contextInfo, ["foo": "bar", "test_attribute": "overwritten"]) + DDAssertDictionariesEqual(scope.attributes, ["test_attribute": "abc", "other_attribute": "my attribute"]) + } + func testWhenLongTaskIsAddedWithConfiguredSource_itSendsLongTaskEventWithConfiguredSource() throws { let startViewDate: Date = .mockDecember15th2019At10AMUTC() diff --git a/DatadogSDK.podspec b/DatadogSDK.podspec index 7ddf1c3220..98c15659f7 100644 --- a/DatadogSDK.podspec +++ b/DatadogSDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "DatadogSDK" - s.version = "2.0.0" + s.version = "2.1.0" s.summary = "Official Datadog Swift SDK for iOS." s.homepage = "https://www.datadoghq.com" diff --git a/DatadogSDKAlamofireExtension.podspec b/DatadogSDKAlamofireExtension.podspec index 51113bf9e2..40f49bdac3 100644 --- a/DatadogSDKAlamofireExtension.podspec +++ b/DatadogSDKAlamofireExtension.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "DatadogSDKAlamofireExtension" s.module_name = "DatadogAlamofireExtension" - s.version = "2.0.0" + s.version = "2.1.0" s.summary = "An Official Extensions of Datadog Swift SDK for Alamofire." s.homepage = "https://www.datadoghq.com" diff --git a/DatadogSDKCrashReporting.podspec b/DatadogSDKCrashReporting.podspec index 17f2798d43..d368034221 100644 --- a/DatadogSDKCrashReporting.podspec +++ b/DatadogSDKCrashReporting.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "DatadogSDKCrashReporting" s.module_name = "DatadogCrashReporting" - s.version = "2.0.0" + s.version = "2.1.0" s.summary = "Official Datadog Crash Reporting SDK for iOS." s.homepage = "https://www.datadoghq.com" @@ -26,5 +26,5 @@ Pod::Spec.new do |s| s.source_files = "DatadogCrashReporting/Sources/**/*.swift" s.dependency 'DatadogInternal', s.version.to_s - s.dependency 'PLCrashReporter', '~> 1.11.0' + s.dependency 'PLCrashReporter', '~> 1.11.1' end diff --git a/DatadogSDKObjc.podspec b/DatadogSDKObjc.podspec index d705b520b8..f5bbdff42d 100644 --- a/DatadogSDKObjc.podspec +++ b/DatadogSDKObjc.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "DatadogSDKObjc" s.module_name = "DatadogObjc" - s.version = "2.0.0" + s.version = "2.1.0" s.summary = "Official Datadog Objective-C SDK for iOS." s.homepage = "https://www.datadoghq.com" @@ -28,4 +28,5 @@ Pod::Spec.new do |s| s.dependency 'DatadogRUM', s.version.to_s s.dependency 'DatadogLogs', s.version.to_s s.dependency 'DatadogTrace', s.version.to_s + s.dependency 'DatadogSessionReplay', s.version.to_s end diff --git a/DatadogSessionReplay.podspec b/DatadogSessionReplay.podspec index 432c065da8..519718460c 100644 --- a/DatadogSessionReplay.podspec +++ b/DatadogSessionReplay.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "DatadogSessionReplay" - s.version = "2.0.0" + s.version = "2.1.0" s.summary = "Official Datadog Session Replay SDK for iOS." s.homepage = "https://www.datadoghq.com" diff --git a/DatadogSessionReplay/Sources/Feature/RequestBuilder/RequestBuilder.swift b/DatadogSessionReplay/Sources/Feature/RequestBuilder/RequestBuilder.swift index af4ae765fd..7de96dbb3d 100644 --- a/DatadogSessionReplay/Sources/Feature/RequestBuilder/RequestBuilder.swift +++ b/DatadogSessionReplay/Sources/Feature/RequestBuilder/RequestBuilder.swift @@ -30,35 +30,15 @@ internal struct RequestBuilder: FeatureRequestBuilder { // error to let the core delete the batch: let records = try events.map { try EnrichedRecordJSON(jsonObjectData: $0.data) } let segment = try segmentBuilder.createSegmentJSON(from: records) - let url = customUploadURL ?? intakeURL(for: context.site) - return try createRequest(url: url, segment: segment, context: context) + return try createRequest(segment: segment, context: context) } - private func intakeURL(for site: DatadogSite) -> URL { - // swiftlint:disable force_unwrapping - switch site { - case .us1: - return URL(string: "https://session-replay.browser-intake-datadoghq.com/api/v2/replay")! - case .us3: - return URL(string: "https://session-replay.browser-intake-us3-datadoghq.com/api/v2/replay")! - case .us5: - return URL(string: "https://session-replay.browser-intake-us5-datadoghq.com/api/v2/replay")! - case .eu1: - return URL(string: "https://session-replay.browser-intake-datadoghq.eu/api/v2/replay")! - case .ap1: - return URL(string: "https://session-replay.browser-intake-datadoghq.eu/api/v2/replay")! - case .us1_fed: - return URL(string: "https://session-replay.browser-intake-ddog-gov.com/api/v2/replay")! - } - // swiftlint:enable force_unwrapping - } - - private func createRequest(url: URL, segment: SegmentJSON, context: DatadogContext) throws -> URLRequest { + private func createRequest(segment: SegmentJSON, context: DatadogContext) throws -> URLRequest { var multipart = multipartBuilder let builder = URLRequestBuilder( - url: url, + url: url(with: context), queryItems: [], headers: [ .contentTypeHeader(contentType: .multipartFormData(boundary: multipart.boundary.uuidString)), @@ -97,4 +77,8 @@ internal struct RequestBuilder: FeatureRequestBuilder { // Data is already compressed, so request building request w/o compression: return builder.uploadRequest(with: multipart.data, compress: false) } + + private func url(with context: DatadogContext) -> URL { + customUploadURL ?? context.site.endpoint.appendingPathComponent("api/v2/replay") + } } diff --git a/DatadogSessionReplay/Tests/Feature/RequestBuilder/RequestBuilderTests.swift b/DatadogSessionReplay/Tests/Feature/RequestBuilder/RequestBuilderTests.swift index 3a875f6295..f861dd98e5 100644 --- a/DatadogSessionReplay/Tests/Feature/RequestBuilder/RequestBuilderTests.swift +++ b/DatadogSessionReplay/Tests/Feature/RequestBuilder/RequestBuilderTests.swift @@ -42,12 +42,12 @@ class RequestBuilderTests: XCTestCase { } // Then - XCTAssertEqual(try url(for: .us1), "https://session-replay.browser-intake-datadoghq.com/api/v2/replay") - XCTAssertEqual(try url(for: .us3), "https://session-replay.browser-intake-us3-datadoghq.com/api/v2/replay") - XCTAssertEqual(try url(for: .us5), "https://session-replay.browser-intake-us5-datadoghq.com/api/v2/replay") - XCTAssertEqual(try url(for: .eu1), "https://session-replay.browser-intake-datadoghq.eu/api/v2/replay") - XCTAssertEqual(try url(for: .ap1), "https://session-replay.browser-intake-datadoghq.eu/api/v2/replay") - XCTAssertEqual(try url(for: .us1_fed), "https://session-replay.browser-intake-ddog-gov.com/api/v2/replay") + XCTAssertEqual(try url(for: .us1), "https://browser-intake-datadoghq.com/api/v2/replay") + XCTAssertEqual(try url(for: .us3), "https://browser-intake-us3-datadoghq.com/api/v2/replay") + XCTAssertEqual(try url(for: .us5), "https://browser-intake-us5-datadoghq.com/api/v2/replay") + XCTAssertEqual(try url(for: .eu1), "https://browser-intake-datadoghq.eu/api/v2/replay") + XCTAssertEqual(try url(for: .ap1), "https://browser-intake-ap1-datadoghq.com/api/v2/replay") + XCTAssertEqual(try url(for: .us1_fed), "https://browser-intake-ddog-gov.com/api/v2/replay") } func testItSetsCustomIntakeURL() { diff --git a/DatadogTrace.podspec b/DatadogTrace.podspec index 1a6e0c9793..b9c696eed6 100644 --- a/DatadogTrace.podspec +++ b/DatadogTrace.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "DatadogTrace" - s.version = "2.0.0" + s.version = "2.1.0" s.summary = "Datadog Trace Module." s.homepage = "https://www.datadoghq.com" diff --git a/DatadogTrace/Sources/DDFormat.swift b/DatadogTrace/Sources/DDFormat.swift index 785db3de65..01d52cb045 100644 --- a/DatadogTrace/Sources/DDFormat.swift +++ b/DatadogTrace/Sources/DDFormat.swift @@ -9,11 +9,11 @@ import DatadogInternal extension HTTPHeadersReader: OTFormatReader {} extension W3CHTTPHeadersReader: OTFormatReader {} -extension OTelHTTPHeadersReader: OTFormatReader {} +extension B3HTTPHeadersReader: OTFormatReader {} extension HTTPHeadersWriter: OTFormatWriter {} extension W3CHTTPHeadersWriter: OTFormatWriter {} -extension OTelHTTPHeadersWriter: OTFormatWriter {} +extension B3HTTPHeadersWriter: OTFormatWriter {} extension TracePropagationHeadersWriter where Self: OTFormatWriter { public func inject(spanContext: OTSpanContext) { diff --git a/DatadogTrace/Sources/Integrations/TracingURLSessionHandler.swift b/DatadogTrace/Sources/Integrations/TracingURLSessionHandler.swift index 989e7ee184..f6f53b139d 100644 --- a/DatadogTrace/Sources/Integrations/TracingURLSessionHandler.swift +++ b/DatadogTrace/Sources/Integrations/TracingURLSessionHandler.swift @@ -43,12 +43,12 @@ internal struct TracingURLSessionHandler: DatadogURLSessionHandler { case .datadog: writer = HTTPHeadersWriter(sampler: tracingSampler) case .b3: - writer = OTelHTTPHeadersWriter( + writer = B3HTTPHeadersWriter( sampler: tracingSampler, injectEncoding: .single ) case .b3multi: - writer = OTelHTTPHeadersWriter( + writer = B3HTTPHeadersWriter( sampler: tracingSampler, injectEncoding: .multiple ) diff --git a/DatadogTrace/Sources/TraceConfiguration.swift b/DatadogTrace/Sources/TraceConfiguration.swift index ef94ec2ffd..21c7d58414 100644 --- a/DatadogTrace/Sources/TraceConfiguration.swift +++ b/DatadogTrace/Sources/TraceConfiguration.swift @@ -12,6 +12,10 @@ import DatadogInternal @_exported import class DatadogInternal.DatadogURLSessionDelegate @_exported import typealias DatadogInternal.DDURLSessionDelegate @_exported import protocol DatadogInternal.__URLSessionDelegateProviding + +@_exported import class DatadogInternal.HTTPHeadersWriter +@_exported import class DatadogInternal.B3HTTPHeadersWriter +@_exported import class DatadogInternal.W3CHTTPHeadersWriter // swiftlint:enable duplicate_imports extension Trace { diff --git a/DatadogTrace/Tests/TracingURLSessionHandlerTests.swift b/DatadogTrace/Tests/TracingURLSessionHandlerTests.swift index 42ad94f7a2..38439854bb 100644 --- a/DatadogTrace/Tests/TracingURLSessionHandlerTests.swift +++ b/DatadogTrace/Tests/TracingURLSessionHandlerTests.swift @@ -65,11 +65,11 @@ class TracingURLSessionHandlerTests: XCTestCase { XCTAssertEqual(request.value(forHTTPHeaderField: TracingHTTPHeaders.traceIDField), "1") XCTAssertEqual(request.value(forHTTPHeaderField: TracingHTTPHeaders.parentSpanIDField), "1") XCTAssertEqual(request.value(forHTTPHeaderField: TracingHTTPHeaders.samplingPriorityField), "1") - XCTAssertEqual(request.value(forHTTPHeaderField: OTelHTTPHeaders.Multiple.traceIDField), "00000000000000000000000000000001") - XCTAssertEqual(request.value(forHTTPHeaderField: OTelHTTPHeaders.Multiple.spanIDField), "0000000000000001") - XCTAssertNil(request.value(forHTTPHeaderField: OTelHTTPHeaders.Multiple.parentSpanIDField)) - XCTAssertEqual(request.value(forHTTPHeaderField: OTelHTTPHeaders.Multiple.sampledField), "1") - XCTAssertEqual(request.value(forHTTPHeaderField: OTelHTTPHeaders.Single.b3Field), "00000000000000000000000000000001-0000000000000001-1") + XCTAssertEqual(request.value(forHTTPHeaderField: B3HTTPHeaders.Multiple.traceIDField), "00000000000000000000000000000001") + XCTAssertEqual(request.value(forHTTPHeaderField: B3HTTPHeaders.Multiple.spanIDField), "0000000000000001") + XCTAssertNil(request.value(forHTTPHeaderField: B3HTTPHeaders.Multiple.parentSpanIDField)) + XCTAssertEqual(request.value(forHTTPHeaderField: B3HTTPHeaders.Multiple.sampledField), "1") + XCTAssertEqual(request.value(forHTTPHeaderField: B3HTTPHeaders.Single.b3Field), "00000000000000000000000000000001-0000000000000001-1") XCTAssertEqual(request.value(forHTTPHeaderField: W3CHTTPHeaders.traceparent), "00-00000000000000000000000000000001-0000000000000001-01") } @@ -96,11 +96,11 @@ class TracingURLSessionHandlerTests: XCTestCase { XCTAssertNil(request.value(forHTTPHeaderField: TracingHTTPHeaders.traceIDField)) XCTAssertNil(request.value(forHTTPHeaderField: TracingHTTPHeaders.parentSpanIDField)) XCTAssertEqual(request.value(forHTTPHeaderField: TracingHTTPHeaders.samplingPriorityField), "0") - XCTAssertNil(request.value(forHTTPHeaderField: OTelHTTPHeaders.Multiple.traceIDField)) - XCTAssertNil(request.value(forHTTPHeaderField: OTelHTTPHeaders.Multiple.spanIDField)) - XCTAssertNil(request.value(forHTTPHeaderField: OTelHTTPHeaders.Multiple.parentSpanIDField)) - XCTAssertEqual(request.value(forHTTPHeaderField: OTelHTTPHeaders.Multiple.sampledField), "0") - XCTAssertEqual(request.value(forHTTPHeaderField: OTelHTTPHeaders.Single.b3Field), "0") + XCTAssertNil(request.value(forHTTPHeaderField: B3HTTPHeaders.Multiple.traceIDField)) + XCTAssertNil(request.value(forHTTPHeaderField: B3HTTPHeaders.Multiple.spanIDField)) + XCTAssertNil(request.value(forHTTPHeaderField: B3HTTPHeaders.Multiple.parentSpanIDField)) + XCTAssertEqual(request.value(forHTTPHeaderField: B3HTTPHeaders.Multiple.sampledField), "0") + XCTAssertEqual(request.value(forHTTPHeaderField: B3HTTPHeaders.Single.b3Field), "0") XCTAssertEqual(request.value(forHTTPHeaderField: W3CHTTPHeaders.traceparent), "00-00000000000000000000000000000001-0000000000000001-00") } diff --git a/DatadogWebViewTracking.podspec b/DatadogWebViewTracking.podspec index 3d217d0b32..c3453a4840 100644 --- a/DatadogWebViewTracking.podspec +++ b/DatadogWebViewTracking.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "DatadogWebViewTracking" - s.version = "2.0.0" + s.version = "2.1.0" s.summary = "Datadog WebView Tracking Module." s.homepage = "https://www.datadoghq.com" diff --git a/IntegrationTests/IntegrationScenarios/Scenarios/CrashReporting/CrashReportingWithLoggingScenarioTests.swift b/IntegrationTests/IntegrationScenarios/Scenarios/CrashReporting/CrashReportingWithLoggingScenarioTests.swift index 649e66a106..6f791feb37 100644 --- a/IntegrationTests/IntegrationScenarios/Scenarios/CrashReporting/CrashReportingWithLoggingScenarioTests.swift +++ b/IntegrationTests/IntegrationScenarios/Scenarios/CrashReporting/CrashReportingWithLoggingScenarioTests.swift @@ -97,7 +97,7 @@ class CrashReportingWithLoggingScenarioTests: IntegrationTests, LoggingCommonAss ) // Assert other characteristics important for crash reporting - crashLog.assertServiceName(equals: "ui-tests-service-name") + crashLog.assertService(equals: "ui-tests-service-name") crashLog.assertLoggerName(equals: "crash-reporter") } } diff --git a/IntegrationTests/IntegrationScenarios/Scenarios/Logging/LoggingScenarioTests.swift b/IntegrationTests/IntegrationScenarios/Scenarios/Logging/LoggingScenarioTests.swift index 21e46dd80b..a4d7e99bd4 100644 --- a/IntegrationTests/IntegrationScenarios/Scenarios/Logging/LoggingScenarioTests.swift +++ b/IntegrationTests/IntegrationScenarios/Scenarios/Logging/LoggingScenarioTests.swift @@ -48,7 +48,7 @@ class LoggingScenarioTests: IntegrationTests, LoggingCommonAsserts { logMatchers.forEach { matcher in matcher.assertDate(matches: { Date().timeIntervalSince($0) < dataDeliveryTimeout * 2 }) - matcher.assertServiceName(equals: "ui-tests-service-name") + matcher.assertService(equals: "ui-tests-service-name") matcher.assertLoggerName(equals: "logger-name") matcher.assertLoggerVersion(matches: { $0.matches(regex: semverRegex)}) matcher.assertApplicationVersion(equals: "1.0") diff --git a/IntegrationTests/IntegrationScenarios/Scenarios/WebView/WebViewScenarioTest.swift b/IntegrationTests/IntegrationScenarios/Scenarios/WebView/WebViewScenarioTest.swift index 27eb0adc8a..ff4f04f502 100644 --- a/IntegrationTests/IntegrationScenarios/Scenarios/WebView/WebViewScenarioTest.swift +++ b/IntegrationTests/IntegrationScenarios/Scenarios/WebView/WebViewScenarioTest.swift @@ -75,7 +75,7 @@ class WebViewScenarioTest: IntegrationTests, RUMCommonAsserts { let logMatchers = try LogMatcher.from(requests: recordedRequests) let browserLog = logMatchers[0] - browserLog.assertServiceName(equals: expectedBrowserServiceName) + browserLog.assertService(equals: expectedBrowserServiceName) browserLog.assertAttributes(equal: [ "application_id": expectedBrowserRUMApplicationID, "session_id": expectedBrowserSessionID, diff --git a/Package.swift b/Package.swift index 3b15599556..0e542a9f40 100644 --- a/Package.swift +++ b/Package.swift @@ -44,7 +44,7 @@ let package = Package( ), ], dependencies: [ - .package(name: "PLCrashReporter", url: "https://github.com/microsoft/plcrashreporter.git", from: "1.11.0"), + .package(name: "PLCrashReporter", url: "https://github.com/microsoft/plcrashreporter.git", from: "1.11.1"), ], targets: [ .target( @@ -63,6 +63,7 @@ let package = Package( .target(name: "DatadogLogs"), .target(name: "DatadogTrace"), .target(name: "DatadogRUM"), + .target(name: "DatadogSessionReplay"), ], path: "DatadogObjc/Sources" ), diff --git a/TestUtilities.podspec b/TestUtilities.podspec index 515f2fd14a..43476622b4 100644 --- a/TestUtilities.podspec +++ b/TestUtilities.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "TestUtilities" - s.version = "2.0.0" + s.version = "2.1.0" s.summary = "Datadog Testing Utilities. This module is for internal testing and should not be published." s.homepage = "https://www.datadoghq.com" diff --git a/TestUtilities/Helpers/zlib.swift b/TestUtilities/Helpers/Decompression.swift similarity index 76% rename from TestUtilities/Helpers/zlib.swift rename to TestUtilities/Helpers/Decompression.swift index 5a58ecba28..e205315c3c 100644 --- a/TestUtilities/Helpers/zlib.swift +++ b/TestUtilities/Helpers/Decompression.swift @@ -58,3 +58,25 @@ public struct zlib { } } } + +public struct DecompressionError: Error, CustomStringConvertible { + public var description: String +} + +public extension URLRequest { + /// Returns a copy of the URLRequest with a potentially decompressed body. + /// - Returns: A copy of the URLRequest with a decompressed body if applicable, otherwise the original request. + /// - Throws: A `DecompressionError` if decompression fails. + func decompressed() throws -> URLRequest { + let isCompressed = value(forHTTPHeaderField: "Content-Encoding") == "deflate" + guard isCompressed, let body = httpBody else { + return self + } + guard let decompressedBody = zlib.decode(body) else { + throw DecompressionError(description: "Failed to decompress request body: \(self)") + } + var request = self + request.httpBody = decompressedBody + return request + } +} diff --git a/TestUtilities/Helpers/FuzzyHelpers.swift b/TestUtilities/Helpers/FuzzyHelpers.swift index 3a188cdfad..a86faefb1e 100644 --- a/TestUtilities/Helpers/FuzzyHelpers.swift +++ b/TestUtilities/Helpers/FuzzyHelpers.swift @@ -7,11 +7,12 @@ import Foundation /// Randomly picks and executes one of provided effects. -public func oneOf(_ effects: [() -> Void]) { +@discardableResult +public func oneOf(_ effects: [() -> T]) -> T { guard let randomEffect = effects.randomElement() else { - return + preconditionFailure("At least one effect must be specified") } - randomEffect() + return randomEffect() } /// Randomly picks and executes one or more of provided effects. diff --git a/TestUtilities/Mocks/FoundationMocks.swift b/TestUtilities/Mocks/FoundationMocks.swift index 5c154b3410..098d055630 100644 --- a/TestUtilities/Mocks/FoundationMocks.swift +++ b/TestUtilities/Mocks/FoundationMocks.swift @@ -542,7 +542,7 @@ extension URLResponse { extension URLRequest: AnyMockable { public static func mockAny() -> URLRequest { - return URLRequest(url: .mockAny()) + return .mockWith() } public static func mockWith(httpMethod: String) -> URLRequest { @@ -554,11 +554,24 @@ extension URLRequest: AnyMockable { public static func mockWith( url: String, queryParams: [URLQueryItem]? = nil, - httpMethod: String = "GET" + httpMethod: String = "GET", + headers: [String: String]? = nil, + body: Data? = nil ) -> URLRequest { let url: URL = .mockWith(url: url, queryParams: queryParams) + return mockWith(url: url, httpMethod: httpMethod, headers: headers, body: body) + } + + public static func mockWith( + url: URL = .mockAny(), + httpMethod: String = "GET", + headers: [String: String]? = nil, + body: Data? = nil + ) -> URLRequest { var request = URLRequest(url: url) request.httpMethod = httpMethod + request.allHTTPHeaderFields = headers + request.httpBody = body return request } } diff --git a/tools/distribution/release.py b/tools/distribution/release.py index 1969a0a759..c149be91ed 100755 --- a/tools/distribution/release.py +++ b/tools/distribution/release.py @@ -123,6 +123,7 @@ CPPodspec(name='DatadogCrashReporting'), CPPodspec(name='DatadogWebViewTracking'), CPPodspec(name='DatadogObjc'), + CPPodspec(name='DatadogAlamofireExtension'), CPPodspec(name='DatadogSDK'), CPPodspec(name='DatadogSDKObjc'), CPPodspec(name='DatadogSDKCrashReporting'), diff --git a/tools/distribution/requirements.txt b/tools/distribution/requirements.txt index 0fb1f1b8d3..e98ec5956e 100644 --- a/tools/distribution/requirements.txt +++ b/tools/distribution/requirements.txt @@ -1,4 +1,4 @@ gitdb==4.0.10 -GitPython==3.1.29 +GitPython==3.1.32 smmap==5.0.0 packaging==23.1 \ No newline at end of file diff --git a/tools/distribution/src/utils.py b/tools/distribution/src/utils.py index 40a120c9a3..042b3996a7 100644 --- a/tools/distribution/src/utils.py +++ b/tools/distribution/src/utils.py @@ -11,7 +11,6 @@ import os import subprocess import contextlib -from packaging.version import VERSION_PATTERN @contextlib.contextmanager def remember_cwd(): @@ -71,20 +70,14 @@ def read_sdk_version() -> str: Reads SDK version from 'Sources/Datadog/Versioning.swift'. """ file = 'DatadogCore/Sources/Versioning.swift' + regex = r'^internal let __sdkVersion = \"(.*)?\"$' - # https://packaging.pypa.io/en/latest/version.html#packaging.version.VERSION_PATTERN - version_regex = re.compile(r'^.+\"' + VERSION_PATTERN + r'\"$', re.VERBOSE | re.IGNORECASE) # e.g. 'internal let __sdkVersion = "1.8.0-beta.1" - - versions: list[str] = [] with open(file) as version_file: for line in version_file.readlines(): - if match := re.match(version_regex, line): - versions.append(match.groups()[0]) - - if len(versions) != 1: - raise Exception(f'Expected one `sdk_version` in {file}, but found {len(versions)}: {versions}') - - return versions[0] + if match := re.match(regex, line): + return match.group(1) + + raise Exception(f'Expected `__sdkVersion` not found in {file}') def read_xcode_version() -> str: