Introduction to Baggage
Baggage in OpenTelemetry is a mechanism for propagating contextual information across service boundaries. It consists of key-value pairs that are transmitted alongside distributed traces, allowing you to pass additional context through your distributed system.
Core Concepts
Baggage vs. Span Attributes
- Span Attributes: Associated with specific spans, used for observability
- Baggage: Propagated across service boundaries, used for application context
Key Components
- Baggage: Collection of key-value pairs
- BaggageEntry: Individual key-value pair with optional metadata
- BaggageBuilder: For constructing baggage instances
- BaggageManager: Manages baggage context propagation
Basic Baggage Usage
Setting Up Dependencies
<!-- Maven dependencies --> <dependencies> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-api</artifactId> <version>1.28.0</version> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-sdk</artifactId> <version>1.28.0</version> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-logging</artifactId> <version>1.28.0</version> </dependency> </dependencies>
Basic Baggage Operations
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.baggage.BaggageBuilder;
import io.opentelemetry.api.baggage.BaggageEntry;
import io.opentelemetry.api.baggage.BaggageEntryMetadata;
public class BasicBaggageExample {
public static void main(String[] args) {
// Get current baggage from context
Baggage currentBaggage = Baggage.current();
System.out.println("Initial baggage: " + currentBaggage);
// Create new baggage
Baggage baggage = Baggage.builder()
.put("user.id", "12345")
.put("tenant.id", "acme-corp")
.put("request.source", "mobile-app")
.build();
System.out.println("Created baggage: " + baggage);
// Access baggage values
String userId = baggage.getEntryValue("user.id");
System.out.println("User ID: " + userId);
// Check if key exists
boolean hasTenant = baggage.containsKey("tenant.id");
System.out.println("Has tenant: " + hasTenant);
// Get all entries
baggage.forEach((key, value) ->
System.out.println("Key: " + key + ", Value: " + value.getValue()));
}
}
Baggage with Metadata
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.baggage.BaggageEntryMetadata;
public class BaggageMetadataExample {
public static void main(String[] args) {
// Create baggage with metadata
Baggage baggage = Baggage.builder()
.put("session.id", "sess-789",
BaggageEntryMetadata.create("propagate=true;max-age=3600"))
.put("internal.trace.id", "trace-456",
BaggageEntryMetadata.create("propagate=false"))
.put("feature.flags", "new_ui,beta_feature",
BaggageEntryMetadata.create("propagate=true;comment=feature-toggles"))
.build();
// Access entries with metadata
baggage.forEach((key, baggageEntry) -> {
System.out.println("Key: " + key);
System.out.println(" Value: " + baggageEntry.getValue());
System.out.println(" Metadata: " + baggageEntry.getMetadata().getValue());
});
// Parse metadata
BaggageEntry sessionEntry = baggage.getEntry("session.id");
if (sessionEntry != null) {
String metadata = sessionEntry.getMetadata().getValue();
System.out.println("Session metadata: " + metadata);
// Simple metadata parsing
if (metadata.contains("propagate=true")) {
System.out.println("Session ID will be propagated");
}
}
}
}
Baggage in Distributed Context
Propagating Baggage Across Services
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
public class BaggagePropagationExample {
// Simulate HTTP headers propagation
static class HttpHeaders {
private final java.util.Map<String, String> headers = new java.util.HashMap<>();
public void setHeader(String key, String value) {
headers.put(key, value);
}
public String getHeader(String key) {
return headers.get(key);
}
public java.util.Map<String, String> getAllHeaders() {
return new java.util.HashMap<>(headers);
}
}
// Client side: adding baggage to outgoing request
public static HttpHeaders addBaggageToRequest(HttpHeaders headers) {
Baggage baggage = Baggage.current()
.toBuilder()
.put("user.id", "user-123")
.put("correlation.id", java.util.UUID.randomUUID().toString())
.put("request.source", "web-frontend")
.put("client.version", "1.2.3")
.build();
// In real scenario, this would be done by OpenTelemetry propagators
// Simulating W3C Baggage propagation
StringBuilder baggageHeader = new StringBuilder();
baggage.forEach((key, entry) -> {
if (baggageHeader.length() > 0) {
baggageHeader.append(",");
}
baggageHeader.append(key).append("=").append(entry.getValue());
});
headers.setHeader("baggage", baggageHeader.toString());
return headers;
}
// Server side: extracting baggage from incoming request
public static void extractBaggageFromRequest(HttpHeaders headers) {
String baggageHeader = headers.getHeader("baggage");
if (baggageHeader != null) {
BaggageBuilder baggageBuilder = Baggage.builder();
// Parse W3C Baggage header (simplified)
String[] entries = baggageHeader.split(",");
for (String entry : entries) {
String[] keyValue = entry.split("=", 2);
if (keyValue.length == 2) {
baggageBuilder.put(keyValue[0].trim(), keyValue[1].trim());
}
}
Baggage baggage = baggageBuilder.build();
Context contextWithBaggage = Context.current().with(baggage);
try (Scope scope = contextWithBaggage.makeCurrent()) {
// Now Baggage.current() will return the extracted baggage
processRequestWithBaggage();
}
}
}
private static void processRequestWithBaggage() {
Baggage baggage = Baggage.current();
Span currentSpan = Span.current();
// Add baggage entries as span attributes for observability
baggage.forEach((key, value) -> {
currentSpan.setAttribute("baggage." + key, value.getValue());
});
System.out.println("Processing request with baggage:");
baggage.forEach((key, value) ->
System.out.println(" " + key + ": " + value.getValue()));
// Use baggage values in business logic
String userId = baggage.getEntryValue("user.id");
if (userId != null) {
System.out.println("Processing request for user: " + userId);
}
}
public static void main(String[] args) {
// Simulate client making request
HttpHeaders outgoingHeaders = new HttpHeaders();
addBaggageToRequest(outgoingHeaders);
System.out.println("Outgoing headers: " + outgoingHeaders.getAllHeaders());
// Simulate server receiving request
extractBaggageFromRequest(outgoingHeaders);
}
}
Advanced Baggage Patterns
Baggage with Scoped Context
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
public class ScopedBaggageExample {
public static void main(String[] args) {
// Initial context
System.out.println("Initial baggage: " + Baggage.current().isEmpty());
// Create scoped baggage context
Baggage baggage = Baggage.builder()
.put("transaction.id", "txn-001")
.put("processing.stage", "initial")
.build();
Context context = Context.current().with(baggage);
try (Scope scope = context.makeCurrent()) {
processTransaction();
// Nested scope with additional baggage
Baggage updatedBaggage = Baggage.current()
.toBuilder()
.put("processing.stage", "validation")
.put("validation.attempt", "1")
.build();
Context nestedContext = Context.current().with(updatedBaggage);
try (Scope nestedScope = nestedContext.makeCurrent()) {
validateTransaction();
}
// Back to original baggage context
continueProcessing();
}
// Back to original context (no baggage)
System.out.println("Back to original context: " + Baggage.current().isEmpty());
}
private static void processTransaction() {
Baggage baggage = Baggage.current();
System.out.println("Processing transaction: " +
baggage.getEntryValue("transaction.id"));
System.out.println("Stage: " + baggage.getEntryValue("processing.stage"));
}
private static void validateTransaction() {
Baggage baggage = Baggage.current();
System.out.println("Validating transaction: " +
baggage.getEntryValue("transaction.id"));
System.out.println("Stage: " + baggage.getEntryValue("processing.stage"));
System.out.println("Attempt: " + baggage.getEntryValue("validation.attempt"));
}
private static void continueProcessing() {
Baggage baggage = Baggage.current();
System.out.println("Continuing transaction: " +
baggage.getEntryValue("transaction.id"));
// Note: validation.attempt is no longer available
System.out.println("Has validation.attempt: " +
baggage.containsKey("validation.attempt"));
}
}
Thread-Safe Baggage Operations
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ConcurrentBaggageExample {
private static final ExecutorService executor = Executors.newFixedThreadPool(3);
static class ProcessingTask implements Runnable {
private final String taskId;
private final Context parentContext;
public ProcessingTask(String taskId, Context parentContext) {
this.taskId = taskId;
this.parentContext = parentContext;
}
@Override
public void run() {
// Create task-specific baggage
Baggage taskBaggage = Baggage.current()
.toBuilder()
.put("task.id", taskId)
.put("thread.name", Thread.currentThread().getName())
.put("processing.start.time", String.valueOf(System.currentTimeMillis()))
.build();
Context taskContext = parentContext.with(taskBaggage);
try (Scope scope = taskContext.makeCurrent()) {
processTask();
}
}
private void processTask() {
Baggage baggage = Baggage.current();
String taskId = baggage.getEntryValue("task.id");
String threadName = baggage.getEntryValue("thread.name");
System.out.println("Processing task " + taskId + " on thread " + threadName);
// Simulate work
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// Update baggage during processing
Baggage updatedBaggage = Baggage.current()
.toBuilder()
.put("processing.end.time", String.valueOf(System.currentTimeMillis()))
.put("task.status", "completed")
.build();
try (Scope scope = Context.current().with(updatedBaggage).makeCurrent()) {
completeTask();
}
}
private void completeTask() {
Baggage baggage = Baggage.current();
System.out.println("Completed task " + baggage.getEntryValue("task.id") +
" with status: " + baggage.getEntryValue("task.status"));
}
}
public static void main(String[] args) throws InterruptedException {
// Setup initial context with common baggage
Baggage initialBaggage = Baggage.builder()
.put("batch.id", "batch-2024-01")
.put("application.name", "data-processor")
.put("environment", "production")
.build();
Context initialContext = Context.current().with(initialBaggage);
// Submit tasks with shared context
for (int i = 1; i <= 5; i++) {
executor.submit(new ProcessingTask("task-" + i, initialContext));
}
executor.shutdown();
executor.awaitTermination(2, TimeUnit.SECONDS);
}
}
Integration with Tracing
Correlating Baggage with Spans
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
public class BaggageTracingIntegration {
private static final Tracer tracer =
GlobalOpenTelemetry.getTracer("baggage.example");
static class OrderService {
public void processOrder(String orderId) {
Span span = tracer.spanBuilder("processOrder")
.setAttribute("order.id", orderId)
.startSpan();
try (Scope scope = span.makeCurrent()) {
// Add current baggage to span attributes
Baggage.current().forEach((key, value) -> {
span.setAttribute("baggage." + key, value.getValue());
});
// Business logic
validateOrder(orderId);
chargePayment(orderId);
fulfillOrder(orderId);
} finally {
span.end();
}
}
private void validateOrder(String orderId) {
Span span = tracer.spanBuilder("validateOrder").startSpan();
try (Scope scope = span.makeCurrent()) {
// Use baggage in validation
Baggage baggage = Baggage.current();
String userId = baggage.getEntryValue("user.id");
String tenant = baggage.getEntryValue("tenant.id");
span.setAttribute("validating.user", userId);
span.setAttribute("validating.tenant", tenant);
System.out.println("Validating order " + orderId +
" for user " + userId + " in tenant " + tenant);
// Simulate validation
Thread.sleep(50);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
span.end();
}
}
private void chargePayment(String orderId) {
Span span = tracer.spanBuilder("chargePayment").startSpan();
try (Scope scope = span.makeCurrent()) {
Baggage baggage = Baggage.current();
String paymentMethod = baggage.getEntryValue("payment.method");
if (paymentMethod != null) {
span.setAttribute("payment.method", paymentMethod);
}
// Payment processing logic
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
span.end();
}
}
private void fulfillOrder(String orderId) {
Span span = tracer.spanBuilder("fulfillOrder").startSpan();
try (Scope scope = span.makeCurrent()) {
Baggage baggage = Baggage.current();
String shippingTier = baggage.getEntryValue("shipping.tier");
if (shippingTier != null) {
span.setAttribute("shipping.tier", shippingTier);
}
// Fulfillment logic
Thread.sleep(75);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
span.end();
}
}
}
public static void main(String[] args) {
// Setup OpenTelemetry (simplified)
setupOpenTelemetry();
OrderService orderService = new OrderService();
// Process orders with different baggage contexts
processOrderWithContext(orderService, "order-001",
Baggage.builder()
.put("user.id", "customer-123")
.put("tenant.id", "us-west")
.put("payment.method", "credit-card")
.put("shipping.tier", "express")
.build());
processOrderWithContext(orderService, "order-002",
Baggage.builder()
.put("user.id", "customer-456")
.put("tenant.id", "eu-central")
.put("payment.method", "paypal")
.put("shipping.tier", "standard")
.build());
}
private static void processOrderWithContext(OrderService service,
String orderId,
Baggage baggage) {
Context context = Context.current().with(baggage);
try (Scope scope = context.makeCurrent()) {
service.processOrder(orderId);
}
}
private static void setupOpenTelemetry() {
// In a real application, you'd configure OpenTelemetry SDK
System.out.println("OpenTelemetry setup complete");
}
}
Custom Baggage Management
Baggage Manager Utility
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.baggage.BaggageBuilder;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class BaggageManager {
private static final Map<String, String> GLOBAL_BAGGAGE_DEFAULTS =
new ConcurrentHashMap<>();
static {
GLOBAL_BAGGAGE_DEFAULTS.put("application.version", "1.0.0");
GLOBAL_BAGGAGE_DEFAULTS.put("deployment.region", System.getenv().getOrDefault("REGION", "unknown"));
}
public static Baggage createBaggage(Map<String, String> customEntries) {
BaggageBuilder builder = Baggage.builder();
// Add global defaults
GLOBAL_BAGGAGE_DEFAULTS.forEach(builder::put);
// Add custom entries
if (customEntries != null) {
customEntries.forEach(builder::put);
}
return builder.build();
}
public static Baggage createBaggageWithMetadata(Map<String, BaggageEntry> entries) {
BaggageBuilder builder = Baggage.builder();
// Add global defaults first
GLOBAL_BAGGAGE_DEFAULTS.forEach((key, value) ->
builder.put(key, value, io.opentelemetry.api.baggage.BaggageEntryMetadata.create("propagate=true")));
// Add custom entries with metadata
if (entries != null) {
entries.forEach((key, entry) ->
builder.put(key, entry.getValue(), entry.getMetadata()));
}
return builder.build();
}
public static Context createContextWithBaggage(Map<String, String> entries) {
Baggage baggage = createBaggage(entries);
return Context.current().with(baggage);
}
public static void runWithBaggage(Map<String, String> entries, Runnable task) {
Context context = createContextWithBaggage(entries);
try (Scope scope = context.makeCurrent()) {
task.run();
}
}
public static String getBaggageValue(String key) {
return Baggage.current().getEntryValue(key);
}
public static void updateBaggage(String key, String value) {
Baggage current = Baggage.current();
Baggage updated = current.toBuilder().put(key, value).build();
Context updatedContext = Context.current().with(updated);
updatedContext.makeCurrent();
}
public static void removeBaggage(String key) {
Baggage current = Baggage.current();
Baggage updated = current.toBuilder().remove(key).build();
Context updatedContext = Context.current().with(updated);
updatedContext.makeCurrent();
}
public static Map<String, String> getCurrentBaggageAsMap() {
Map<String, String> result = new ConcurrentHashMap<>();
Baggage.current().forEach((key, value) -> result.put(key, value.getValue()));
return result;
}
public static void printCurrentBaggage() {
System.out.println("=== Current Baggage ===");
Baggage.current().forEach((key, value) ->
System.out.println(key + " = " + value.getValue() + " [metadata: " + value.getMetadata().getValue() + "]"));
System.out.println("=======================");
}
// Custom BaggageEntry record for easier handling
public record BaggageEntry(String value, String metadata) {
public io.opentelemetry.api.baggage.BaggageEntry toOtelEntry() {
return new io.opentelemetry.api.baggage.BaggageEntry() {
@Override
public String getValue() {
return value;
}
@Override
public io.opentelemetry.api.baggage.BaggageEntryMetadata getMetadata() {
return io.opentelemetry.api.baggage.BaggageEntryMetadata.create(metadata);
}
};
}
}
public static void main(String[] args) {
// Example usage
Map<String, String> customEntries = Map.of(
"user.session", "sess-abc-123",
"request.id", "req-789",
"feature.flags", "dark_mode,new_ui"
);
runWithBaggage(customEntries, () -> {
printCurrentBaggage();
// Update baggage
updateBaggage("processing.stage", "validation");
System.out.println("\nAfter update:");
printCurrentBaggage();
// Remove entry
removeBaggage("feature.flags");
System.out.println("\nAfter removal:");
printCurrentBaggage();
});
}
}
Security Considerations
Sensitive Data Handling
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.baggage.BaggageEntryMetadata;
import java.util.Set;
import java.util.HashSet;
public class SecureBaggageExample {
private static final Set<String> SENSITIVE_KEYS = Set.of(
"user.password", "auth.token", "credit.card.number",
"social.security.number", "api.secret"
);
private static final Set<String> PROPAGATION_BLACKLIST = Set.of(
"internal.secret", "debug.flag", "test.mode"
);
public static class SecureBaggageBuilder {
private final Baggage.Builder builder = Baggage.builder();
public SecureBaggageBuilder put(String key, String value) {
return put(key, value, "");
}
public SecureBaggageBuilder put(String key, String value, String metadata) {
if (SENSITIVE_KEYS.contains(key)) {
throw new SecurityException("Attempt to add sensitive data to baggage: " + key);
}
BaggageEntryMetadata entryMetadata = BaggageEntryMetadata.create(metadata);
// Add propagation restriction for blacklisted keys
if (PROPAGATION_BLACKLIST.contains(key)) {
entryMetadata = BaggageEntryMetadata.create("propagate=false");
}
builder.put(key, value, entryMetadata);
return this;
}
public SecureBaggageBuilder putSensitive(String key, String value) {
// For sensitive data, we hash it or use a reference
String hashedValue = "hash:" + Integer.toHexString(value.hashCode());
builder.put(key, hashedValue, BaggageEntryMetadata.create("sensitive=true;propagate=false"));
return this;
}
public Baggage build() {
return builder.build();
}
}
public static Baggage sanitizeBaggage(Baggage baggage) {
Baggage.Builder sanitized = Baggage.builder();
baggage.forEach((key, value) -> {
if (!SENSITIVE_KEYS.contains(key) && !PROPAGATION_BLACKLIST.contains(key)) {
// Check metadata for propagation flags
String metadata = value.getMetadata().getValue();
if (!metadata.contains("propagate=false")) {
sanitized.put(key, value.getValue(), value.getMetadata());
}
}
});
return sanitized.build();
}
public static void main(String[] args) {
try {
SecureBaggageBuilder secureBuilder = new SecureBaggageBuilder();
// Safe entries
Baggage safeBaggage = secureBuilder
.put("user.id", "12345", "propagate=true")
.put("request.source", "mobile-app")
.putSensitive("auth.token", "secret-token-123")
.build();
System.out.println("Safe baggage created:");
safeBaggage.forEach((k, v) -> System.out.println(k + ": " + v.getValue()));
// Attempt to add sensitive data (will throw exception)
try {
secureBuilder.put("user.password", "password123");
} catch (SecurityException e) {
System.out.println("Security exception: " + e.getMessage());
}
// Sanitize existing baggage
Baggage unsanitized = Baggage.builder()
.put("user.id", "12345")
.put("internal.secret", "should-not-propagate")
.put("debug.flag", "true")
.build();
Baggage sanitized = sanitizeBaggage(unsanitized);
System.out.println("\nSanitized baggage:");
sanitized.forEach((k, v) -> System.out.println(k + ": " + v.getValue()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
Testing Baggage
Unit Testing with Baggage
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class BaggageTestExamples {
static class UserService {
public String processUserRequest(String requestData) {
Baggage baggage = Baggage.current();
String userId = baggage.getEntryValue("user.id");
String tenant = baggage.getEntryValue("tenant.id");
if (userId == null) {
throw new IllegalStateException("User ID not found in baggage");
}
return String.format("Processing request for user %s in tenant %s: %s",
userId, tenant, requestData);
}
}
@Test
void testUserServiceWithBaggage() {
UserService service = new UserService();
// Create test baggage
Baggage testBaggage = Baggage.builder()
.put("user.id", "test-user-123")
.put("tenant.id", "test-tenant")
.build();
Context testContext = Context.current().with(testBaggage);
try (Scope scope = testContext.makeCurrent()) {
String result = service.processUserRequest("test data");
assertEquals("Processing request for user test-user-123 in tenant test-tenant: test data",
result);
}
}
@Test
void testUserServiceWithoutBaggage() {
UserService service = new UserService();
// Ensure no baggage in context
try (Scope scope = Context.current().makeCurrent()) {
assertThrows(IllegalStateException.class, () -> {
service.processUserRequest("test data");
});
}
}
@Test
void testBaggagePropagationInNestedCalls() {
Baggage initialBaggage = Baggage.builder()
.put("trace.id", "trace-001")
.put("initial.data", "initial-value")
.build();
Context initialContext = Context.current().with(initialBaggage);
try (Scope outerScope = initialContext.makeCurrent()) {
// First level
assertEquals("trace-001", Baggage.current().getEntryValue("trace.id"));
// Nested context with additional baggage
Baggage nestedBaggage = Baggage.current()
.toBuilder()
.put("nested.data", "nested-value")
.build();
Context nestedContext = Context.current().with(nestedBaggage);
try (Scope nestedScope = nestedContext.makeCurrent()) {
// Should have both initial and nested data
assertEquals("trace-001", Baggage.current().getEntryValue("trace.id"));
assertEquals("nested-value", Baggage.current().getEntryValue("nested.data"));
}
// Back to original context - nested data should be gone
assertEquals("trace-001", Baggage.current().getEntryValue("trace.id"));
assertNull(Baggage.current().getEntryValue("nested.data"));
}
}
public static void main(String[] args) {
BaggageTestExamples tests = new BaggageTestExamples();
try {
tests.testUserServiceWithBaggage();
tests.testUserServiceWithoutBaggage();
tests.testBaggagePropagationInNestedCalls();
System.out.println("All tests passed!");
} catch (Exception e) {
System.out.println("Test failed: " + e.getMessage());
e.printStackTrace();
}
}
}
Best Practices
1. Baggage Size Limitations
public class BaggageSizeLimiter {
private static final int MAX_BAGGAGE_SIZE = 8192; // 8KB
private static final int MAX_ENTRIES = 64;
public static Baggage createSizeLimitedBaggage(Map<String, String> entries) {
BaggageBuilder builder = Baggage.builder();
int totalSize = 0;
int entryCount = 0;
for (Map.Entry<String, String> entry : entries.entrySet()) {
int entrySize = entry.getKey().length() + entry.getValue().length();
if (totalSize + entrySize > MAX_BAGGAGE_SIZE) {
System.err.println("Baggage size limit exceeded, skipping: " + entry.getKey());
continue;
}
if (entryCount >= MAX_ENTRIES) {
System.err.println("Baggage entry limit exceeded, skipping: " + entry.getKey());
continue;
}
builder.put(entry.getKey(), entry.getValue());
totalSize += entrySize;
entryCount++;
}
return builder.build();
}
}
2. Performance Considerations
- Keep baggage small and focused
- Avoid storing large values in baggage
- Use meaningful keys that describe the context
- Consider the performance impact of baggage propagation
3. Security Guidelines
- Never store sensitive data in baggage
- Use metadata to control propagation
- Validate and sanitize baggage entries
- Implement proper access controls
Conclusion
The OpenTelemetry Baggage API provides a powerful mechanism for propagating contextual information across distributed systems. Key takeaways:
- Use for Cross-Cutting Concerns: Baggage is ideal for data that needs to flow across service boundaries
- Correlate with Tracing: Baggage complements distributed tracing by providing additional context
- Manage Size and Performance: Be mindful of baggage size and its impact on performance
- Implement Security: Never include sensitive data and use metadata to control propagation
- Test Thoroughly: Ensure baggage behaves correctly in different contexts and scenarios
By following these patterns and best practices, you can effectively use Baggage to enhance observability and context propagation in your distributed Java applications.