Skip to content

Commit 09490e5

Browse files
Merge pull request #2219 from DataDog/maxep/RUM-8448/benchmarks-additional-telemetry
RUM-8448 Benchmarks Additional Metrics Co-authored-by: maxep <maxime.epain@datadoghq.com>
2 parents 46a05d1 + b211cbd commit 09490e5

15 files changed

+129
-12
lines changed

BenchmarkTests/Runner/BenchmarkMeter.swift

+12
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ internal final class Meter: DatadogInternal.BenchmarkMeter {
6868
func gauge(metric: @autoclosure () -> String) -> DatadogInternal.BenchmarkGauge {
6969
meter.createDoubleMeasure(name: metric())
7070
}
71+
72+
func observe(metric: @autoclosure () -> String, callback: @escaping (any DatadogInternal.BenchmarkGauge) -> Void) {
73+
_ = meter.createDoubleObserver(name: metric()) { callback(DoubleObserverWrapper(observer: $0)) }
74+
}
7175
}
7276

7377
extension AnyCounterMetric<Double>: DatadogInternal.BenchmarkCounter {
@@ -81,3 +85,11 @@ extension AnyMeasureMetric<Double>: DatadogInternal.BenchmarkGauge {
8185
record(value: value, labelset: LabelSet(labels: attributes()))
8286
}
8387
}
88+
89+
private struct DoubleObserverWrapper: DatadogInternal.BenchmarkGauge {
90+
let observer: DoubleObserverMetric
91+
92+
func record(value: Double, attributes: @autoclosure () -> [String: String]) {
93+
observer.observe(value: value, labelset: LabelSet(labels: attributes()))
94+
}
95+
}

Datadog/Datadog.xcodeproj/project.pbxproj

