Skip to content

Commit f9826a6

Browse files
RUM-8454 Trace scenario and UI
1 parent d1794fd commit f9826a6

File tree

4 files changed

+226
-0
lines changed

4 files changed

+226
-0
lines changed

BenchmarkTests/BenchmarkTests.xcodeproj/project.pbxproj

+24
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
86252B1B2D818E360098FAEF /* TraceScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86252B1A2D818E360098FAEF /* TraceScenario.swift */; };
11+
86252B1D2D8190DA0098FAEF /* TraceContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86252B1C2D8190DA0098FAEF /* TraceContentView.swift */; };
1012
868DA2E62D706456007D20D5 /* LogsCustomScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 868DA2E52D706456007D20D5 /* LogsCustomScenario.swift */; };
1113
868DA2EC2D706E61007D20D5 /* LogsCustomContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 868DA2EB2D706E61007D20D5 /* LogsCustomContentView.swift */; };
1214
868DA2F52D71DD47007D20D5 /* LogsHeavyTrafficScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 868DA2F42D71DD47007D20D5 /* LogsHeavyTrafficScenario.swift */; };
@@ -238,6 +240,8 @@
238240
/* End PBXCopyFilesBuildPhase section */
239241

240242
/* Begin PBXFileReference section */
243+
86252B1A2D818E360098FAEF /* TraceScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TraceScenario.swift; sourceTree = "<group>"; };
244+
86252B1C2D8190DA0098FAEF /* TraceContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TraceContentView.swift; sourceTree = "<group>"; };
241245
868DA2E52D706456007D20D5 /* LogsCustomScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogsCustomScenario.swift; sourceTree = "<group>"; };
242246
868DA2EB2D706E61007D20D5 /* LogsCustomContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogsCustomContentView.swift; sourceTree = "<group>"; };
243247
868DA2F42D71DD47007D20D5 /* LogsHeavyTrafficScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogsHeavyTrafficScenario.swift; sourceTree = "<group>"; };
@@ -471,6 +475,23 @@
471475
/* End PBXFrameworksBuildPhase section */
472476

473477
/* Begin PBXGroup section */
478+
86252B182D818DFB0098FAEF /* Trace */ = {
479+
isa = PBXGroup;
480+
children = (
481+
86252B192D818E1F0098FAEF /* UI */,
482+
86252B1A2D818E360098FAEF /* TraceScenario.swift */,
483+
);
484+
path = Trace;
485+
sourceTree = "<group>";
486+
};
487+
86252B192D818E1F0098FAEF /* UI */ = {
488+
isa = PBXGroup;
489+
children = (
490+
86252B1C2D8190DA0098FAEF /* TraceContentView.swift */,
491+
);
492+
path = UI;
493+
sourceTree = "<group>";
494+
};
474495
868DA2E42D70643A007D20D5 /* Logs */ = {
475496
isa = PBXGroup;
476497
children = (
@@ -591,6 +612,7 @@
591612
D24BFD462C6B916B00AB9604 /* SyntheticScenario.swift */,
592613
D27606992C514F37002D2A14 /* SessionReplay */,
593614
868DA2E42D70643A007D20D5 /* Logs */,
615+
86252B182D818DFB0098FAEF /* Trace */,
594616
);
595617
path = Scenarios;
596618
sourceTree = "<group>";
@@ -1277,6 +1299,7 @@
12771299
buildActionMask = 2147483647;
12781300
files = (
12791301
868DA2E62D706456007D20D5 /* LogsCustomScenario.swift in Sources */,
1302+
86252B1D2D8190DA0098FAEF /* TraceContentView.swift in Sources */,
12801303
868DA2F72D71DD8A007D20D5 /* LogsHeavyTrafficContentView.swift in Sources */,
12811304
868DA2EC2D706E61007D20D5 /* LogsCustomContentView.swift in Sources */,
12821305
D27606A42C514F37002D2A14 /* AppConfiguration.swift in Sources */,
@@ -1289,6 +1312,7 @@
12891312
D24BFD472C6B916B00AB9604 /* SyntheticScenario.swift in Sources */,
12901313
D27606A32C514F37002D2A14 /* Scenario.swift in Sources */,
12911314
D23734972D773DD8004CCAED /* BenchmarkMeter.swift in Sources */,
1315+
86252B1B2D818E360098FAEF /* TraceScenario.swift in Sources */,
12921316
);
12931317
runOnlyForDeploymentPostprocessing = 0;
12941318
};

