B3 Propagation in Java: Complete Guide

Introduction to B3 Propagation

B3 Propagation is a distributed tracing context propagation format developed by Zipkin. It defines how trace context is passed between services in a distributed system. B3 supports multiple formats including single-header and multi-header variants.


B3 Propagation Headers Overview

Single Header Format:
b3: {TraceId}-{SpanId}-{SamplingDecision}-{ParentSpanId}
Multi-Header Format:
X-B3-TraceId: {TraceId}
X-B3-SpanId: {SpanId}
X-B3-ParentSpanId: {ParentSpanId}
X-B3-Sampled: {1|0}
X-B3-Flags: {1}  # Debug flag

Core Implementation

1. Maven Dependencies

<properties>
<opentelemetry.version>1.32.0</opentelemetry.version>
<brave.version>5.16.0</brave.version>
<spring-cloud.version>2021.0.8</spring-cloud.version>
</properties>
<dependencies>
<!-- OpenTelemetry -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
<version>${opentelemetry.version}</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
<version>${opentelemetry.version}</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
<version>${opentelemetry.version}</version>
</dependency>
<!-- Brave (Zipkin) for B3 -->
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave</artifactId>
<version>${brave.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-context-slf4j</artifactId>
<version>${brave.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-spring-web</artifactId>
<version>${brave.version}</version>
</dependency>
<!-- Spring Cloud Sleuth (Alternative approach) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
<version>${spring-cloud.version}</version>
</dependency>
<!-- Web dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.7.0</version>
</dependency>
</dependencies>

2. OpenTelemetry with B3 Propagation

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.extension.trace.propagation.B3Propagator;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
@Configuration
public class B3OpenTelemetryConfig {
@Bean
public OpenTelemetrySdk openTelemetry() {
// Configure B3 Propagator
TextMapPropagator b3Propagator = B3Propagator.injectingMultiHeaders();
// Alternatively, for single header format:
// TextMapPropagator b3Propagator = B3Propagator.injectingSingleHeader();
SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
.addSpanProcessor(BatchSpanProcessor.builder(
OtlpGrpcSpanExporter.builder()
.setEndpoint("http://localhost:4317")
.setTimeout(30, TimeUnit.SECONDS)
.build()
).build())
.build();
OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder()
.setTracerProvider(tracerProvider)
.setPropagators(ContextPropagators.create(b3Propagator))
.build();
GlobalOpenTelemetry.set(openTelemetry);
return openTelemetry;
}
@Bean
public Tracer tracer(OpenTelemetrySdk openTelemetry) {
return openTelemetry.getTracer("my-service", "1.0.0");
}
@Bean
public TextMapPropagator propagator() {
return B3Propagator.injectingMultiHeaders();
}
}

3. B3 Propagation Service

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.api.trace.TraceFlags;
import io.opentelemetry.api.trace.TraceState;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapGetter;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.context.propagation.TextMapSetter;
import org.slf4j.MDC;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@Service
public class B3PropagationService {
private final TextMapPropagator propagator;
// B3 Header Constants
public static final String X_B3_TRACE_ID = "X-B3-TraceId";
public static final String X_B3_SPAN_ID = "X-B3-SpanId";
public static final String X_B3_PARENT_SPAN_ID = "X-B3-ParentSpanId";
public static final String X_B3_SAMPLED = "X-B3-Sampled";
public static final String X_B3_FLAGS = "X-B3-Flags";
public static final String B3_SINGLE_HEADER = "b3";
public B3PropagationService(TextMapPropagator propagator) {
this.propagator = propagator;
}
/**
* Extract B3 context from HTTP headers
*/
public Context extractContextFromHttp(HttpServletRequest request) {
return propagator.extract(
Context.current(),
request,
HttpServletRequestGetter.INSTANCE
);
}
/**
* Extract B3 context from a map of headers
*/
public Context extractContextFromHeaders(Map<String, String> headers) {
return propagator.extract(
Context.current(),
headers,
MapGetter.INSTANCE
);
}
/**
* Inject B3 context into HTTP headers map
*/
public void injectContextToHeaders(Context context, Map<String, String> headers) {
propagator.inject(
context,
headers,
MapSetter.INSTANCE
);
}
/**
* Get current trace ID
*/
public String getCurrentTraceId() {
Span currentSpan = Span.current();
return currentSpan.getSpanContext().getTraceId();
}
/**
* Get current span ID
*/
public String getCurrentSpanId() {
Span currentSpan = Span.current();
return currentSpan.getSpanContext().getSpanId();
}
/**
* Check if current span is sampled
*/
public boolean isCurrentSpanSampled() {
Span currentSpan = Span.current();
return currentSpan.getSpanContext().isSampled();
}
/**
* Create child context with B3 propagation
*/
public Context createChildContext(String operationName) {
Span parentSpan = Span.current();
Span childSpan = Span.fromContext(Context.current())
.spanBuilder(operationName)
.setParent(Context.current().with(parentSpan))
.setSpanKind(SpanKind.INTERNAL)
.startSpan();
return Context.current().with(childSpan);
}
/**
* Setup MDC for logging with B3 context
*/
public void setupMDC(Context context) {
Span span = Span.fromContext(context);
SpanContext spanContext = span.getSpanContext();
MDC.put("traceId", spanContext.getTraceId());
MDC.put("spanId", spanContext.getSpanId());
MDC.put("sampled", String.valueOf(spanContext.isSampled()));
}
/**
* Clear MDC
*/
public void clearMDC() {
MDC.remove("traceId");
MDC.remove("spanId");
MDC.remove("sampled");
}
/**
* Parse B3 single header format
*/
public B3Context parseB3SingleHeader(String b3Header) {
if (b3Header == null || b3Header.isEmpty()) {
return null;
}
String[] parts = b3Header.split("-");
if (parts.length < 2) {
return null;
}
B3Context context = new B3Context();
context.setTraceId(parts[0]);
context.setSpanId(parts[1]);
if (parts.length > 2) {
context.setSampled(parseSampled(parts[2]));
}
if (parts.length > 3) {
context.setParentSpanId(parts[3]);
}
return context;
}
/**
* Create B3 single header format
*/
public String createB3SingleHeader(SpanContext spanContext, String parentSpanId) {
StringBuilder b3 = new StringBuilder();
b3.append(spanContext.getTraceId())
.append("-")
.append(spanContext.getSpanId());
if (spanContext.isSampled()) {
b3.append("-1");
} else {
b3.append("-0");
}
if (parentSpanId != null && !parentSpanId.isEmpty()) {
b3.append("-").append(parentSpanId);
}
return b3.toString();
}
private Boolean parseSampled(String sampled) {
if ("1".equals(sampled) || "true".equalsIgnoreCase(sampled)) {
return true;
} else if ("0".equals(sampled) || "false".equalsIgnoreCase(sampled)) {
return false;
}
return null;
}
// TextMapGetter implementation for HttpServletRequest
private enum HttpServletRequestGetter implements TextMapGetter<HttpServletRequest> {
INSTANCE;
@Override
public Iterable<String> keys(HttpServletRequest carrier) {
return Collections.list(carrier.getHeaderNames());
}
@Override
public String get(HttpServletRequest carrier, String key) {
return carrier.getHeader(key);
}
}
// TextMapGetter implementation for Map
private enum MapGetter implements TextMapGetter<Map<String, String>> {
INSTANCE;
@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);
}
}
// TextMapSetter implementation for Map
private enum MapSetter implements TextMapSetter<Map<String, String>> {
INSTANCE;
@Override
public void set(Map<String, String> carrier, String key, String value) {
carrier.put(key, value);
}
}
// B3 Context holder
public static class B3Context {
private String traceId;
private String spanId;
private String parentSpanId;
private Boolean sampled;
private String flags;
// Getters and setters
public String getTraceId() { return traceId; }
public void setTraceId(String traceId) { this.traceId = traceId; }
public String getSpanId() { return spanId; }
public void setSpanId(String spanId) { this.spanId = spanId; }
public String getParentSpanId() { return parentSpanId; }
public void setParentSpanId(String parentSpanId) { this.parentSpanId = parentSpanId; }
public Boolean getSampled() { return sampled; }
public void setSampled(Boolean sampled) { this.sampled = sampled; }
public String getFlags() { return flags; }
public void setFlags(String flags) { this.flags = flags; }
}
}

