Skip to content

Commit 4888169

Browse files
author
Ignacio Bonafonte
authored
Merge pull request #213 from bryce-b/otlp-exporter-config
Add functionality for Exporter headers env-var #204
2 parents 1f85050 + c14151e commit 4888169

File tree

7 files changed

+204
-18
lines changed

7 files changed

+204
-18
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import Foundation
7+
import OpenTelemetryApi
8+
9+
/// Provides a framework for detection of resource information from the environment variable
10+
public struct EnvVarHeaders {
11+
private static let otelAttributesEnv = "OTEL_EXPORTER_OTLP_HEADERS"
12+
private static let labelListSplitter = Character(",")
13+
private static let labelKeyValueSplitter = Character("=")
14+
15+
/// This resource information is loaded from the
16+
/// environment variable.
17+
public static let attributes : [(String,String)]? = parseAttributes(rawEnvAttributes: ProcessInfo.processInfo.environment[otelAttributesEnv])
18+
19+
private init() {}
20+
21+
/// Creates a label map from the environment variable string.
22+
/// - Parameter rawEnvLabels: the comma-separated list of labels
23+
private static func parseAttributes(rawEnvAttributes: String?) -> [(String, String)]? {
24+
guard let rawEnvLabels = rawEnvAttributes else { return nil }
25+
26+
var labels = [(String, String)]()
27+
28+
rawEnvLabels.split(separator: labelListSplitter).forEach {
29+
let split = $0.split(separator: labelKeyValueSplitter)
30+
if split.count != 2 {
31+
return
32+
}
33+
let key = split[0].trimmingCharacters(in: .whitespaces)
34+
let value = split[1].trimmingCharacters(in: CharacterSet(charactersIn: "^\"|\"$"))
35+
labels.append((key,value))
36+
}
37+
return labels
38+
}
39+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import Foundation
7+
8+
9+
10+
public struct OtlpConfiguration {
11+
public static let DefaultTimeoutInterval : TimeInterval = TimeInterval(10)
12+
13+
14+
/*
15+
* This is a first pass addition to satisfy the OTLP Configuration specification:
16+
* https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md
17+
* It's possible to satisfy a few of these configuration options through the configuration of the GRPC channel
18+
* It's worth considering re-factoring the initialization of the OTLP exporters to collect all the configuration
19+
* in one locations.
20+
*
21+
* I've left several of the configuration options stubbed in comments, so that may be implemented in the future.
22+
*/
23+
// let endpoint : URL? = URL(string: "https://localhost:4317")
24+
// let certificateFile
25+
// let compression
26+
let headers : [(String,String)]?
27+
let timeout : TimeInterval
28+
29+
public init(timeout : TimeInterval = OtlpConfiguration.DefaultTimeoutInterval, headers: [(String,String)]? = nil) {
30+
self.headers = headers
31+
self.timeout = timeout
32+
}
33+
}

Sources/Exporters/OpenTelemetryProtocol/metric/OtelpMetricExporter.swift renamed to Sources/Exporters/OpenTelemetryProtocol/metric/OtlpMetricExporter.swift

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,28 @@
66
import Foundation
77
import GRPC
88
import NIO
9+
import NIOHPACK
910
import OpenTelemetryApi
1011
import OpenTelemetrySdk
1112

12-
public class OtelpMetricExporter: MetricExporter {
13+
public class OtlpMetricExporter: MetricExporter {
1314
let channel: GRPCChannel
1415
let metricClient: Opentelemetry_Proto_Collector_Metrics_V1_MetricsServiceClient
15-
let timeoutNanos: Int64
16+
let config : OtlpConfiguration
17+
var callOptions : CallOptions? = nil
1618

17-
public init(channel: GRPCChannel, timeoutNanos: Int64 = 0) {
19+
20+
21+
public init(channel: GRPCChannel, config: OtlpConfiguration = OtlpConfiguration()) {
1822
self.channel = channel
19-
self.timeoutNanos = timeoutNanos
23+
self.config = config
2024
self.metricClient = Opentelemetry_Proto_Collector_Metrics_V1_MetricsServiceClient(channel: self.channel)
25+
26+
if let headers = EnvVarHeaders.attributes {
27+
callOptions = CallOptions(customMetadata: HPACKHeaders(headers))
28+
} else if let headers = config.headers {
29+
callOptions = CallOptions(customMetadata: HPACKHeaders(headers))
30+
}
2131
}
2232

2333
public func export(metrics: [Metric], shouldCancel: (() -> Bool)?) -> MetricExporterResultCode {
@@ -26,11 +36,11 @@ public class OtelpMetricExporter: MetricExporter {
2636
$0.resourceMetrics = MetricsAdapter.toProtoResourceMetrics(metricDataList: metrics)
2737
}
2838

29-
if timeoutNanos > 0 {
30-
metricClient.defaultCallOptions.timeLimit = TimeLimit.timeout(TimeAmount.nanoseconds(timeoutNanos))
39+
if config.timeout > 0 {
40+
metricClient.defaultCallOptions.timeLimit = TimeLimit.timeout(TimeAmount.nanoseconds(Int64(config.timeout.toNanoseconds)))
3141
}
3242

33-
let export = metricClient.export(exportRequest)
43+
let export = metricClient.export(exportRequest, callOptions: callOptions)
3444

3545
do {
3646
_ = try export.response.wait()

Sources/Exporters/OpenTelemetryProtocol/trace/OtlpTraceExporter.swift

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,37 @@
66
import Foundation
77
import GRPC
88
import NIO
9+
import NIOHPACK
910
import OpenTelemetryApi
1011
import OpenTelemetrySdk
1112

1213
public class OtlpTraceExporter: SpanExporter {
1314
let channel: GRPCChannel
1415
let traceClient: Opentelemetry_Proto_Collector_Trace_V1_TraceServiceClient
15-
let timeoutNanos: Int64
16+
let config : OtlpConfiguration
17+
var callOptions : CallOptions? = nil
1618

17-
public init(channel: GRPCChannel, timeoutNanos: Int64 = 0) {
19+
public init(channel: GRPCChannel, config: OtlpConfiguration = OtlpConfiguration()) {
1820
self.channel = channel
1921
traceClient = Opentelemetry_Proto_Collector_Trace_V1_TraceServiceClient(channel: channel)
20-
self.timeoutNanos = timeoutNanos
22+
self.config = config
23+
if let headers = EnvVarHeaders.attributes {
24+
callOptions = CallOptions(customMetadata: HPACKHeaders(headers))
25+
} else if let headers = config.headers {
26+
callOptions = CallOptions(customMetadata: HPACKHeaders(headers))
27+
}
2128
}
2229

2330
public func export(spans: [SpanData]) -> SpanExporterResultCode {
2431
let exportRequest = Opentelemetry_Proto_Collector_Trace_V1_ExportTraceServiceRequest.with {
2532
$0.resourceSpans = SpanAdapter.toProtoResourceSpans(spanDataList: spans)
2633
}
2734

28-
if timeoutNanos > 0 {
29-
traceClient.defaultCallOptions.timeLimit = TimeLimit.timeout(TimeAmount.nanoseconds(timeoutNanos))
35+
if config.timeout > 0 {
36+
traceClient.defaultCallOptions.timeLimit = TimeLimit.timeout(TimeAmount.nanoseconds(Int64(config.timeout.toNanoseconds)))
3037
}
3138

32-
let export = traceClient.export(exportRequest)
39+
let export = traceClient.export(exportRequest, callOptions: callOptions)
3340

3441
do {
3542
// wait() on the response to stop the program from exiting before the response is received.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
import Foundation
6+
7+
import OpenTelemetryApi
8+
9+
public class MeterProviderBuilder {
10+
public private(set) var resource : Resource = Resource()
11+
public private(set) var metricExporter : MetricExporter = NoopMetricExporter()
12+
public private(set) var metricPushInterval : TimeInterval = MeterProviderSdk.defaultPushInterval
13+
public private(set) var metricProcessor : MetricProcessor = NoopMetricProcessor()
14+
15+
public init() {}
16+
17+
public func with(processor: MetricProcessor) -> Self {
18+
self.metricProcessor = processor
19+
return self
20+
}
21+
22+
public func with(exporter: MetricExporter) -> Self {
23+
self.metricExporter = exporter
24+
return self
25+
}
26+
27+
public func with(pushInterval: TimeInterval) -> Self {
28+
self.metricPushInterval = pushInterval
29+
return self
30+
}
31+
32+
public func with(resource: Resource) -> Self {
33+
self.resource = resource
34+
return self
35+
}
36+
37+
public func build() -> MeterProvider {
38+
return MeterProviderSdk(metricProcessor:metricProcessor, metricExporter: metricExporter, metricPushInterval: metricPushInterval, resource: resource)
39+
}
40+
}
41+
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import Foundation
7+
import OpenTelemetryApi
8+
9+
public class TracerProviderBuilder {
10+
public private(set) var clock : Clock = MillisClock()
11+
public private(set) var idGenerator : IdGenerator = RandomIdGenerator()
12+
public private(set) var resource : Resource = Resource()
13+
public private(set) var spanLimits : SpanLimits = SpanLimits()
14+
public private(set) var sampler : Sampler = Samplers.parentBased(root: Samplers.alwaysOn)
15+
public private(set) var spanProcessors : [SpanProcessor] = []
16+
17+
public init() {}
18+
19+
public func with(clock: Clock) -> Self {
20+
self.clock = clock
21+
return self
22+
}
23+
24+
public func with(idGenerator: IdGenerator) -> Self {
25+
self.idGenerator = idGenerator
26+
return self
27+
}
28+
29+
public func with(resource: Resource) -> Self {
30+
self.resource = resource
31+
return self
32+
}
33+
public func with(spanLimits: SpanLimits) -> Self {
34+
self.spanLimits = spanLimits
35+
return self
36+
}
37+
38+
public func with(sampler: Sampler) -> Self {
39+
self.sampler = sampler
40+
return self
41+
}
42+
43+
public func add(spanProcessor: SpanProcessor) -> Self {
44+
spanProcessors.append(spanProcessor)
45+
return self
46+
}
47+
48+
public func add(spanProcessors: [SpanProcessor]) -> Self {
49+
self.spanProcessors.append(contentsOf: spanProcessors)
50+
return self
51+
}
52+
53+
public func build() -> TracerProviderSdk {
54+
return TracerProviderSdk(clock: clock, idGenerator: idGenerator, resource: resource, spanLimits: spanLimits, sampler: sampler, spanProcessors: spanProcessors)
55+
}
56+
}

Tests/ExportersTests/OpenTelemetryProtocol/OtlpMetricExporterTests.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class OtlpMetricExproterTests: XCTestCase {
2828

2929
func testExport() {
3030
let metric = generateSumMetric()
31-
let exporter = OtelpMetricExporter(channel: channel)
31+
let exporter = OtlpMetricExporter(channel: channel)
3232
let result = exporter.export(metrics: [metric]) { () -> Bool in
3333
false
3434
}
@@ -39,7 +39,7 @@ class OtlpMetricExproterTests: XCTestCase {
3939

4040
func testGaugeExport() {
4141
let metric = generateGaugeMetric()
42-
let exporter = OtelpMetricExporter(channel: channel)
42+
let exporter = OtlpMetricExporter(channel: channel)
4343

4444
let result = exporter.export(metrics: [metric]) { () -> Bool in
4545
false
@@ -62,7 +62,7 @@ class OtlpMetricExproterTests: XCTestCase {
6262
for _ in 0 ..< 10 {
6363
metrics.append(generateSumMetric())
6464
}
65-
let exporter = OtelpMetricExporter(channel: channel)
65+
let exporter = OtlpMetricExporter(channel: channel)
6666
let result = exporter.export(metrics: metrics) { () -> Bool in
6767
false
6868
}
@@ -73,7 +73,7 @@ class OtlpMetricExproterTests: XCTestCase {
7373

7474
func testExportAfterShutdown() {
7575
let metric = generateSumMetric()
76-
let exporter = OtelpMetricExporter(channel: channel)
76+
let exporter = OtlpMetricExporter(channel: channel)
7777
exporter.shutdown()
7878
let result = exporter.export(metrics: [metric]) { () -> Bool in
7979
false
@@ -83,7 +83,7 @@ class OtlpMetricExproterTests: XCTestCase {
8383

8484
func testExportCancelled() {
8585
fakeCollector.returnedStatus = GRPCStatus(code: .cancelled, message: nil)
86-
let exporter = OtelpMetricExporter(channel: channel)
86+
let exporter = OtlpMetricExporter(channel: channel)
8787
let metric = generateSumMetric()
8888
let result = exporter.export(metrics: [metric]) { () -> Bool in
8989
false

0 commit comments

Comments
 (0)