BenchmarkTests/Runner/Scenarios/SyntheticScenario.swift

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ internal struct SyntheticScenario: Scenario {
1616
case sessionReplaySwiftUI
1717
case logsCustom
1818
case logsHeavyTraffic
19+
case trace
1920
}
2021
/// The scenario's name.
2122
let name: Name
@@ -45,6 +46,8 @@ internal struct SyntheticScenario: Scenario {
4546
_scenario = LogsCustomScenario()
4647
case .logsHeavyTraffic:
4748
_scenario = LogsHeavyTrafficScenario()
49+
case .trace:
50+
_scenario = TraceScenario()
4851
}
4952

5053
self.name = name
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2019-Present Datadog, Inc.
5+
*/
6+
7+
import Foundation
8+
import SwiftUI
9+
10+
import DatadogCore
11+
import DatadogTrace
12+
13+
struct TraceScenario: Scenario {
14+
var initialViewController: UIViewController {
15+
UIHostingController(rootView: TraceContentView())
16+
}
17+
18+
func instrument(with info: AppInfo) {
19+
Datadog.initialize(
20+
with: .benchmark(info: info),
21+
trackingConsent: .granted
22+
)
23+
24+
Trace.enable()
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2019-Present Datadog, Inc.
5+
*/
6+
7+
import DatadogTrace
8+
import SwiftUI
9+
10+
struct TraceContentView: View {
11+
@State private var operationName: String
12+
@State private var resourceName: String
13+
@State private var isError: Bool
14+
@State private var depth: Int
15+
@State private var childrenCount: Int
16+
@State private var childDelay: TimeInterval
17+
@State private var traceCount: Int
18+
@State private var isSending: Bool
19+
20+
var tracer: OTTracer { Tracer.shared() }
21+
22+
private let queue1 = DispatchQueue(label: "com.datadoghq.benchmark-tracing1")
23+
private let queue2 = DispatchQueue(label: "com.datadoghq.benchmark-tracing2")
24+
25+
init() {
26+
operationName = "iOS Benchmark span operation"
27+
resourceName = "iOS Benchmark span resource"
28+
isError = false
29+
depth = 1
30+
childrenCount = 0
31+
childDelay = 1
32+
traceCount = 0
33+
isSending = false
34+
}
35+
36+
var body: some View {
37+
VStack {
38+
Form {
39+
Section(header: Text("Span configuration")) {
40+
TextField("Operation name", text: $operationName)
41+
TextField("Resource name", text: $resourceName)
42+
Toggle("Is Error", isOn: $isError)
43+
.tint(.purple)
44+
}
45+
46+
Section(header: Text("Complex span configuration")) {
47+
HStack {
48+
Text("Children count:")
49+
Spacer()
50+
TextField("Children count:", value: $childrenCount, formatter: NumberFormatter())
51+
.textFieldStyle(RoundedBorderTextFieldStyle())
52+
.frame(width: 80)
53+
.keyboardType(.numberPad)
54+
Stepper("", value: $childrenCount, in: 0 ... 100, step: 1)
55+
.frame(width: 80)
56+
}
57+
58+
HStack {
59+
Text("Depth:")
60+
Spacer()
61+
TextField("Depth:", value: $depth, formatter: NumberFormatter())
62+
.textFieldStyle(RoundedBorderTextFieldStyle())
63+
.frame(width: 80)
64+
.keyboardType(.numberPad)
65+
Stepper("", value: $depth, in: 1 ... 100, step: 1)
66+
.frame(width: 80)
67+
}
68+
69+
HStack {
70+
Text("Child delay:")
71+
Spacer()
72+
TextField("Child delay:", value: $childDelay, formatter: NumberFormatter())
73+
.textFieldStyle(RoundedBorderTextFieldStyle())
74+
.frame(width: 80)
75+
.keyboardType(.numberPad)
76+
Stepper("", value: $childDelay, in: 1 ... 100, step: 1)
77+
.frame(width: 80)
78+
}
79+
}
80+
81+
Button(action: sendTrace) {
82+
Text("Send")
83+
.foregroundColor(.white)
84+
.padding()
85+
.frame(maxWidth: .infinity)
86+
.background(Color.purple)
87+
.cornerRadius(8)
88+
}
89+
.listRowBackground(EmptyView())
90+
.listRowInsets(EdgeInsets())
91+
92+
VStack(alignment: .center) {
93+
ProgressView()
94+
.progressViewStyle(CircularProgressViewStyle())
95+
.opacity(isSending ? 1 : 0)
96+
.padding()
97+
Text("Traces sent: \(traceCount)")
98+
.padding(.bottom, 16)
99+
}
100+
.frame(maxWidth: .infinity, alignment: .center)
101+
.listRowBackground(EmptyView())
102+
.listRowInsets(EdgeInsets())
103+
}
104+
}
105+
}
106+
107+
/// Starts the tracing process based on the current configuration:
108+
/// 1. Creates a root span. with the given configuration.
109+
/// 2. Recursively generates child spans via `sendSpanTree`.
110+
/// 3. Finishes the root span and updates the trace counter.
111+
private func sendTrace() {
112+
DispatchQueue.main.async {
113+
isSending = true
114+
}
115+
116+
queue1.async { [self] in
117+
let rootSpan = tracer.startSpan(operationName: operationName)
118+
rootSpan.setTag(key: SpanTags.resource, value: resourceName)
119+
120+
if isError {
121+
rootSpan.log(
122+
fields: [
123+
OTLogFields.event: "error",
124+
OTLogFields.errorKind: "Simulated error",
125+
OTLogFields.message: "Describe what happened",
126+
OTLogFields.stack: "Foo.swift:42",
127+
]
128+
)
129+
}
130+
131+
wait(seconds: 0.5)
132+
133+
queue2.sync {
134+
sendSpanTree(parent: rootSpan, currentLevel: 0, maxDepth: depth)
135+
}
136+
137+
wait(seconds: 0.5)
138+
rootSpan.finish()
139+
140+
DispatchQueue.main.async {
141+
traceCount += 1
142+
isSending = false
143+
}
144+
}
145+
}
146+
147+
/// Recursively generates a tree of child spans starting from the given parent span, waiting for `childDelay` between span creation.
148+
/// If the current level reaches `maxDepth` or if `childrenCount` is 0, recursion stops.
149+
/// - Parameters:
150+
/// - parent: The parent span from which to create child spans.
151+
/// - currentLevel: The current depth level in the span tree.
152+
/// - maxDepth: The maximum depth (levels) for the span tree generation.
153+
private func sendSpanTree(parent: OTSpan, currentLevel: Int, maxDepth: Int) {
154+
guard currentLevel < maxDepth, childrenCount > 0 else { return }
155+
156+
for i in 1 ... childrenCount {
157+
let childOperation = "\(operationName) - Child \(i) at level \(currentLevel + 1)"
158+
let childSpan = tracer.startSpan(operationName: childOperation, childOf: parent.context)
159+
wait(seconds: childDelay)
160+
161+
sendSpanTree(parent: childSpan, currentLevel: currentLevel + 1, maxDepth: maxDepth)
162+
childSpan.finish()
163+
}
164+
}
165+
166+
private func wait(seconds: TimeInterval) {
167+
Thread.sleep(forTimeInterval: seconds)
168+
}
169+
}
170+
171+
#Preview {
172+
TraceContentView()
173+
}

0 commit comments

Comments
 (0)