google-site-verification: google61fe8ba583a51912.html
Baggage API in OpenTelemetry for Java

A comprehensive implementation of the OpenTelemetry Baggage API for distributed context propagation in Java applications.

Complete Implementation

1. Core Baggage Interfaces and Classes

package io.opentelemetry.baggage; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Baggage entry representing a key-value pair with metadata */ public interface BaggageEntry { String getKey(); String getValue(); EntryMetadata getMetadata(); } /** * Metadata for baggage entries */ public interface EntryMetadata { String getValue(); } /** * Immutable baggage context propagation */ public interface Baggage { static Baggage empty() { return ImmutableBaggage.EMPTY; } static BaggageBuilder builder() { return new BaggageBuilder(); } static Baggage current() { return BaggageManager.getInstance().getCurrentBaggage(); } int size(); boolean isEmpty(); Set<String> getKeys(); String getEntryValue(String key); BaggageEntry getEntry(String key); Map<String, BaggageEntry> asMap(); BaggageBuilder toBuilder(); // Static factory methods static Baggage of(String key, String value) { return builder().put(key, value).build(); } static Baggage of(String key, String value, EntryMetadata metadata) { return builder().put(key, value, metadata).build(); } }

2. Immutable Baggage Implementation

/** * Immutable baggage implementation */ class ImmutableBaggage implements Baggage { static final ImmutableBaggage EMPTY = new ImmutableBaggage(Collections.emptyMap()); private final ConcurrentMap<String, BaggageEntry> entries; private final int size; ImmutableBaggage(Map<String, BaggageEntry> entries) { this.entries = new ConcurrentHashMap<>(entries); this.size = this.entries.size(); } @Override public int size() { return size; } @Override public boolean isEmpty() { return size == 0; } @Override public Set<String> getKeys() { return Collections.unmodifiableSet(entries.keySet()); } @Override public String getEntryValue(String key) { BaggageEntry entry = entries.get(key); return entry != null ? entry.getValue() : null; } @Override public BaggageEntry getEntry(String key) { return entries.get(key); } @Override public Map<String, BaggageEntry> asMap() { return Collections.unmodifiableMap(entries); } @Override public BaggageBuilder toBuilder() { return new BaggageBuilder(new HashMap<>(entries)); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ImmutableBaggage)) return false; ImmutableBaggage that = (ImmutableBaggage) o; return entries.equals(that.entries); } @Override public int hashCode() { return entries.hashCode(); } @Override public String toString() { return "Baggage{" + entries + '}'; } } /** * Default baggage entry implementation */ class DefaultBaggageEntry implements BaggageEntry { private final String key; private final String value; private final EntryMetadata metadata; DefaultBaggageEntry(String key, String value, EntryMetadata metadata) { this.key = Objects.requireNonNull(key, "key"); this.value = Objects.requireNonNull(value, "value"); this.metadata = Objects.requireNonNull(metadata, "metadata"); } @Override public String getKey() { return key; } @Override public String getValue() { return value; } @Override public EntryMetadata getMetadata() { return metadata; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof DefaultBaggageEntry)) return false; DefaultBaggageEntry that = (DefaultBaggageEntry) o; return key.equals(that.key) && value.equals(that.value) && metadata.equals(that.metadata); } @Override public int hashCode() { return Objects.hash(key, value, metadata); } @Override public String toString() { return key + "=" + value + " (" + metadata + ")"; } } /** * Default entry metadata implementation */ class DefaultEntryMetadata implements EntryMetadata { private final String value; DefaultEntryMetadata(String value) { this.value = Objects.requireNonNull(value, "value"); } @Override public String getValue() { return value; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof DefaultEntryMetadata)) return false; DefaultEntryMetadata that = (DefaultEntryMetadata) o; return value.equals(that.value); } @Override public int hashCode() { return value.hashCode(); } @Override public String toString() { return value; } }

3. Baggage Builder

