Skip to content
This repository was archived by the owner on Apr 23, 2021. It is now read-only.

Commit 4cc3412

Browse files
slashmoktoso
andauthored
Add instructions to README 📖 (#139)
Co-authored-by: Konrad `ktoso` Malawski <[email protected]>
1 parent c757bf6 commit 4cc3412

File tree

2 files changed

+240
-4
lines changed

2 files changed

+240
-4
lines changed

README.md

Lines changed: 240 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,253 @@
11
# Swift (Server) Tracing
22

33
[![Swift 5.2](https://img.shields.io/badge/Swift-5.2-ED523F.svg?style=flat)](https://swift.org/download/)
4+
[![Swift 5.1](https://img.shields.io/badge/Swift-5.1-ED523F.svg?style=flat)](https://swift.org/download/)
5+
[![Swift 5.0](https://img.shields.io/badge/Swift-5.0-ED523F.svg?style=flat)](https://swift.org/download/)
46
[![CI](https://github.com/slashmo/gsoc-swift-tracing/workflows/CI/badge.svg)](https://github.com/slashmo/gsoc-swift-tracing/actions?query=workflow%3ACI)
57

6-
This is a WIP collection of Swift libraries enabling Tracing for your Swift (Server) systems. Check out the [GSoC project overview](https://summerofcode.withgoogle.com/projects/#6092707967008768) to learn more. For a more detailed project plan please take a look [at this Google doc](https://docs.google.com/document/d/19j9x515dR0IAwF3Zoevxoj6jvMdGpP4UuyGhmEXO_B8).
8+
This is a collection of Swift libraries enabling the instrumentation of your server side applications using tools such
9+
as tracers. Our goal is to provide a common foundation that allows you too freely choose how to instrument your system
10+
with minimal changes to your actual code.
711

8-
## Dependencies
12+
While Swift Tracing allows building all kinds of _instruments_ which can co-exist in applications transparently,
13+
it's primary use is instrumenting multi-threaded and distributed systems with Distributed Traces.
914

10-
The Swift packages contained in this repository make use of the library package `Baggage`, which can be found in a separate repository: https://github.com/slashmo/gsoc-swift-baggage-context
15+
> The tracing API is compatible with the [Open Telemetry specification](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md).
16+
17+
---
18+
19+
## Instrumentation
20+
21+
When instrumenting server applications there are typically three parties involved:
22+
23+
1. [Application developers](#application-developers-setting-up-instruments) creating server-side applications
24+
2. [Library/Framework developers](#libraryframework-developers-instrumenting-your-software) providing building blocks to create these applications
25+
3. [Instrument developers](#instrument-developers-creating-an-instrument) providing tools to collect distributed metadata about your application
26+
27+
For applications to be instrumented correctly these three parts have to play along nicely.
28+
29+
### Use-case Example
30+
31+
Let's say you build an API for a fruit store that has two services, one for ordering goods and one that checks what
32+
items are available in storage. Your frontend might make an HTTP request to the order service which then makes a
33+
subsequent request to the storage service, just to find out that your application isn't behaving not as it should. Now
34+
you could debug each service individually to figure out where the bug is located. If you'd instead had instrumentation
35+
in place you could take a look take at the bigger picture instead.
36+
37+
One specific type of instrument that might be helpful in this case is called *Tracer*. It collects pieces of metadata
38+
about your system (spans) and then links them to present the entire call from start to finish (trace). This metadata
39+
contains, depending on the piece being instrumented, e.g. the request URL or an SQL query. The following image shows
40+
one such trace presented in [Zipkin](https://zipkin.io):
41+
42+
![Screenshot of trace presented in Zipkin](images/zipkin_trace.png)
43+
44+
**TODO**: Replace with fruit store example screenshot
45+
46+
In the fruit store example, we'd want a span for both the incoming HTTP request to the order service, its outgoing
47+
request to the storage service, one for handling that request, and a span respresenting the querying of a database.
48+
Spans naturally form a parent-child relationship, and by visually analyzing a trace we can easily spot bottlenecks for
49+
performance profiling use-cases, and for failure debugging situations we can benefit from the application specific
50+
metadata that spans can carry (such as noticing that failures only occur e.g. when the username contains some illegal
51+
character or some other otherwise hard to notice situation).
52+
53+
### Context propagation
54+
55+
For instrumentation and tracing to work, certain pieces of metadata (usually in the form of identifiers), must be
56+
carried throughout the entire system–including across process and service boundaries. Because of that, it's essential
57+
for a context object to be passed around your application and the libraries/frameworks you depend on, but also carried
58+
over asynchronous boundaries like an HTTP call to another service of your app.
59+
60+
In Swift this is done by passing a **`BaggageContext`** explicitly through APIs which participate in
61+
instrumentation/tracing. It's vendored in its own library,
62+
[Baggage](https://github.com/slashmo/gsoc-swift-baggage-context).
63+
64+
> We intentionally didn't call this something like TraceContext as we aim on supporting any kind of instrument,
65+
not only tracers.
66+
67+
For each party involved we offer different libraries that all make use of `BaggageContext`.
68+
69+
---
70+
71+
## Adoption
72+
73+
The following libraries already support swift tracing or baggage context in their APIs:
74+
75+
| Library | Integrates | Status |
76+
| --- | --- | --- |
77+
| Swift NIO | `Baggage` | [PoC #1574](https://github.com/apple/swift-nio/pull/1574) |
78+
| AsyncHTTPClient | `Tracing` | [PoC #289](https://github.com/swift-server/async-http-client/pull/289) |
79+
| Swift gRPC | `Tracing` | [PoC #941](https://github.com/grpc/grpc-swift/pull/941) |
80+
81+
If you know of any other library please send in a PR to add it to the list, thank you!
82+
83+
---
84+
85+
## Application Developers: Setting up instruments
86+
87+
As an end-user building server applications you get to choose what instruments to use to instrument your system. Here's
88+
all the steps you need to take to get up and running:
89+
90+
Add a package dependency for this repository in your `Package.swift` file, and one for the specific instrument you want
91+
to use, in this case `FancyInstrument`:
92+
93+
```swift
94+
.package(url: "https://github.com/slashmo/gsoc-swift-tracing.git", .branch("main")),
95+
.package(url: "<https://repo-of-fancy-instrument.git>", from: "<4.2.0>"),
96+
```
97+
98+
To your main target, add a dependency on the `Instrumentation library` and the instrument you want to use:
99+
100+
```swift
101+
.target(name: "MyApplication", dependencies: ["Instrumentation", "FancyInstrument"]),
102+
```
103+
104+
Then, [bootstrap the instrumentation system](#Bootstrapping-the-Instrumentation-System) to use `FancyInstrument`.
105+
106+
### Passing BaggageContext
107+
108+
`BaggageContext` should always be passed around explicitly, so it's very likely for the libraries you use to expect
109+
a `BaggageContext`. Make sure to always pass along the context that's previously handed to you. E.g., when making an
110+
HTTP request using `AsyncHTTPClient` in a `NIO` handler, you can use the `ChannelHandlerContext`s `baggage` property to
111+
access the `BaggageContext`.
112+
113+
[Check out the `BaggageContext` repository](https://github.com/slashmo/gsoc-swift-baggage-context) for detailed
114+
documentation about context passing.
115+
116+
> Note that instrumentation of `AsyncHTTPClient` and the `baggage` property of `ChannelHandlerContext` are still WIP.
117+
118+
## Library/Framework developers: Instrumenting your software
119+
120+
### Extracting & injecting BaggageContext
121+
122+
When hitting boundaries like an outgoing HTTP request you call out to the [configured instrument(s)](#Bootstrapping-the-Instrumentation-System):
123+
124+
An HTTP client e.g. should inject the given `BaggageContext` into the HTTP headers of its outbound request:
125+
126+
```swift
127+
func get(url: String, context: BaggageContextCarrier) {
128+
var request = HTTPRequest(url: url)
129+
InstrumentationSystem.instrument.inject(
130+
context.baggage,
131+
into: &request.headers,
132+
using: HTTPHeadersInjector()
133+
)
134+
}
135+
```
136+
137+
On the receiving side, an HTTP server should use the following `Instrument` API to extract the HTTP headers of the given
138+
`HTTPRequest` into:
139+
140+
```swift
141+
func handler(request: HTTPRequest, context: BaggageContextCarrier) {
142+
InstrumentationSystem.instrument.extract(
143+
request.headers,
144+
into: &context.baggage,
145+
using: HTTPHeadersExtractor()
146+
)
147+
// ...
148+
}
149+
```
150+
151+
> In case your library makes use of the `NIOHTTP1.HTTPHeaders` type we already have an `HTTPHeadersInjector` &
152+
`HTTPHeadersExtractor` available as part of the `NIOInstrumentation` library.
153+
154+
For your library/framework to be able to carry `BaggageContext` across asynchronous boundaries, it's crucial that you
155+
carry the context throughout your entire call chain in order to avoid dropping metadata.
156+
157+
> For more information on `BaggageContext` & `BaggageContextCarrier` check out the
158+
[Baggage library's documentation](https://github.com/slashmo/gsoc-swift-baggage-context).
159+
160+
### Tracing your library
161+
162+
When your library/framework can benefit from tracing, you should make use of it by addentionally integrating the
163+
`Tracing` library. In order to work with the tracer
164+
[configured by the end-user](#Bootstrapping-the-Instrumentation-System), it adds a property to `InstrumentationSystem`
165+
that gives you back a `Tracer`. You can then use that tracer to start `Span`s. In an HTTP client you e.g.
166+
should start a `Span` when sending the outgoing HTTP request:
167+
168+
```swift
169+
func get(url: String, context: BaggageContextCarrier) {
170+
var request = HTTPRequest(url: url)
171+
172+
// inject the request headers into the baggage context as explained above
173+
174+
// start a span for the outgoing request
175+
let tracer = InstrumentationSystem.tracer
176+
var span = tracer.startSpan(named: "HTTP GET", context: context, ofKind: .client)
177+
178+
// set attributes on the span
179+
span.attributes.http.method = "GET"
180+
// ...
181+
182+
self.execute(request).always { _ in
183+
// set some more attributes & potentially record an error
184+
185+
// end the span
186+
span.end()
187+
}
188+
}
189+
```
190+
191+
> ⚠️ Make sure to ALWAYS end spans. Ensure that all paths taken by the code will result in ending the span.
192+
> Make sure that error cases also set the error attribute and end the span.
193+
194+
> In the above example we used the semantic `http.method` attribute that gets exposed via the
195+
`OpenTelemetryInstrumentationSupport` library.
196+
197+
## Instrument developers: Creating an instrument
198+
199+
Creating an instrument means adopting the `Instrument` protocol (or `Tracer` in case you develop a tracer).
200+
`Instrument` is part of the `Instrumentation` library & `Tracing` contains the `Tracer` protocol.
201+
202+
`Instrument` has two requirements:
203+
204+
1. A method to inject values inside a `BaggageContext` into a generic carrier (e.g. HTTP headers)
205+
2. A method to extract values from a generic carrier (e.g. HTTP headers) and store them in a `BaggageContext`
206+
207+
The two methods will be called by instrumented libraries/frameworks at asynchronous boundaries, giving you a chance to
208+
act on the provided information or to add additional information to be carried across these boundaries.
209+
210+
> Check out the [`Baggage` documentation](https://github.com/slashmo/gsoc-swift-baggage-context) for more information on
211+
how to retrieve values from the `BaggageContext` and how to set values on it.
212+
213+
### Creating a `Tracer`
214+
215+
When creating a tracer you need to create two types:
216+
217+
1. Your tracer conforming to `Tracer`
218+
2. A span class conforming to `Span`
219+
220+
> The `Span` conforms to the standard rules defined in [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md#span), so if unsure about usage patterns, you can refer to this specification and examples referring to it.
221+
222+
---
223+
224+
## Bootstrapping the Instrumentation System
225+
226+
Instead of providing each instrumented library with a specific instrument explicitly, you *bootstrap* the
227+
`InstrumentationSystem` which acts as a singleton that libraries/frameworks access when calling out to the configured
228+
`Instrument`:
229+
230+
```swift
231+
InstrumentationSystem.bootstrap(FancyInstrument())
232+
```
233+
234+
### Bootstrapping multiple instruments using MultiplexInstrument
235+
236+
It is important to note that `InstrumentationSystem.bootstrap(_: Instrument)` must only be called once. In case you
237+
want to bootstrap the system to use multiple instruments, you group them in a `MultiplexInstrument` first, which you
238+
then pass along to the `bootstrap` method like this:
239+
240+
```swift
241+
InstrumentationSystem.bootstrap(MultiplexInstrument([FancyInstrument(), OtherFancyInstrument()]))
242+
```
243+
244+
`MultiplexInstrument` will then call out to each instrument it has been initialized with.
11245

12246
## Discussions
13247

14-
Discussions about this topic are **more than welcome**. During this project we'll use a mixture of [GitHub issues](https://github.com/slashmo/gsoc-swift-tracing/issues) and [Swift forum posts](https://forums.swift.org/c/server/serverdev/14).
248+
Discussions about this topic are **more than welcome**. During this project we'll use a mixture of
249+
[GitHub issues](https://github.com/slashmo/gsoc-swift-tracing/issues)
250+
and [Swift forum posts](https://forums.swift.org/c/server/serverdev/14).
15251

16252
## Contributing
17253

images/zipkin_trace.png

64 KB
Loading

0 commit comments

Comments
 (0)