forked from apple/swift-distributed-tracing
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSpanProtocol.swift
741 lines (645 loc) · 27.7 KB
/
SpanProtocol.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Distributed Tracing open source project
//
// Copyright (c) 2020-2023 Apple Inc. and the Swift Distributed Tracing project
// authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@_exported import ServiceContextModule
/// A `Span` represents an interval from the start of an operation to its end, along with additional metadata included
/// with it.
///
/// A `Span` can be created from a `ServiceContext` or `LoggingContext` which MAY contain existing span identifiers,
/// in which case this span should be considered as "child" of the previous span.
///
/// Spans are created by invoking the `withSpan`` method which delegates to the currently configured bootstrapped
/// tracer.By default the task-local "current" `ServiceContext` is used to perform this association.
///
/// ### Reference semantics
/// A `Span` always exhibits reference semantics. Even if a span were to be implemented using a struct (or enum),
/// it must exhibit reference semantics. In other words, passing around a `Span` object and mutating it from multiple
/// places must mutate the same underlying storage, and must do so safely (i.e. take locks or use other synchronization
/// mechanisms to protect the mutations). This is because conceptually a span is not a value, and refers to a specific
/// resource that must be started, accumulate all possible information from the span's duration, and ended exactly once.
///
/// - SeeAlso: For more details refer to the [OpenTelemetry Specification: Span](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/api.md#span) which this type is compatible with.
public protocol Span: Sendable {
/// The read-only `ServiceContext` of this `Span`, set when starting this `Span`.
var context: ServiceContext { get }
/// Returns the name of the operation this span represents.
///
/// The name may be changed during the lifetime of a `Span`, this change
/// may or may not impact the sampling decision and actually emitting the span,
/// depending on how a backend decides to treat renames.
///
/// This can still be useful when, for example, we want to immediately start
/// span when receiving a request but make it more precise as handling of the request proceeds.
/// For example, we can start a span immediately when a request is received in a server,
/// and update it to reflect the matched route, if it did match one:
///
/// - 1) Start span with basic path (e.g. `operationName = request.head.uri` during `withSpan`)
/// - 2.1) "Route Not Found" -> Record error
/// - 2.2) "Route Found" -> Rename to route (`/users/1` becomes `/users/:userID`)
/// - 3) End span
var operationName: String {
get
nonmutating set
}
/// Set the status of the span.
///
/// - Parameter status: The status of this `Span`.
func setStatus(_ status: SpanStatus)
/// Add a ``SpanEvent`` to this span.
///
/// Span events are similar to log statements in logging systems, in the sense
/// that they can carry a name of some event that has happened as well as an associated timestamp.
///
/// Events can be used to complement a span with "interesting" point-in-time information.
/// For example if code executing a span has been cancelled it might be useful interesting
/// to emit an event representing this task cancellation, which then (presumably) leads
/// to an quicker termination of the task.
///
/// - Parameter event: The ``SpanEvent`` to add to this `Span`.
func addEvent(_ event: SpanEvent)
/// Record an error of the given type described by the the given message.
///
/// - Parameters:
/// - error: The error to be recorded
/// - attributes: Additional attributes describing the error
/// - instant: the time instant at which the event occurred
func recordError<Instant: TracerInstant>(
_ error: Error,
attributes: SpanAttributes,
at instant: @autoclosure () -> Instant
)
/// The attributes describing this `Span`.
var attributes: SpanAttributes {
get
nonmutating set
}
/// Returns true if this `Span` is recording information like events, attributes, status, etc.
var isRecording: Bool { get }
/// Add a ``SpanLink`` in place.
///
/// - Parameter link: The `SpanLink` to add to this `Span`.
func addLink(_ link: SpanLink)
/// End this `Span` at the given time.
///
/// ### Rules about ending Spans
/// A Span must be ended only ONCE. Ending a Span multiple times or never at all is considered a programming error.
///
/// A tracer implementation MAY decide to crash the application if e.g. running in debug mode and noticing such misuse.
///
/// Implementations SHOULD prevent double-emitting by marking a span as ended internally, however it still is a
/// programming mistake to rely on this behavior.
///
/// Parameters:
/// - instant: the time instant at which the span ended
///
/// - SeeAlso: `Span.end()` which automatically uses the "current" time.
func end<Instant: TracerInstant>(at instant: @autoclosure () -> Instant)
}
extension Span {
/// Record an error of the given type, at the current time, described by the the given message
///
/// - Parameters:
/// - error: The error to be recorded
/// - attributes: Additional attributes describing the error
public func recordError(_ error: Error, attributes: SpanAttributes) {
self.recordError(error, attributes: attributes, at: DefaultTracerClock.now)
}
/// End this `Span` at the current time.
///
/// ### Rules about ending Spans
/// A Span must be ended only ONCE. Ending a Span multiple times or never at all is considered a programming error.
///
/// A tracer implementation MAY decide to crash the application if e.g. running in debug mode and noticing such misuse.
///
/// Implementations SHOULD prevent double-emitting by marking a span as ended internally, however it still is a
/// programming mistake to rely on this behavior.
///
/// - SeeAlso: ``end(at:)`` which allows passing in a specific time, e.g. if the operation was ended and recorded somewhere and we need to post-factum record it.
/// Generally though prefer using the ``end()`` version of this API in user code and structure your system such that it can be called in the right place and time.
public func end() {
self.end(at: DefaultTracerClock.now)
}
/// Adds a ``SpanLink`` between this `Span` and the given `Span`.
///
/// - Parameter other: The `Span` to link to.
/// - Parameter attributes: The ``SpanAttributes`` describing this link. Defaults to no attributes.
public func addLink(_ other: Self, attributes: SpanAttributes = [:]) {
self.addLink(SpanLink(context: other.context, attributes: attributes))
}
}
extension Span {
/// Record a failure described by the given error.
///
/// - Parameters:
/// - error: The error to be recorded.
public func recordError(_ error: Error) {
self.recordError(error, attributes: [:])
}
}
extension Span {
/// Update Span attributes in a block instead of individually.
///
/// Updating a span attribute will involve some type of thread synchronisation
/// primitive to avoid multiple threads updating the attributes at the same
/// time. If you update each attributes individually this can cause slowdown.
/// This function updates the attributes in one call to avoid hitting the
/// thread synchronisation code multiple times.
///
/// - Parameter update: closure used to update span attributes
public func updateAttributes(_ update: (inout SpanAttributes) -> Void) {
var attributes = self.attributes
update(&attributes)
self.attributes = attributes
}
}
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: Span Event
/// An event that occurred during a ``Span``.
public struct SpanEvent: Equatable {
/// The human-readable name of this `SpanEvent`.
public let name: String
/// One or more ``SpanAttribute``s with the same restrictions as defined for ``Span`` attributes.
public var attributes: SpanAttributes
/// The timestamp at which this event occurred.
///
/// It should be expressed as the number of nanoseconds since UNIX Epoch (January 1st 1970).
public let nanosecondsSinceEpoch: UInt64
/// Representation of the timestamp this event occurred as the number of milliseconds since UNIX Epoch (January 1st 1970).
public var millisecondsSinceEpoch: UInt64 {
self.nanosecondsSinceEpoch / 1_000_000
}
/// Create a new `SpanEvent`.
/// - Parameters:
/// - name: The human-readable name of this event.
/// - attributes: attributes describing this event. Defaults to no attributes.
/// - instant: the time instant at which the event occurred
public init<Instant: TracerInstant>(
name: String,
at instant: @autoclosure () -> Instant,
attributes: SpanAttributes = [:]
) {
self.name = name
self.attributes = attributes
self.nanosecondsSinceEpoch = instant().nanosecondsSinceEpoch
}
public init(
name: String,
attributes: SpanAttributes = [:]
) {
self.name = name
self.attributes = attributes
self.nanosecondsSinceEpoch = DefaultTracerClock.now.nanosecondsSinceEpoch
}
}
extension SpanEvent: ExpressibleByStringLiteral {
public init(stringLiteral name: String) {
self.init(name: name)
}
}
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: Span Attribute
public struct SpanAttributeKey<T>: Hashable, ExpressibleByStringLiteral where T: SpanAttributeConvertible {
public let name: String
public init(name: String) {
self.name = name
}
public init(stringLiteral value: StringLiteralType) {
self.name = value
}
}
@dynamicMemberLookup
public protocol SpanAttributeNamespace {
/// Type that contains the nested attributes, e.g. HTTPAttributes which would contain `statusCode` and similar vars.
associatedtype NestedSpanAttributes: NestedSpanAttributesProtocol
var attributes: SpanAttributes { get set }
subscript<T>(dynamicMember dynamicMember: KeyPath<NestedSpanAttributes, SpanAttributeKey<T>>) -> T? where T: SpanAttributeConvertible {
get
set
}
subscript<Namespace>(dynamicMember dynamicMember: KeyPath<SpanAttribute, Namespace>) -> Namespace
where Namespace: SpanAttributeNamespace
{
get
}
}
public protocol NestedSpanAttributesProtocol {
init()
/// INTERNAL API
/// Magic value allowing the dynamicMember lookup inside SpanAttributeNamespaces to work.
///
/// There is no need of ever implementing this function explicitly, the default implementation is good enough.
static var __namespace: Self { get }
}
extension NestedSpanAttributesProtocol {
public static var __namespace: Self {
.init()
}
}
extension NestedSpanAttributesProtocol {
public typealias Key = SpanAttributeKey
}
extension SpanAttributeNamespace {
public subscript<T>(dynamicMember dynamicMember: KeyPath<NestedSpanAttributes, SpanAttributeKey<T>>) -> T?
where T: SpanAttributeConvertible
{
get {
let key = NestedSpanAttributes.__namespace[keyPath: dynamicMember]
let spanAttribute = self.attributes[key.name]?.toSpanAttribute()
switch spanAttribute {
case .int32(let value):
if T.self == Int.self {
return (Int(exactly: value) as! T)
} else {
return value as? T
}
case .int64(let value):
if T.self == Int.self {
return (Int(exactly: value) as! T)
} else {
return value as? T
}
default:
if let value = spanAttribute?.anyValue {
return value as? T
} else {
return nil
}
}
}
set {
let key = NestedSpanAttributes.__namespace[keyPath: dynamicMember]
self.attributes[key.name] = newValue?.toSpanAttribute()
}
}
public subscript<Namespace>(dynamicMember dynamicMember: KeyPath<SpanAttribute, Namespace>) -> Namespace
where Namespace: SpanAttributeNamespace
{
SpanAttribute.int(0)[keyPath: dynamicMember]
}
}
/// The value of an attribute used to describe a ``Span`` or ``SpanEvent``.
///
/// Arrays are allowed but are enforced to be homogenous.
///
/// Attributes cannot be "nested" their structure is a flat key/value representation.
public enum SpanAttribute: Equatable {
public typealias Key = SpanAttributeKey
case int32(Int32)
case int64(Int64)
case int32Array([Int32])
case int64Array([Int64])
case double(Double)
case doubleArray([Double])
case bool(Bool)
case boolArray([Bool])
case string(String)
case stringArray([String])
case __DO_NOT_SWITCH_EXHAUSTIVELY_OVER_THIS_ENUM_USE_DEFAULT_INSTEAD
case stringConvertible(CustomStringConvertible & Sendable)
case stringConvertibleArray([CustomStringConvertible & Sendable])
public static func int(_ value: Int64) -> SpanAttribute {
.int64(value)
}
/// This is a "magic value" that is used to enable the KeyPath based accessors to specific attributes.
internal static var _namespace: SpanAttribute {
.int(0)
}
internal var anyValue: Any {
switch self {
case .int32(let value):
return value
case .int64(let value):
return value
case .int32Array(let value):
return value
case .int64Array(let value):
return value
case .double(let value):
return value
case .doubleArray(let value):
return value
case .bool(let value):
return value
case .boolArray(let value):
return value
case .string(let value):
return value
case .stringArray(let value):
return value
case .stringConvertible(let value):
return value
case .stringConvertibleArray(let value):
return value
case .__DO_NOT_SWITCH_EXHAUSTIVELY_OVER_THIS_ENUM_USE_DEFAULT_INSTEAD:
fatalError("Cannot have values of __DO_NOT_SWITCH_EXHAUSTIVELY_OVER_THIS_ENUM")
}
}
public static func == (lhs: SpanAttribute, rhs: SpanAttribute) -> Bool {
switch (lhs, rhs) {
case (.int32(let l), .int32(let r)): return l == r
case (.int64(let l), .int64(let r)): return l == r
case (.int32Array(let l), .int32Array(let r)): return l == r
case (.int64Array(let l), .int64Array(let r)): return l == r
case (.double(let l), .double(let r)): return l == r
case (.doubleArray(let l), .doubleArray(let r)): return l == r
case (.bool(let l), .bool(let r)): return l == r
case (.boolArray(let l), .boolArray(let r)): return l == r
case (.string(let l), .string(let r)): return l == r
case (.stringArray(let l), .stringArray(let r)): return l == r
case (.stringConvertible(let l), .stringConvertible(let r)): return "\(l)" == "\(r)"
case (.stringConvertibleArray(let l), .stringConvertibleArray(let r)): return "\(l)" == "\(r)"
default:
return false
}
}
}
extension SpanAttribute: SpanAttributeConvertible {
public func toSpanAttribute() -> SpanAttribute {
self
}
}
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: Attribute Values
public protocol SpanAttributeConvertible {
func toSpanAttribute() -> SpanAttribute
}
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: Attribute Values: Arrays
extension Array where Element == Int {
public func toSpanAttribute() -> SpanAttribute {
if MemoryLayout<Int>.stride == 8 {
return .int64Array(self.map(Int64.init))
} else if MemoryLayout<Int>.stride == 4 {
return .int32Array(self.map(Int32.init))
} else {
fatalError("Not supported Int width: \(MemoryLayout<Int>.stride)")
}
}
}
extension Array where Element == Int64 {
public func toSpanAttribute() -> SpanAttribute {
.int64Array(self)
}
}
extension Array where Element == Double {
public func toSpanAttribute() -> SpanAttribute {
.doubleArray(self)
}
}
// fallback implementation
extension Array: SpanAttributeConvertible where Element: SpanAttributeConvertible {
public func toSpanAttribute() -> SpanAttribute {
if let value = self as? [Int32] {
return .int32Array(value)
} else if let value = self as? [Int64] {
return .int64Array(value)
} else if let value = self as? [Double] {
return .doubleArray(value)
} else if let value = self as? [Bool] {
return .boolArray(value)
} else if let value = self as? [String] {
return .stringArray(value)
} else if let value = self as? [CustomStringConvertible & Sendable] {
return .stringConvertibleArray(value)
}
fatalError("Not supported SpanAttribute array type: \(type(of: self))")
}
}
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: Attribute Values: String
extension String: SpanAttributeConvertible {
public func toSpanAttribute() -> SpanAttribute {
.string(self)
}
}
extension SpanAttribute: ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
self = .string(value)
}
}
extension SpanAttribute: ExpressibleByStringInterpolation {
public init(stringInterpolation value: SpanAttribute.StringInterpolation) {
self = .string("\(value)")
}
}
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: Attribute Values: Int
extension SpanAttribute: ExpressibleByIntegerLiteral {
public init(integerLiteral value: Int) {
self = .int(Int64(value))
}
}
extension Int: SpanAttributeConvertible {
public func toSpanAttribute() -> SpanAttribute {
if MemoryLayout<Int>.stride == 8 {
return .int64(Int64(self))
} else if MemoryLayout<Int>.stride == 4 {
return .int32(Int32(exactly: self)!)
} else {
fatalError("Not supported int width: \(MemoryLayout<Int>.stride)")
}
}
}
extension Int64: SpanAttributeConvertible {
public func toSpanAttribute() -> SpanAttribute {
.int64(self)
}
}
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: Attribute Values: Float/Double
extension Float: SpanAttributeConvertible {
public func toSpanAttribute() -> SpanAttribute {
.double(Double(self))
}
}
extension Double: SpanAttributeConvertible {
public func toSpanAttribute() -> SpanAttribute {
.double(self)
}
}
extension SpanAttribute: ExpressibleByFloatLiteral {
public init(floatLiteral value: Double) {
self = .double(value)
}
}
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: Attribute Values: Bool
extension Bool: SpanAttributeConvertible {
public func toSpanAttribute() -> SpanAttribute {
.bool(self)
}
}
extension SpanAttribute: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = .bool(value)
}
}
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: SpanAttributes: Namespaces
/// A container of ``SpanAttribute``s.
@dynamicMemberLookup
public struct SpanAttributes: Equatable {
private var _attributes = [String: SpanAttribute]()
}
extension SpanAttributes {
/// Create a set of attributes by wrapping the given dictionary.
///
/// - Parameter attributes: The attributes dictionary to wrap.
public init(_ attributes: [String: SpanAttribute]) {
self._attributes = attributes
}
/// Accesses the ``SpanAttribute`` with the given name for reading and writing.
///
/// Please be cautious to not abuse this APIs power to read attributes to "smuggle" values between calls.
/// Only `ServiceContext` is intended to carry information in a readable fashion between functions / processes / nodes.
/// The API does allow reading in order to support the subscript-based dynamic member lookup implementation of
/// attributes which allows accessing them as `span.attributes.http.statusCode`, forcing us to expose a get operation on the attributes,
/// due to the lack of set-only subscripts.
///
/// - Parameter name: The name of the attribute used to identify the attribute.
/// - Returns: The `SpanAttribute` identified by the given name, or `nil` if it's not present.
public subscript(_ name: String) -> SpanAttributeConvertible? {
get {
self._attributes[name]
}
set {
self._attributes[name] = newValue?.toSpanAttribute()
}
}
/// Similar to `subscript(_:)` however returns the stored `SpanAttribute` rather than going through `SpanAttributeConvertible`.
public func get(_ name: String) -> SpanAttribute? {
self._attributes[name]
}
/// Similar to `subscript(_:)` however accepts a `SpanAttribute` rather than going through `SpanAttributeConvertible`.
public mutating func set(_ name: String, value: SpanAttribute?) {
self._attributes[name] = value
}
/// - Parameter callback: The function to call for each attribute.
public func forEach(_ callback: (String, SpanAttribute) -> Void) {
self._attributes.forEach {
callback($0.key, $0.1)
}
}
/// Merges the given `SpanAttributes` into these `SpanAttributes` by overwriting values of duplicate keys with those of the
/// `other` attributes.
///
/// - Parameter other: `SpanAttributes` to merge.
public mutating func merge(_ other: SpanAttributes) {
self._attributes.merge(other._attributes, uniquingKeysWith: { _, rhs in rhs })
}
/// - Returns: Number of attributes stored.
public var count: Int {
self._attributes.count
}
/// Returns true if the collection contains no attributes.
public var isEmpty: Bool {
self._attributes.isEmpty
}
}
extension SpanAttributes {
/// Enables for type-safe fluent accessors for attributes.
public subscript<T>(dynamicMember dynamicMember: KeyPath<SpanAttribute, SpanAttributeKey<T>>) -> SpanAttribute? {
get {
let key = SpanAttribute._namespace[keyPath: dynamicMember]
return self._attributes[key.name]
}
set {
let key = SpanAttribute._namespace[keyPath: dynamicMember]
self._attributes[key.name] = newValue
}
}
/// Enables for type-safe nested namespaces for attribute accessors.
public subscript<Namespace>(dynamicMember dynamicMember: KeyPath<SpanAttribute, Namespace>) -> Namespace
where Namespace: SpanAttributeNamespace
{
SpanAttribute._namespace[keyPath: dynamicMember]
}
}
extension SpanAttributes: ExpressibleByDictionaryLiteral {
public init(dictionaryLiteral elements: (String, SpanAttribute)...) {
self._attributes = [String: SpanAttribute](uniqueKeysWithValues: elements)
}
}
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: Span Status
/// Represents the status of a finished Span.
///
/// It's composed of a status code in conjunction with an optional descriptive message.
public struct SpanStatus: Equatable {
public let code: Code
public let message: String?
/// Create a new `SpanStatus`.
///
/// - Parameters:
/// - code: The ``SpanStatus/Code-swift.enum`` of this `SpanStatus`.
/// - message: The optional descriptive message of this `SpanStatus`. Defaults to nil.
public init(code: Code, message: String? = nil) {
self.code = code
self.message = message
}
/// A code representing the status of a ``Span``.
///
/// - SeeAlso: For the semantics of status codes see [OpenTelemetry Specification: setStatus](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/api.md#set-status)
public enum Code {
/// The Span has been validated by an Application developer or Operator to have completed successfully.
case ok
/// The Span contains an error.
case error
}
}
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: Span Kind
/// Describes the relationship between the ``Span``, its parents, and its children in a Trace.
public enum SpanKind {
/// Indicates that the span covers server-side handling of a synchronous RPC or other remote request.
/// This span is the child of a remote `.client` span that was expected to wait for a response.
case server
/// Indicates that the span describes a synchronous request to some remote service.
/// This span is the parent of a remote `.server` span and waits for its response.
case client
/// Indicates that the span describes the parent of an asynchronous request. This parent span is expected to end before the corresponding child
/// `.consumer` span, possibly even before the child span starts. In messaging scenarios with batching,
/// tracing individual messages requires a new `.producer` span per message to be created.
case producer
/// Indicates that the span describes the child of an asynchronous `.producer` request.
case consumer
/// Indicates that the span represents an internal operation within an application, as opposed to an operations with remote parents or children.
case `internal`
}
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: Span Link
/// A link to another ``Span``.
/// The other ``Span``s information is stored in `context` and `attributes` may be used to
/// further describe the link.
public struct SpanLink {
/// A `ServiceContext` containing identifying information about the link target ``Span``.
public let context: ServiceContext
/// ``SpanAttributes`` further describing the connection between the ``Span``s.
public let attributes: SpanAttributes
/// Create a new `SpanLink`.
///
/// - Parameters:
/// - context: The `ServiceContext` identifying the targeted ``Span``.
/// - attributes: ``SpanAttributes`` that further describe the link. Defaults to no attributes.
public init(context: ServiceContext, attributes: SpanAttributes) {
self.context = context
self.attributes = attributes
}
}
extension SpanAttributes: Sendable {}
extension SpanAttribute: Sendable {} // @unchecked because some payloads are CustomStringConvertible
extension SpanStatus: Sendable {}
extension SpanEvent: Sendable {}
extension SpanKind: Sendable {}
extension SpanStatus.Code: Sendable {}
extension SpanLink: Sendable {}