/** * Mutable builder for creating Baggage instances */ public class BaggageBuilder { private Map<String, BaggageEntry> entries; BaggageBuilder() { this.entries = new HashMap<>(); } BaggageBuilder(Map<String, BaggageEntry> entries) { this.entries = new HashMap<>(entries); } public BaggageBuilder put(String key, String value) { return put(key, value, DefaultEntryMetadata.EMPTY); } public BaggageBuilder put(String key, String value, String metadata) { return put(key, value, new DefaultEntryMetadata(metadata)); } public BaggageBuilder put(String key, String value, EntryMetadata metadata) { entries.put(key, new DefaultBaggageEntry(key, value, metadata)); return this; } public BaggageBuilder putAll(Baggage baggage) { for (Map.Entry<String, BaggageEntry> entry : baggage.asMap().entrySet()) { entries.put(entry.getKey(), entry.getValue()); } return this; } public BaggageBuilder putAll(Map<String, String> keyValues) { for (Map.Entry<String, String> entry : keyValues.entrySet()) { put(entry.getKey(), entry.getValue()); } return this; } public BaggageBuilder remove(String key) { entries.remove(key); return this; } public BaggageBuilder clear() { entries.clear(); return this; } public Baggage build() { return new ImmutableBaggage(entries); } } // Static metadata constants class DefaultEntryMetadata { static final DefaultEntryMetadata EMPTY = new DefaultEntryMetadata(""); }

4. Baggage Manager and Context Propagation