+6
Original file line numberDiff line numberDiff line change
@@ -1710,6 +1710,8 @@
17101710
D2DC4BF727F484AA00E4FB96 /* DataEncryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2DC4BF527F484AA00E4FB96 /* DataEncryption.swift */; };
17111711
D2DE63532A30A7CA00441A54 /* CoreRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2DE63522A30A7CA00441A54 /* CoreRegistry.swift */; };
17121712
D2DE63542A30A7CA00441A54 /* CoreRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2DE63522A30A7CA00441A54 /* CoreRegistry.swift */; };
1713+
D2E6E8FB2D8039BB00FF1398 /* BenchmarkURLSessionTaskDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E6E8FA2D8039B200FF1398 /* BenchmarkURLSessionTaskDelegate.swift */; };
1714+
D2E6E8FC2D8039BB00FF1398 /* BenchmarkURLSessionTaskDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E6E8FA2D8039B200FF1398 /* BenchmarkURLSessionTaskDelegate.swift */; };
17131715
D2EA0F462C0E1AE300CB20F8 /* SessionReplayConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EA0F452C0E1AE200CB20F8 /* SessionReplayConfiguration.swift */; };
17141716
D2EBEE1F29BA160F00B15732 /* HTTPHeadersReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 618E13A92524B8700098C6B0 /* HTTPHeadersReader.swift */; };
17151717
D2EBEE2029BA160F00B15732 /* TracePropagationHeadersWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EBEDCF29B8A02100B15732 /* TracePropagationHeadersWriter.swift */; };
@@ -3114,6 +3116,7 @@
31143116
D2DA23C9298D5C1300C6C7E6 /* UIKitMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitMocks.swift; sourceTree = "<group>"; };
31153117
D2DC4BF527F484AA00E4FB96 /* DataEncryption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataEncryption.swift; sourceTree = "<group>"; };
31163118
D2DE63522A30A7CA00441A54 /* CoreRegistry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreRegistry.swift; sourceTree = "<group>"; };
3119+
D2E6E8FA2D8039B200FF1398 /* BenchmarkURLSessionTaskDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BenchmarkURLSessionTaskDelegate.swift; sourceTree = "<group>"; };
31173120
D2E8D59728C7AB90007E5DE1 /* ContextMessageReceiverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMessageReceiverTests.swift; sourceTree = "<group>"; };
31183121
D2EA0F452C0E1AE200CB20F8 /* SessionReplayConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionReplayConfiguration.swift; sourceTree = "<group>"; };
31193122
D2EBEDCC29B893D800B15732 /* TraceID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TraceID.swift; sourceTree = "<group>"; };
@@ -5026,6 +5029,7 @@
50265029
6174D6082BFDDD1E00EC7469 /* SDKMetrics */ = {
50275030
isa = PBXGroup;
50285031
children = (
5032+
D2E6E8FA2D8039B200FF1398 /* BenchmarkURLSessionTaskDelegate.swift */,
50295033
614396712A67D74F00197326 /* BatchMetrics.swift */,
50305034
);
50315035
path = SDKMetrics;
@@ -8002,6 +8006,7 @@
80028006
D2FB1254292E0E96005B13F8 /* TrackingConsentPublisher.swift in Sources */,
80038007
61D3E0D6277B23F1008BE766 /* KronosClock.swift in Sources */,
80048008
D2A7841129A53B2F003B03BB /* File.swift in Sources */,
8009+
D2E6E8FB2D8039BB00FF1398 /* BenchmarkURLSessionTaskDelegate.swift in Sources */,
80058010
D286626E2A43487500852CE3 /* Datadog.swift in Sources */,
80068011
61F930C22BA1C41A005F0EE2 /* TLVBlockReader.swift in Sources */,
80078012
613E793B2577B6EE00DFCC17 /* DataReader.swift in Sources */,
@@ -9335,6 +9340,7 @@
93359340
D2CB6E5527C50EAE00A62B57 /* KronosNSTimer+ClosureKit.swift in Sources */,
93369341
D2CB6E6627C50EAE00A62B57 /* Reader.swift in Sources */,
93379342
D2CB6E6927C50EAE00A62B57 /* KronosDNSResolver.swift in Sources */,
9343+
D2E6E8FC2D8039BB00FF1398 /* BenchmarkURLSessionTaskDelegate.swift in Sources */,
93389344
D286626F2A43487500852CE3 /* Datadog.swift in Sources */,
93399345
61F930C32BA1C41A005F0EE2 /* TLVBlockReader.swift in Sources */,
93409346
D2A7841229A53B2F003B03BB /* File.swift in Sources */,

DatadogCore/Sources/Core/Storage/Files/File.swift

+3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ internal protocol ReadableFile {
3535
/// Name of this file.
3636
var name: String { get }
3737

38+
/// Current size of this file.
39+
func size() throws -> UInt64
40+
3841
/// Creates InputStream for reading the available data from this file.
3942
func stream() throws -> InputStream
4043

DatadogCore/Sources/Core/Storage/FilesOrchestrator.swift

+15
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,15 @@ internal class FilesOrchestrator: FilesOrchestratorType {
8080
self.dateProvider = dateProvider
8181
self.telemetry = telemetry
8282
self.metricsData = metricsData
83+
84+
#if DD_BENCHMARK
85+
bench.meter.observe(metric: "ios.benchmark.batch_count") {[weak self] gauge in
86+
if let self {
87+
let files = try? directory.files()
88+
files.map { gauge.record($0.count, attributes: ["track": self.trackName]) }
89+
}
90+
}
91+
#endif
8392
}
8493

8594
// MARK: - `WritableFile` orchestration
@@ -211,6 +220,12 @@ internal class FilesOrchestrator: FilesOrchestratorType {
211220

212221
func delete(readableFile: ReadableFile, deletionReason: BatchDeletedMetric.RemovalReason) {
213222
do {
223+
#if DD_BENCHMARK
224+
if case .intakeCode = deletionReason {
225+
try bench.meter.counter(metric: "ios.benchmark.bytes_deleted")
226+
.increment(by: readableFile.size(), attributes: ["track": trackName])
227+
}
228+
#endif
214229
try readableFile.delete()
215230
// Decrement pending batches at each batch deletion
216231
_pendingBatches.mutate { $0 -= 1 }

DatadogCore/Sources/Core/Upload/DataUploadWorker.swift

+4
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ internal class DataUploadWorker: DataUploadWorkerType {
139139
batch,
140140
reason: .intakeCode(responseCode: uploadStatus.responseCode)
141141
)
142+
#if DD_BENCHMARK
143+
bench.meter.counter(metric: "ios.benchmark.upload_count")
144+
.increment(attributes: ["track": self.featureName])
145+
#endif
142146

143147
previousUploadStatus = nil
144148

DatadogCore/Sources/Core/Upload/DataUploader.swift

+20-2
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,17 @@ internal final class DataUploader: DataUploaderType {
2626

2727
private let httpClient: HTTPClient
2828
private let requestBuilder: FeatureRequestBuilder
29+
/// Name of the feature this worker is performing uploads for.
30+
private let featureName: String
2931

30-
init(httpClient: HTTPClient, requestBuilder: FeatureRequestBuilder) {
32+
init(
33+
httpClient: HTTPClient,
34+
requestBuilder: FeatureRequestBuilder,
35+
featureName: String
36+
) {
3137
self.httpClient = httpClient
3238
self.requestBuilder = requestBuilder
39+
self.featureName = featureName
3340
}
3441

3542
/// Uploads data synchronously (will block current thread) and returns the upload status.
@@ -51,7 +58,13 @@ internal final class DataUploader: DataUploaderType {
5158

5259
let semaphore = DispatchSemaphore(value: 0)
5360

54-
httpClient.send(request: request) { result in
61+
#if DD_BENCHMARK
62+
let delegate: URLSessionTaskDelegate = BenchmarkURLSessionTaskDelegate(track: featureName)
63+
#else
64+
let delegate: URLSessionTaskDelegate? = nil
65+
#endif
66+
67+
httpClient.send(request: request, delegate: delegate) { result in
5568
switch result {
5669
case .success(let httpResponse):
5770
uploadStatus = DataUploadStatus(
@@ -71,6 +84,11 @@ internal final class DataUploader: DataUploaderType {
7184

7285
_ = semaphore.wait(timeout: .distantFuture)
7386

87+
#if DD_BENCHMARK
88+
bench.meter.counter(metric: "ios.benchmark.bytes_uploaded")
89+
.increment(by: request.httpBody?.count ?? 0, attributes: ["track": featureName])
90+
#endif
91+
7492
return uploadStatus ?? DataUploader.unreachableUploadStatus
7593
}
7694
}

DatadogCore/Sources/Core/Upload/FeatureUpload.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ internal struct FeatureUpload {
3030

3131
let dataUploader = DataUploader(
3232
httpClient: httpClient,
33-
requestBuilder: requestBuilder
33+
requestBuilder: requestBuilder,
34+
featureName: featureName
3435
)
3536

3637
#if canImport(UIKit)

DatadogCore/Sources/Core/Upload/HTTPClient.swift

+12-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,17 @@ internal protocol HTTPClient {
1111
/// Sends the provided request using HTTP.
1212
/// - Parameters:
1313
/// - request: The request to be sent.
14+
/// - delegate: The task-specific delegate.
1415
/// - completion: A closure that receives a Result containing either an HTTPURLResponse or an Error.
15-
func send(request: URLRequest, completion: @escaping (Result<HTTPURLResponse, Error>) -> Void)
16+
func send(request: URLRequest, delegate: URLSessionTaskDelegate?, completion: @escaping (Result<HTTPURLResponse, Error>) -> Void)
17+
}
18+
19+
extension HTTPClient {
20+
/// Sends the provided request using HTTP.
21+
/// - Parameters:
22+
/// - request: The request to be sent.
23+
/// - completion: A closure that receives a Result containing either an HTTPURLResponse or an Error.
24+
func send(request: URLRequest, completion: @escaping (Result<HTTPURLResponse, Error>) -> Void) {
25+
self.send(request: request, delegate: nil, completion: completion)
26+
}
1627
}

DatadogCore/Sources/Core/Upload/URLSessionClient.swift

+4-1
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,13 @@ internal class URLSessionClient: HTTPClient {
3535
self.session = session
3636
}
3737

38-
func send(request: URLRequest, completion: @escaping (Result<HTTPURLResponse, Error>) -> Void) {
38+
func send(request: URLRequest, delegate: URLSessionTaskDelegate?, completion: @escaping (Result<HTTPURLResponse, Error>) -> Void) {
3939
let task = session.dataTask(with: request) { data, response, error in
4040
completion(httpClientResult(for: (data, response, error)))
4141
}
42+
if #available(iOS 15.0, tvOS 15.0, watchOS 8.0, *) {
43+
task.delegate = delegate
44+
}
4245
task.resume()
4346
}
4447
}
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+
#if DD_BENCHMARK
8+
9+
import Foundation
10+
import DatadogInternal
11+
12+
/// `URLSessionTaskDelegate` implementation to collect network request metrics during benchmark execution.
13+
internal final class BenchmarkURLSessionTaskDelegate: NSObject, URLSessionTaskDelegate {
14+
let track: String
15+
16+
init(track: String) {
17+
self.track = track
18+
}
19+
20+
func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
21+
bench.meter.gauge(metric: "ios.benchmark.reponse_latency")
22+
.record(metrics.taskInterval.duration, attributes: ["track": track])
23+
}
24+
}
25+
26+
#endif

DatadogCore/Tests/Datadog/Core/Upload/DataUploadWorkerTests.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,8 @@ class DataUploadWorkerTests: XCTestCase {
765765

766766
let dataUploader = DataUploader(
767767
httpClient: httpClient,
768-
requestBuilder: FeatureRequestBuilderMock()
768+
requestBuilder: FeatureRequestBuilderMock(),
769+
featureName: .mockRandom()
769770
)
770771
let worker = DataUploadWorker(
771772
queue: uploaderQueue,

DatadogCore/Tests/Datadog/Core/Upload/DataUploaderTests.swift

+6-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ class DataUploaderTests: XCTestCase {
2121

2222
let uploader = DataUploader(
2323
httpClient: HTTPClientMock(response: randomResponse),
24-
requestBuilder: FeatureRequestBuilderMock(request: randomRequest)
24+
requestBuilder: FeatureRequestBuilderMock(request: randomRequest),
25+
featureName: .mockRandom()
2526
)
2627

2728
// When
@@ -50,7 +51,8 @@ class DataUploaderTests: XCTestCase {
5051

5152
let uploader = DataUploader(
5253
httpClient: HTTPClientMock(error: randomError),
53-
requestBuilder: FeatureRequestBuilderMock(request: randomRequest)
54+
requestBuilder: FeatureRequestBuilderMock(request: randomRequest),
55+
featureName: .mockRandom()
5456
)
5557

5658
// When
@@ -72,7 +74,8 @@ class DataUploaderTests: XCTestCase {
7274

7375
let uploader = DataUploader(
7476
httpClient: HTTPClientMock(),
75-
requestBuilder: FailingRequestBuilderMock(error: error)
77+
requestBuilder: FailingRequestBuilderMock(error: error),
78+
featureName: .mockRandom()
7679
)
7780

7881
// When & Then

DatadogCore/Tests/Datadog/Mocks/HTTPClientMock.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ internal class HTTPClientMock: HTTPClient {
4242

4343
// MARK: - HTTPClient conformance
4444

45-
func send(request: URLRequest, completion: @escaping (Result<HTTPURLResponse, Error>) -> Void) {
45+
func send(request: URLRequest, delegate: URLSessionTaskDelegate?, completion: @escaping (Result<HTTPURLResponse, any Error>) -> Void) {
4646
queue.async {
4747
completion(self.result(request))
4848
self.requests.append(request)

DatadogInternal/Sources/Benchmarks/BenchmarkProfiler.swift

+15-1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ public protocol BenchmarkMeter {
5656
/// - Parameter metric: The metric name.
5757
/// - Returns: The gauge instance.
5858
func gauge(metric: @autoclosure () -> String) -> BenchmarkGauge
59+
60+
/// Observe a measure value.
61+
///
62+
/// - Parameters:
63+
/// - metric: The metric name.
64+
/// - callback: Callback providing a gauge instance to record a value.
65+
func observe(metric: @autoclosure () -> String, callback: @escaping (BenchmarkGauge) -> Void )
5966
}
6067

6168
/// The Benchmark Tracer will create and start spans in a benchmark environment.
@@ -87,6 +94,11 @@ public protocol BenchmarkCounter {
8794
}
8895

8996
extension BenchmarkCounter {
97+
/// Increment the counter by one.
98+
public func increment(attributes: @autoclosure () -> [String: String] = [:]) {
99+
add(value: 1, attributes: attributes())
100+
}
101+
90102
/// Increment the counter.
91103
///
92104
/// - parameters:
@@ -99,7 +111,7 @@ extension BenchmarkCounter {
99111
///
100112
/// - parameters:
101113
/// - by: Amount to increment by.
102-
public func increment<Integer>(by amount: Integer = 1, attributes: @autoclosure () -> [String: String] = [:]) where Integer: BinaryInteger {
114+
public func increment<Integer>(by amount: Integer, attributes: @autoclosure () -> [String: String] = [:]) where Integer: BinaryInteger {
103115
add(value: Double(amount), attributes: attributes())
104116
}
105117
}
@@ -132,6 +144,8 @@ private final class NOPBench: BenchmarkProfiler, BenchmarkTracer, BenchmarkSpan,
132144
/// no-op
133145
func gauge(metric: @autoclosure () -> String) -> BenchmarkGauge { self }
134146
/// no-op
147+
func observe(metric: @autoclosure () -> String, callback: @escaping (any BenchmarkGauge) -> Void) { }
148+
/// no-op
135149
func add(value: Double, attributes: @autoclosure () -> [String: String]) { }
136150
/// no-op
137151
func record(value: Double, attributes: @autoclosure () -> [String: String]) { }

Package.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ let package = Package(
8181
resources: [
8282
.copy("Resources/PrivacyInfo.xcprivacy")
8383
],
84-
swiftSettings: [.define("SPM_BUILD")]
84+
swiftSettings: [.define("SPM_BUILD")] + internalSwiftSettings
8585
),
8686
.target(
8787
name: "DatadogObjc",

0 commit comments

Comments
 (0)