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.