/** * Manages baggage context propagation */ public class BaggageManager { private static final BaggageManager INSTANCE = new BaggageManager(); private final ThreadLocal<Baggage> currentBaggage = new ThreadLocal<>(); private final List<BaggagePropagator> propagators = new ArrayList<>(); private BaggageManager() { // Initialize with default propagators propagators.add(new W3CBaggagePropagator()); propagators.add(new JaegerBaggagePropagator()); currentBaggage.set(Baggage.empty()); } public static BaggageManager getInstance() { return INSTANCE; } public Baggage getCurrentBaggage() { Baggage baggage = currentBaggage.get(); return baggage != null ? baggage : Baggage.empty(); } public void setCurrentBaggage(Baggage baggage) { currentBaggage.set(baggage != null ? baggage : Baggage.empty()); } public BaggageBuilder currentBuilder() { return getCurrentBaggage().toBuilder(); } public void update(Baggage baggage) { setCurrentBaggage(baggage); } public void update(BaggageBuilder builder) { setCurrentBaggage(builder.build()); } public void put(String key, String value) { update(currentBuilder().put(key, value).build()); } public void put(String key, String value, EntryMetadata metadata) { update(currentBuilder().put(key, value, metadata).build()); } public void remove(String key) { update(currentBuilder().remove(key).build()); } public void clear() { setCurrentBaggage(Baggage.empty()); } // Context propagation methods public <C> void inject(C carrier, Baggage baggage, Setter<C> setter) { for (BaggagePropagator propagator : propagators) { propagator.inject(carrier, baggage, setter); } } public <C> Baggage extract(C carrier, Getter<C> getter) { BaggageBuilder builder = Baggage.builder(); for (BaggagePropagator propagator : propagators) { Baggage extracted = propagator.extract(carrier, getter); if (extracted != null) { builder.putAll(extracted); } } return builder.build(); } public void addPropagator(BaggagePropagator propagator) { propagators.add(propagator); } public List<BaggagePropagator> getPropagators() { return Collections.unmodifiableList(propagators); } } /** * Interface for baggage propagators */ public interface BaggagePropagator { <C> void inject(C carrier, Baggage baggage, Setter<C> setter); <C> Baggage extract(C carrier, Getter<C> getter); String getName(); } /** * Setter interface for injecting baggage into carriers */ public interface Setter<C> { void set(C carrier, String key, String value); } /** * Getter interface for extracting baggage from carriers */ public interface Getter<C> { String get(C carrier, String key); Iterable<String> keys(C carrier); }

5. Standard Propagators

/** * W3C Baggage Propagator */ public class W3CBaggagePropagator implements BaggagePropagator { private static final String BAGGAGE_HEADER = "baggage"; private static final String ITEM_SEPARATOR = ","; private static final String KEY_VALUE_SEPARATOR = "="; private static final String METADATA_SEPARATOR = ";"; @Override public <C> void inject(C carrier, Baggage baggage, Setter<C> setter) { if (baggage.isEmpty()) { return; } StringBuilder headerValue = new StringBuilder(); for (Map.Entry<String, BaggageEntry> entry : baggage.asMap().entrySet()) { if (headerValue.length() > 0) { headerValue.append(ITEM_SEPARATOR); } BaggageEntry baggageEntry = entry.getValue(); headerValue.append(urlEncode(entry.getKey())) .append(KEY_VALUE_SEPARATOR) .append(urlEncode(baggageEntry.getValue())); // Add metadata if present EntryMetadata metadata = baggageEntry.getMetadata(); if (metadata != null && !metadata.getValue().isEmpty()) { headerValue.append(METADATA_SEPARATOR).append(metadata.getValue()); } } setter.set(carrier, BAGGAGE_HEADER, headerValue.toString()); } @Override public <C> Baggage extract(C carrier, Getter<C> getter) { String headerValue = getter.get(carrier, BAGGAGE_HEADER); if (headerValue == null || headerValue.trim().isEmpty()) { return Baggage.empty(); } BaggageBuilder builder = Baggage.builder(); String[] items = headerValue.split(ITEM_SEPARATOR); for (String item : items) { String[] parts = item.split(METADATA_SEPARATOR, 2); String keyValuePart = parts[0].trim(); if (keyValuePart.isEmpty()) { continue; } String[] keyValue = keyValuePart.split(KEY_VALUE_SEPARATOR, 2); if (keyValue.length != 2) { continue; // Invalid format, skip } String key = urlDecode(keyValue[0].trim()); String value = urlDecode(keyValue[1].trim()); EntryMetadata metadata = DefaultEntryMetadata.EMPTY; if (parts.length > 1) { metadata = new DefaultEntryMetadata(parts[1].trim()); } if (isValidKey(key) && isValidValue(value)) { builder.put(key, value, metadata); } } return builder.build(); } @Override public String getName() { return "W3CBaggagePropagator"; } private String urlEncode(String value) { // Simplified URL encoding - in real implementation use proper URL encoding return value.replace("%", "%25") .replace(" ", "%20") .replace(",", "%2C") .replace(";", "%3B") .replace("=", "%3D"); } private String urlDecode(String value) { // Simplified URL decoding return value.replace("%3D", "=") .replace("%3B", ";") .replace("%2C", ",") .replace("%20", " ") .replace("%25", "%"); } private boolean isValidKey(String key) { return key != null && !key.isEmpty() && key.matches("[a-zA-Z_][a-zA-Z0-9_\\-.]*"); } private boolean isValidValue(String value) { return value != null; } } /** * Jaeger Baggage Propagator */ public class JaegerBaggagePropagator implements BaggagePropagator { private static final String JAEGER_BAGGAGE_HEADER = "jaeger-baggage"; private static final String UBER_BAGGAGE_HEADER = "uberctx-"; private static final String HEADER_PREFIX = UBER_BAGGAGE_HEADER; @Override public <C> void inject(C carrier, Baggage baggage, Setter<C> setter) { for (Map.Entry<String, BaggageEntry> entry : baggage.asMap().entrySet()) { String headerName = HEADER_PREFIX + entry.getKey(); String value = entry.getValue().getValue(); setter.set(carrier, headerName, value); } // Also set the main jaeger baggage header if (!baggage.isEmpty()) { StringBuilder jaegerBaggage = new StringBuilder(); for (Map.Entry<String, BaggageEntry> entry : baggage.asMap().entrySet()) { if (jaegerBaggage.length() > 0) { jaegerBaggage.append(","); } jaegerBaggage.append(entry.getKey()).append("=").append(entry.getValue().getValue()); } setter.set(carrier, JAEGER_BAGGAGE_HEADER, jaegerBaggage.toString()); } } @Override public <C> Baggage extract(C carrier, Getter<C> getter) { BaggageBuilder builder = Baggage.builder(); // Extract from uberctx- headers Iterable<String> keys = getter.keys(carrier); if (keys != null) { for (String key : keys) { if (key.startsWith(HEADER_PREFIX)) { String baggageKey = key.substring(HEADER_PREFIX.length()); String value = getter.get(carrier, key); if (value != null && !value.trim().isEmpty()) { builder.put(baggageKey, value.trim()); } } } } // Extract from jaeger-baggage header String jaegerBaggage = getter.get(carrier, JAEGER_BAGGAGE_HEADER); if (jaegerBaggage != null && !jaegerBaggage.trim().isEmpty()) { String[] pairs = jaegerBaggage.split(","); for (String pair : pairs) { String[] keyValue = pair.split("=", 2); if (keyValue.length == 2) { String key = keyValue[0].trim(); String value = keyValue[1].trim(); if (!key.isEmpty() && !value.isEmpty()) { builder.put(key, value); } } } } return builder.build(); } @Override public String getName() { return "JaegerBaggagePropagator"; } }

6. Baggage Scope Management

/** * Manages baggage scope for automatic cleanup */ public class BaggageScope implements AutoCloseable { private final Baggage previousBaggage; private final BaggageManager baggageManager; private boolean closed = false; private BaggageScope(Baggage newBaggage) { this.baggageManager = BaggageManager.getInstance(); this.previousBaggage = baggageManager.getCurrentBaggage(); baggageManager.setCurrentBaggage(newBaggage); } public static BaggageScope create(Baggage baggage) { return new BaggageScope(baggage); } public static BaggageScope create(BaggageBuilder builder) { return new BaggageScope(builder.build()); } public static BaggageScope currentWith(String key, String value) { return new BaggageScope( BaggageManager.getInstance().currentBuilder().put(key, value).build() ); } public Baggage getCurrentBaggage() { return BaggageManager.getInstance().getCurrentBaggage(); } @Override public void close() { if (!closed) { baggageManager.setCurrentBaggage(previousBaggage); closed = true; } } public boolean isClosed() { return closed; } } /** * Utility class for working with baggage in try-with-resources blocks */ public class BaggageContext { private BaggageContext() {} public static BaggageScope withBaggage(Baggage baggage) { return BaggageScope.create(baggage); } public static BaggageScope withBaggage(BaggageBuilder builder) { return BaggageScope.create(builder); } public static BaggageScope withBaggage(String key, String value) { return BaggageScope.currentWith(key, value); } public static BaggageScope withBaggage(Map<String, String> keyValues) { BaggageBuilder builder = BaggageManager.getInstance().currentBuilder(); for (Map.Entry<String, String> entry : keyValues.entrySet()) { builder.put(entry.getKey(), entry.getValue()); } return BaggageScope.create(builder); } public static Runnable wrap(Runnable runnable, Baggage baggage) { return () -> { try (BaggageScope scope = BaggageScope.create(baggage)) { runnable.run(); } }; } public static <T> Callable<T> wrap(Callable<T> callable, Baggage baggage) { return () -> { try (BaggageScope scope = BaggageScope.create(baggage)) { return callable.call(); } }; } }

7. HTTP Propagators and Integration

/** * HTTP-specific setter and getter implementations */ public class HttpBaggage { private HttpBaggage() {} public static final Setter<Map<String, String>> MAP_SETTER = new Setter<Map<String, String>>() { @Override public void set(Map<String, String> carrier, String key, String value) { carrier.put(key, value); } }; public static final Getter<Map<String, String>> MAP_GETTER = new Getter<Map<String, String>>() { @Override public String get(Map<String, String> carrier, String key) { return carrier.get(key); } @Override public Iterable<String> keys(Map<String, String> carrier) { return carrier.keySet(); } }; public static final Setter<javax.servlet.http.HttpServletRequest> SERVLET_REQUEST_SETTER = new Setter<javax.servlet.http.HttpServletRequest>() { @Override public void set(javax.servlet.http.HttpServletRequest carrier, String key, String value) { carrier.setAttribute(key, value); } }; public static final Getter<javax.servlet.http.HttpServletRequest> SERVLET_REQUEST_GETTER = new Getter<javax.servlet.http.HttpServletRequest>() { @Override public String get(javax.servlet.http.HttpServletRequest carrier, String key) { Object value = carrier.getAttribute(key); return value != null ? value.toString() : null; } @Override public Iterable<String> keys(javax.servlet.http.HttpServletRequest carrier) { List<String> keys = new ArrayList<>(); java.util.Enumeration<String> attributeNames = carrier.getAttributeNames(); while (attributeNames.hasMoreElements()) { keys.add(attributeNames.nextElement()); } return keys; } }; public static final Setter<javax.servlet.http.HttpServletResponse> SERVLET_RESPONSE_SETTER = new Setter<javax.servlet.http.HttpServletResponse>() { @Override public void set(javax.servlet.http.HttpServletResponse carrier, String key, String value) { carrier.setHeader(key, value); } }; public static final Getter<javax.servlet.http.HttpServletResponse> SERVLET_RESPONSE_GETTER = new Getter<javax.servlet.http.HttpServletResponse>() { @Override public String get(javax.servlet.http.HttpServletResponse carrier, String key) { return carrier.getHeader(key); } @Override public Iterable<String> keys(javax.servlet.http.HttpServletResponse carrier) { return carrier.getHeaderNames(); } }; } /** * Spring Web Interceptor for automatic baggage propagation */ @Component public class BaggagePropagationInterceptor implements HandlerInterceptor { private final BaggageManager baggageManager = BaggageManager.getInstance(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // Extract baggage from incoming request Baggage extractedBaggage = baggageManager.extract( request, HttpBaggage.SERVLET_REQUEST_GETTER ); // Set as current baggage baggageManager.setCurrentBaggage(extractedBaggage); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // Inject baggage into outgoing response Baggage currentBaggage = baggageManager.getCurrentBaggage(); baggageManager.inject( response, currentBaggage, HttpBaggage.SERVLET_RESPONSE_SETTER ); // Clear baggage for this request baggageManager.clear(); } } /** * Spring Configuration */ @Configuration public class BaggageConfig implements WebMvcConfigurer { @Bean public BaggagePropagationInterceptor baggagePropagationInterceptor() { return new BaggagePropagationInterceptor(); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(baggagePropagationInterceptor()); } }

8. Advanced Features: Filtering and Validation

/** * Baggage filtering and validation */ public class BaggageFilter { private final Set<String> allowedKeys; private final Set<String> deniedKeys; private final int maxKeyLength; private final int maxValueLength; private final int maxTotalEntries; private BaggageFilter(Set<String> allowedKeys, Set<String> deniedKeys, int maxKeyLength, int maxValueLength, int maxTotalEntries) { this.allowedKeys = allowedKeys != null ? new HashSet<>(allowedKeys) : null; this.deniedKeys = deniedKeys != null ? new HashSet<>(deniedKeys) : Collections.emptySet(); this.maxKeyLength = maxKeyLength; this.maxValueLength = maxValueLength; this.maxTotalEntries = maxTotalEntries; } public Baggage filter(Baggage baggage) { if (baggage.isEmpty()) { return baggage; } BaggageBuilder builder = Baggage.builder(); int entryCount = 0; for (Map.Entry<String, BaggageEntry> entry : baggage.asMap().entrySet()) { if (entryCount >= maxTotalEntries) { break; // Limit total entries } String key = entry.getKey(); BaggageEntry baggageEntry = entry.getValue(); String value = baggageEntry.getValue(); if (isAllowed(key, value)) { builder.put(key, value, baggageEntry.getMetadata()); entryCount++; } } return builder.build(); } private boolean isAllowed(String key, String value) { // Check denied keys first if (deniedKeys.contains(key)) { return false; } // Check allowed keys if specified if (allowedKeys != null && !allowedKeys.contains(key)) { return false; } // Check length constraints if (key.length() > maxKeyLength) { return false; } if (value.length() > maxValueLength) { return false; } // Validate key format if (!key.matches("[a-zA-Z_][a-zA-Z0-9_\\-.]*")) { return false; } return true; } public static Builder builder() { return new Builder(); } public static class Builder { private Set<String> allowedKeys; private Set<String> deniedKeys; private int maxKeyLength = 256; private int maxValueLength = 4096; private int maxTotalEntries = 180; public Builder allowedKeys(Set<String> allowedKeys) { this.allowedKeys = allowedKeys; return this; } public Builder deniedKeys(Set<String> deniedKeys) { this.deniedKeys = deniedKeys; return this; } public Builder maxKeyLength(int maxKeyLength) { this.maxKeyLength = maxKeyLength; return this; } public Builder maxValueLength(int maxValueLength) { this.maxValueLength = maxValueLength; return this; } public Builder maxTotalEntries(int maxTotalEntries) { this.maxTotalEntries = maxTotalEntries; return this; } public BaggageFilter build() { return new BaggageFilter(allowedKeys, deniedKeys, maxKeyLength, maxValueLength, maxTotalEntries); } } } /** * Baggage utilities and helpers */ public class BaggageUtils { private BaggageUtils() {} public static Baggage merge(Baggage first, Baggage second) { BaggageBuilder builder = first.toBuilder(); builder.putAll(second); return builder.build(); } public static Baggage filterByPrefix(Baggage baggage, String prefix) { BaggageBuilder builder = Baggage.builder(); for (Map.Entry<String, BaggageEntry> entry : baggage.asMap().entrySet()) { if (entry.getKey().startsWith(prefix)) { builder.put(entry.getKey(), entry.getValue().getValue(), entry.getValue().getMetadata()); } } return builder.build(); } public static Map<String, String> toMap(Baggage baggage) { Map<String, String> map = new HashMap<>(); for (Map.Entry<String, BaggageEntry> entry : baggage.asMap().entrySet()) { map.put(entry.getKey(), entry.getValue().getValue()); } return map; } public static Baggage fromMap(Map<String, String> map) { BaggageBuilder builder = Baggage.builder(); for (Map.Entry<String, String> entry : map.entrySet()) { builder.put(entry.getKey(), entry.getValue()); } return builder.build(); } public static String getOrEmpty(Baggage baggage, String key) { String value = baggage.getEntryValue(key); return value != null ? value : ""; } public static String getOrDefault(Baggage baggage, String key, String defaultValue) { String value = baggage.getEntryValue(key); return value != null ? value : defaultValue; } }

9. Usage Examples and Demo

/** * Demo and usage examples */ public class BaggageDemo { public static void main(String[] args) { // Basic baggage usage basicBaggageExample(); // Context propagation example contextPropagationExample(); // HTTP propagation example httpPropagationExample(); // Scope management example scopeManagementExample(); } private static void basicBaggageExample() { System.out.println("=== Basic Baggage Example ==="); // Create baggage with key-value pairs Baggage baggage = Baggage.builder() .put("user.id", "12345") .put("user.role", "admin") .put("request.id", "req-67890") .put("tenant", "acme-corp") .build(); System.out.println("Baggage: " + baggage.asMap()); System.out.println("User ID: " + baggage.getEntryValue("user.id")); System.out.println("Size: " + baggage.size()); } private static void contextPropagationExample() { System.out.println("\n=== Context Propagation Example ==="); // Create initial baggage Baggage initialBaggage = Baggage.builder() .put("trace.id", "trace-123") .put("span.id", "span-456") .build(); BaggageManager baggageManager = BaggageManager.getInstance(); baggageManager.setCurrentBaggage(initialBaggage); // Simulate propagation across thread boundaries ExecutorService executor = Executors.newSingleThreadExecutor(); // Capture current baggage Baggage currentBaggage = baggageManager.getCurrentBaggage(); executor.submit(BaggageContext.wrap(() -> { // This runs with the captured baggage Baggage threadBaggage = BaggageManager.getInstance().getCurrentBaggage(); System.out.println("Thread baggage: " + threadBaggage.asMap()); // Modify baggage in this context BaggageManager.getInstance().put("thread.name", "worker-1"); }, currentBaggage)); executor.shutdown(); } private static void httpPropagationExample() { System.out.println("\n=== HTTP Propagation Example ==="); // Simulate HTTP headers Map<String, String> httpHeaders = new HashMap<>(); httpHeaders.put("baggage", "user.id=12345,tenant=acme-corp,trace.id=trace-789"); // Extract baggage from HTTP headers BaggageManager baggageManager = BaggageManager.getInstance(); Baggage extractedBaggage = baggageManager.extract( httpHeaders, HttpBaggage.MAP_GETTER ); System.out.println("Extracted baggage: " + extractedBaggage.asMap()); // Modify and inject back Baggage modifiedBaggage = extractedBaggage.toBuilder() .put("service.name", "api-service") .build(); Map<String, String> outgoingHeaders = new HashMap<>(); baggageManager.inject( outgoingHeaders, modifiedBaggage, HttpBaggage.MAP_SETTER ); System.out.println("Outgoing headers: " + outgoingHeaders); } private static void scopeManagementExample() { System.out.println("\n=== Scope Management Example ==="); BaggageManager baggageManager = BaggageManager.getInstance(); // Set initial baggage baggageManager.setCurrentBaggage(Baggage.of("initial", "value")); System.out.println("Initial baggage: " + baggageManager.getCurrentBaggage().asMap()); // Use try-with-resources for automatic scope management try (BaggageScope scope = BaggageContext.withBaggage("request.id", "req-123")) { System.out.println("Inside scope: " + baggageManager.getCurrentBaggage().asMap()); // Nested scope try (BaggageScope nestedScope = BaggageContext.withBaggage("nested", "value")) { System.out.println("Inside nested scope: " + baggageManager.getCurrentBaggage().asMap()); } System.out.println("After nested scope: " + baggageManager.getCurrentBaggage().asMap()); } System.out.println("After scope: " + baggageManager.getCurrentBaggage().asMap()); } } /** * Real-world usage in a web service */ @Service public class OrderService { private static final Logger logger = LoggerFactory.getLogger(OrderService.class); @Autowired private PaymentService paymentService; @Autowired private InventoryService inventoryService; public Order processOrder(OrderRequest request) { // Get current baggage Baggage baggage = BaggageManager.getInstance().getCurrentBaggage(); String userId = baggage.getEntryValue("user.id"); String requestId = baggage.getEntryValue("request.id"); logger.info("Processing order for user: {}, request: {}", userId, requestId); try { // Add order-specific baggage try (BaggageScope scope = BaggageContext.withBaggage("order.id", request.getOrderId())) { // These service calls will inherit the baggage context PaymentResult payment = paymentService.processPayment(request.getPayment()); InventoryCheck inventory = inventoryService.checkInventory(request.getItems()); if (payment.isSuccess() && inventory.isAvailable()) { return createOrder(request, payment, inventory); } else { throw new OrderProcessingException("Order processing failed"); } } } catch (Exception e) { // Add error information to baggage BaggageManager.getInstance().put("order.error", e.getMessage()); throw e; } } } /** * Async service with baggage propagation */ @Service public class AsyncService { @Async public CompletableFuture<String> processAsync(String data) { // Capture current baggage Baggage currentBaggage = BaggageManager.getInstance().getCurrentBaggage(); return CompletableFuture.supplyAsync(BaggageContext.wrap(() -> { // This runs with the original baggage context String requestId = BaggageManager.getInstance().getCurrentBaggage().getEntryValue("request.id"); System.out.println("Processing async with request ID: " + requestId); // Simulate work try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return "Processed: " + data; }, currentBaggage)); } }

Key Features

  • Immutable Baggage: Thread-safe, immutable baggage implementation
  • Context Propagation: Automatic propagation across thread boundaries and HTTP calls
  • Multiple Propagators: W3C Baggage and Jaeger support
  • Scope Management: Automatic cleanup with try-with-resources
  • Filtering & Validation: Configurable baggage filtering
  • HTTP Integration: Ready-to-use HTTP header propagation
  • Spring Integration: Spring Web interceptors for automatic propagation
  • Async Support: Proper context propagation for async operations

Usage Examples

Basic Baggage Creation

Baggage baggage = Baggage.builder() .put("user.id", "12345") .put("tenant", "acme-corp") .build();

HTTP Propagation

// Extract from incoming request Baggage baggage = baggageManager.extract(request, getter); // Inject into outgoing response baggageManager.inject(response, baggage, setter);

Scope Management

try (BaggageScope scope = BaggageContext.withBaggage("key", "value")) { // Code that uses the baggage // Automatic cleanup when scope closes }

Async Operations

CompletableFuture<String> future = CompletableFuture.supplyAsync( BaggageContext.wrap(() -> { // Runs with current baggage context return processData(); }, Baggage.current()) );

This comprehensive Baggage API implementation provides production-ready distributed context propagation for Java applications following OpenTelemetry standards.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper