Skip to content

Commit a916fb9

Browse files
committed
Move TracingInstrument & related types to separate library
Unit-test MultiplexInstrument instance casting Adjust naming of InstrumentationSystem getters Document InstrumentationSystem getters 📖
1 parent f20b0b2 commit a916fb9

File tree

14 files changed

+93
-25
lines changed

14 files changed

+93
-25
lines changed

Package.resolved

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ let package = Package(
66
products: [
77
.library(name: "BaggageLogging", targets: ["BaggageLogging"]),
88
.library(name: "Instrumentation", targets: ["Instrumentation"]),
9+
.library(name: "TracingInstrumentation", targets: ["TracingInstrumentation"]),
910
.library(name: "NIOInstrumentation", targets: ["NIOInstrumentation"])
1011
],
1112
dependencies: [
@@ -52,6 +53,22 @@ let package = Package(
5253
]
5354
),
5455

56+
.target(
57+
name: "TracingInstrumentation",
58+
dependencies: [
59+
.product(name: "Baggage", package: "swift-baggage-context"),
60+
"Instrumentation"
61+
]
62+
),
63+
.testTarget(
64+
name: "TracingInstrumentationTests",
65+
dependencies: [
66+
"Instrumentation",
67+
"TracingInstrumentation",
68+
"BaggageLogging"
69+
]
70+
),
71+
5572
.target(
5673
name: "NIOInstrumentation",
5774
dependencies: [

Sources/Instrumentation/InstrumentationSystem.swift

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,24 @@
1414
import Baggage
1515

1616
/// `InstrumentationSystem` is a global facility where the default cross-cutting tool can be configured.
17-
/// It is set up just once in a given program select the desired `Instrument` implementation.
17+
/// It is set up just once in a given program to select the desired `Instrument` implementation.
1818
///
19-
/// - Note: If you need to use more that one cross-cutting tool you can do so by using `MultiplexInstrument`.
19+
/// # Bootstrap multiple Instruments
20+
/// If you need to use more that one cross-cutting tool you can do so by using `MultiplexInstrument`.
21+
///
22+
/// # Access the Instrument
23+
/// There are two ways of getting the bootstrapped instrument.
24+
/// 1. `InstrumentationSystem.instrument`: Returns whatever you passed to `.bootstrap` as an `Instrument`.
25+
/// 2. `InstrumentationSystem.instrument(of: MyInstrument.self)`: Returns the bootstrapped `Instrument` if it's
26+
/// an instance of the given type or the first instance of `MyInstrument` if it's part of a `MultiplexInstrument`.
27+
///
28+
/// ## What getter to use
29+
/// - Default to using `InstrumentationSystem.instrument`
30+
/// - Use `InstrumentationSystem.instrument(of: MyInstrument.self)` only if you need to use specific `MyInstrument` APIs
31+
/// - Protocols that "inherit" from `Instrument` may also wrap `.instrument(of: TheirInstrument.self)` in a convenience method
2032
public enum InstrumentationSystem {
2133
private static let lock = ReadWriteLock()
2234
private static var _instrument: Instrument = NoOpInstrument()
23-
private static var _tracer: TracingInstrument?
2435
private static var isInitialized = false
2536

2637
/// Globally select the desired `Instrument` implementation.
@@ -35,21 +46,14 @@ public enum InstrumentationSystem {
3546
you need to use multiple instruments.
3647
"""
3748
)
38-
if let tracer = instrument as? TracingInstrument {
39-
self._tracer = tracer
40-
}
4149
self._instrument = instrument
42-
4350
self.isInitialized = true
4451
}
4552
}
4653

4754
// for our testing we want to allow multiple bootstrapping
4855
internal static func bootstrapInternal(_ instrument: Instrument) {
4956
self.lock.withWriterLock {
50-
if let tracer = instrument as? TracingInstrument {
51-
self._tracer = tracer
52-
}
5357
self._instrument = instrument
5458
}
5559
}
@@ -58,13 +62,20 @@ public enum InstrumentationSystem {
5862
public static var instrument: Instrument {
5963
self.lock.withReaderLock { self._instrument }
6064
}
65+
}
6166

62-
// FIXME: smarter impl
63-
public static var tracer: TracingInstrument {
67+
extension InstrumentationSystem {
68+
/// Get an `Instrument` instance of the given type.
69+
///
70+
/// When using `MultiplexInstrument`, this returns the first instance of the given type stored in the `MultiplexInstrument`.
71+
/// - Parameter instrumentType: The type of `Instrument` you want to retrieve an instance for.
72+
/// - Returns: An `Instrument` instance of the given type or `nil` if no `Instrument` of that type has been bootstrapped.
73+
public static func instrument<I>(of instrumentType: I.Type) -> I? {
6474
self.lock.withReaderLock {
65-
let tracer: TracingInstrument? = self._tracer
66-
let res: TracingInstrument = tracer ?? NoOpTracingInstrument()
67-
return res
75+
if let multiplexInstrument = self._instrument as? MultiplexInstrument {
76+
return multiplexInstrument.firstInstance(of: I.self)
77+
}
78+
return self._instrument as? I
6879
}
6980
}
7081
}

Sources/Instrumentation/MultiplexInstrument.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ public struct MultiplexInstrument {
2727
}
2828
}
2929

30+
extension MultiplexInstrument {
31+
func firstInstance<I>(of instrument: I.Type) -> I? {
32+
self.instruments.first(where: { $0 is I }) as? I
33+
}
34+
}
35+
3036
extension MultiplexInstrument: Instrument {
3137
public func inject<Carrier, Injector>(
3238
_ baggage: BaggageContext, into carrier: inout Carrier, using injector: Injector

Sources/Instrumentation/Tracing/NoOpTracing.swift renamed to Sources/TracingInstrumentation/NoOpTracing.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
import Baggage
15+
import Instrumentation
1516

1617
/// No operation TracingInstrument, used when no tracing is required.
1718
public struct NoOpTracingInstrument: TracingInstrument {

Sources/Instrumentation/Tracing/TracingInstrument.swift renamed to Sources/TracingInstrumentation/TracingInstrument.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
import Baggage
15+
import Instrumentation
1516

1617
/// An `Instrument` with added functionality for distributed tracing. Is uses the span-based tracing model and is
1718
/// based on the OpenTracing/OpenTelemetry spec.
@@ -46,3 +47,13 @@ extension TracingInstrument {
4647
self.startSpan(named: operationName, context: context, ofKind: .internal, at: nil)
4748
}
4849
}
50+
51+
extension InstrumentationSystem {
52+
/// Returns the `TracingInstrument` bootstrapped as part of the `InstrumentationSystem`.
53+
///
54+
/// - Warning: Only call this after you bootstrapped the `InstrumentationSystem`. Calling it before is
55+
/// considered a programmer error and leads to a crash.
56+
public static var tracingInstrument: TracingInstrument {
57+
instrument(of: TracingInstrument.self)!
58+
}
59+
}

Tests/InstrumentationTests/InstrumentationSystemTests.swift

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,43 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
import Baggage
15-
import Instrumentation
15+
@testable import Instrumentation
1616
import XCTest
1717

1818
final class InstrumentationSystemTests: XCTestCase {
1919
func testItProvidesAccessToASingletonInstrument() {
2020
let tracer = FakeTracer()
21+
let instrument = FakeInstrument()
22+
let multiplexInstrument = MultiplexInstrument([tracer, instrument])
2123

22-
InstrumentationSystem.bootstrap(tracer) // FIXME, must use bootstrapInternal
23-
XCTAssertTrue(InstrumentationSystem.instrument as? FakeTracer === tracer)
24+
XCTAssertNil(InstrumentationSystem.instrument(of: FakeTracer.self))
25+
XCTAssertNil(InstrumentationSystem.instrument(of: FakeInstrument.self))
26+
27+
InstrumentationSystem.bootstrapInternal(multiplexInstrument)
28+
XCTAssert(InstrumentationSystem.instrument is MultiplexInstrument)
29+
XCTAssert(InstrumentationSystem.instrument(of: FakeTracer.self) === tracer)
30+
XCTAssert(InstrumentationSystem.instrument(of: FakeInstrument.self) === instrument)
31+
32+
InstrumentationSystem.bootstrapInternal(tracer)
33+
XCTAssertFalse(InstrumentationSystem.instrument is MultiplexInstrument)
34+
XCTAssert(InstrumentationSystem.instrument(of: FakeTracer.self) === tracer)
35+
XCTAssertNil(InstrumentationSystem.instrument(of: FakeInstrument.self))
2436
}
2537
}
2638

2739
private final class FakeTracer: Instrument {
28-
enum TraceIDKey: BaggageContextKey {
29-
typealias Value = String
30-
}
40+
func inject<Carrier, Injector>(_ baggage: BaggageContext, into carrier: inout Carrier, using injector: Injector)
41+
where
42+
Injector: InjectorProtocol,
43+
Carrier == Injector.Carrier {}
3144

32-
static let headerName = "fake-trace-id"
33-
static let defaultTraceID = UUID().uuidString
45+
func extract<Carrier, Extractor>(_ carrier: Carrier, into baggage: inout BaggageContext, using extractor: Extractor)
46+
where
47+
Extractor: ExtractorProtocol,
48+
Carrier == Extractor.Carrier {}
49+
}
3450

51+
private final class FakeInstrument: Instrument {
3552
func inject<Carrier, Injector>(_ baggage: BaggageContext, into carrier: inout Carrier, using injector: Injector)
3653
where
3754
Injector: InjectorProtocol,

Tests/InstrumentationTests/SpanTests.swift renamed to Tests/TracingInstrumentationTests/SpanTests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import Baggage
1515
import Instrumentation
16+
import TracingInstrumentation
1617
import XCTest
1718

1819
final class SpanTests: XCTestCase {

0 commit comments

Comments
 (0)