Skip to content

Commit 426742c

Browse files
author
Ignacio Bonafonte
authored
Merge pull request #227 from open-telemetry/Datadog-Exporter-Migrate-to-Intake-v2
Datadog exporter: migrate to intake v2
2 parents d717e57 + 703c477 commit 426742c

29 files changed

+920
-775
lines changed

Examples/Datadog Sample/main.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ import OpenTelemetrySdk
1111
import UIKit
1212
#endif
1313

14-
let clientToken = ""
15-
let apikey = ""
14+
let apikeyOrClientToken = ""
1615

1716
let sampleKey = "sampleKey"
1817
let sampleValue = "sampleValue"
@@ -36,9 +35,8 @@ let exporterConfiguration = ExporterConfiguration(
3635
applicationName: "SwiftDatadogSample",
3736
applicationVersion: "1.0.0",
3837
environment: "test",
39-
clientToken: clientToken,
40-
apiKey: apikey,
41-
endpoint: Endpoint.us,
38+
apiKey: apikeyOrClientToken,
39+
endpoint: Endpoint.us1,
4240
uploadCondition: { true },
4341
performancePreset: .instantDataDelivery,
4442
hostName: hostName

Package.resolved

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ let package = Package(
5151
.package(name: "swift-atomics", url: "https://github.com/apple/swift-atomics.git", from: "0.0.1"),
5252
.package(name: "swift-metrics", url: "https://github.com/apple/swift-metrics.git", from: "2.1.1"),
5353
.package(name: "Reachability", url: "https://github.com/ashleymills/Reachability.swift", from: "5.1.0"),
54-
54+
.package(name: "DataCompression", url: "https://github.com/mw99/DataCompression.git", from: "3.5.0")
5555
],
5656
targets: [
5757
.target(name: "OpenTelemetryApi",
@@ -109,7 +109,7 @@ let package = Package(
109109
dependencies: ["OpenTelemetrySdk"],
110110
path: "Sources/Exporters/InMemory"),
111111
.target(name: "DatadogExporter",
112-
dependencies: ["OpenTelemetrySdk"],
112+
dependencies: ["OpenTelemetrySdk", "DataCompression"],
113113
path: "Sources/Exporters/DatadogExporter",
114114
exclude: ["NOTICE", "README.md"]),
115115
.testTarget(name: "NetworkStatusTests",

Sources/Exporters/DatadogExporter/DatadogExporter.swift

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,10 @@ public class DatadogExporter: SpanExporter, MetricExporter {
1313
var metricsExporter: MetricsExporter?
1414

1515
public init(config: ExporterConfiguration) throws {
16-
guard config.clientToken != nil || config.apiKey != nil else {
17-
throw ExporterError(description: "Traces exporter needs a client token and Metrics Exporter need an api key")
18-
}
19-
2016
self.configuration = config
21-
if configuration.clientToken != nil {
22-
spansExporter = try SpansExporter(config: configuration)
23-
logsExporter = try LogsExporter(config: configuration)
24-
}
25-
if configuration.apiKey != nil {
26-
metricsExporter = try MetricsExporter(config: configuration)
27-
}
17+
spansExporter = try SpansExporter(config: configuration)
18+
logsExporter = try LogsExporter(config: configuration)
19+
metricsExporter = try MetricsExporter(config: configuration)
2820
}
2921

3022
public func export(spans: [SpanData]) -> SpanExporterResultCode {

Sources/Exporters/DatadogExporter/ExporterConfiguration.swift

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,17 @@ public struct ExporterConfiguration {
1313
/// The name of the service, resource, version,... that will be reported to the backend.
1414
var serviceName: String
1515
var resource: String
16-
var applicationName: String
16+
var applicationName: String
1717
var version: String
1818
var environment: String
1919

20-
/// Either the RUM client token (which supports RUM, Logging and APM) or regular client token, only for Logging and APM.
21-
var clientToken: String?
22-
/// The api key, needed for metrics reporting
23-
var apiKey: String?
20+
/// Either the API key or a regular client token
21+
/// For metrics reporting API key is needed
22+
var apiKey: String
2423
/// Endpoint that will be used for reporting.
2524
var endpoint: Endpoint
25+
26+
var source: String
2627
/// This conditon will be evaluated before trying to upload data
2728
/// Can be used to avoid reporting when no connection
2829
var uploadCondition: () -> Bool
@@ -37,15 +38,15 @@ public struct ExporterConfiguration {
3738
/// Option to add a custom prefix to all the metrics sent by the exporter
3839
var metricsNamePrefix: String?
3940

40-
public init(serviceName: String, resource: String, applicationName: String, applicationVersion: String, environment: String, clientToken: String?, apiKey: String?, endpoint: Endpoint, uploadCondition: @escaping () -> Bool, performancePreset: PerformancePreset = .default, exportUnsampledSpans: Bool = true, exportUnsampledLogs: Bool = true, hostName: String? = nil, metricsNamePrefix: String? = "otel") {
41+
public init(serviceName: String, resource: String, applicationName: String, applicationVersion: String, environment: String, apiKey: String, endpoint: Endpoint, source: String = "ios", uploadCondition: @escaping () -> Bool, performancePreset: PerformancePreset = .default, exportUnsampledSpans: Bool = true, exportUnsampledLogs: Bool = true, hostName: String? = nil, metricsNamePrefix: String? = "otel") {
4142
self.serviceName = serviceName
4243
self.resource = resource
4344
self.applicationName = applicationName
4445
self.version = applicationVersion
4546
self.environment = environment
46-
self.clientToken = clientToken
4747
self.apiKey = apiKey
4848
self.endpoint = endpoint
49+
self.source = source
4950
self.uploadCondition = uploadCondition
5051
self.performancePreset = performancePreset
5152
self.exportUnsampledSpans = exportUnsampledSpans

Sources/Exporters/DatadogExporter/Logs/LogsExporter.swift

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@ internal class LogsExporter {
2727
let logsUploadQueue = DispatchQueue(label: "com.otel.datadog.logsupload", target: .global(qos: .userInteractive))
2828

2929
init(config: ExporterConfiguration) throws {
30-
guard let clientToken = config.clientToken else {
31-
throw ExporterError(description: "Logs Exporter need a client token")
32-
}
33-
3430
self.configuration = config
3531

3632
let filesOrchestrator = FilesOrchestrator(
@@ -39,7 +35,7 @@ internal class LogsExporter {
3935
dateProvider: SystemDateProvider()
4036
)
4137

42-
let dataFormat = DataFormat(prefix: "", suffix: "", separator: "\n")
38+
let dataFormat = DataFormat(prefix: "[", suffix: "]", separator: ",")
4339

4440
let logsFileWriter = FileWriter(
4541
dataFormat: dataFormat,
@@ -55,27 +51,29 @@ internal class LogsExporter {
5551

5652
logsStorage = FeatureStorage(writer: logsFileWriter, reader: logsFileReader)
5753

58-
let urlProvider = UploadURLProvider(
59-
urlWithClientToken: try configuration.endpoint.logsUrlWithClientToken(clientToken: clientToken),
60-
queryItemProviders: [
61-
.ddsource(),
62-
.batchTime(using: SystemDateProvider())
54+
let requestBuilder = RequestBuilder(
55+
url: configuration.endpoint.logsURL,
56+
queryItems: [
57+
.ddsource(source: configuration.source)
58+
],
59+
headers: [
60+
.contentTypeHeader(contentType: .applicationJSON),
61+
.contentEncodingHeader(contentEncoding: .deflate),
62+
.userAgentHeader(
63+
appName: configuration.applicationName,
64+
appVersion: configuration.version,
65+
device: Device.current
66+
),
67+
.ddAPIKeyHeader(apiKey: configuration.apiKey),
68+
.ddEVPOriginHeader(source: configuration.source),
69+
.ddEVPOriginVersionHeader(version: configuration.version),
70+
.ddRequestIDHeader()
6371
]
6472
)
6573

66-
let httpHeaders = HTTPHeaders(headers: [
67-
.contentTypeHeader(contentType: .textPlainUTF8),
68-
.userAgentHeader(
69-
appName: configuration.applicationName,
70-
appVersion: configuration.version,
71-
device: Device.current
72-
)
73-
])
74-
7574
logsUpload = FeatureUpload(featureName: "logsUpload",
7675
storage: logsStorage,
77-
uploadHTTPHeaders: httpHeaders,
78-
uploadURLProvider: urlProvider,
76+
requestBuilder: requestBuilder,
7977
performance: configuration.performancePreset,
8078
uploadCondition: configuration.uploadCondition)
8179
}

Sources/Exporters/DatadogExporter/Metrics/MetricsExporter.swift

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@ internal class MetricsExporter {
1818
let metricsUploadQueue = DispatchQueue(label: "com.otel.datadog.metricsupload", target: .global(qos: .userInteractive))
1919

2020
init(config: ExporterConfiguration) throws {
21-
guard let apiKey = config.apiKey else {
22-
throw ExporterError(description: "Metrics Exporter need an api key")
23-
}
24-
2521
configuration = config
2622

2723
let filesOrchestrator = FilesOrchestrator(
@@ -46,26 +42,26 @@ internal class MetricsExporter {
4642

4743
metricsStorage = FeatureStorage(writer: spanFileWriter, reader: spanFileReader)
4844

49-
let urlProvider = UploadURLProvider(
50-
urlWithClientToken: configuration.endpoint.metricsURL,
51-
queryItemProviders: [
52-
.apiKey(apiKey: apiKey),
45+
let requestBuilder = RequestBuilder(
46+
url: configuration.endpoint.metricsURL,
47+
queryItems: [],
48+
headers: [
49+
.contentTypeHeader(contentType: .textPlainUTF8),
50+
.userAgentHeader(
51+
appName: configuration.applicationName,
52+
appVersion: configuration.version,
53+
device: Device.current
54+
),
55+
.ddAPIKeyHeader(apiKey: configuration.apiKey),
56+
.ddEVPOriginHeader(source: configuration.source),
57+
.ddEVPOriginVersionHeader(version: configuration.version),
58+
.ddRequestIDHeader(),
5359
]
5460
)
5561

56-
let httpHeaders = HTTPHeaders(headers: [
57-
.contentTypeHeader(contentType: .textPlainUTF8),
58-
.userAgentHeader(
59-
appName: configuration.applicationName,
60-
appVersion: configuration.version,
61-
device: Device.current
62-
),
63-
])
64-
6562
metricsUpload = FeatureUpload(featureName: "metricsUpload",
6663
storage: metricsStorage,
67-
uploadHTTPHeaders: httpHeaders,
68-
uploadURLProvider: urlProvider,
64+
requestBuilder: requestBuilder,
6965
performance: configuration.performancePreset,
7066
uploadCondition: configuration.uploadCondition)
7167
}

Sources/Exporters/DatadogExporter/Persistence/FileReader.swift

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,13 @@ internal final class FileReader {
3232
// MARK: - Reading batches
3333

3434
func readNextBatch() -> Batch? {
35-
queue.sync {
36-
synchronizedReadNextBatch()
37-
}
38-
}
39-
40-
private func synchronizedReadNextBatch() -> Batch? {
4135
if let file = orchestrator.getReadableFile(excludingFilesNamed: Set(filesRead.map { $0.name })) {
4236
do {
4337
let fileData = try file.read()
4438
let batchData = dataFormat.prefixData + fileData + dataFormat.suffixData
4539
return Batch(data: batchData, file: file)
4640
} catch {
47-
print("🔥 Failed to read file: \(error)")
41+
print("Failed to read data from file")
4842
return nil
4943
}
5044
}
@@ -53,31 +47,23 @@ internal final class FileReader {
5347
}
5448

5549
/// This method gets remaining files at once, and process each file after with the block passed.
56-
/// Being on a queue assures that no other previous batches are uploaded while these are being handled
57-
internal func onRemainingBatches(process: (Batch)->()) -> Bool {
58-
queue.sync {
59-
do {
60-
try orchestrator.getAllFiles(excludingFilesNamed: Set(filesRead.map { $0.name }))?.forEach {
61-
let fileData = try $0.read()
62-
let batchData = dataFormat.prefixData + fileData + dataFormat.suffixData
63-
process(Batch(data: batchData, file: $0))
64-
}
65-
} catch {
66-
return false
50+
/// Currently called from flush method
51+
func onRemainingBatches(process: (Batch) -> ()) -> Bool {
52+
do {
53+
try orchestrator.getAllFiles(excludingFilesNamed: Set(filesRead.map { $0.name }))?.forEach {
54+
let fileData = try $0.read()
55+
let batchData = dataFormat.prefixData + fileData + dataFormat.suffixData
56+
process(Batch(data: batchData, file: $0))
6757
}
68-
return true
58+
} catch {
59+
return false
6960
}
61+
return true
7062
}
7163

7264
// MARK: - Accepting batches
7365

7466
func markBatchAsRead(_ batch: Batch) {
75-
queue.sync { [weak self] in
76-
self?.synchronizedMarkBatchAsRead(batch)
77-
}
78-
}
79-
80-
func synchronizedMarkBatchAsRead(_ batch: Batch) {
8167
orchestrator.delete(readableFile: batch.file)
8268
filesRead.append(batch.file)
8369
}

Sources/Exporters/DatadogExporter/Spans/SpansExporter.swift

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@ internal class SpansExporter {
1818
let tracesUploadQueue = DispatchQueue(label: "com.otel.datadog.tracesupload", target: .global(qos: .userInteractive))
1919

2020
init(config: ExporterConfiguration) throws {
21-
guard let clientToken = config.clientToken else {
22-
throw ExporterError(description: "Span Exporter need a client token")
23-
}
24-
2521
self.configuration = config
2622

2723
let filesOrchestrator = FilesOrchestrator(
@@ -46,26 +42,27 @@ internal class SpansExporter {
4642

4743
tracesStorage = FeatureStorage(writer: spanFileWriter, reader: spanFileReader)
4844

49-
let urlProvider = UploadURLProvider(
50-
urlWithClientToken: try configuration.endpoint.tracesUrlWithClientToken(clientToken: clientToken),
51-
queryItemProviders: [
52-
.batchTime(using: SystemDateProvider())
45+
let requestBuilder = RequestBuilder(
46+
url: configuration.endpoint.tracesURL,
47+
queryItems: [],
48+
headers: [
49+
.contentTypeHeader(contentType: .textPlainUTF8),
50+
.contentEncodingHeader(contentEncoding: .deflate),
51+
.userAgentHeader(
52+
appName: configuration.applicationName,
53+
appVersion: configuration.version,
54+
device: Device.current
55+
),
56+
.ddAPIKeyHeader(apiKey: config.apiKey),
57+
.ddEVPOriginHeader(source: configuration.source),
58+
.ddEVPOriginVersionHeader(version: configuration.version),
59+
.ddRequestIDHeader(),
5360
]
5461
)
5562

56-
let httpHeaders = HTTPHeaders(headers: [
57-
.contentTypeHeader(contentType: .textPlainUTF8),
58-
.userAgentHeader(
59-
appName: configuration.applicationName,
60-
appVersion: configuration.version,
61-
device: Device.current
62-
)
63-
])
64-
6563
tracesUpload = FeatureUpload(featureName: "tracesUpload",
6664
storage: tracesStorage,
67-
uploadHTTPHeaders: httpHeaders,
68-
uploadURLProvider: urlProvider,
65+
requestBuilder: requestBuilder,
6966
performance: configuration.performancePreset,
7067
uploadCondition: configuration.uploadCondition)
7168
}

0 commit comments

Comments
 (0)