4. HTTP Client with B3 Propagation

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@Component
public class B3PropagationInterceptor implements ClientHttpRequestInterceptor {
private final B3PropagationService b3PropagationService;
public B3PropagationInterceptor(B3PropagationService b3PropagationService) {
this.b3PropagationService = b3PropagationService;
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, 
ClientHttpRequestExecution execution) throws IOException {
// Extract current context and inject B3 headers
Map<String, String> headers = new HashMap<>();
b3PropagationService.injectContextToHeaders(Context.current(), headers);
// Add B3 headers to the outgoing request
headers.forEach((key, value) -> {
if (value != null) {
request.getHeaders().add(key, value);
}
});
return execution.execute(request, body);
}
}
@Component
public class WebClientB3Config {
private final B3PropagationService b3PropagationService;
public WebClientB3Config(B3PropagationService b3PropagationService) {
this.b3PropagationService = b3PropagationService;
}
@Bean
public WebClient webClient() {
return WebClient.builder()
.filter(b3PropagationFilter())
.build();
}
private ExchangeFilterFunction b3PropagationFilter() {
return ExchangeFilterFunction.ofRequestProcessor(request -> {
// Create headers map and inject B3 context
Map<String, String> headers = new HashMap<>();
b3PropagationService.injectContextToHeaders(Context.current(), headers);
// Build new request with B3 headers
ClientRequest newRequest = ClientRequest.from(request)
.headers(httpHeaders -> {
headers.forEach((key, value) -> {
if (value != null) {
httpHeaders.add(key, value);
}
});
})
.build();
return Mono.just(newRequest);
});
}
}

