Skip to content
This repository was archived by the owner on Jun 6, 2023. It is now read-only.

Commit 127cd43

Browse files
committed
introducing "core" module
1 parent ac91f39 commit 127cd43

30 files changed

+394
-3106
lines changed

Package.swift

+7-45
Original file line numberDiff line numberDiff line change
@@ -2,70 +2,32 @@
22
import PackageDescription
33

44
let package = Package(
5-
name: "swift-baggage-context",
5+
name: "swift-distributed-tracing-baggage-core",
66
products: [
77
.library(
8-
name: "Baggage",
8+
name: "CoreBaggage",
99
targets: [
10-
"Baggage",
11-
]
12-
),
13-
.library(
14-
name: "BaggageLogging",
15-
targets: [
16-
"BaggageLogging",
10+
"CoreBaggage",
1711
]
1812
),
1913
],
2014
dependencies: [
21-
.package(url: "https://github.com/apple/swift-log.git", from: "1.3.0"),
15+
// no dependencies
2216
],
2317
targets: [
2418
.target(
25-
name: "Baggage",
19+
name: "CoreBaggage",
2620
dependencies: []
2721
),
2822

29-
.target(
30-
name: "BaggageLogging",
31-
dependencies: [
32-
"Baggage",
33-
.product(name: "Logging", package: "swift-log"),
34-
]
35-
),
36-
3723
// ==== --------------------------------------------------------------------------------------------------------
3824
// MARK: Tests
3925

4026
.testTarget(
41-
name: "BaggageTests",
42-
dependencies: [
43-
"Baggage",
44-
]
45-
),
46-
47-
.testTarget(
48-
name: "BaggageLoggingTests",
49-
dependencies: [
50-
"Baggage",
51-
"BaggageLogging",
52-
]
53-
),
54-
55-
// ==== --------------------------------------------------------------------------------------------------------
56-
// MARK: Performance / Benchmarks
57-
58-
.target(
59-
name: "BaggageBenchmarks",
27+
name: "CoreBaggageTests",
6028
dependencies: [
61-
"Baggage",
62-
"BaggageLogging",
63-
"BaggageBenchmarkTools",
29+
"CoreBaggage",
6430
]
6531
),
66-
.target(
67-
name: "BaggageBenchmarkTools",
68-
dependencies: []
69-
),
7032
]
7133
)

README.md

+32-177
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
1-
# Baggage Context
1+
# 🧳 Distributed Tracing Baggage Core
22

