diff --git a/BenchmarkTests/Benchmarks/Sources/Benchmarks.swift b/BenchmarkTests/Benchmarks/Sources/Benchmarks.swift index 33126efe2..a9073a2bb 100644 --- a/BenchmarkTests/Benchmarks/Sources/Benchmarks.swift +++ b/BenchmarkTests/Benchmarks/Sources/Benchmarks.swift @@ -156,14 +156,25 @@ public enum Benchmarks { environment: "benchmarks", apiKey: configuration.apiKey, endpoint: .us1, - uploadCondition: { true } + uploadCondition: { true }, + performancePreset: .instantDataDelivery ) let exporter = try! DatadogExporter(config: exporterConfiguration) let processor = SimpleSpanProcessor(spanExporter: exporter) let provider = TracerProviderBuilder() + .with(resource: Resource(attributes: [ + "device_model": configuration.context.deviceModel, + "os": configuration.context.osName, + "os_version": configuration.context.osVersion, + "run": configuration.context.run, + "scenario": configuration.context.scenario, + "sdk_version": configuration.context.sdkVersion, + "branch": configuration.context.branch, + ].mapValues { .string($0) })) .add(spanProcessor: processor) + .with(sampler: Samplers.traceIdRatio(ratio: 0.01)) .build() OpenTelemetry.registerTracerProvider(tracerProvider: provider) diff --git a/BenchmarkTests/Runner/BenchmarkProfiler.swift b/BenchmarkTests/Runner/BenchmarkProfiler.swift index 8030f5b50..153df4ad2 100644 --- a/BenchmarkTests/Runner/BenchmarkProfiler.swift +++ b/BenchmarkTests/Runner/BenchmarkProfiler.swift @@ -5,22 +5,50 @@ */ import Foundation - import DatadogInternal import DatadogBenchmarks +import OpenTelemetryApi +import OpenTelemetrySdk internal final class Profiler: DatadogInternal.BenchmarkProfiler { + let opentelemetry: OpenTelemetry + + init(opentelemetry: OpenTelemetry = .instance) { + self.opentelemetry = opentelemetry + } + func tracer(operation: @autoclosure () -> String) -> any DatadogInternal.BenchmarkTracer { - DummyTracer() + TracerWrapper( + tracer: opentelemetry.tracerProvider.get(instrumentationName: operation(), instrumentationVersion: nil) + ) } } -internal final class DummyTracer: DatadogInternal.BenchmarkTracer { +private final class TracerWrapper: DatadogInternal.BenchmarkTracer { + let tracer: OpenTelemetryApi.Tracer + + init(tracer: OpenTelemetryApi.Tracer) { + self.tracer = tracer + } + func startSpan(named: @autoclosure () -> String) -> any DatadogInternal.BenchmarkSpan { - DummySpan() + SpanWrapper( + span: tracer + .spanBuilder(spanName: named()) + .setActive(true) + .startSpan() + ) } } -internal final class DummySpan: DatadogInternal.BenchmarkSpan { - func stop() { } +private final class SpanWrapper: DatadogInternal.BenchmarkSpan { + let span: OpenTelemetryApi.Span + + init(span: OpenTelemetryApi.Span) { + self.span = span + } + + func stop() { + span.end() + } } diff --git a/DatadogInternal/Sources/Benchmarks/BenchmarkProfiler.swift b/DatadogInternal/Sources/Benchmarks/BenchmarkProfiler.swift index d9149f888..39996a11f 100644 --- a/DatadogInternal/Sources/Benchmarks/BenchmarkProfiler.swift +++ b/DatadogInternal/Sources/Benchmarks/BenchmarkProfiler.swift @@ -61,3 +61,11 @@ private final class NOPBenchmarkProfiler: BenchmarkProfiler, BenchmarkTracer, Be /// no-op func stop() {} } + +public final class NOPBenchmarkTracer: BenchmarkTracer, BenchmarkSpan { + public init() {} + /// no-op + public func startSpan(named: @autoclosure () -> String) -> BenchmarkSpan { self } + /// no-op + public func stop() {} +} diff --git a/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/ViewTreeRecorder.swift b/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/ViewTreeRecorder.swift index 12f7d0998..d4975e87b 100644 --- a/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/ViewTreeRecorder.swift +++ b/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/ViewTreeRecorder.swift @@ -28,6 +28,9 @@ internal struct ViewTreeRecorder { view: UIView, context: ViewTreeRecordingContext ) { + let span = context.benchmarkTracer.startSpan(named: "") + defer { span.stop() } + var context = context if let viewController = view.next as? UIViewController { context.viewControllerContext.parentType = .init(viewController) @@ -61,6 +64,9 @@ internal struct ViewTreeRecorder { var semantics: NodeSemantics = UnknownElement.constant for nodeRecorder in nodeRecorders { + let span = context.benchmarkTracer.startSpan(named: String(describing: type(of: nodeRecorder))) + defer { span.stop() } + guard let nextSemantics = nodeRecorder.semantics(of: view, with: attributes, in: context) else { continue } diff --git a/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/ViewTreeRecordingContext.swift b/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/ViewTreeRecordingContext.swift index a98d1a091..f52486134 100644 --- a/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/ViewTreeRecordingContext.swift +++ b/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/ViewTreeRecordingContext.swift @@ -9,6 +9,7 @@ import Foundation import SafariServices import SwiftUI import WebKit +import DatadogInternal /// The context of recording subtree hierarchy. /// @@ -25,6 +26,8 @@ public struct SessionReplayViewTreeRecordingContext { var viewControllerContext: ViewControllerContext = .init() /// Webviews caching. let webViewCache: NSHashTable + + let benchmarkTracer: BenchmarkTracer } // This alias enables us to have a more unique name exposed through public-internal access level diff --git a/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/ViewTreeSnapshotBuilder.swift b/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/ViewTreeSnapshotBuilder.swift index 7d195a5db..e1c7de46c 100644 --- a/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/ViewTreeSnapshotBuilder.swift +++ b/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/ViewTreeSnapshotBuilder.swift @@ -8,6 +8,7 @@ import Foundation import UIKit import WebKit +import DatadogInternal /// Builds `ViewTreeSnapshot` for given root view. /// @@ -20,6 +21,8 @@ internal struct ViewTreeSnapshotBuilder { /// The webviews cache. let webViewCache: NSHashTable = .weakObjects() + let benchmarkTracer: BenchmarkTracer = profiler.tracer(operation: "ViewTreeSnapshotBuilder") + /// Builds the `ViewTreeSnapshot` for given root view. /// /// - Parameter rootView: the root view @@ -32,9 +35,14 @@ internal struct ViewTreeSnapshotBuilder { recorder: recorderContext, coordinateSpace: rootView, ids: idsGenerator, - webViewCache: webViewCache + webViewCache: webViewCache, + benchmarkTracer: benchmarkTracer ) + + let span = benchmarkTracer.startSpan(named: "record") let nodes = viewTreeRecorder.record(rootView, in: context) + span.stop() + let snapshot = ViewTreeSnapshot( date: recorderContext.date.addingTimeInterval(recorderContext.viewServerTimeOffset ?? 0), context: recorderContext, diff --git a/DatadogSessionReplay/Tests/Mocks/RecorderMocks.swift b/DatadogSessionReplay/Tests/Mocks/RecorderMocks.swift index a4244397b..eca015692 100644 --- a/DatadogSessionReplay/Tests/Mocks/RecorderMocks.swift +++ b/DatadogSessionReplay/Tests/Mocks/RecorderMocks.swift @@ -10,6 +10,7 @@ import XCTest import UIKit import WebKit +import DatadogInternal @_spi(Internal) @testable import DatadogSessionReplay @testable import TestUtilities @@ -337,7 +338,8 @@ extension ViewTreeRecordingContext: AnyMockable, RandomMockable { recorder: .mockRandom(), coordinateSpace: UIView.mockRandom(), ids: NodeIDGenerator(), - webViewCache: .weakObjects() + webViewCache: .weakObjects(), + benchmarkTracer: NOPBenchmarkTracer() ) } @@ -351,7 +353,8 @@ extension ViewTreeRecordingContext: AnyMockable, RandomMockable { recorder: recorder, coordinateSpace: coordinateSpace, ids: ids, - webViewCache: webViewCache + webViewCache: webViewCache, + benchmarkTracer: NOPBenchmarkTracer() ) } }