5. Spring MVC Filter for B3 Propagation

import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class B3PropagationFilter extends OncePerRequestFilter {
private final B3PropagationService b3PropagationService;
private final Tracer tracer;
public B3PropagationFilter(B3PropagationService b3PropagationService, Tracer tracer) {
this.b3PropagationService = b3PropagationService;
this.tracer = tracer;
}
@Override
protected void doFilterInternal(HttpServletRequest request, 
HttpServletResponse response, 
FilterChain filterChain) throws ServletException, IOException {
// Extract B3 context from incoming headers
Context context = b3PropagationService.extractContextFromHttp(request);
// Create span for this request
String spanName = request.getMethod() + " " + request.getRequestURI();
Span span = tracer.spanBuilder(spanName)
.setParent(context)
.setSpanKind(SpanKind.SERVER)
.startSpan();
try (Scope scope = span.makeCurrent()) {
// Setup MDC for logging
b3PropagationService.setupMDC(Context.current());
// Add B3 headers to response for client debugging
addB3ResponseHeaders(response, span);
// Continue with the filter chain
filterChain.doFilter(request, response);
} catch (Exception e) {
span.recordException(e);
throw e;
} finally {
span.end();
b3PropagationService.clearMDC();
}
}
private void addB3ResponseHeaders(HttpServletResponse response, Span span) {
SpanContext spanContext = span.getSpanContext();
response.setHeader(B3PropagationService.X_B3_TRACE_ID, spanContext.getTraceId());
response.setHeader(B3PropagationService.X_B3_SPAN_ID, spanContext.getSpanId());
if (spanContext.isSampled()) {
response.setHeader(B3PropagationService.X_B3_SAMPLED, "1");
} else {
response.setHeader(B3PropagationService.X_B3_SAMPLED, "0");
}
}
}

