Skip to content

Commit 2ba43ce

Browse files
author
Ignacio Bonafonte
committed
Merge branch 'main' into Context-handling-rework
2 parents de24f6f + 8da92e7 commit 2ba43ce

File tree

18 files changed

+646
-77
lines changed

18 files changed

+646
-77
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2020, OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
16+
import Foundation
17+
18+
/**
19+
* Implementation of the Jaeger propagation protocol. See
20+
* https://www.jaegertracing.io/docs/client-libraries/#propagation-format
21+
*/
22+
23+
public class JaegerBaggagePropagator: TextMapBaggagePropagator {
24+
public static let baggageHeader = "jaeger-baggage"
25+
public static let baggagePrefix = "uberctx-"
26+
27+
public var fields: Set<String> = [baggageHeader]
28+
29+
public init() {}
30+
31+
public func inject<S>(baggage: Baggage, carrier: inout [String: String], setter: S) where S: Setter {
32+
baggage.getEntries().forEach {
33+
setter.set(carrier: &carrier, key: JaegerBaggagePropagator.baggagePrefix + $0.key.name, value: $0.value.string)
34+
}
35+
}
36+
37+
public func extract<G>(carrier: [String: String], getter: G) -> Baggage? where G: Getter {
38+
let builder = OpenTelemetry.instance.baggageManager.baggageBuilder()
39+
40+
carrier.forEach {
41+
if $0.key.hasPrefix(JaegerBaggagePropagator.baggagePrefix) {
42+
if $0.key.count == JaegerBaggagePropagator.baggagePrefix.count {
43+
return
44+
}
45+
46+
if let key = EntryKey(name: String($0.key.dropFirst(JaegerBaggagePropagator.baggagePrefix.count))),
47+
let value = EntryValue(string: $0.value)
48+
{
49+
builder.put(key: key, value: value, metadata: nil)
50+
}
51+
} else if $0.key == JaegerBaggagePropagator.baggageHeader {
52+
$0.value.split(separator: ",").forEach { entry in
53+
let keyValue = entry.split(separator: "=")
54+
if keyValue.count != 2 {
55+
return
56+
}
57+
if let key = EntryKey(name: String(keyValue[0])),
58+
let value = EntryValue(string: String(keyValue[1]))
59+
{
60+
builder.put(key: key, value: value, metadata: nil)
61+
}
62+
}
63+
}
64+
}
65+
66+
return builder.build()
67+
}
68+
}

