1
- # Baggage Context
1
+ # 🧳 Distributed Tracing Baggage Core
2
2
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.
7
5
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.
13
12
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.
17
20
18
21
## Installation
19
22
@@ -23,178 +26,30 @@ so that's what you'd import in your Swift files.
23
26
``` swift
24
27
dependencies: [
25
28
.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" ,
28
31
from : " 0.3.0"
29
32
)
30
33
]
31
34
```
32
35
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
+ ]
67
48
```
68
49
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
108
51
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.
198
53
199
54
200
55
## Contributing
0 commit comments