6. REST Controller with B3 Context

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.StatusCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api")
public class OrderController {
private static final Logger logger = LoggerFactory.getLogger(OrderController.class);
private final B3PropagationService b3PropagationService;
private final OrderService orderService;
private final WebClient webClient;
public OrderController(B3PropagationService b3PropagationService, 
OrderService orderService, 
WebClient webClient) {
this.b3PropagationService = b3PropagationService;
this.orderService = orderService;
this.webClient = webClient;
}
@PostMapping("/orders")
public ResponseEntity<Order> createOrder(@RequestBody OrderRequest request) {
// Log with B3 context
logger.info("Creating order for customer: {}", request.getCustomerId());
try {
// Add custom attributes to span
Span.current().setAttribute("customer.id", request.getCustomerId());
Span.current().setAttribute("order.amount", request.getAmount());
Order order = orderService.createOrder(request);
// Call external service with B3 propagation
callInventoryService(order);
logger.info("Order created successfully: {}", order.getId());
return ResponseEntity.ok(order);
} catch (Exception e) {
logger.error("Failed to create order", e);
Span.current().setStatus(StatusCode.ERROR, "Order creation failed");
Span.current().recordException(e);
throw e;
}
}
@GetMapping("/orders/{orderId}")
public ResponseEntity<Order> getOrder(@PathVariable String orderId) {
logger.debug("Fetching order: {}", orderId);
Span.current().setAttribute("order.id", orderId);
Order order = orderService.getOrder(orderId);
if (order == null) {
Span.current().setStatus(StatusCode.ERROR, "Order not found");
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(order);
}
private void callInventoryService(Order order) {
// This call will automatically propagate B3 headers via WebClient
webClient.post()
.uri("http://inventory-service/api/items/reserve")
.bodyValue(Map.of(
"orderId", order.getId(),
"items", order.getItems()
))
.retrieve()
.bodyToMono(Void.class)
.doOnSuccess(result -> {
logger.info("Inventory reservation successful for order: {}", order.getId());
})
.doOnError(error -> {
logger.error("Inventory reservation failed for order: {}", order.getId(), error);
Span.current().recordException(error);
})
.block(); // In real application, use reactive properly
}
}

7. Async Processing with B3 Context

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
@Service
public class AsyncOrderProcessor {
private final B3PropagationService b3PropagationService;
private final Tracer tracer;
public AsyncOrderProcessor(B3PropagationService b3PropagationService, Tracer tracer) {
this.b3PropagationService = b3PropagationService;
this.tracer = tracer;
}
@Async
public void processOrderAsync(Order order) {
// Capture current context before async execution
Context currentContext = Context.current();
// Execute async task with proper context
CompletableFuture.runAsync(() -> {
try (Scope scope = currentContext.makeCurrent()) {
// Create child span for async processing
Span asyncSpan = tracer.spanBuilder("async.order.process")
.setParent(currentContext)
.setSpanKind(SpanKind.INTERNAL)
.startSpan();
try (Scope asyncScope = asyncSpan.makeCurrent()) {
b3PropagationService.setupMDC(currentContext);
logger.info("Processing order asynchronously: {}", order.getId());
// Actual processing logic
processOrder(order);
logger.info("Async order processing completed: {}", order.getId());
} catch (Exception e) {
asyncSpan.recordException(e);
asyncSpan.setStatus(StatusCode.ERROR, "Async processing failed");
logger.error("Async order processing failed: {}", order.getId(), e);
throw e;
} finally {
asyncSpan.end();
b3PropagationService.clearMDC();
}
}
});
}
private void processOrder(Order order) {
// Order processing logic
Span.current().setAttribute("order.total.items", order.getItems().size());
// ... processing implementation
}
}

8. Message Queue Integration with B3

import org.springframework.jms.annotation.JmsListener;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import javax.jms.TextMessage;
import java.util.Map;
@Component
public class OrderMessageListener {
private final B3PropagationService b3PropagationService;
private final Tracer tracer;
public OrderMessageListener(B3PropagationService b3PropagationService, Tracer tracer) {
this.b3PropagationService = b3PropagationService;
this.tracer = tracer;
}
@JmsListener(destination = "orders.queue")
public void processOrderMessage(TextMessage message, 
@Header Map<String, String> headers) {
// Extract B3 context from message headers
Context context = b3PropagationService.extractContextFromHeaders(headers);
Span span = tracer.spanBuilder("process.order.message")
.setParent(context)
.setSpanKind(SpanKind.CONSUMER)
.startSpan();
try (Scope scope = span.makeCurrent()) {
b3PropagationService.setupMDC(context);
logger.info("Processing order message: {}", message.getJMSMessageID());
// Process the message
processMessage(message);
logger.info("Order message processed successfully: {}", message.getJMSMessageID());
} catch (Exception e) {
span.recordException(e);
span.setStatus(StatusCode.ERROR, "Message processing failed");
logger.error("Failed to process order message: {}", message.getJMSMessageID(), e);
throw e;
} finally {
span.end();
b3PropagationService.clearMDC();
}
}
private void processMessage(TextMessage message) throws Exception {
// Message processing logic
String orderId = message.getText();
Span.current().setAttribute("order.id", orderId);
// ... processing implementation
}
}
@Component
public class OrderMessageProducer {
private final JmsTemplate jmsTemplate;
private final B3PropagationService b3PropagationService;
public OrderMessageProducer(JmsTemplate jmsTemplate, 
B3PropagationService b3PropagationService) {
this.jmsTemplate = jmsTemplate;
this.b3PropagationService = b3PropagationService;
}
public void sendOrderMessage(String destination, String message) {
jmsTemplate.send(destination, session -> {
TextMessage textMessage = session.createTextMessage(message);
// Inject B3 headers into JMS message
Map<String, String> headers = new HashMap<>();
b3PropagationService.injectContextToHeaders(Context.current(), headers);
headers.forEach((key, value) -> {
if (value != null) {
try {
textMessage.setStringProperty(key, value);
} catch (Exception e) {
logger.warn("Failed to set JMS property: {}={}", key, value, e);
}
}
});
return textMessage;
});
}
}

9. Testing B3 Propagation

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class B3PropagationTest {
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private B3PropagationService b3PropagationService;
@Test
void testB3Propagation() {
// Create headers with B3 context
HttpHeaders headers = new HttpHeaders();
headers.add(B3PropagationService.X_B3_TRACE_ID, "463ac35c9f6413ad48485a3953bb6124");
headers.add(B3PropagationService.X_B3_SPAN_ID, "a2fb4a1d1a96d312");
headers.add(B3PropagationService.X_B3_SAMPLED, "1");
HttpEntity<String> entity = new HttpEntity<>(headers);
// Make request
ResponseEntity<String> response = restTemplate.exchange(
"/api/orders/123", 
HttpMethod.GET, 
entity, 
String.class
);
// Verify response contains B3 headers
assertThat(response.getHeaders().containsKey(B3PropagationService.X_B3_TRACE_ID)).isTrue();
assertThat(response.getHeaders().containsKey(B3PropagationService.X_B3_SPAN_ID)).isTrue();
}
@Test
void testB3SingleHeaderPropagation() {
HttpHeaders headers = new HttpHeaders();
headers.add(B3PropagationService.B3_SINGLE_HEADER, 
"463ac35c9f6413ad48485a3953bb6124-a2fb4a1d1a96d312-1");
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange(
"/api/orders/123", 
HttpMethod.GET, 
entity, 
String.class
);
assertThat(response.getStatusCode().is2xxSuccessful()).isTrue();
}
}

10. Configuration Properties

# application.yml
opentelemetry:
tracer:
name: order-service
propagators: b3
sdk:
disabled: false
logging:
pattern:
level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"
management:
tracing:
sampling:
probability: 1.0
endpoints:
web:
exposure:
include: health,info,metrics
spring:
sleuth:
enabled: true
trace-id128: true
sampler:
probability: 1.0
zipkin:
base-url: http://localhost:9411

Best Practices

1. Header Management

// Always check for existing headers before creating new context
public Context extractOrCreateContext(HttpServletRequest request) {
Context extractedContext = b3PropagationService.extractContextFromHttp(request);
if (extractedContext == Context.current()) {
// No context found in headers, create new
return createNewContext();
}
return extractedContext;
}

2. Sampling Decisions

// Respect sampling decisions from upstream
public boolean shouldSample(Context context) {
Span span = Span.fromContext(context);
return span.getSpanContext().isSampled();
}

3. Error Handling

public Context safeExtract(HttpServletRequest request) {
try {
return b3PropagationService.extractContextFromHttp(request);
} catch (Exception e) {
logger.warn("Failed to extract B3 context, creating new", e);
return createNewContext();
}
}

4. Performance Considerations

// Cache propagator instances
@Component
public class B3PropagatorFactory {
private final TextMapPropagator multiHeaderPropagator = B3Propagator.injectingMultiHeaders();
private final TextMapPropagator singleHeaderPropagator = B3Propagator.injectingSingleHeader();
public TextMapPropagator getPropagator(boolean useSingleHeader) {
return useSingleHeader ? singleHeaderPropagator : multiHeaderPropagator;
}
}

Conclusion

This comprehensive B3 Propagation implementation provides:

