You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: Sources/Tracing/Docs.docc/Guides/ImplementATracer.md
+60-36Lines changed: 60 additions & 36 deletions
Original file line number
Diff line number
Diff line change
@@ -25,7 +25,7 @@ In order to implement an instrument you need to implement the `Instrument` proto
25
25
The two methods will be called by instrumented libraries/frameworks at asynchronous boundaries, giving you a chance to
26
26
act on the provided information or to add additional information to be carried across these boundaries.
27
27
28
-
> The [`ServiceContext` documentation](https://github.com/apple/swift-service-context) type is declared in the swift-service-context package.
28
+
> The [`ServiceContext`](https://swiftpackageindex.com/apple/swift-service-context/documentation/servicecontextmodule) type is declared in the swift-service-context package.
29
29
30
30
### Creating a `Tracer`
31
31
@@ -36,7 +36,7 @@ When creating a tracer you will need to implement two types:
36
36
37
37
> ``Span`` largely resembles span concept as defined [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/api.md#span), however this package does not enforce certain rules specified in there - that is left up to a specific open telemetry tracer implementation.
38
38
39
-
### Defining, injecting and extracting ServiceContext
39
+
### Defining, injecting and extracting `ServiceContext`
40
40
41
41
In order to be able to extract and inject values into the `ServiceContext` which is the value that is "carried around" across asynchronous contexts,
42
42
we need to declare a `ServiceContextKey`. ServiceContext generally acts as a type-safe dictionary and declaring the keys this way allows us to perform lookups
For your library/framework to be able to carry `ServiceContext` across asynchronous boundaries, it's crucial that you carry the context throughout your entire call chain in order to avoid dropping metadata.
120
120
121
-
##Creating an instrument
121
+
### Starting and ending spans
122
122
123
-
Creating an instrument means adopting the `Instrument` protocol (or `Tracer` in case you develop a tracer).
124
-
`Instrument` is part of the `Instrumentation` library & `Tracing` contains the `Tracer` protocol.
123
+
The primary goal and user-facing API of a ``Tracer`` is to create spans.
125
124
126
-
`Instrument` has two requirements:
125
+
While you will need to implement all methods of the tracer protocol, the most important one is `startSpan`:
127
126
128
-
1. A method to inject values inside a `ServiceContext` into a generic carrier (e.g. HTTP headers)
129
-
2. A method to extract values from a generic carrier (e.g. HTTP headers) and store them in a `ServiceContext`
127
+
```swift
128
+
#ifswift(>=5.7.0)
129
+
extensionMyTracer: Tracer {
130
+
funcstartSpan<Instant: TracerInstant>(
131
+
_operationName: String,
132
+
context: @autoclosure () -> ServiceContext,
133
+
ofKindkind: SpanKind,
134
+
atinstant: @autoclosure () -> Instant,
135
+
function: String,
136
+
filefileID: String,
137
+
line: UInt
138
+
) -> MySpan {
139
+
let span =MySpan(
140
+
operationName: operationName,
141
+
startTime: instant(),
142
+
context: context(),
143
+
kind: kind,
144
+
onEnd: self.onEndSpan
145
+
)
146
+
self.spans.append(span)
147
+
return span
148
+
}
149
+
}
150
+
#endif
151
+
```
130
152
131
-
The two methods will be called by instrumented libraries/frameworks at asynchronous boundaries, giving you a chance to
132
-
act on the provided information or to add additional information to be carried across these boundaries.
153
+
If you can require Swift 5.7 prefer doing so, and return the concrete ``Span`` type from the `startSpan` method.
154
+
This allows users who decide to use your tracer explicitly, and not via the global bootstrapped system to avoid
155
+
wrapping tracers in existentials which can be beneficial in some situations.
133
156
134
-
> Check out the [`ServiceContext` documentation](https://github.com/apple/swift-service-context) for more information on
135
-
how to retrieve values from the `ServiceContext` and how to set values on it.
157
+
Next, eventually the user started span will be ended and the `Span/end()` method will be invoked:
136
158
137
-
### Creating a `Tracer`
159
+
```swift
160
+
publicstructMySpan: Tracing.Span {
161
+
// ... implement all protocol requirements of Span ...
138
162
139
-
When creating a tracer you need to create two types:
It is possible to implement a span as a struct or a class, but a ``Span``**must exhibit reference type behavior**.
171
+
In other words, adding an attribute to one reference of a span must be visible in other references to it.
143
172
144
-
> The `Span` conforms to the standard rules defined in [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/api.md#span), so if unsure about usage patterns, you can refer to this specification and examples referring to it.
173
+
The ability to implement a span using a struct comes in handy when implementing a "Noop" (do nothing) span and avoids heap allocations. Normal spans though generally will be backed by `class` based storage and should flush themselfes to the owning tracer once the span has been ended.
145
174
146
-
###Defining, injecting and extracting ServiceContext
175
+
#### Detecting not-ended spans
147
176
148
-
```swift
149
-
importTracing
177
+
It is a programmer error to NOT `end()` a span (be it by using `withSpan` or calling `startSpan` paired with `Span.end()`),
178
+
and a tracer MAY choose to detect and error or fatal error in such situations.
150
179
151
-
privateenumTraceIDKey: ServiceContextKey {
152
-
typealiasValue=String
153
-
}
180
+
It is possible to detect when a span has not been properly ended if the span's storage `deinit` is executed,
181
+
however `end()` has not been called on it explicitly, like this:
154
182
155
-
extensionServiceContext {
156
-
var traceID: String? {
157
-
get {
158
-
returnself[TraceIDKey.self]
159
-
}
160
-
set {
161
-
self[TraceIDKey.self] = newValue
183
+
```swift
184
+
structMySpan: Tracing.Span {
185
+
finalclass_Storage {
186
+
var endInstant: Instant?=nil
187
+
188
+
deinit {
189
+
precondition(endInstant !=nil, "Span [...] was not properly ended before it was deinitialized!")
190
+
}
162
191
}
163
-
}
164
192
}
165
-
166
-
var context = ServiceContext.topLevel(logger: ...)
0 commit comments