Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions DatadogSDKTesting.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
56B58A2FC91F21D1ABB0155E /* TelemetryApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB970146040DD4967C7411 /* TelemetryApi.swift */; };
821FA0CC8B62245A4949FDBB /* APITypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E85241CF97F1B6DAA49ECA23 /* APITypes.swift */; };
86137D23E1704D17A7B6D486 /* TelemetryExporterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DFE52D135C457D9160920A /* TelemetryExporterTests.swift */; };
A7926AF42FCDD411007834F2 /* TelemetryMetricExporterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7926AF32FCDD411007834F2 /* TelemetryMetricExporterTests.swift */; };
A7926AF62FCDD411007834F2 /* TelemetryLogExporterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7926AF52FCDD411007834F2 /* TelemetryLogExporterTests.swift */; };
8EF6F88255506A4CEE6D36B6 /* GitUploadApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F3AA8F51FDDEF0C319080E8 /* GitUploadApi.swift */; };
97326DD4F8EF4EBDB05AC1AA /* TelemetryExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC161A86B91745F4BDC88FF1 /* TelemetryExporter.swift */; };
98D8D496CC4210A0D1D07F93 /* Synced.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12536CCE14EC1D3DAAC8DB81 /* Synced.swift */; };
Expand Down Expand Up @@ -78,6 +80,7 @@
A77C123C2D9425C7009C2BA1 /* KnownTestsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A77C123B2D9425C7009C2BA1 /* KnownTestsTests.swift */; };
A77C123E2D9474E6009C2BA1 /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = A77C123D2D9474E6009C2BA1 /* Models.swift */; };
A7926AEF2FCDCBFE007834F2 /* TelemetryLogExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7926AEE2FCDCBFE007834F2 /* TelemetryLogExporter.swift */; };
A7926AF12FCDD411007834F2 /* TelemetryMetricExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7926AF02FCDD411007834F2 /* TelemetryMetricExporter.swift */; };
A794A8FF2E7990D20044FE3C /* TestManagementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A794A8FE2E7990C40044FE3C /* TestManagementTests.swift */; };
A79DA5B42F928F87004BCA8C /* SwiftTestingSmokeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79DA5B32F928F87004BCA8C /* SwiftTestingSmokeTests.swift */; };
A79DA5B72F929812004BCA8C /* DatadogSDKTesting.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7FC25642BA1E83500067E26 /* DatadogSDKTesting.framework */; };
Expand Down Expand Up @@ -422,6 +425,8 @@
0083E7241E320B00198C0D71 /* TestManagementApi.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TestManagementApi.swift; sourceTree = "<group>"; };
01EB970146040DD4967C7411 /* TelemetryApi.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TelemetryApi.swift; sourceTree = "<group>"; };
04DFE52D135C457D9160920A /* TelemetryExporterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TelemetryExporterTests.swift; sourceTree = "<group>"; };
A7926AF32FCDD411007834F2 /* TelemetryMetricExporterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryMetricExporterTests.swift; sourceTree = "<group>"; };
A7926AF52FCDD411007834F2 /* TelemetryLogExporterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryLogExporterTests.swift; sourceTree = "<group>"; };
0A9081E25D194D74A4DA020E /* TestImpactAnalysisSwiftTestingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestImpactAnalysisSwiftTestingTests.swift; sourceTree = "<group>"; };
12536CCE14EC1D3DAAC8DB81 /* Synced.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = Synced.swift; sourceTree = "<group>"; };
1495758A45B516A2C584B845 /* TestImpactAnalysisApi.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TestImpactAnalysisApi.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -491,6 +496,7 @@
A77C123B2D9425C7009C2BA1 /* KnownTestsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnownTestsTests.swift; sourceTree = "<group>"; };
A77C123D2D9474E6009C2BA1 /* Models.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Models.swift; sourceTree = "<group>"; };
A7926AEE2FCDCBFE007834F2 /* TelemetryLogExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryLogExporter.swift; sourceTree = "<group>"; };
A7926AF02FCDD411007834F2 /* TelemetryMetricExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryMetricExporter.swift; sourceTree = "<group>"; };
A794A8FE2E7990C40044FE3C /* TestManagementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestManagementTests.swift; sourceTree = "<group>"; };
A79DA5B22F928F71004BCA8C /* IntegrationTests-UnitTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = "IntegrationTests-UnitTests.xctestplan"; sourceTree = "<group>"; };
A79DA5B32F928F87004BCA8C /* SwiftTestingSmokeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftTestingSmokeTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -806,6 +812,8 @@
isa = PBXGroup;
children = (
04DFE52D135C457D9160920A /* TelemetryExporterTests.swift */,
A7926AF32FCDD411007834F2 /* TelemetryMetricExporterTests.swift */,
A7926AF52FCDD411007834F2 /* TelemetryLogExporterTests.swift */,
);
path = Telemetry;
sourceTree = "<group>";
Expand Down Expand Up @@ -986,6 +994,7 @@
children = (
BC161A86B91745F4BDC88FF1 /* TelemetryExporter.swift */,
A7926AEE2FCDCBFE007834F2 /* TelemetryLogExporter.swift */,
A7926AF02FCDD411007834F2 /* TelemetryMetricExporter.swift */,
);
path = Telemetry;
sourceTree = "<group>";
Expand Down Expand Up @@ -2153,6 +2162,7 @@
A7FC264A2BA1ECF800067E26 /* File.swift in Sources */,
A7FC264D2BA1ED1200067E26 /* Exporter.swift in Sources */,
A7FC26302BA1ECBD00067E26 /* CoverageExporter.swift in Sources */,
A7926AF12FCDD411007834F2 /* TelemetryMetricExporter.swift in Sources */,
A7AB7C2026E7EB1100000011 /* CoverageData.swift in Sources */,
A7AB7C2026E7EB1100000013 /* CoverageProcessor.swift in Sources */,
A7FC263B2BA1ECCF00067E26 /* Feature.swift in Sources */,
Expand Down Expand Up @@ -2206,6 +2216,8 @@
A7FC26822BA1F81D00067E26 /* DeviceMock.swift in Sources */,
A7FC26722BA1F80000067E26 /* EncodableValueTests.swift in Sources */,
86137D23E1704D17A7B6D486 /* TelemetryExporterTests.swift in Sources */,
A7926AF42FCDD411007834F2 /* TelemetryMetricExporterTests.swift in Sources */,
A7926AF62FCDD411007834F2 /* TelemetryLogExporterTests.swift in Sources */,
A7FC26702BA1F7FA00067E26 /* SpansExporterTests.swift in Sources */,
A7FC268C2BA1F82300067E26 /* HTTPClientTests.swift in Sources */,
A7FC26742BA1F80000067E26 /* JSONEncoderTests.swift in Sources */,
Expand Down
64 changes: 63 additions & 1 deletion Sources/EventsExporter/API/TelemetryApi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public protocol TelemetryPayload: Encodable {
/// The set of telemetry request_types this service implements.
public enum TelemetryRequestType: String, Codable {
case generateMetrics = "generate-metrics"
case distributions
case logs
case messageBatch = "message-batch"
case appStarted = "app-started"
Expand Down Expand Up @@ -102,6 +103,52 @@ public struct TelemetryMetric: TelemetryPayload, Codable {
public var requestType: TelemetryRequestType { .generateMetrics }
}

/// A single telemetry distribution payload for `distributions`.
///
/// Unlike `generate-metrics`, distribution series carry raw sample values
/// (not `[timestamp, value]` pairs), and the backend computes the statistical
/// summary (p50/p75/p90/p95/p99/max). The namespace set is also distinct.
public struct TelemetryDistribution: TelemetryPayload, Codable {
/// Namespace for distribution metrics.
public enum Namespace: String, Codable {
case tracers, profilers, rum, appsec
}

/// A distribution series with one or more raw sample values.
public struct Series: Codable {
public var metric: String
/// Raw sample values — the backend computes the distribution.
public var points: [Double]
public var tags: [String]?
public var common: Bool?
/// Per-series override for the payload-level namespace.
public var namespace: Namespace?

public init(metric: String,
points: [Double],
tags: [String]? = nil,
common: Bool? = nil,
namespace: Namespace? = nil)
{
self.metric = metric
self.points = points
self.tags = tags
self.common = common
self.namespace = namespace
}
}

public var namespace: Namespace?
public var series: [Series]

public init(namespace: Namespace?, series: [Series]) {
self.namespace = namespace
self.series = series
}

public var requestType: TelemetryRequestType { .distributions }
}

/// A single telemetry log message for the `logs` request type.
///
/// The telemetry intake's `log_message` schema is intentionally minimal — it
Expand Down Expand Up @@ -347,6 +394,8 @@ public struct TelemetryMessageBatch: TelemetryPayload, Codable {
message = try container.decode(TelemetryLog.Logs.self, forKey: .payload)
case .generateMetrics:
message = try container.decode(TelemetryMetric.self, forKey: .payload)
case .distributions:
message = try container.decode(TelemetryDistribution.self, forKey: .payload)
case .messageBatch:
message = try container.decode(TelemetryMessageBatch.self, forKey: .payload)
}
Expand Down Expand Up @@ -409,6 +458,10 @@ public protocol TelemetryApi: APIService {
func sendMetrics(_ series: [TelemetryMetric.Series],
namespace: TelemetryMetric.Namespace?) async throws(APICallError)

/// Send a batch of distribution series via the `distributions` request type.
func sendDistributions(_ series: [TelemetryDistribution.Series],
namespace: TelemetryDistribution.Namespace?) async throws(APICallError)

/// Send a batch of log messages via the `logs` request type.
func sendLogs(_ logs: [TelemetryLog]) async throws(APICallError)

Expand Down Expand Up @@ -518,6 +571,13 @@ internal struct TelemetryApiService: TelemetryApi {
try await send(payload: payload)
}

func sendDistributions(_ series: [TelemetryDistribution.Series],
namespace: TelemetryDistribution.Namespace?) async throws(APICallError)
{
let payload = TelemetryDistribution(namespace: namespace, series: series)
try await send(payload: payload)
}

func sendLogs(_ logs: [TelemetryLog]) async throws(APICallError) {
guard !logs.isEmpty else { return }
try await send(payload: TelemetryLog.Logs(logs))
Expand All @@ -535,8 +595,10 @@ internal struct TelemetryApiService: TelemetryApi {
let dataFormat: DataFormat
do {
dataFormat = try DataFormat(header: header, encoder: encoder)
} catch let error as EncodingError {
throw .encoding(value: header, error: error)
} catch {
throw .transport(error)
throw .unknownError(error)
}
try await send(requestType: .messageBatch,
body: dataFormat.prefix + data + dataFormat.suffix)
Expand Down
17 changes: 17 additions & 0 deletions Sources/EventsExporter/OpenTelemetry+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,23 @@ public extension Resource {
get { attributes[SemanticConventions.Deployment.environmentName.rawValue]?.description }
set { attributes[SemanticConventions.Deployment.environmentName.rawValue] = newValue.map { .string($0) } }
}

/// Datadog telemetry namespace for `generate-metrics` series (gauge / count /
/// summary) produced by a meter provider. Used by the metric exporter as the
/// per-series namespace override.
var telemetryMetricNamespace: TelemetryMetric.Namespace? {
get { (attributes["dd.telemetry.metric.namespace"]?.description).flatMap(TelemetryMetric.Namespace.init(rawValue:)) }
set { attributes["dd.telemetry.metric.namespace"] = newValue.map { .string($0.rawValue) } }
}

/// Datadog telemetry namespace for `distributions` series (histograms) produced
/// by a meter provider. Used by the metric exporter as the per-series namespace
/// override. Kept separate from `telemetryMetricNamespace` because the two
/// payload types accept different namespace value sets.
var telemetryDistributionNamespace: TelemetryDistribution.Namespace? {
get { (attributes["dd.telemetry.distribution.namespace"]?.description).flatMap(TelemetryDistribution.Namespace.init(rawValue:)) }
set { attributes["dd.telemetry.distribution.namespace"] = newValue.map { .string($0.rawValue) } }
}
}

// MARK: - Test-attribute accessors on SpanData / AttributeValue dictionaries
Expand Down
Loading
Loading