From fa7c8a6c40bfa95f43086363b3aeedb716bc4090 Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Wed, 8 May 2024 11:32:39 +0200 Subject: [PATCH] impr: Send Cocoa SDK features (#3948) Send enabled SDK features with the event payload, so we can track their adoption in Looker. --- CHANGELOG.md | 8 ++- Sentry.xcodeproj/project.pbxproj | 8 +++ Sources/Sentry/Public/SentryEvent.h | 3 +- Sources/Sentry/SentryClient.m | 6 ++- .../Helper/SentryEnabledFeaturesBuilder.swift | 45 ++++++++++++++++ .../SentryEnabledFeaturesBuilderTests.swift | 53 +++++++++++++++++++ Tests/SentryTests/SentryClientTests.swift | 13 +++++ 7 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 Sources/Swift/Helper/SentryEnabledFeaturesBuilder.swift create mode 100644 Tests/SentryTests/Helper/SentryEnabledFeaturesBuilderTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fa3b3b53d8..8318446a156 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Improvements + +- Send Cocoa SDK features (#3948) + ## 8.25.2 ### Fixes @@ -25,7 +31,7 @@ - Session replay Improvements (#3877) - Use image average color and text font color to redact session replay - Removed iOS 16 restriction from session replay - - Performance improvement + - Performance improvement ## 8.25.0 diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index e9ce34fbcd3..f0b63173c2c 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -77,6 +77,7 @@ 620379DB2AFE1415005AC0C1 /* SentryBuildAppStartSpans.h in Headers */ = {isa = PBXBuildFile; fileRef = 620379DA2AFE1415005AC0C1 /* SentryBuildAppStartSpans.h */; }; 620379DD2AFE1432005AC0C1 /* SentryBuildAppStartSpans.m in Sources */ = {isa = PBXBuildFile; fileRef = 620379DC2AFE1432005AC0C1 /* SentryBuildAppStartSpans.m */; }; 621D9F2F2B9B0320003D94DE /* SentryCurrentDateProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621D9F2E2B9B0320003D94DE /* SentryCurrentDateProvider.swift */; }; + 621F61F12BEA073A005E654F /* SentryEnabledFeaturesBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621F61F02BEA073A005E654F /* SentryEnabledFeaturesBuilder.swift */; }; 62262B862BA1C46D004DA3DD /* SentryStatsdClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 62262B852BA1C46D004DA3DD /* SentryStatsdClient.h */; }; 62262B882BA1C490004DA3DD /* SentryStatsdClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 62262B872BA1C490004DA3DD /* SentryStatsdClient.m */; }; 62262B8B2BA1C4C1004DA3DD /* EncodeMetrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62262B8A2BA1C4C1004DA3DD /* EncodeMetrics.swift */; }; @@ -91,6 +92,7 @@ 626866742BA89683006995EA /* BucketMetricsAggregatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 626866732BA89683006995EA /* BucketMetricsAggregatorTests.swift */; }; 626866762BA896AD006995EA /* TestMetricsClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 626866752BA896AD006995EA /* TestMetricsClient.swift */; }; 626866782BA89928006995EA /* BucketsMetricsAggregator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 626866772BA89928006995EA /* BucketsMetricsAggregator.swift */; }; + 626E2D4C2BEA0C37005596FE /* SentryEnabledFeaturesBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 626E2D4B2BEA0C37005596FE /* SentryEnabledFeaturesBuilderTests.swift */; }; 6271ADF32BA06D9B0098D2E9 /* SentryInternalSerializable.h in Headers */ = {isa = PBXBuildFile; fileRef = 6271ADF22BA06D9B0098D2E9 /* SentryInternalSerializable.h */; }; 627E7589299F6FE40085504D /* SentryInternalDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 627E7588299F6FE40085504D /* SentryInternalDefines.h */; }; 62862B1C2B1DDBC8009B16E3 /* SentryDelayedFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 62862B1B2B1DDBC8009B16E3 /* SentryDelayedFrame.h */; }; @@ -1033,6 +1035,7 @@ 620379DA2AFE1415005AC0C1 /* SentryBuildAppStartSpans.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryBuildAppStartSpans.h; path = include/SentryBuildAppStartSpans.h; sourceTree = ""; }; 620379DC2AFE1432005AC0C1 /* SentryBuildAppStartSpans.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryBuildAppStartSpans.m; sourceTree = ""; }; 621D9F2E2B9B0320003D94DE /* SentryCurrentDateProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryCurrentDateProvider.swift; sourceTree = ""; }; + 621F61F02BEA073A005E654F /* SentryEnabledFeaturesBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryEnabledFeaturesBuilder.swift; sourceTree = ""; }; 62262B852BA1C46D004DA3DD /* SentryStatsdClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryStatsdClient.h; path = include/SentryStatsdClient.h; sourceTree = ""; }; 62262B872BA1C490004DA3DD /* SentryStatsdClient.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryStatsdClient.m; sourceTree = ""; }; 62262B8A2BA1C4C1004DA3DD /* EncodeMetrics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncodeMetrics.swift; sourceTree = ""; }; @@ -1049,6 +1052,7 @@ 626866732BA89683006995EA /* BucketMetricsAggregatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BucketMetricsAggregatorTests.swift; sourceTree = ""; }; 626866752BA896AD006995EA /* TestMetricsClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestMetricsClient.swift; sourceTree = ""; }; 626866772BA89928006995EA /* BucketsMetricsAggregator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BucketsMetricsAggregator.swift; sourceTree = ""; }; + 626E2D4B2BEA0C37005596FE /* SentryEnabledFeaturesBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryEnabledFeaturesBuilderTests.swift; sourceTree = ""; }; 6271ADF22BA06D9B0098D2E9 /* SentryInternalSerializable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryInternalSerializable.h; path = include/SentryInternalSerializable.h; sourceTree = ""; }; 627E7588299F6FE40085504D /* SentryInternalDefines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryInternalDefines.h; path = include/SentryInternalDefines.h; sourceTree = ""; }; 62862B1B2B1DDBC8009B16E3 /* SentryDelayedFrame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryDelayedFrame.h; path = include/SentryDelayedFrame.h; sourceTree = ""; }; @@ -2027,6 +2031,7 @@ isa = PBXGroup; children = ( 621D9F2E2B9B0320003D94DE /* SentryCurrentDateProvider.swift */, + 621F61F02BEA073A005E654F /* SentryEnabledFeaturesBuilder.swift */, ); path = Helper; sourceTree = ""; @@ -3003,6 +3008,7 @@ 7B34721628086A9D0041F047 /* SentrySwizzleWrapperTests.swift */, D808FB89281BCE46009A2A33 /* TestSentrySwizzleWrapper.swift */, 7BE3C7742445C82300A38442 /* SentryCurrentDateTests.swift */, + 626E2D4B2BEA0C37005596FE /* SentryEnabledFeaturesBuilderTests.swift */, 7BD729992463EA4A00EA3610 /* SentryDateUtilTests.swift */, 7B85BD8D24C5C3A6000A4225 /* SentryFileManagerTestExtension.swift */, 7B4C817124D1BC2B0076ACE4 /* SentryFileManager+Test.h */, @@ -4598,6 +4604,7 @@ 626866782BA89928006995EA /* BucketsMetricsAggregator.swift in Sources */, 6344DDB51EC309E000D9160D /* SentryCrashReportSink.m in Sources */, 8EAE9806261E87120073B6B3 /* SentryUIViewControllerPerformanceTracker.m in Sources */, + 621F61F12BEA073A005E654F /* SentryEnabledFeaturesBuilder.swift in Sources */, D88817D826D7149100BF2251 /* SentryTraceContext.m in Sources */, 8EBF870926140D37001A6853 /* SentryPerformanceTracker.m in Sources */, D80CD8D02B75143F002F710B /* UrlSanitized.swift in Sources */, @@ -4655,6 +4662,7 @@ 7BA61CC6247CFC5F00C130A8 /* SentryCrashDefaultBinaryImageProviderTests.swift in Sources */, 7BBC827925DFD7D7005F1ED8 /* SentryInAppLogicTests.swift in Sources */, 7BD7299A2463EA4A00EA3610 /* SentryDateUtilTests.swift in Sources */, + 626E2D4C2BEA0C37005596FE /* SentryEnabledFeaturesBuilderTests.swift in Sources */, 7BEF4957270C4B9D00F8F30E /* SentryUIViewControllerSwizzlingTests.swift in Sources */, 630436161EC0AD3100C4D3FA /* SentryNSDataCompressionTests.m in Sources */, 63FE722420DA66EC00CDBAE8 /* SentryCrashMonitor_NSException_Tests.m in Sources */, diff --git a/Sources/Sentry/Public/SentryEvent.h b/Sources/Sentry/Public/SentryEvent.h index 15f49eb380b..58d0e1ebc7e 100644 --- a/Sources/Sentry/Public/SentryEvent.h +++ b/Sources/Sentry/Public/SentryEvent.h @@ -103,7 +103,8 @@ NS_SWIFT_NAME(Event) * name: "sentry.cocoa", * integrations: [ * "react-native" - * ] + * ], + * features: ["performanceV2"] * } * @endcode * @warning This is automatically maintained and should not normally need to be modified. diff --git a/Sources/Sentry/SentryClient.m b/Sources/Sentry/SentryClient.m index 0dd2139a01c..6443d7b2f22 100644 --- a/Sources/Sentry/SentryClient.m +++ b/Sources/Sentry/SentryClient.m @@ -768,10 +768,14 @@ - (void)setSdk:(SentryEvent *)event #endif } + NSArray *features = + [SentryEnabledFeaturesBuilder getEnabledFeaturesWithOptions:self.options]; + event.sdk = @{ @"name" : SentryMeta.sdkName, @"version" : SentryMeta.versionString, - @"integrations" : integrations + @"integrations" : integrations, + @"features" : features }; } diff --git a/Sources/Swift/Helper/SentryEnabledFeaturesBuilder.swift b/Sources/Swift/Helper/SentryEnabledFeaturesBuilder.swift new file mode 100644 index 00000000000..288b43e5372 --- /dev/null +++ b/Sources/Swift/Helper/SentryEnabledFeaturesBuilder.swift @@ -0,0 +1,45 @@ +import Foundation + +@objcMembers class SentryEnabledFeaturesBuilder: NSObject { + + static func getEnabledFeatures(options: Options) -> [String] { + + var features: [String] = [] + + if options.enableCaptureFailedRequests { + features.append("captureFailedRequests") + } + + if options.enablePerformanceV2 { + features.append("performanceV2") + } + + if options.enableTimeToFullDisplayTracing { + features.append("timeToFullDisplayTracing") + } + +#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + if options.enableAppLaunchProfiling { + features.append("appLaunchProfiling") + } +#endif // os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + +#if os(iOS) || os(tvOS) +#if canImport(UIKit) && !SENTRY_NO_UIKIT + if options.enablePreWarmedAppStartTracing { + features.append("preWarmedAppStartTracing") + } +#endif // canImport(UIKit) +#endif // os(iOS) || os(tvOS) + + if options.swiftAsyncStacktraces { + features.append("swiftAsyncStacktraces") + } + + if options.enableMetrics { + features.append("metrics") + } + + return features + } +} diff --git a/Tests/SentryTests/Helper/SentryEnabledFeaturesBuilderTests.swift b/Tests/SentryTests/Helper/SentryEnabledFeaturesBuilderTests.swift new file mode 100644 index 00000000000..435083af65a --- /dev/null +++ b/Tests/SentryTests/Helper/SentryEnabledFeaturesBuilderTests.swift @@ -0,0 +1,53 @@ +import Nimble +@testable import Sentry +import XCTest + +final class SentryEnabledFeaturesBuilderTests: XCTestCase { + + func testDefaultFeatures() throws { + let features = SentryEnabledFeaturesBuilder.getEnabledFeatures(options: Options()) + + expect(features) == ["captureFailedRequests"] + } + + func testEnableAllFeatures() throws { + + let options = Options() + options.enablePerformanceV2 = true + options.enableTimeToFullDisplayTracing = true + options.swiftAsyncStacktraces = true + options.enableMetrics = true + +#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + options.enableAppLaunchProfiling = true +#endif // os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + +#if os(iOS) || os(tvOS) +#if canImport(UIKit) && !SENTRY_NO_UIKIT + options.enablePreWarmedAppStartTracing = true +#endif // canImport(UIKit) +#endif // os(iOS) || os(tvOS) + + let features = SentryEnabledFeaturesBuilder.getEnabledFeatures(options: options) + + expect(features).to(contain([ + "captureFailedRequests", + "performanceV2", + "timeToFullDisplayTracing", + "swiftAsyncStacktraces", + "metrics" + ])) + +#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + expect(features).to(contain(["appLaunchProfiling"])) +#endif // os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + +#if os(iOS) || os(tvOS) +#if canImport(UIKit) && !SENTRY_NO_UIKIT + expect(features).to(contain([ + "preWarmedAppStartTracing" + ])) +#endif // canImport(UIKit) +#endif // os(iOS) || os(tvOS) + } +} diff --git a/Tests/SentryTests/SentryClientTests.swift b/Tests/SentryTests/SentryClientTests.swift index 14e83c0c935..1d57d338bbe 100644 --- a/Tests/SentryTests/SentryClientTests.swift +++ b/Tests/SentryTests/SentryClientTests.swift @@ -1305,6 +1305,19 @@ class SentryClientTest: XCTestCase { ) } } + + func testSetSDKFeatures() throws { + let sut = fixture.getSut { + $0.enablePerformanceV2 = true + } + + sut.capture(message: "message") + + try assertLastSentEvent { actual in + expect(actual.sdk?["features"] as? [String]).to(contain("performanceV2", "captureFailedRequests")) + + } + } #if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) func testTrackPreWarmedAppStartTracking() throws {