Skip to content

Commit c43d7d7

Browse files
Merge pull request #2469 from DataDog/mariedm/rum-feature-operations
RUM-11702 Merge Feature Operations branch Co-authored-by: mariedm <marie.denis@datadoghq.com>
2 parents b347f76 + b47ca3d commit c43d7d7

21 files changed

Lines changed: 812 additions & 4 deletions

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
# Unreleased
2+
3+
- [FEATURE] Add Start and End Feature Operations APIs. See [#2469][]
24
- [FEATURE] Send Accessibility attributes in View Updates. See [#2410][]
35
- [IMPROVEMENT] Add missing `versionMajor` property to the `DDLogEventOperatingSystem` definition in Objective-C. See [#2463][]
46
- [IMPROVEMENT] Add `ddtags` to RUM events. See [#2436][]
@@ -8,7 +10,7 @@
810
Release `3.0` introduces breaking changes. Follow the [Migration Guide](MIGRATION.md) to upgrade from `2.x` versions.
911

1012
- [FIX] Fix `DDLogEvent.accountInfo` property initialization in case of missing account info. See [#2442][]
11-
- [IMPROVEMENT] Update Session Replay batch maximum age to 5hrs. See[#2455][]
13+
- [IMPROVEMENT] Update Session Replay batch maximum age to 5hrs. See [#2455][]
1214
- [IMPROVEMENT] Update the default tracing sampling rate to 100%. See [#2253][]
1315
- [IMPROVEMENT] Update the default TraceContextInjection to `.sampled`. See [#2253][]
1416
- [IMPROVEMENT] Enforce head-based sampling on Trace by default. See [#2288][]
@@ -953,11 +955,13 @@ Release `2.0` introduces breaking changes. Follow the [Migration Guide](MIGRATIO
953955
[#2370]: https://github.com/DataDog/dd-sdk-ios/pull/2370
954956
[#2395]: https://github.com/DataDog/dd-sdk-ios/pull/2395
955957
[#2405]: https://github.com/DataDog/dd-sdk-ios/pull/2405
958+
[#2410]: https://github.com/DataDog/dd-sdk-ios/pull/2410
956959
[#2442]: https://github.com/DataDog/dd-sdk-ios/pull/2442
957960
[#2455]: https://github.com/DataDog/dd-sdk-ios/pull/2455
958961
[#2463]: https://github.com/DataDog/dd-sdk-ios/pull/2463
959962
[#2410]: https://github.com/DataDog/dd-sdk-ios/pull/2410
960963
[#2436]: https://github.com/DataDog/dd-sdk-ios/pull/2436
964+
[#2469]: https://github.com/DataDog/dd-sdk-ios/pull/2469
961965

962966
[@00fa9a]: https://github.com/00FA9A
963967
[@britton-earnin]: https://github.com/Britton-Earnin

Datadog/Datadog.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,8 +981,12 @@
981981
965497062D761FCB006428EE /* SwiftUIViewNameExtractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965497042D761FC2006428EE /* SwiftUIViewNameExtractor.swift */; };
982982
9654971D2D774060006428EE /* SwiftUIViewNameExtractorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9654971C2D77404E006428EE /* SwiftUIViewNameExtractorTests.swift */; };
983983
9654971E2D774060006428EE /* SwiftUIViewNameExtractorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9654971C2D77404E006428EE /* SwiftUIViewNameExtractorTests.swift */; };
984+
9678E2762E55CD200094B106 /* RUMFeatureOperationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9678E2752E55CD200094B106 /* RUMFeatureOperationManagerTests.swift */; };
985+
9678E2772E55CD200094B106 /* RUMFeatureOperationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9678E2752E55CD200094B106 /* RUMFeatureOperationManagerTests.swift */; };
984986
96867B992D08826B004AE0BC /* TextReflectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96867B982D08826B004AE0BC /* TextReflectionTests.swift */; };
985987
96867B9B2D0883DD004AE0BC /* ColorReflectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96867B9A2D0883DD004AE0BC /* ColorReflectionTests.swift */; };
988+
969994042E54A26800C3E989 /* RUMFeatureOperationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 969994032E54A26800C3E989 /* RUMFeatureOperationManager.swift */; };
989+
969994052E54A26800C3E989 /* RUMFeatureOperationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 969994032E54A26800C3E989 /* RUMFeatureOperationManager.swift */; };
986990
969B3B212C33F80500D62400 /* UIActivityIndicatorRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 969B3B202C33F80500D62400 /* UIActivityIndicatorRecorder.swift */; };
987991
969B3B232C33F81E00D62400 /* UIActivityIndicatorRecorderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 969B3B222C33F81E00D62400 /* UIActivityIndicatorRecorderTests.swift */; };
988992
96D331ED2CFF740700649EE8 /* GraphicImagePrivacyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96D331EC2CFF740700649EE8 /* GraphicImagePrivacyTests.swift */; };
@@ -3005,8 +3009,10 @@
30053009
965497042D761FC2006428EE /* SwiftUIViewNameExtractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIViewNameExtractor.swift; sourceTree = "<group>"; };
30063010
9654971C2D77404E006428EE /* SwiftUIViewNameExtractorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIViewNameExtractorTests.swift; sourceTree = "<group>"; };
30073011
966253B52C98807400B90B63 /* SessionReplayPrivacyOverrides.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionReplayPrivacyOverrides.swift; sourceTree = "<group>"; };
3012+
9678E2752E55CD200094B106 /* RUMFeatureOperationManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMFeatureOperationManagerTests.swift; sourceTree = "<group>"; };
30083013
96867B982D08826B004AE0BC /* TextReflectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextReflectionTests.swift; sourceTree = "<group>"; };
30093014
96867B9A2D0883DD004AE0BC /* ColorReflectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorReflectionTests.swift; sourceTree = "<group>"; };
3015+
969994032E54A26800C3E989 /* RUMFeatureOperationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMFeatureOperationManager.swift; sourceTree = "<group>"; };
30103016
969B3B202C33F80500D62400 /* UIActivityIndicatorRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorRecorder.swift; sourceTree = "<group>"; };
30113017
969B3B222C33F81E00D62400 /* UIActivityIndicatorRecorderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorRecorderTests.swift; sourceTree = "<group>"; };
30123018
96D331EC2CFF740700649EE8 /* GraphicImagePrivacyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphicImagePrivacyTests.swift; sourceTree = "<group>"; };
@@ -5471,6 +5477,7 @@
54715477
618715F624DC0CDE00FC0F69 /* RUMCommandTests.swift */,
54725478
6176C1712ABDBA2E00131A70 /* MonitorTests.swift */,
54735479
61CE2E5E2BF2177100EC7D42 /* Monitor+GlobalAttributesTests.swift */,
5480+
9678E2752E55CD200094B106 /* RUMFeatureOperationManagerTests.swift */,
54745481
);
54755482
path = RUMMonitor;
54765483
sourceTree = "<group>";
@@ -5618,6 +5625,7 @@
56185625
61C713A92A3B790B00FA735A /* Monitor.swift */,
56195626
61C3E63624BF191F008053F2 /* RUMScope.swift */,
56205627
61C3E63A24BF1A4B008053F2 /* RUMCommand.swift */,
5628+
969994032E54A26800C3E989 /* RUMFeatureOperationManager.swift */,
56215629
61C3E63C24BF1B7F008053F2 /* Scopes */,
56225630
);
56235631
path = RUMMonitor;
@@ -9069,6 +9077,7 @@
90699077
61C713A62A3B78F900FA735A /* RUMMonitorProtocol+Internal.swift in Sources */,
90709078
D23F8E7829DDCD28001CFAE8 /* LongTaskObserver.swift in Sources */,
90719079
615E2B962D425F5600D85243 /* ViewEndedMetric.swift in Sources */,
9080+
969994042E54A26800C3E989 /* RUMFeatureOperationManager.swift in Sources */,
90729081
864A707C2DDF743900AC0619 /* AccessibilityReader.swift in Sources */,
90739082
D23F8E7A29DDCD28001CFAE8 /* SessionReplayDependency.swift in Sources */,
90749083
9632900E2DF1F04200E9199E /* LegacySwiftUIComponentDetector.swift in Sources */,
@@ -9164,6 +9173,7 @@
91649173
6167E6DB2B8004A500C3CA2D /* AppHangsWatchdogThreadTests.swift in Sources */,
91659174
3C0D5DEA2A543EA300446CF9 /* RUMViewEventsFilterTests.swift in Sources */,
91669175
D23F8EC429DDCD38001CFAE8 /* RUMCommandTests.swift in Sources */,
9176+
9678E2762E55CD200094B106 /* RUMFeatureOperationManagerTests.swift in Sources */,
91679177
);
91689178
runOnlyForDeploymentPostprocessing = 0;
91699179
};
@@ -9541,6 +9551,7 @@
95419551
61C713A52A3B78F900FA735A /* RUMMonitorProtocol+Internal.swift in Sources */,
95429552
D29A9F5129DD85BB005C54A4 /* LongTaskObserver.swift in Sources */,
95439553
615E2B952D425F5600D85243 /* ViewEndedMetric.swift in Sources */,
9554+
969994052E54A26800C3E989 /* RUMFeatureOperationManager.swift in Sources */,
95449555
864A707D2DDF743900AC0619 /* AccessibilityReader.swift in Sources */,
95459556
D29A9F8629DD85BB005C54A4 /* SessionReplayDependency.swift in Sources */,
95469557
9632900F2DF1F04200E9199E /* LegacySwiftUIComponentDetector.swift in Sources */,
@@ -9636,6 +9647,7 @@
96369647
6167E6DA2B8004A500C3CA2D /* AppHangsWatchdogThreadTests.swift in Sources */,
96379648
3C0D5DE92A543EA200446CF9 /* RUMViewEventsFilterTests.swift in Sources */,
96389649
D29A9FA729DDB483005C54A4 /* RUMCommandTests.swift in Sources */,
9650+
9678E2772E55CD200094B106 /* RUMFeatureOperationManagerTests.swift in Sources */,
96399651
);
96409652
runOnlyForDeploymentPostprocessing = 0;
96419653
};

DatadogCore/Tests/Objc/DDRUMMonitorTests.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@ class DDRUMUserActionTypeTests: XCTestCase {
9696
}
9797
}
9898

99+
class DDRUMFeatureOperationFailureReasonTests: XCTestCase {
100+
func testMappingToSwiftRUMFeatureOperationFailureReason() {
101+
XCTAssertEqual(objc_RUMFeatureOperationFailureReason.error.swiftType, .error)
102+
XCTAssertEqual(objc_RUMFeatureOperationFailureReason.abandoned.swiftType, .abandoned)
103+
XCTAssertEqual(objc_RUMFeatureOperationFailureReason.other.swiftType, .other)
104+
}
105+
}
106+
99107
class SwiftUIRUMViewsPredicateBridgeTests: XCTestCase {
100108
func testItForwardsCallToObjcPredicate() {
101109
class MockPredicate: objc_SwiftUIRUMViewsPredicate {

DatadogCore/Tests/Objc/ObjcAPITests/DDRUMMonitor+apiTests.m

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ - (void)testDDRUMMethodAPI {
4949
DDRUMMethodTrace; DDRUMMethodOptions;
5050
}
5151

52+
- (void)testDDRUMFeatureOperationFailureReasonAPI {
53+
DDRUMFeatureOperationFailureReasonError; DDRUMFeatureOperationFailureReasonAbandoned; DDRUMFeatureOperationFailureReasonOther;
54+
}
55+
5256
- (void)testDDRUMMonitorAPI {
5357
UIViewController *anyVC = [UIViewController new];
5458

@@ -81,6 +85,9 @@ - (void)testDDRUMMonitorAPI {
8185
[monitor addAttributes:@{@"string": @"value", @"integer": @1, @"boolean": @true}];
8286
[monitor removeAttributesForKeys:@[@"string",@"integer",@"boolean"]];
8387
[monitor addFeatureFlagEvaluationWithName: @"name" value: @"value"];
88+
[monitor startFeatureOperationWithName:@"test_flow" operationKey:@"operation_1" attributes:@{}];
89+
[monitor succeedFeatureOperationWithName:@"test_flow" operationKey:@"operation_1" attributes:@{}];
90+
[monitor failFeatureOperationWithName:@"test_flow" operationKey:@"operation_1" reason:DDRUMFeatureOperationFailureReasonError attributes:@{}];
8491

8592
[monitor _internal_sync_addError:[NSError errorWithDomain:NSCocoaErrorDomain code:-100 userInfo:nil]
8693
source:DDRUMErrorSourceCustom attributes:@{}];

DatadogInternal/Sources/Telemetry/Telemetry.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,10 @@ public struct UsageTelemetry: SampledTelemetry {
114114
case setAccount
115115
/// addFeatureFlagEvaluation API
116116
case addFeatureFlagEvaluation
117-
/// addFeatureFlagEvaluation API
117+
/// addViewLoadingTime API
118118
case addViewLoadingTime(ViewLoadingTime)
119+
/// addOperationStepVital API
120+
case addOperationStepVital(TelemetryUsageEvent.Telemetry.Usage.TelemetryCommonFeaturesUsage.AddOperationStepVital)
119121

120122
/// Describes the properties of `addViewLoadingTime` usage telemetry.
121123
public struct ViewLoadingTime {

DatadogRUM/Sources/Integrations/TelemetryReceiver.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,14 @@ private extension TelemetryUsageEvent.Telemetry.Usage {
367367
)
368368
)
369369
)
370+
case .addOperationStepVital(let addOperationStepVital):
371+
self = .telemetryCommonFeaturesUsage(
372+
value: .addOperationStepVital(
373+
value: .init(
374+
actionType: addOperationStepVital.actionType
375+
)
376+
)
377+
)
370378
}
371379
}
372380
}

DatadogRUM/Sources/RUM+objc.swift

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,22 @@ public enum objc_VitalsFrequency: Int {
290290
}
291291
}
292292

293+
@objc(DDRUMFeatureOperationFailureReason)
294+
@_spi(objc)
295+
public enum objc_RUMFeatureOperationFailureReason: Int {
296+
case error
297+
case abandoned
298+
case other
299+
300+
internal var swiftType: RUMFeatureOperationFailureReason {
301+
switch self {
302+
case .error: return .error
303+
case .abandoned: return .abandoned
304+
case .other: return .other
305+
}
306+
}
307+
}
308+
293309
@objc(DDRUMFirstPartyHostsTracing)
294310
@objcMembers
295311
@_spi(objc)
@@ -685,6 +701,36 @@ public class objc_RUMMonitor: NSObject {
685701
swiftRUMMonitor.addFeatureFlagEvaluation(name: name, value: AnyEncodable(value))
686702
}
687703

704+
public func startFeatureOperation(
705+
name: String,
706+
operationKey: String?,
707+
attributes: [String: Any]
708+
) {
709+
swiftRUMMonitor.startFeatureOperation(name: name, operationKey: operationKey, attributes: attributes.dd.swiftAttributes)
710+
}
711+
712+
public func succeedFeatureOperation(
713+
name: String,
714+
operationKey: String?,
715+
attributes: [String: Any]
716+
) {
717+
swiftRUMMonitor.succeedFeatureOperation(name: name, operationKey: operationKey, attributes: attributes.dd.swiftAttributes)
718+
}
719+
720+
public func failFeatureOperation(
721+
name: String,
722+
operationKey: String?,
723+
reason: objc_RUMFeatureOperationFailureReason,
724+
attributes: [String: Any]
725+
) {
726+
swiftRUMMonitor.failFeatureOperation(
727+
name: name,
728+
operationKey: operationKey,
729+
reason: reason.swiftType,
730+
attributes: attributes.dd.swiftAttributes
731+
)
732+
}
733+
688734
public var debug: Bool {
689735
set { swiftRUMMonitor.debug = newValue }
690736
get { swiftRUMMonitor.debug }

DatadogRUM/Sources/RUMMonitor/Monitor.swift

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ internal class Monitor: RUMCommandSubscriber {
108108
private var attributes: [AttributeKey: AttributeValue] = [:]
109109

110110
private let fatalErrorContext: FatalErrorContextNotifying
111+
private let rumUUIDGenerator: RUMUUIDGenerator
112+
private let telemetry: Telemetry
111113

112114
init(
113115
dependencies: RUMScopeDependencies,
@@ -117,6 +119,8 @@ internal class Monitor: RUMCommandSubscriber {
117119
self.scopes = RUMApplicationScope(dependencies: dependencies)
118120
self.dateProvider = dateProvider
119121
self.fatalErrorContext = dependencies.fatalErrorContext
122+
self.rumUUIDGenerator = dependencies.rumUUIDGenerator
123+
self.telemetry = dependencies.telemetry
120124
}
121125

122126
func process(command: RUMCommand) {
@@ -466,6 +470,69 @@ extension Monitor: RUMMonitorProtocol {
466470
)
467471
}
468472

473+
// MARK: - Feature Operations
474+
475+
func startFeatureOperation(name: String, operationKey: String?, attributes: [AttributeKey: AttributeValue]) {
476+
DD.logger.debug("Feature Operation `\(name)`\(instanceSuffix(operationKey)) started")
477+
478+
telemetry.send(telemetry: .usage(.init(event: .addOperationStepVital(.init(actionType: .start)))))
479+
480+
process(
481+
command: RUMOperationStepVitalCommand(
482+
vitalId: rumUUIDGenerator.generateUnique().toRUMDataFormat,
483+
name: name,
484+
operationKey: operationKey,
485+
stepType: .start,
486+
failureReason: nil,
487+
time: dateProvider.now,
488+
attributes: attributes
489+
)
490+
)
491+
}
492+
493+
func succeedFeatureOperation(name: String, operationKey: String?, attributes: [AttributeKey: AttributeValue]) {
494+
DD.logger.debug("Feature Operation `\(name)`\(instanceSuffix(operationKey)) successfully ended")
495+
496+
telemetry.send(telemetry: .usage(.init(event: .addOperationStepVital(.init(actionType: .succeed)))))
497+
498+
process(
499+
command: RUMOperationStepVitalCommand(
500+
vitalId: rumUUIDGenerator.generateUnique().toRUMDataFormat,
501+
name: name,
502+
operationKey: operationKey,
503+
stepType: .end,
504+
failureReason: nil,
505+
time: dateProvider.now,
506+
attributes: attributes
507+
)
508+
)
509+
}
510+
511+
func failFeatureOperation(name: String, operationKey: String?, reason: RUMFeatureOperationFailureReason, attributes: [AttributeKey: AttributeValue]) {
512+
DD.logger.debug("Feature Operation `\(name)`\(instanceSuffix(operationKey)) unsuccessfully ended with the following failure reason: \(reason.rawValue)")
513+
514+
telemetry.send(telemetry: .usage(.init(event: .addOperationStepVital(.init(actionType: .fail)))))
515+
516+
process(
517+
command: RUMOperationStepVitalCommand(
518+
vitalId: rumUUIDGenerator.generateUnique().toRUMDataFormat,
519+
name: name,
520+
operationKey: operationKey,
521+
stepType: .end,
522+
failureReason: reason,
523+
time: dateProvider.now,
524+
attributes: attributes
525+
)
526+
)
527+
}
528+
529+
private func instanceSuffix(_ operationKey: String?) -> String {
530+
guard let operationKey = operationKey else {
531+
return ""
532+
}
533+
return " (instance `\(operationKey)`)"
534+
}
535+
469536
// MARK: - debugging
470537

471538
var debug: Bool {

DatadogRUM/Sources/RUMMonitor/RUMCommand.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,32 @@ internal struct RUMKeepSessionAliveCommand: RUMCommand {
703703
let missedEventType: SessionEndedMetric.MissedEventType? = nil
704704
}
705705

706+
// MARK: - Feature Operation Steps (Vital) commands
707+
/// Vital is the model used under the hood to track Feature Operations in RUM. Each step in a Feature Operation is sent as a Vital.
708+
internal struct RUMOperationStepVitalCommand: RUMCommand {
709+
/// The vital ID for this operation step
710+
var vitalId: String
711+
/// The name of the operation (e.g., `login_flow`)
712+
var name: String
713+
/// The key of the operation for this operation step (when running several instances of the same operation)
714+
var operationKey: String?
715+
/// The step type (start, end, retry, etc.)
716+
var stepType: RUMVitalEvent.Vital.StepType
717+
/// The reason for failure, if applicable
718+
var failureReason: RUMVitalEvent.Vital.FailureReason?
719+
// Common properties
720+
var time: Date
721+
var globalAttributes: [AttributeKey: AttributeValue] = [:]
722+
var attributes: [AttributeKey: AttributeValue]
723+
let canStartBackgroundView = false
724+
let isUserInteraction = false
725+
let missedEventType: SessionEndedMetric.MissedEventType? = nil
726+
let canStartApplicationLaunchView = true
727+
let shouldRestartLastViewAfterSessionExpiration = true
728+
let shouldRestartLastViewAfterSessionStop = false
729+
let canStartBackgroundViewAfterSessionStop = false
730+
}
731+
706732
// MARK: - Cross-platform attributes
707733

708734
internal struct RUMUpdatePerformanceMetric: RUMCommand {

0 commit comments

Comments
 (0)