Skip to content

Commit 12a92d3

Browse files
authored
Merge pull request #1415 from DataDog/ncreated/RUMM-3374/fix-manual-trace-injection-impossible
RUMM-3374 fix: Manual trace injection APIs are not available in `DatadogTrace`
2 parents e21b0f5 + 99dbb48 commit 12a92d3

36 files changed

+731
-451
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Unreleased
22

3+
- [BUGFIX] Manual trace injection APIs are not available in DatadogTrace. See [#1415][].
34
- [BUGFIX] Fix session replay uploads to AP1 site. See [#1418][].
45
- [BUGFIX] Allow instantiating custom instance of the SDK after default one. See [#1413][].
56
- [BUGFIX] Do not propagate attributes from Errors and LongTasks to Views.
@@ -497,6 +498,7 @@ Release `2.0` introduces breaking changes. Follow the [Migration Guide](MIGRATIO
497498
[#1355]: https://github.com/DataDog/dd-sdk-ios/pull/1355
498499
[#1410]: https://github.com/DataDog/dd-sdk-ios/pull/1410
499500
[#1413]: https://github.com/DataDog/dd-sdk-ios/pull/1413
501+
[#1415]: https://github.com/DataDog/dd-sdk-ios/pull/1415
500502
[#1418]: https://github.com/DataDog/dd-sdk-ios/pull/1418
501503
[#1419]: https://github.com/DataDog/dd-sdk-ios/pull/1419
502504
[@00fa9a]: https://github.com/00FA9A

Datadog/Datadog.xcodeproj/project.pbxproj

+52-48
Large diffs are not rendered by default.

Datadog/E2ETests/Tracing/TracerE2ETests.swift

-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
*/
66

77
import DatadogCore
8-
import DatadogInternal
98
import DatadogTrace
109

1110
class TracerE2ETests: E2ETests {

Datadog/Example/Base.lproj/Main iOS.storyboard

+51-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19455" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="gra-d4-cht">
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="gra-d4-cht">
33
<device id="retina6_1" orientation="portrait" appearance="light"/>
44
<dependencies>
55
<deployment identifier="iOS"/>
6-
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19454"/>
6+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/>
77
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
88
<capability name="System colors in document resources" minToolsVersion="11.0"/>
99
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -65,8 +65,28 @@
6565
<segue destination="FaI-gu-eql" kind="show" id="ePB-Ph-XzE"/>
6666
</connections>
6767
</tableViewCell>
68+
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="M3j-VO-N1P" style="IBUITableViewCellStyleDefault" id="F5A-gJ-7Vm">
69+
<rect key="frame" x="0.0" y="137" width="414" height="43.5"/>
70+
<autoresizingMask key="autoresizingMask"/>
71+
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="F5A-gJ-7Vm" id="Y3A-pt-TCA">
72+
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
73+
<autoresizingMask key="autoresizingMask"/>
74+
<subviews>
75+
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Trace Injection (manual)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="M3j-VO-N1P">
76+
<rect key="frame" x="20" y="0.0" width="374" height="43.5"/>
77+
<autoresizingMask key="autoresizingMask"/>
78+
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
79+
<nil key="textColor"/>
80+
<nil key="highlightedColor"/>
81+
</label>
82+
</subviews>
83+
</tableViewCellContentView>
84+
<connections>
85+
<segue destination="1Dj-vX-hFb" kind="show" id="IrQ-UG-cMw"/>
86+
</connections>
87+
</tableViewCell>
6888
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="yCu-pq-IYL" style="IBUITableViewCellStyleDefault" id="3G8-Wa-fSQ">
69-
<rect key="frame" x="0.0" y="131.5" width="414" height="43.5"/>
89+
<rect key="frame" x="0.0" y="180.5" width="414" height="43.5"/>
7090
<autoresizingMask key="autoresizingMask"/>
7191
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="3G8-Wa-fSQ" id="7IJ-XI-RAR">
7292
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
@@ -1662,6 +1682,34 @@
16621682
</objects>
16631683
<point key="canvasLocation" x="1419" y="1175"/>
16641684
</scene>
1685+
<!--Debug Manual Trace Injection View Controller-->
1686+
<scene sceneID="d34-e7-Qn8">
1687+
<objects>
1688+
<viewController id="1Dj-vX-hFb" customClass="DebugManualTraceInjectionViewController" customModule="Example" customModuleProvider="target" sceneMemberID="viewController">
1689+
<view key="view" contentMode="scaleToFill" id="h6o-Ng-hRs">
1690+
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
1691+
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
1692+
<subviews>
1693+
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="This screen requires iO14+" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pe0-Ro-rxm">
1694+
<rect key="frame" x="105" y="437.5" width="204" height="21"/>
1695+
<fontDescription key="fontDescription" type="system" pointSize="17"/>
1696+
<nil key="textColor"/>
1697+
<nil key="highlightedColor"/>
1698+
</label>
1699+
</subviews>
1700+
<viewLayoutGuide key="safeArea" id="LmF-Gg-KKf"/>
1701+
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
1702+
<constraints>
1703+
<constraint firstItem="pe0-Ro-rxm" firstAttribute="centerX" secondItem="h6o-Ng-hRs" secondAttribute="centerX" id="WU4-aG-e4N"/>
1704+
<constraint firstItem="pe0-Ro-rxm" firstAttribute="centerY" secondItem="h6o-Ng-hRs" secondAttribute="centerY" id="cB7-WP-Qma"/>
1705+
</constraints>
1706+
</view>
1707+
<navigationItem key="navigationItem" id="yjy-W3-IxO"/>
1708+
</viewController>
1709+
<placeholder placeholderIdentifier="IBFirstResponder" id="R6e-tl-J7Q" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
1710+
</objects>
1711+
<point key="canvasLocation" x="2201" y="1175"/>
1712+
</scene>
16651713
</scenes>
16661714
<resources>
16671715
<systemColor name="secondaryLabelColor">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
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 SwiftUI
8+
import DatadogTrace
9+
10+
@available(iOS 14, *)
11+
internal class DebugManualTraceInjectionViewController: UIHostingController<DebugManualTraceInjectionView> {
12+
required init?(coder aDecoder: NSCoder) {
13+
super.init(coder: aDecoder, rootView: DebugManualTraceInjectionView())
14+
}
15+
}
16+
17+
private var currentSession: URLSession? = nil
18+
19+
@available(iOS 14.0, *)
20+
internal struct DebugManualTraceInjectionView: View {
21+
enum TraceHeaderType: String, CaseIterable {
22+
case datadog = "Datadog"
23+
case w3c = "W3C"
24+
case b3Single = "B3-Single"
25+
case b3Multiple = "B3-Multiple"
26+
}
27+
28+
@State private var spanName = "network request"
29+
@State private var requestURL = "http://127.0.0.1:8000"
30+
@State private var selectedTraceHeaderType: TraceHeaderType = .datadog
31+
@State private var sampleRate: Float = 100.0
32+
@State private var isRequestPending = false
33+
34+
private let session: URLSession = URLSession(
35+
configuration: .ephemeral,
36+
delegate: DDURLSessionDelegate(),
37+
delegateQueue: nil
38+
)
39+
40+
var body: some View {
41+
let isButtonDisabled = isRequestPending || spanName.isEmpty || requestURL.isEmpty
42+
43+
VStack() {
44+
VStack(spacing: 8) {
45+
Text("Trace injection")
46+
.font(.caption.weight(.bold))
47+
.frame(maxWidth: .infinity, alignment: .leading)
48+
49+
Text("After tapping \"SEND REQUEST\", a POST request will be sent to the given URL. The request will be traced using the chosen tracing header type and sample rate. A span with specified name will be sent to Datadog.")
50+
.font(.caption.weight(.light))
51+
.frame(maxWidth: .infinity, alignment: .leading)
52+
}
53+
.padding()
54+
55+
Form {
56+
Section(header: Text("Traced URL:")) {
57+
TextField("", text: $requestURL)
58+
}
59+
Section(header: Text("Span name:")) {
60+
TextField("", text: $spanName)
61+
}
62+
Picker("Trace header type:", selection: $selectedTraceHeaderType) {
63+
ForEach(TraceHeaderType.allCases, id: \.self) { headerType in
64+
Text(headerType.rawValue)
65+
}
66+
}
67+
.pickerStyle(.inline)
68+
Section(header: Text("Trace sample Rate")) {
69+
Slider(
70+
value: $sampleRate,
71+
in: 0...100, step: 1,
72+
minimumValueLabel: Text("0"),
73+
maximumValueLabel: Text("100")
74+
) {
75+
Text("Sample Rate")
76+
}
77+
}
78+
}
79+
80+
Spacer()
81+
82+
Button(action: { prepareAndSendRequest() }) {
83+
Text("SEND REQUEST")
84+
.fontWeight(.bold)
85+
.foregroundColor(.white)
86+
}
87+
.frame(maxWidth: .infinity)
88+
.padding()
89+
.background(isButtonDisabled ? Color.gray : Color.datadogPurple)
90+
.cornerRadius(10)
91+
.disabled(isButtonDisabled)
92+
.padding(.horizontal, 8)
93+
.padding(.bottom, 30)
94+
}
95+
}
96+
97+
private func prepareAndSendRequest() {
98+
guard let url = URL(string: requestURL) else {
99+
print("🔥 POST Request not sent - invalid url: \(requestURL)")
100+
return
101+
}
102+
103+
var request = URLRequest(url: url)
104+
request.httpMethod = "POST"
105+
106+
let span = Tracer.shared().startRootSpan(operationName: spanName)
107+
108+
switch selectedTraceHeaderType {
109+
case .datadog:
110+
let writer = HTTPHeadersWriter(sampleRate: sampleRate)
111+
Tracer.shared().inject(spanContext: span.context, writer: writer)
112+
writer.traceHeaderFields.forEach { request.setValue($0.value, forHTTPHeaderField: $0.key) }
113+
case .w3c:
114+
let writer = W3CHTTPHeadersWriter(sampleRate: sampleRate)
115+
Tracer.shared().inject(spanContext: span.context, writer: writer)
116+
writer.traceHeaderFields.forEach { request.setValue($0.value, forHTTPHeaderField: $0.key) }
117+
case .b3Single:
118+
let writer = B3HTTPHeadersWriter(sampleRate: sampleRate, injectEncoding: .single)
119+
Tracer.shared().inject(spanContext: span.context, writer: writer)
120+
writer.traceHeaderFields.forEach { request.setValue($0.value, forHTTPHeaderField: $0.key) }
121+
case .b3Multiple:
122+
let writer = B3HTTPHeadersWriter(sampleRate: sampleRate, injectEncoding: .multiple)
123+
Tracer.shared().inject(spanContext: span.context, writer: writer)
124+
writer.traceHeaderFields.forEach { request.setValue($0.value, forHTTPHeaderField: $0.key) }
125+
}
126+
127+
send(request: request) {
128+
span.finish()
129+
print("✅ Request sent to \(requestURL)")
130+
}
131+
}
132+
133+
private func send(request: URLRequest, completion: @escaping () -> Void) {
134+
isRequestPending = true
135+
let task = session.dataTask(with: request) { _, _, _ in
136+
completion()
137+
DispatchQueue.main.async { self.isRequestPending = false }
138+
}
139+
task.resume()
140+
}
141+
}
142+
143+
// MARK - Preview
144+
145+
@available(iOS 14.0, *)
146+
147+
struct DebugTraceInjectionView_Previews: PreviewProvider {
148+
static var previews: some View {
149+
NavigationView {
150+
DebugManualTraceInjectionView()
151+
}
152+
}
153+
}

Datadog/Example/Debugging/DebugWebviewViewController.swift

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import UIKit
88
import WebKit
9-
import DatadogInternal
109
import DatadogRUM
1110
import DatadogWebViewTracking
1211

Datadog/Example/Utils/ConsoleOutputInterceptor.swift

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
#if DEBUG
88

9-
import DatadogInternal
109
import UIKit
1110

1211
class ConsoleOutputInterceptor {

DatadogCore/Tests/Datadog/TracerTests.swift

+14-14
Original file line numberDiff line numberDiff line change
@@ -744,14 +744,14 @@ class TracerTests: XCTestCase {
744744
XCTAssertEqual(httpHeadersWriter.traceHeaderFields, expectedHTTPHeaders2)
745745
}
746746

747-
func testItInjectsSpanContextWithOTelHTTPHeadersWriter_usingMultipleHeaders() {
747+
func testItInjectsSpanContextWithB3HTTPHeadersWriter_usingMultipleHeaders() {
748748
Trace.enable(with: config, in: core)
749749
let tracer = Tracer.shared(in: core)
750750
let spanContext1 = DDSpanContext(traceID: 1, spanID: 2, parentSpanID: 3, baggageItems: .mockAny())
751751
let spanContext2 = DDSpanContext(traceID: 4, spanID: 5, parentSpanID: 6, baggageItems: .mockAny())
752752
let spanContext3 = DDSpanContext(traceID: 77, spanID: 88, parentSpanID: nil, baggageItems: .mockAny())
753753

754-
let httpHeadersWriter = OTelHTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .multiple)
754+
let httpHeadersWriter = B3HTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .multiple)
755755
XCTAssertEqual(httpHeadersWriter.traceHeaderFields, [:])
756756

757757
// When
@@ -790,14 +790,14 @@ class TracerTests: XCTestCase {
790790
XCTAssertEqual(httpHeadersWriter.traceHeaderFields, expectedHTTPHeaders3)
791791
}
792792

793-
func testItInjectsSpanContextWithOTelHTTPHeadersWriter_usingSingleHeader() {
793+
func testItInjectsSpanContextWithB3HTTPHeadersWriter_usingSingleHeader() {
794794
Trace.enable(with: config, in: core)
795795
let tracer = Tracer.shared(in: core)
796796
let spanContext1 = DDSpanContext(traceID: 1, spanID: 2, parentSpanID: 3, baggageItems: .mockAny())
797797
let spanContext2 = DDSpanContext(traceID: 4, spanID: 5, parentSpanID: 6, baggageItems: .mockAny())
798798
let spanContext3 = DDSpanContext(traceID: 77, spanID: 88, parentSpanID: nil, baggageItems: .mockAny())
799799

800-
let httpHeadersWriter = OTelHTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .single)
800+
let httpHeadersWriter = B3HTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .single)
801801
XCTAssertEqual(httpHeadersWriter.traceHeaderFields, [:])
802802

803803
// When
@@ -828,12 +828,12 @@ class TracerTests: XCTestCase {
828828
XCTAssertEqual(httpHeadersWriter.traceHeaderFields, expectedHTTPHeaders3)
829829
}
830830

831-
func testItInjectsRejectedSpanContextWithOTelHTTPHeadersWriter_usingSingleHeader() {
831+
func testItInjectsRejectedSpanContextWithB3HTTPHeadersWriter_usingSingleHeader() {
832832
Trace.enable(with: config, in: core)
833833
let tracer = Tracer.shared(in: core)
834834
let spanContext = DDSpanContext(traceID: 1, spanID: 2, parentSpanID: .mockAny(), baggageItems: .mockAny())
835835

836-
let httpHeadersWriter = OTelHTTPHeadersWriter(sampler: .mockRejectAll())
836+
let httpHeadersWriter = B3HTTPHeadersWriter(sampler: .mockRejectAll())
837837
XCTAssertEqual(httpHeadersWriter.traceHeaderFields, [:])
838838

839839
// When
@@ -846,12 +846,12 @@ class TracerTests: XCTestCase {
846846
XCTAssertEqual(httpHeadersWriter.traceHeaderFields, expectedHTTPHeaders)
847847
}
848848

849-
func testItInjectsRejectedSpanContextWithOTelHTTPHeadersWriter_usingMultipleHeader() {
849+
func testItInjectsRejectedSpanContextWithB3HTTPHeadersWriter_usingMultipleHeader() {
850850
Trace.enable(with: config, in: core)
851851
let tracer = Tracer.shared(in: core)
852852
let spanContext = DDSpanContext(traceID: 1, spanID: 2, parentSpanID: .mockAny(), baggageItems: .mockAny())
853853

854-
let httpHeadersWriter = OTelHTTPHeadersWriter(sampler: .mockRejectAll(), injectEncoding: .multiple)
854+
let httpHeadersWriter = B3HTTPHeadersWriter(sampler: .mockRejectAll(), injectEncoding: .multiple)
855855
XCTAssertEqual(httpHeadersWriter.traceHeaderFields, [:])
856856

857857
// When
@@ -956,15 +956,15 @@ class TracerTests: XCTestCase {
956956
XCTAssertNil(extractedSpanContext?.dd.parentSpanID)
957957
}
958958

959-
func testItExtractsSpanContextWithOTelHTTPHeadersReader_forMultipleHeaders() {
959+
func testItExtractsSpanContextWithB3HTTPHeadersReader_forMultipleHeaders() {
960960
Trace.enable(with: config, in: core)
961961
let tracer = Tracer.shared(in: core)
962962
let injectedSpanContext = DDSpanContext(traceID: 1, spanID: 2, parentSpanID: 3, baggageItems: .mockAny())
963963

964-
let httpHeadersWriter = OTelHTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .multiple)
964+
let httpHeadersWriter = B3HTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .multiple)
965965
tracer.inject(spanContext: injectedSpanContext, writer: httpHeadersWriter)
966966

967-
let httpHeadersReader = OTelHTTPHeadersReader(
967+
let httpHeadersReader = B3HTTPHeadersReader(
968968
httpHeaderFields: httpHeadersWriter.traceHeaderFields
969969
)
970970
let extractedSpanContext = tracer.extract(reader: httpHeadersReader)
@@ -974,15 +974,15 @@ class TracerTests: XCTestCase {
974974
XCTAssertEqual(extractedSpanContext?.dd.parentSpanID, injectedSpanContext.dd.parentSpanID)
975975
}
976976

977-
func testItExtractsSpanContextWithOTelHTTPHeadersReader_forSingleHeader() {
977+
func testItExtractsSpanContextWithB3HTTPHeadersReader_forSingleHeader() {
978978
Trace.enable(with: config, in: core)
979979
let tracer = Tracer.shared(in: core)
980980
let injectedSpanContext = DDSpanContext(traceID: 1, spanID: 2, parentSpanID: 3, baggageItems: .mockAny())
981981

982-
let httpHeadersWriter = OTelHTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .single)
982+
let httpHeadersWriter = B3HTTPHeadersWriter(sampler: .mockKeepAll(), injectEncoding: .single)
983983
tracer.inject(spanContext: injectedSpanContext, writer: httpHeadersWriter)
984984

985-
let httpHeadersReader = OTelHTTPHeadersReader(
985+
let httpHeadersReader = B3HTTPHeadersReader(
986986
httpHeaderFields: httpHeadersWriter.traceHeaderFields
987987
)
988988
let extractedSpanContext = tracer.extract(reader: httpHeadersReader)

0 commit comments

Comments
 (0)