3-
[![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/)
6-
[![CI](https://github.com/slashmo/gsoc-swift-baggage-context/workflows/CI/badge.svg)](https://github.com/slashmo/gsoc-swift-baggage-context/actions?query=workflow%3ACI)
3+
`Baggage` is a minimal (zero-dependency) context propagation container, intended to "carry" baggage items
4+
for purposes of cross-cutting tools to be built on top of it.
75

8-
`BaggageContext` is a minimal (zero-dependency) "context" library meant to "carry" baggage (metadata) for cross-cutting
9-
tools such as tracers. It is purposefully not tied to any specific use-case (in the spirit of the
10-
[Tracing Plane paper](https://cs.brown.edu/~jcmace/papers/mace18universal.pdf)'s BaggageContext). However, it should
11-
enable a vast majority of use cases cross-cutting tools need to support. Unlike mentioned in the paper, our
12-
`BaggageContext` does not implement its own serialization scheme (today).
6+
It is modeled after the concepts explained in [W3C Baggage](https://w3c.github.io/baggage/) and the
7+
in the spirit of [Tracing Plane](https://cs.brown.edu/~jcmace/papers/mace18universal.pdf) baggage context type,
8+
although by itself it does not define a specific serialization format.
9+
10+
Please refer to [Swift Distributed Tracing Baggage](https://github.com/apple/swift-distributed-tracing-baggage)
11+
and [Swift Distributed Tracing](https://github.com/apple/swift-distributed-tracing) for usage guides of this type.
1312

14-
See https://github.com/slashmo/gsoc-swift-tracing for actual instrument types and implementations which can be used to
15-
deploy various cross-cutting instruments all reusing the same baggage type. More information can be found in the
16-
[SSWG meeting notes](https://gist.github.com/ktoso/4d160232407e4d5835b5ba700c73de37#swift-baggage-context--distributed-tracing).
13+
14+
## Dependencies
15+
16+
It should be noted that most libraries and frameworks do NOT need to depend on this package explicitly,
17+
but rather should depend on [Swift Distributed Tracing Baggage](https://github.com/apple/swift-distributed-tracing-baggage) or [Swift Distributed Tracing](https://github.com/apple/swift-distributed-tracing) which will pull the
18+
`CoreBaggage` via their transitive dependencies. This package and the `CoreBaggage` module exist only for
19+
libraries which want to maintain an absolutely minimal dependency footprint, and e.g. do not want to depend on `Logging` modules, which the higher level packages may do.
1720

1821
## Installation
1922

@@ -23,178 +26,30 @@ so that's what you'd import in your Swift files.
2326
```swift
2427
dependencies: [
2528
.package(
26-
name: "swift-baggage-context",
27-
url: "https://github.com/slashmo/gsoc-swift-baggage-context.git",
29+
name: "swift-baggage-context-core",
30+
url: "https://github.com/apple/swift-distributed-tracing-baggage-core.git",
2831
from: "0.3.0"
2932
)
3033
]
3134
```
3235

33-
## Usage
34-
35-
`BaggageContext` is intended to be used in conjunction with the instrumentation of distributed systems. To make this
36-
instrumentation work, all parties involved operate on the same `BaggageContext` type. These are the three common
37-
parties, in no specific order, and guidance on how to use `BaggageContext`:
38-
39-
### End Users - explicit context passing
40-
41-
You'll likely interact with some API that takes a context. In most cases you already have a context at hand so you
42-
should pass that along. If you're certain you don't have a context at hand, pass along an empty one after thinking about
43-
why that's the case.
44-
45-
**TODO**: Document the reasoning behind `.background` & `.TODO` once merged ([#26](#26))
46-
47-
While this might seem like a burden to take on, this will allow you to immediately add instrumentation (e.g. tracing)
48-
once your application grows. Let's say your profiling some troublesome performance regressions. You won't have the time
49-
to go through the entire system to start passing contexts around.
50-
51-
> TL;DR: You should always pass around `BaggageContext`, so that you're ready for when you need it.
52-
53-
Once you are ready to instrument your application, you already have everything in place to get going. Instead of each
54-
instrument operating on its own context type they'll be using the same `BaggageContext` that you're already passing
55-
around to the various instrumentable libraries & frameworks you make use of, so you're free to mix & match any
56-
compatible instrument(s) 🙌 Check out the [swift-tracing](https://github.com/slashmo/gsoc-swift-tracing) repository for
57-
instructions on how to get up & running.
58-
59-
### Library & Framework Authors - passing context and instrumenting libraries
60-
61-
Developers creating frameworks/libraries (e.g. NIO, gRPC, AsyncHTTPClient, ...) which benefit from being instrumented
62-
should adopt `BaggageContext` as part of their public API. AsyncHTTPClient for example might accept a context like this:
63-
64-
```swift
65-
let context = BaggageContext()
66-
client.get(url: "https://swift.org", context: context)
36+
and depend on the module:
37+
38+
```swift
39+
targets: [
40+
.target(
41+
name: "MyAwesomeApp",
42+
dependencies: [
43+
"CoreBaggage",
44+
]
45+
),
46+
// ...
47+
]
6748
```
6849

69-
For more information on where to place this argument and how to name it, take a look at the
70-
[Context-Passing Guidelines](#Context-Passing-Guidelines).
71-
72-
Generally speaking, frameworks and libraries should treat baggage as an _opaque container_ and simply thread it along
73-
all asynchronous boundaries a call may have to go through. Libraries and frameworks should not attempt to reuse context
74-
as a means of passing values that they need for "normal" operation.
75-
76-
At cross-cutting boundaries, e.g. right before sending an HTTP
77-
request, they inject the `BaggageContext` into the HTTP headers, allowing context propagation. On the receiving side, an
78-
HTTP server should extract the request headers into a `BaggageContext`. Injecting/extracting is part of the
79-
`swift-tracing` libraries [and documented in its own repository](https://github.com/slashmo/gsoc-swift-tracing).
80-
81-
### Instrumentation Authors - defining, injecting and extracting baggage
82-
83-
When implementing instrumentation for cross-cutting tools, `BaggageContext` becomes the way you propagate metadata such
84-
as trace ids. Because each instrument knows what values might be added to the `BaggageContext` they are the ones
85-
creating `BaggageContextKey` types dictating the type of value associated with each key added to the context. To make
86-
accessing values a bit more convenient, we encourage you to add computed properties to `BaggageContextProtocol`:
87-
88-
```swift
89-
private enum TraceIDKey: BaggageContextKey {
90-
typealias Value = String
91-
}
92-
93-
extension BaggageContextProtocol {
94-
var traceID: String? {
95-
get {
96-
return self[TraceIDKey.self]
97-
}
98-
set {
99-
self[TraceIDKey.self] = newValue
100-
}
101-
}
102-
}
103-
104-
var context = BaggageContext()
105-
context.traceID = "4bf92f3577b34da6a3ce929d0e0e4736"
106-
print(context.traceID ?? "new trace id")
107-
```
50+
## Usage
10851

109-
## Context-Passing Guidelines
110-
111-
For context-passing to feel consistent and Swifty among all server-side (and not only) libraries and frameworks
112-
aiming to adopt `BaggageContext` (or any of its uses, such as Distributed Tracing), we suggest the following set of
113-
guidelines:
114-
115-
### Argument naming/positioning
116-
117-
Propagating baggage context through your system is to be done explicitly, meaning as a parameter in function calls,
118-
following the "flow" of execution.
119-
120-
When passing baggage context explicitly we strongly suggest sticking to the following style guideline:
121-
122-
- Assuming the general parameter ordering of Swift function is as follows (except DSL exceptions):
123-
1. Required non-function parameters (e.g. `(url: String)`),
124-
2. Defaulted non-function parameters (e.g. `(mode: Mode = .default)`),
125-
3. Required function parameters, including required trailing closures (e.g. `(onNext elementHandler: (Value) -> ())`),
126-
4. Defaulted function parameters, including optional trailing closures (e.g. `(onComplete completionHandler: (Reason) -> ()) = { _ in }`).
127-
- Baggage Context should be passed as **the last parameter in the required non-function parameters group in a function declaration**.
128-
129-
This way when reading the call side, users of these APIs can learn to "ignore" or "skim over" the context parameter and
130-
the method signature remains human-readable and “Swifty”.
131-
132-
Examples:
133-
134-
- `func request(_ url: URL,` **`context: BaggageContext`** `)`, which may be called as `httpClient.request(url, context: context)`
135-
- `func handle(_ request: RequestObject,` **`context: BaggageContextCarrier`** `)`
136-
- if a "framework context" exists and _carries_ the baggage context already, it is permitted to pass that context
137-
together with the baggage;
138-
- it is _strongly recommended_ to store the baggage context as `baggage` property of `FrameworkContext` in such cases,
139-
in order to avoid the confusing spelling of `context.context`, and favoring the self-explanatory `context.baggage`
140-
spelling when the baggage is contained in a framework context object.
141-
- `func receiveMessage(_ message: Message, context: FrameworkContext)`
142-
- `func handle(element: Element,` **`context: BaggageContextCarrier`** `, settings: Settings? = nil)`
143-
- before any defaulted non-function parameters
144-
- `func handle(element: Element,` **`context: BaggageContextCarrier`** `, settings: Settings? = nil, onComplete: () -> ())`
145-
- before defaulted parameters, which themselfes are before required function parameters
146-
- `func handle(element: Element,` **`context: BaggageContextCarrier`** `, onError: (Error) -> (), onComplete: (() -> ())? = nil)`
147-
148-
In case there are _multiple_ "framework-ish" parameters, such as passing a NIO `EventLoop` or similar, we suggest:
149-
150-
- `func perform(_ work: Work, for user: User,` _`frameworkThing: Thing, eventLoop: NIO.EventLoop,`_ **`context: BaggageContext`** `)`
151-
- pass the baggage as **last** of such non-domain specific parameters as it will be _by far more_ omnipresent than any
152-
specific framework parameter - as it is expected that any framework should be accepting a context if it can do so.
153-
While not all libraries are necessarily going to be implemented using the same frameworks.
154-
155-
We feel it is important to preserve Swift's human-readable nature of function definitions. In other words, we intend to
156-
keep the read-out-loud phrasing of methods to remain _"request that URL (ignore reading out loud the context parameter)"_
157-
rather than _"request (ignore this context parameter when reading) that URL"_.
158-
159-
#### When to use what context type?
160-
161-
This library defines the following context (carrier) types:
162-
163-
- `struct BaggageContext` - which is the actual context object,
164-
- `protocol BaggageContextCarrier` - which should be used whenever a library implements an API and does not necessarily
165-
care where it gets a `context` value from
166-
- this pattern enables other frameworks to pass their `FrameworkContext`, like so:
167-
`get(context: MyFrameworkContext())` if they already have such context in scope (e.g. Vapor's `Request` object is a
168-
good example, or Lambda Runtime's `Lambda.Context`
169-
- `protocol LoggingBaggageContextCarrier` - which in addition exposes a logger bound to the passed context
170-
171-
Finally, some frameworks will have APIs which accept the specific `MyFrameworkContext`, withing frameworks specifically
172-
a lot more frequently than libraries one would hope. It is important when designing APIs to keep in mind -- can this API
173-
work with any context, or is it always going to require _my framework context_, and erring on accepting the most
174-
general type possible.
175-
176-
#### Existing context argument
177-
178-
When adapting an existing library/framework to support `BaggageContext` and it already has a "framework context" which
179-
is expected to be passed through "everywhere", we suggest to follow these guidelines for adopting BaggageContext:
180-
181-
1. Add a `BaggageContext` as a property called `baggage` to your own `context` type, so that the call side for your
182-
users becomes `context.baggage` (rather than the confusing `context.context`)
183-
2. If you cannot or it would not make sense to carry baggage inside your framework's context object,
184-
pass (and accept (!)) the `BaggageContext` in your framework functions like follows:
185-
- if they take no framework context, accept a `context: BaggageContext` which is the same guideline as for all other
186-
cases
187-
- if they already _must_ take a context object and you are out of words (or your API already accepts your framework
188-
context as "context"), pass the baggage as **last** parameter (see above) yet call the parameter `baggage` to
189-
disambiguate your `context` object from the `baggage` context object.
190-
191-
Examples:
192-
193-
- `Lamda.Context` may contain `baggage` and this way offer traceIDs and other values
194-
- passing context to a `Lambda.Context` unaware library becomes: `http.request(url: "...", context: context.baggage)`.
195-
- TODO: We are considering a protocol which would simplify this if it is known that Lambda.Context "carries" baggage...
196-
- `ChannelHandlerContext` offers a way to set/get baggage on the underlying channel via `context.baggage = ...`
197-
- WorkInProgress, see: https://github.com/apple/swift-nio/pull/1574
52+
Please refer to [Swift Distributed Tracing Baggage](https://github.com/apple/swift-distributed-tracing-baggage) for the intended usage.
19853

19954

20055
## Contributing

0 commit comments

Comments
 (0)