0
# Context Propagation
1
2
Context propagation enables transmission of contextual information across service boundaries using configurable text map propagators. This is essential for distributed tracing, baggage propagation, and other cross-cutting concerns in microservices architectures.
3
4
## Context Propagators
5
6
### Creating Context Propagators
7
8
Creates a ContextPropagators instance with a text map propagator.
9
10
```java { .api }
11
static ContextPropagators create(TextMapPropagator textPropagator);
12
```
13
14
**Parameters:**
15
- `textPropagator` - The text map propagator to use
16
17
**Returns:** A ContextPropagators instance
18
19
**Usage Example:**
20
```java
21
// Create with single propagator
22
TextMapPropagator traceContext = HttpTraceContext.getInstance();
23
ContextPropagators propagators = ContextPropagators.create(traceContext);
24
25
// Create with composite propagator
26
TextMapPropagator composite = TextMapPropagator.composite(
27
HttpTraceContext.getInstance(),
28
W3CBaggagePropagator.getInstance(),
29
new CustomPropagator()
30
);
31
ContextPropagators propagators = ContextPropagators.create(composite);
32
```
33
34
### No-op Context Propagators
35
36
Creates a no-op ContextPropagators that performs no injection or extraction.
37
38
```java { .api }
39
static ContextPropagators noop();
40
```
41
42
**Usage Example:**
43
```java
44
ContextPropagators noopPropagators = ContextPropagators.noop();
45
TextMapPropagator propagator = noopPropagators.getTextMapPropagator();
46
// propagator.fields() returns empty collection
47
// propagator.inject() does nothing
48
// propagator.extract() returns original context
49
```
50
51
### Get Text Map Propagator
52
53
Returns the composite text map propagator.
54
55
```java { .api }
56
TextMapPropagator getTextMapPropagator();
57
```
58
59
**Usage Example:**
60
```java
61
ContextPropagators propagators = ContextPropagators.create(somePropagator);
62
TextMapPropagator textMapPropagator = propagators.getTextMapPropagator();
63
64
// Use for injection/extraction
65
Map<String, String> headers = new HashMap<>();
66
textMapPropagator.inject(Context.current(), headers, Map::put);
67
```
68
69
## Text Map Propagation
70
71
### Composite Propagators
72
73
Creates a composite propagator that delegates to multiple propagators.
74
75
```java { .api }
76
static TextMapPropagator composite(TextMapPropagator... propagators);
77
static TextMapPropagator composite(Iterable<TextMapPropagator> propagators);
78
```
79
80
**Usage Example:**
81
```java
82
// Array version
83
TextMapPropagator composite = TextMapPropagator.composite(
84
HttpTraceContext.getInstance(),
85
W3CBaggagePropagator.getInstance(),
86
JaegerPropagator.getInstance()
87
);
88
89
// Iterable version
90
List<TextMapPropagator> propagatorList = Arrays.asList(
91
HttpTraceContext.getInstance(),
92
CustomPropagator.getInstance()
93
);
94
TextMapPropagator composite = TextMapPropagator.composite(propagatorList);
95
```
96
97
### No-op Text Map Propagator
98
99
Creates a no-op propagator that performs no operations.
100
101
```java { .api }
102
static TextMapPropagator noop();
103
```
104
105
### Get Propagation Fields
106
107
Returns the field names that will be used for propagation.
108
109
```java { .api }
110
Collection<String> fields();
111
```
112
113
**Usage Example:**
114
```java
115
TextMapPropagator propagator = TextMapPropagator.composite(
116
HttpTraceContext.getInstance(),
117
W3CBaggagePropagator.getInstance()
118
);
119
120
Collection<String> fields = propagator.fields();
121
// Might return: ["traceparent", "tracestate", "baggage"]
122
123
// Clear fields before injection if reusing carrier
124
for (String field : fields) {
125
headers.remove(field);
126
}
127
```
128
129
### Context Injection
130
131
Injects context data into a carrier for downstream consumption.
132
133
```java { .api }
134
<C> void inject(Context context, C carrier, TextMapSetter<C> setter);
135
```
136
137
**Parameters:**
138
- `context` - The context containing values to inject
139
- `carrier` - The carrier to inject into (e.g., HTTP headers)
140
- `setter` - Function to set values in the carrier
141
142
**Usage Example:**
143
```java
144
// HTTP client injection
145
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
146
Map<String, String> headers = new HashMap<>();
147
148
// Inject current context
149
TextMapPropagator propagator = propagators.getTextMapPropagator();
150
propagator.inject(Context.current(), headers, (map, key, value) -> {
151
map.put(key, value);
152
connection.setRequestProperty(key, value);
153
});
154
155
// Alternative with lambda
156
propagator.inject(Context.current(), connection, HttpURLConnection::setRequestProperty);
157
```
158
159
### Context Extraction
160
161
Extracts context data from a carrier and merges with existing context.
162
163
```java { .api }
164
<C> Context extract(Context context, C carrier, TextMapGetter<C> getter);
165
```
166
167
**Parameters:**
168
- `context` - The base context to merge extracted data into
169
- `carrier` - The carrier containing propagated data
170
- `getter` - Function to retrieve values from the carrier
171
172
**Returns:** A new Context with extracted data merged in
173
174
**Usage Example:**
175
```java
176
// HTTP server extraction
177
HttpServletRequest request = getRequest();
178
179
TextMapPropagator propagator = propagators.getTextMapPropagator();
180
Context extractedContext = propagator.extract(
181
Context.current(),
182
request,
183
new TextMapGetter<HttpServletRequest>() {
184
@Override
185
public Iterable<String> keys(HttpServletRequest carrier) {
186
return Collections.list(carrier.getHeaderNames());
187
}
188
189
@Override
190
public String get(HttpServletRequest carrier, String key) {
191
return carrier.getHeader(key);
192
}
193
}
194
);
195
196
// Use extracted context
197
try (Scope scope = extractedContext.makeCurrent()) {
198
processRequest();
199
}
200
```
201
202
## Text Map Getters and Setters
203
204
### Text Map Getter
205
206
Interface for extracting values from carriers.
207
208
```java { .api }
209
interface TextMapGetter<C> {
210
Iterable<String> keys(C carrier);
211
String get(C carrier, String key);
212
default Iterator<String> getAll(C carrier, String key);
213
}
214
```
215
216
**Usage Example:**
217
```java
218
// HTTP headers getter
219
TextMapGetter<Map<String, String>> httpGetter = new TextMapGetter<Map<String, String>>() {
220
@Override
221
public Iterable<String> keys(Map<String, String> carrier) {
222
return carrier.keySet();
223
}
224
225
@Override
226
public String get(Map<String, String> carrier, String key) {
227
return carrier.get(key);
228
}
229
230
@Override
231
public Iterator<String> getAll(Map<String, String> carrier, String key) {
232
String value = carrier.get(key);
233
return value != null ? Collections.singleton(value).iterator()
234
: Collections.emptyIterator();
235
}
236
};
237
```
238
239
### Text Map Setter
240
241
Interface for setting values in carriers.
242
243
```java { .api }
244
interface TextMapSetter<C> {
245
void set(C carrier, String key, String value);
246
}
247
```
248
249
**Usage Example:**
250
```java
251
// HTTP headers setter
252
TextMapSetter<HttpURLConnection> httpSetter = (connection, key, value) -> {
253
connection.setRequestProperty(key, value);
254
};
255
256
// Map setter
257
TextMapSetter<Map<String, String>> mapSetter = Map::put;
258
```
259
260
## Complete Propagation Examples
261
262
### HTTP Client with Propagation
263
264
```java
265
public class HttpClientWithPropagation {
266
private final ContextPropagators propagators;
267
268
public HttpClientWithPropagation(ContextPropagators propagators) {
269
this.propagators = propagators;
270
}
271
272
public String makeRequest(String url) throws IOException {
273
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
274
275
// Inject current context into HTTP headers
276
TextMapPropagator propagator = propagators.getTextMapPropagator();
277
propagator.inject(Context.current(), connection, (conn, key, value) -> {
278
conn.setRequestProperty(key, value);
279
});
280
281
// Make request
282
try (InputStream is = connection.getInputStream()) {
283
return new String(is.readAllBytes(), StandardCharsets.UTF_8);
284
}
285
}
286
}
287
```
288
289
### HTTP Server with Propagation
290
291
```java
292
public class HttpServerWithPropagation {
293
private final ContextPropagators propagators;
294
295
public HttpServerWithPropagation(ContextPropagators propagators) {
296
this.propagators = propagators;
297
}
298
299
public void handleRequest(HttpServletRequest request, HttpServletResponse response) {
300
// Extract context from incoming request
301
TextMapPropagator propagator = propagators.getTextMapPropagator();
302
Context extractedContext = propagator.extract(
303
Context.current(),
304
request,
305
new HttpServletRequestGetter()
306
);
307
308
// Process request with extracted context
309
try (Scope scope = extractedContext.makeCurrent()) {
310
String result = processRequest(request);
311
response.getWriter().write(result);
312
} catch (IOException e) {
313
response.setStatus(500);
314
}
315
}
316
317
private static class HttpServletRequestGetter implements TextMapGetter<HttpServletRequest> {
318
@Override
319
public Iterable<String> keys(HttpServletRequest request) {
320
return Collections.list(request.getHeaderNames());
321
}
322
323
@Override
324
public String get(HttpServletRequest request, String key) {
325
return request.getHeader(key);
326
}
327
}
328
}
329
```
330
331
### Custom Propagator Implementation
332
333
```java
334
public class CustomBaggagePropagator implements TextMapPropagator {
335
private static final String BAGGAGE_HEADER = "custom-baggage";
336
private static final Collection<String> FIELDS = Collections.singleton(BAGGAGE_HEADER);
337
338
@Override
339
public Collection<String> fields() {
340
return FIELDS;
341
}
342
343
@Override
344
public <C> void inject(Context context, C carrier, TextMapSetter<C> setter) {
345
String baggage = context.get(CUSTOM_BAGGAGE_KEY);
346
if (baggage != null) {
347
setter.set(carrier, BAGGAGE_HEADER, baggage);
348
}
349
}
350
351
@Override
352
public <C> Context extract(Context context, C carrier, TextMapGetter<C> getter) {
353
String baggage = getter.get(carrier, BAGGAGE_HEADER);
354
if (baggage != null) {
355
return context.with(CUSTOM_BAGGAGE_KEY, baggage);
356
}
357
return context;
358
}
359
360
private static final ContextKey<String> CUSTOM_BAGGAGE_KEY =
361
ContextKey.named("custom-baggage");
362
}
363
```
364
365
## Integration Patterns
366
367
### Spring Boot Integration
368
369
```java
370
@Configuration
371
public class TracingConfiguration {
372
373
@Bean
374
public ContextPropagators contextPropagators() {
375
return ContextPropagators.create(
376
TextMapPropagator.composite(
377
HttpTraceContext.getInstance(),
378
W3CBaggagePropagator.getInstance()
379
)
380
);
381
}
382
383
@Bean
384
public RestTemplate restTemplate(ContextPropagators propagators) {
385
RestTemplate restTemplate = new RestTemplate();
386
restTemplate.getInterceptors().add(new TracingClientInterceptor(propagators));
387
return restTemplate;
388
}
389
}
390
```
391
392
### Asynchronous Propagation
393
394
```java
395
public class AsyncPropagationExample {
396
private final ContextPropagators propagators;
397
398
public CompletableFuture<String> processAsync(String data) {
399
// Capture current context for propagation
400
Context currentContext = Context.current();
401
402
return CompletableFuture.supplyAsync(() -> {
403
// Manually apply context in async operation
404
try (Scope scope = currentContext.makeCurrent()) {
405
return processWithContext(data);
406
}
407
});
408
}
409
410
public void sendMessage(String message) {
411
// Serialize context for message queue
412
Map<String, String> headers = new HashMap<>();
413
TextMapPropagator propagator = propagators.getTextMapPropagator();
414
propagator.inject(Context.current(), headers, Map::put);
415
416
// Send message with headers
417
messageQueue.send(message, headers);
418
}
419
420
public void receiveMessage(String message, Map<String, String> headers) {
421
// Deserialize context from message headers
422
TextMapPropagator propagator = propagators.getTextMapPropagator();
423
424
TextMapGetter<Map<String, String>> getter = new TextMapGetter<Map<String, String>>() {
425
@Override
426
public Iterable<String> keys(Map<String, String> carrier) {
427
return carrier.keySet();
428
}
429
430
@Override
431
public String get(Map<String, String> carrier, String key) {
432
return carrier.get(key);
433
}
434
};
435
436
Context extractedContext = propagator.extract(Context.current(), headers, getter);
437
438
try (Scope scope = extractedContext.makeCurrent()) {
439
processMessage(message);
440
}
441
}
442
}