OpenTelemetry Context propagation mechanism for carrying scoped values across API boundaries and between threads in Java applications
—
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.
Creates a ContextPropagators instance with a text map propagator.
static ContextPropagators create(TextMapPropagator textPropagator);Parameters:
textPropagator - The text map propagator to useReturns: A ContextPropagators instance
Usage Example:
// Create with single propagator
TextMapPropagator traceContext = HttpTraceContext.getInstance();
ContextPropagators propagators = ContextPropagators.create(traceContext);
// Create with composite propagator
TextMapPropagator composite = TextMapPropagator.composite(
HttpTraceContext.getInstance(),
W3CBaggagePropagator.getInstance(),
new CustomPropagator()
);
ContextPropagators propagators = ContextPropagators.create(composite);Creates a no-op ContextPropagators that performs no injection or extraction.
static ContextPropagators noop();Usage Example:
ContextPropagators noopPropagators = ContextPropagators.noop();
TextMapPropagator propagator = noopPropagators.getTextMapPropagator();
// propagator.fields() returns empty collection
// propagator.inject() does nothing
// propagator.extract() returns original contextReturns the composite text map propagator.
TextMapPropagator getTextMapPropagator();Usage Example:
ContextPropagators propagators = ContextPropagators.create(somePropagator);
TextMapPropagator textMapPropagator = propagators.getTextMapPropagator();
// Use for injection/extraction
Map<String, String> headers = new HashMap<>();
textMapPropagator.inject(Context.current(), headers, Map::put);Creates a composite propagator that delegates to multiple propagators.
static TextMapPropagator composite(TextMapPropagator... propagators);
static TextMapPropagator composite(Iterable<TextMapPropagator> propagators);Usage Example:
// Array version
TextMapPropagator composite = TextMapPropagator.composite(
HttpTraceContext.getInstance(),
W3CBaggagePropagator.getInstance(),
JaegerPropagator.getInstance()
);
// Iterable version
List<TextMapPropagator> propagatorList = Arrays.asList(
HttpTraceContext.getInstance(),
CustomPropagator.getInstance()
);
TextMapPropagator composite = TextMapPropagator.composite(propagatorList);Creates a no-op propagator that performs no operations.
static TextMapPropagator noop();Returns the field names that will be used for propagation.
Collection<String> fields();Usage Example:
TextMapPropagator propagator = TextMapPropagator.composite(
HttpTraceContext.getInstance(),
W3CBaggagePropagator.getInstance()
);
Collection<String> fields = propagator.fields();
// Might return: ["traceparent", "tracestate", "baggage"]
// Clear fields before injection if reusing carrier
for (String field : fields) {
headers.remove(field);
}Injects context data into a carrier for downstream consumption.
<C> void inject(Context context, C carrier, TextMapSetter<C> setter);Parameters:
context - The context containing values to injectcarrier - The carrier to inject into (e.g., HTTP headers)setter - Function to set values in the carrierUsage Example:
// HTTP client injection
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
Map<String, String> headers = new HashMap<>();
// Inject current context
TextMapPropagator propagator = propagators.getTextMapPropagator();
propagator.inject(Context.current(), headers, (map, key, value) -> {
map.put(key, value);
connection.setRequestProperty(key, value);
});
// Alternative with lambda
propagator.inject(Context.current(), connection, HttpURLConnection::setRequestProperty);Extracts context data from a carrier and merges with existing context.
<C> Context extract(Context context, C carrier, TextMapGetter<C> getter);Parameters:
context - The base context to merge extracted data intocarrier - The carrier containing propagated datagetter - Function to retrieve values from the carrierReturns: A new Context with extracted data merged in
Usage Example:
// HTTP server extraction
HttpServletRequest request = getRequest();
TextMapPropagator propagator = propagators.getTextMapPropagator();
Context extractedContext = propagator.extract(
Context.current(),
request,
new TextMapGetter<HttpServletRequest>() {
@Override
public Iterable<String> keys(HttpServletRequest carrier) {
return Collections.list(carrier.getHeaderNames());
}
@Override
public String get(HttpServletRequest carrier, String key) {
return carrier.getHeader(key);
}
}
);
// Use extracted context
try (Scope scope = extractedContext.makeCurrent()) {
processRequest();
}Interface for extracting values from carriers.
interface TextMapGetter<C> {
Iterable<String> keys(C carrier);
String get(C carrier, String key);
default Iterator<String> getAll(C carrier, String key);
}Usage Example:
// HTTP headers getter
TextMapGetter<Map<String, String>> httpGetter = new TextMapGetter<Map<String, String>>() {
@Override
public Iterable<String> keys(Map<String, String> carrier) {
return carrier.keySet();
}
@Override
public String get(Map<String, String> carrier, String key) {
return carrier.get(key);
}
@Override
public Iterator<String> getAll(Map<String, String> carrier, String key) {
String value = carrier.get(key);
return value != null ? Collections.singleton(value).iterator()
: Collections.emptyIterator();
}
};Interface for setting values in carriers.
interface TextMapSetter<C> {
void set(C carrier, String key, String value);
}Usage Example:
// HTTP headers setter
TextMapSetter<HttpURLConnection> httpSetter = (connection, key, value) -> {
connection.setRequestProperty(key, value);
};
// Map setter
TextMapSetter<Map<String, String>> mapSetter = Map::put;public class HttpClientWithPropagation {
private final ContextPropagators propagators;
public HttpClientWithPropagation(ContextPropagators propagators) {
this.propagators = propagators;
}
public String makeRequest(String url) throws IOException {
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
// Inject current context into HTTP headers
TextMapPropagator propagator = propagators.getTextMapPropagator();
propagator.inject(Context.current(), connection, (conn, key, value) -> {
conn.setRequestProperty(key, value);
});
// Make request
try (InputStream is = connection.getInputStream()) {
return new String(is.readAllBytes(), StandardCharsets.UTF_8);
}
}
}public class HttpServerWithPropagation {
private final ContextPropagators propagators;
public HttpServerWithPropagation(ContextPropagators propagators) {
this.propagators = propagators;
}
public void handleRequest(HttpServletRequest request, HttpServletResponse response) {
// Extract context from incoming request
TextMapPropagator propagator = propagators.getTextMapPropagator();
Context extractedContext = propagator.extract(
Context.current(),
request,
new HttpServletRequestGetter()
);
// Process request with extracted context
try (Scope scope = extractedContext.makeCurrent()) {
String result = processRequest(request);
response.getWriter().write(result);
} catch (IOException e) {
response.setStatus(500);
}
}
private static class HttpServletRequestGetter implements TextMapGetter<HttpServletRequest> {
@Override
public Iterable<String> keys(HttpServletRequest request) {
return Collections.list(request.getHeaderNames());
}
@Override
public String get(HttpServletRequest request, String key) {
return request.getHeader(key);
}
}
}public class CustomBaggagePropagator implements TextMapPropagator {
private static final String BAGGAGE_HEADER = "custom-baggage";
private static final Collection<String> FIELDS = Collections.singleton(BAGGAGE_HEADER);
@Override
public Collection<String> fields() {
return FIELDS;
}
@Override
public <C> void inject(Context context, C carrier, TextMapSetter<C> setter) {
String baggage = context.get(CUSTOM_BAGGAGE_KEY);
if (baggage != null) {
setter.set(carrier, BAGGAGE_HEADER, baggage);
}
}
@Override
public <C> Context extract(Context context, C carrier, TextMapGetter<C> getter) {
String baggage = getter.get(carrier, BAGGAGE_HEADER);
if (baggage != null) {
return context.with(CUSTOM_BAGGAGE_KEY, baggage);
}
return context;
}
private static final ContextKey<String> CUSTOM_BAGGAGE_KEY =
ContextKey.named("custom-baggage");
}@Configuration
public class TracingConfiguration {
@Bean
public ContextPropagators contextPropagators() {
return ContextPropagators.create(
TextMapPropagator.composite(
HttpTraceContext.getInstance(),
W3CBaggagePropagator.getInstance()
)
);
}
@Bean
public RestTemplate restTemplate(ContextPropagators propagators) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add(new TracingClientInterceptor(propagators));
return restTemplate;
}
}public class AsyncPropagationExample {
private final ContextPropagators propagators;
public CompletableFuture<String> processAsync(String data) {
// Capture current context for propagation
Context currentContext = Context.current();
return CompletableFuture.supplyAsync(() -> {
// Manually apply context in async operation
try (Scope scope = currentContext.makeCurrent()) {
return processWithContext(data);
}
});
}
public void sendMessage(String message) {
// Serialize context for message queue
Map<String, String> headers = new HashMap<>();
TextMapPropagator propagator = propagators.getTextMapPropagator();
propagator.inject(Context.current(), headers, Map::put);
// Send message with headers
messageQueue.send(message, headers);
}
public void receiveMessage(String message, Map<String, String> headers) {
// Deserialize context from message headers
TextMapPropagator propagator = propagators.getTextMapPropagator();
TextMapGetter<Map<String, String>> getter = new TextMapGetter<Map<String, String>>() {
@Override
public Iterable<String> keys(Map<String, String> carrier) {
return carrier.keySet();
}
@Override
public String get(Map<String, String> carrier, String key) {
return carrier.get(key);
}
};
Context extractedContext = propagator.extract(Context.current(), headers, getter);
try (Scope scope = extractedContext.makeCurrent()) {
processMessage(message);
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-opentelemetry--opentelemetry-context