Advanced · Distribute

Propagate Context Across Service Boundaries

Use Micrometer Observation and OpenTelemetry export to connect request, message, and async work.

Primary source: Spring Cloud 2024 Reference Documentation

40 min

Lesson 3 of 4

1d streak

The Architectural Question

Per-service logs cannot reconstruct cross-service latency; without W3C traceparent propagation the story of one slow checkout stays fragmented.

What this guide proves

  • Propagate standard trace headers
  • Keep baggage small and non-sensitive
  • Preserve context across executors and message headers

Distributed Tracing Foundations: W3C Trace Context

In a microservices architecture, a single user click may trigger requests across an API Gateway, an Order Service, an Inventory Service, and a Payment Service. When a request fails or experiences latency, diagnosing the bottleneck without distributed tracing is impossible.

W3C Trace Context standardizes cross-service context propagation via two HTTP headers:

1. traceparent: Encodes version, 16-byte Trace ID (shared across all hops), 8-byte Span ID (representing the current operation), and trace flags.

2. tracestate: Carries vendor-specific tracking metadata across systems.

W3C Traceparent Structure
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
              |  |                                | |                | |
        version  |---------------- Trace ID -------| |-- Span ID -----| flags

Micrometer Tracing with OpenTelemetry Exporter

In Spring Boot 3.5, Spring Cloud Sleuth is replaced by Micrometer Tracing. To stream distributed traces to observability backends (Zipkin, Jaeger, Grafana Tempo, Datadog), include micrometer-tracing-bridge-otel and the OpenTelemetry exporter.

src/main/resources/application.yml
management:
  tracing:
    sampling:
      probability: 1.0 # 100% trace sampling in dev/staging; tune in production
  otlp:
    tracing:
      endpoint: http://tempo.internal:4318/v1/traces

Baggage Propagation and MDC Log Correlation

Micrometer Tracing automatically binds the active Trace ID and Span ID into the SLF4J Mapped Diagnostic Context (MDC), ensuring that every log line emitted across any service contains correlation identifiers for centralized aggregation in Loki or Elasticsearch.

Baggage allows propagating custom business metadata (e.g. tenant-id or user-id) transparently across HTTP and Kafka hops without modifying service method signatures.

Inspecting Distributed Trace Waterfalls

Verify end-to-end trace correlation and span waterfalls:

snippet.bash
# 1. Send HTTP request and inspect returned traceparent header
curl -i http://localhost:8080/api/orders
# Response headers: traceparent: 00-e2b8...-f4a1...-01

# 2. Inspect application log output:
# 2026-09-07 14:32:01.104 INFO [order-service,e2b8...,f4a1...] c.e.o.OrderController - Created order ORD-101

# 3. Query OpenTelemetry collector / Tempo for Trace ID:
# Verify waterfall display linking Gateway -> OrderService -> Kafka -> PaymentService

Active Recall Checkpoint

What is the difference between a Trace ID and a Span ID in distributed tracing?

Hint: Think of a tree structure: the tree has one Trace ID; each branch has its own Span ID.

Show answer & explanation

A Trace ID is globally unique and shared by every service hop involved in a single end-to-end user transaction. A Span ID is unique to a specific individual unit of work or service execution within that trace.

Runtime Architecture & Flow

Reliable event flow · runtime visualization

Propagate Context Across Service Boundaries

Local commit

Propagate standard trace headers

Publisher

Keep baggage small and non-sensitive

Broker

Preserve context across executors and message headers

Consumer

Trace checkout through gateway, order service, catalog, and one Kafka event.

Working Implementation

Code Walkthrough

Trace checkout through gateway, order service, catalog, and one Kafka event.

DistributedTracingOtelLab.java
package com.example.tracing;

import io.micrometer.tracing.Baggage;
import io.micrometer.tracing.Span;
import io.micrometer.tracing.Tracer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class DistributedTracingOtelLab {

    private static final Logger log = LoggerFactory.getLogger(DistributedTracingOtelLab.class);
    private final Tracer tracer;

    public DistributedTracingOtelLab(Tracer tracer) {
        this.tracer = tracer;
    }

    public void executeWithTraceCorrelation(String customerId) {
        // Set W3C Baggage to propagate customer identifier across downstream microservices
        try (Baggage baggage = this.tracer.createBaggageInScope("customer-id", customerId)) {
            Span newSpan = this.tracer.nextSpan().name("order-fraud-check").start();
            try (Tracer.SpanInScope ws = this.tracer.withSpan(newSpan)) {
                log.info("Checking fraud score for customer: {}", customerId);
                // Log automatically contains traceId and spanId in MDC!
            } finally {
                newSpan.end();
            }
        }
    }
}
W3C traceparent header propagates 16-byte Trace ID and 8-byte Span ID across HTTP and Kafka hops.
Micrometer Tracing binds Trace ID directly into SLF4J MDC for automatic log aggregation.
Baggage propagates custom business identifiers (e.g. tenant-id) across network hops transparently.

Synthesis & Production Verification

Deliberate Failure Condition

  1. Break: Spawn async work with a plain executor instead of an instrumented one
  2. Observe: Spans arrive orphaned and the trace ends at the hop that handed off
  3. Repair: Propagate context through Observation-aware wrappers and message headers, and keep baggage small and free of sensitive values.

Production Acceptance Criteria

  • Propagate traceparent across HTTP calls and one Kafka topic
  • Preserve trace context across executor handoffs
  • Keep baggage minimal and non-sensitive by policy

Architectural & Technical Review

Core Interview Questions

Q01What is the format and significance of the W3C traceparent header in distributed tracing?Show answer

traceparent contains four dash-delimited fields: version (00), 16-byte hex Trace ID (global across all hops), 8-byte hex Span ID (unique to this segment), and trace flags (01 for sampled). It enables vendor-neutral tracing.

Q02How does Micrometer Tracing integrate with SLF4J MDC to simplify log analysis?Show answer

Micrometer automatically writes traceId and spanId into the MDC. Standard logging patterns (%X{traceId}, %X{spanId}) include correlation IDs on every log line, allowing developers to query all logs for a transaction in Loki.

Q03How does Baggage differ from standard Trace Context headers in OpenTelemetry?Show answer

Standard trace context propagates only IDs and sampling flags. Baggage propagates arbitrary business key-value pairs (e.g. tenant-id, account-type) across all network hops without modifying method signatures.

Production Pitfalls

Critical Anti-Patterns

  • Pitfall: Omitting trace context propagation headers when creating manual HTTP or Kafka requests — Breaks the distributed trace waterfall, creating orphan spans that cannot be linked to the root user request.
  • Pitfall: Sampling 100% of traces in high-traffic production environments without rate limits — Generates massive network egress and storage costs on APM observability platforms like Datadog or Tempo.

Primary Documentation & Specifications

Supplementary Module Resources