  • Multiple propagation formats (single header and multi-header)
  • Spring Boot integration with filters and interceptors
  • Async context propagation for background processing
  • Message queue integration with JMS
  • WebClient support for reactive HTTP calls
  • Comprehensive testing utilities
  • Production-ready configuration

Key benefits:

  • Distributed tracing across service boundaries
  • Vendor compatibility with Zipkin and other B3-compatible systems
  • Performance optimized with minimal overhead
  • Developer friendly with easy-to-use abstractions
  • Comprehensive coverage for various communication patterns

This setup ensures that trace context is properly propagated throughout your distributed system, enabling end-to-end tracing and debugging capabilities.

Java Observability, Logging Intelligence & AI-Driven Monitoring (APM, Tracing, Logs & Anomaly Detection)

https://macronepal.com/blog/beyond-metrics-observing-serverless-and-traditional-java-applications-with-thundra-apm/
Explains using Thundra APM to observe both serverless and traditional Java applications by combining tracing, metrics, and logs into a unified observability platform for faster debugging and performance insights.

https://macronepal.com/blog/dynatrace-oneagent-in-java-2/
Explains Dynatrace OneAgent for Java, which automatically instruments JVM applications to capture metrics, traces, and logs, enabling full-stack monitoring and root-cause analysis with minimal configuration.

https://macronepal.com/blog/lightstep-java-sdk-distributed-tracing-and-observability-implementation/
Explains Lightstep Java SDK for distributed tracing, helping developers track requests across microservices and identify latency issues using OpenTelemetry-based observability.

https://macronepal.com/blog/honeycomb-io-beeline-for-java-complete-guide-2/
Explains Honeycomb Beeline for Java, which provides high-cardinality observability and deep query capabilities to understand complex system behavior and debug distributed systems efficiently.

https://macronepal.com/blog/lumigo-for-serverless-in-java-complete-distributed-tracing-guide-2/
Explains Lumigo for Java serverless applications, offering automatic distributed tracing, log correlation, and error tracking to simplify debugging in cloud-native environments. (Lumigo Docs)

https://macronepal.com/blog/from-noise-to-signals-implementing-log-anomaly-detection-in-java-applications/
Explains how to detect anomalies in Java logs using behavioral patterns and machine learning techniques to separate meaningful incidents from noisy log data and improve incident response.

https://macronepal.com/blog/ai-powered-log-analysis-in-java-from-reactive-debugging-to-proactive-insights/
Explains AI-driven log analysis for Java applications, shifting from manual debugging to predictive insights that identify issues early and improve system reliability using intelligent log processing.

https://macronepal.com/blog/titliel-java-logging-best-practices/
Explains best practices for Java logging, focusing on structured logs, proper log levels, performance optimization, and ensuring logs are useful for debugging and observability systems.

https://macronepal.com/blog/seeking-a-loguru-for-java-the-quest-for-elegant-and-simple-logging/
Explains the search for simpler, more elegant logging frameworks in Java, comparing modern logging approaches that aim to reduce complexity while improving readability and developer experience.

Leave a Reply

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


Macro Nepal Helper