Skip to content

Tracing

Paulo Gomes da Cruz Junior edited this page Apr 10, 2026 · 1 revision

Distributed Tracing with B3 Header Propagation

Overview

Distributed tracing allows you to follow a request as it travels through multiple services—frontend, backend, legacy systems—giving you visibility into performance bottlenecks, failures, and latency. This implementation uses the B3 propagation format, a standard supported by Zipkin, OpenTelemetry, and Spring Cloud Sleuth.

Goals

  • Enable full traceability across the request lifecycle
  • Inject and propagate B3 headers (X-B3-TraceId, X-B3-SpanId) across services
  • Enrich logs with trace context for correlation
  • Support observability tools like Zipkin, Jaeger, and OpenTelemetry

Dependencies

Backend

  • Spring Boot 3
  • Micrometer Tracing
  • SLF4J + Logback (for logging)
  • Custom B3HeaderForwarder class

Frontend

  • React
  • Axios
  • Custom utility: getB3Headers() to generate trace/span IDs

Configuration

application.yml (Backend)

management:
  tracing:
    enabled: true
    propagation:
      type: B3
    sampling:
      probability: 0.7
    baggage:
      enabled: true
    correlation:
      enabled: true
      fields:
        - X-B3-TraceId
        - X-B3-SpanId
    remote-fields:
      - X-B3-TraceId
      - X-B3-SpanId

Logging Pattern

logging:
  pattern:
    correlation: "[${spring.application.name:},%X{${X-B3-TraceId:traceId}:-},%X{${X-B3-SpanId:spanId}:-}]"

Implementation Details

Frontend implementation

  • Utility function getB3Headers() generates 64-bit hex strings:
export const getB3Headers = () => ({
  'X-B3-TraceId': generateHex(32),
  'X-B3-SpanId': generateHex(16),
});
  • Injected into every API call via BackendApiConfig.HEADERS.

Backend implementation

  • B3HeaderForwarder class injects headers into outbound RestClient calls:
public class B3HeaderForwarder implements ClientHttpRequestInitializer {
  private final Tracer tracer;

  @Override
  public void initialize(ClientHttpRequest request) {
    Span currentSpan = tracer.currentSpan();
    if (currentSpan != null) {
      request.getHeaders().add("X-B3-TraceId", currentSpan.context().traceId());
      request.getHeaders().add("X-B3-SpanId", currentSpan.context().spanId());
    }
  }
}
  • Beans updated to include this forwarder:
@Bean
public RestClient legacyApi(HrsConfiguration config, JwtForwarderRequestInitializer jwt, B3HeaderForwarder b3) {
  return RestClient.builder()
    .baseUrl(config.getLegacyApi().getAddress())
    .requestInitializer(jwt)
    .requestInitializer(b3)
    .build();
}
  • Annotated service methods with @NewSpan to create spans:
@NewSpan
public List<CodeDescriptionDto> getDistrictCodes() {
  log.info("Fetching district codes");
  return legacyApiProvider.getDistrictCodes();
}

Mermaid Diagram: Request Lifecycle

sequenceDiagram
  participant FE as Frontend
  participant BE as Backend
  participant LEG as Legacy API

  FE->>BE: HTTP Request with B3 Headers
  BE->>BE: Extract TraceId & SpanId
  BE->>LEG: Forward B3 Headers
  LEG->>LEG: Log TraceId & SpanId
  LEG-->>BE: Response with context
  BE-->>FE: Response with trace info

  Note over FE,LEG: TraceId and SpanId are preserved across all hops
Loading

🔗 References

Waste Plus Wiki

Home

Architecture

Codes

Decisions

Knowledge Base

Development

Changes

Clone this wiki locally