Sources/OpenTelemetryApi/Trace/Propagation/B3Propagator.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ import Foundation
2020
* https://github.com/openzipkin/b3-propagation
2121
*/
2222
public class B3Propagator: TextMapPropagator {
23-
public static let traceIdHeader = "X-B3-TraceId"
24-
public static let spanIdHeader = "X-B3-SpanId"
25-
public static let sampledHeader = "X-B3-Sampled"
26-
public static let trueInt = "1"
27-
public static let falseInt = "0"
28-
public static let combinedHeader = "b3"
29-
public static let combinedHeaderDelimiter = "-"
23+
static let traceIdHeader = "X-B3-TraceId"
24+
static let spanIdHeader = "X-B3-SpanId"
25+
static let sampledHeader = "X-B3-Sampled"
26+
static let trueInt = "1"
27+
static let falseInt = "0"
28+
static let combinedHeader = "b3"
29+
static let combinedHeaderDelimiter = "-"
3030

3131
public let fields: Set<String> = [traceIdHeader, spanIdHeader, sampledHeader]
3232

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Copyright 2020, OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
16+
import Foundation
17+
18+
/**
19+
* Implementation of the Jaeger propagation protocol. See
20+
* https://www.jaegertracing.io/docs/client-libraries/#propagation-format
21+
*/
22+
23+
public class JaegerPropagator: TextMapPropagator {
24+
static let propagationHeader = "uber-trace-id"
25+
// Parent span has been deprecated but Jaeger propagation protocol requires it
26+
static let deprecatedParentSpan = "0"
27+
static let propagationHeaderDelimiter: Character = ":"
28+
29+
private static let maxTraceIdLength = 2 * TraceId.size
30+
private static let maxSpanIdLength = 2 * SpanId.size
31+
private static let maxFlagsLength = 2
32+
33+
private static let isSampledChar = "1"
34+
private static let notSampledChar = "0"
35+
36+
private static let sampledFlags = TraceFlags().settingIsSampled(true)
37+
private static let notSampledFlags = TraceFlags().settingIsSampled(false)
38+
39+
public var fields: Set<String> = [propagationHeader]
40+
41+
public init() {}
42+
43+
public func inject<S>(spanContext: SpanContext, carrier: inout [String: String], setter: S) where S: Setter {
44+
guard spanContext.traceId.isValid, spanContext.spanId.isValid else {
45+
return
46+
}
47+
var propagation = ""
48+
propagation += spanContext.traceId.hexString
49+
propagation += String(JaegerPropagator.propagationHeaderDelimiter)
50+
propagation += spanContext.spanId.hexString
51+
propagation += String(JaegerPropagator.propagationHeaderDelimiter)
52+
propagation += JaegerPropagator.deprecatedParentSpan
53+
propagation += String(JaegerPropagator.propagationHeaderDelimiter)
54+
propagation += spanContext.isSampled ? JaegerPropagator.isSampledChar : JaegerPropagator.notSampledChar
55+
setter.set(carrier: &carrier, key: JaegerPropagator.propagationHeader, value: propagation)
56+
}
57+
58+
public func extract<G>(carrier: [String: String], getter: G) -> SpanContext? where G: Getter {
59+
guard let headerValue = getter.get(carrier: carrier, key: JaegerPropagator.propagationHeader), headerValue.count >= 1 else {
60+
return nil
61+
}
62+
63+
var header = headerValue[0]
64+
if header.lastIndex(of: JaegerPropagator.propagationHeaderDelimiter) == nil {
65+
guard let decodedHeader = header.removingPercentEncoding,
66+
let _ = decodedHeader.lastIndex(of: JaegerPropagator.propagationHeaderDelimiter)
67+
else {
68+
return nil
69+
}
70+
header = decodedHeader
71+
}
72+
73+
let parts = header.split(separator: JaegerPropagator.propagationHeaderDelimiter)
74+
guard parts.count == 4 else {
75+
return nil
76+
}
77+
78+
let traceId = String(parts[0])
79+
if !isTraceIdValid(traceId) {
80+
return nil
81+
}
82+
83+
let spanId = String(parts[1])
84+
if !isSpanIdValid(spanId) {
85+
return nil
86+
}
87+
88+
let flags = String(parts[3])
89+
if !isFlagValid(flags) {
90+
return nil
91+
}
92+
93+
return buildSpanContext(traceId: traceId, spanId: spanId, flags: flags)
94+
}
95+
96+
private func buildSpanContext(traceId: String, spanId: String, flags: String) -> SpanContext? {
97+
let flagsInt = Int(flags) ?? 0
98+
let traceFlags = ((flagsInt & 1) == 1) ? JaegerPropagator.sampledFlags : JaegerPropagator.notSampledFlags
99+
let context = SpanContext.createFromRemoteParent(traceId: TraceId(fromHexString: traceId),
100+
spanId: SpanId(fromHexString: spanId),
101+
traceFlags: traceFlags,
102+
traceState: TraceState())
103+
return context.isValid ? context : nil
104+
}
105+
106+
private func isTraceIdValid(_ traceId: String) -> Bool {
107+
return !(traceId.isEmpty || traceId.count > JaegerPropagator.maxTraceIdLength)
108+
}
109+
110+
private func isSpanIdValid(_ spanId: String) -> Bool {
111+
return !(spanId.isEmpty || spanId.count > JaegerPropagator.maxSpanIdLength)
112+
}
113+
114+
private func isFlagValid(_ flags: String) -> Bool {
115+
return !(flags.isEmpty || flags.count > JaegerPropagator.maxFlagsLength)
116+
}
117+
}

Sources/OpenTelemetrySdk/Resources/EnvVarResource.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public struct EnvVarResource {
2424

2525
/// This resource information is loaded from the OC_RESOURCE_LABELS
2626
/// environment variable.
27-
public static let resource = Resource(attributes: parseResourceAttributes(rawEnvAttributes: ProcessInfo.processInfo.environment[otelResourceAttributesEnv]))
27+
public static let resource = Resource(attributes: parseResourceAttributes(rawEnvAttributes: ProcessInfo.processInfo.environment[otelResourceAttributesEnv])).merging(other: Resource())
2828

2929
private init() {}
3030

Sources/OpenTelemetrySdk/Trace/NoopSpanProcessor.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,17 @@
1616
import Foundation
1717
import OpenTelemetryApi
1818

19-
public struct NoopSpanProcessor: SpanProcessor {
20-
public init() {}
19+
struct NoopSpanProcessor: SpanProcessor {
20+
init() {}
2121

22-
public let isStartRequired = false
23-
public let isEndRequired = false
22+
let isStartRequired = false
23+
let isEndRequired = false
2424

25-
public func onStart(parentContext: SpanContext?, span: ReadableSpan) {}
25+
func onStart(parentContext: SpanContext?, span: ReadableSpan) {}
2626

27-
public func onEnd(span: ReadableSpan) {}
27+
func onEnd(span: ReadableSpan) {}
2828

29-
public func shutdown() {}
29+
func shutdown() {}
3030

31-
public func forceFlush() {}
31+
func forceFlush() {}
3232
}

Sources/OpenTelemetrySdk/Trace/TracerProviderSdk.swift

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,35 @@ import OpenTelemetryApi
2020
public class TracerProviderSdk: TracerProvider {
2121
private var tracerProvider = [InstrumentationLibraryInfo: TracerSdk]()
2222
internal var sharedState: TracerSharedState
23+
internal static let emptyName = "unknown"
2324

2425
/// Returns a new TracerProviderSdk with default Clock, IdGenerator and Resource.
2526
public init(clock: Clock = MillisClock(),
2627
idGenerator: IdGenerator = RandomIdGenerator(),
27-
resource: Resource = EnvVarResource.resource)
28+
resource: Resource = EnvVarResource.resource,
29+
spanLimits: SpanLimits = SpanLimits(),
30+
sampler: Sampler = Samplers.parentBased(root: Samplers.alwaysOn),
31+
spanProcessors: [SpanProcessor] = [])
2832
{
29-
sharedState = TracerSharedState(clock: clock, idGenerator: idGenerator, resource: resource)
33+
sharedState = TracerSharedState(clock: clock,
34+
idGenerator: idGenerator,
35+
resource: resource,
36+
spanLimits: spanLimits,
37+
sampler: sampler,
38+
spanProcessors: spanProcessors)
3039
}
3140

3241
public func get(instrumentationName: String, instrumentationVersion: String? = nil) -> Tracer {
3342
if sharedState.hasBeenShutdown {
3443
return DefaultTracer.instance
3544
}
45+
46+
var instrumentationName = instrumentationName
47+
if instrumentationName.isEmpty {
48+
// Per the spec, empty is "invalid"
49+
print("Tracer requested without instrumentation name.")
50+
instrumentationName = TracerProviderSdk.emptyName
51+
}
3652
let instrumentationLibraryInfo = InstrumentationLibraryInfo(name: instrumentationName, version: instrumentationVersion ?? "")
3753
if let tracer = tracerProvider[instrumentationLibraryInfo] {
3854
return tracer

Sources/OpenTelemetrySdk/Trace/TracerSharedState.swift

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,45 @@ class TracerSharedState {
2121
var idGenerator: IdGenerator
2222
var resource: Resource
2323

24-
var sampler: Sampler = ParentBasedSampler(root: Samplers.alwaysOn)
25-
var activeSpanLimits = SpanLimits()
26-
var activeSpanProcessor: SpanProcessor = NoopSpanProcessor()
24+
var activeSpanLimits: SpanLimits
25+
var sampler: Sampler
26+
var activeSpanProcessor: SpanProcessor
2727
var hasBeenShutdown = false
2828

2929
var registeredSpanProcessors = [SpanProcessor]()
3030

31-
init(clock: Clock, idGenerator: IdGenerator, resource: Resource) {
31+
init(clock: Clock,
32+
idGenerator: IdGenerator,
33+
resource: Resource,
34+
spanLimits: SpanLimits,
35+
sampler: Sampler,
36+
spanProcessors: [SpanProcessor])
37+
{
3238
self.clock = clock
3339
self.idGenerator = idGenerator
3440
self.resource = resource
41+
self.activeSpanLimits = spanLimits
42+
self.sampler = sampler
43+
if spanProcessors.count > 1 {
44+
self.activeSpanProcessor = MultiSpanProcessor(spanProcessors: spanProcessors)
45+
registeredSpanProcessors = spanProcessors
46+
} else if spanProcessors.count == 1 {
47+
self.activeSpanProcessor = spanProcessors[0]
48+
registeredSpanProcessors = spanProcessors
49+
} else {
50+
activeSpanProcessor = NoopSpanProcessor()
51+
}
3552
}
3653

3754
/// Adds a new SpanProcessor
3855
/// - Parameter spanProcessor: the new SpanProcessor to be added.
3956
func addSpanProcessor(_ spanProcessor: SpanProcessor) {
4057
registeredSpanProcessors.append(spanProcessor)
41-
activeSpanProcessor = MultiSpanProcessor(spanProcessors: registeredSpanProcessors)
58+
if registeredSpanProcessors.count > 1 {
59+
activeSpanProcessor = MultiSpanProcessor(spanProcessors: registeredSpanProcessors)
60+
} else {
61+
activeSpanProcessor = registeredSpanProcessors[0]
62+
}
4263
}
4364

4465
/// Stops tracing, including shutting down processors and set to true isStopped.

0 commit comments

Comments
 (0)