Skip to content

Distributed Tracing


Definition

Distributed Tracing Overview


Spans and Traces

// TRACE STRUCTURE
{
    "traceId": "abc123",              // Same for entire request
    "spans": [
        {
            "spanId": "span-1",
            "parentSpanId": null,      // Root span
            "operationName": "HTTP GET /orders",
            "serviceName": "api-gateway",
            "startTime": "2024-01-15T10:00:00.000Z",
            "duration": 1200,          // milliseconds
            "tags": {
                "http.method": "GET",
                "http.url": "/orders/123",
                "http.status_code": 200
            }
        },
        {
            "spanId": "span-2",
            "parentSpanId": "span-1",  // Child of span-1
            "operationName": "getOrder",
            "serviceName": "order-service",
            "startTime": "2024-01-15T10:00:00.050Z",
            "duration": 800,
            "tags": {
                "order.id": "123"
            }
        },
        {
            "spanId": "span-3",
            "parentSpanId": "span-2",  // Child of span-2
            "operationName": "SELECT orders",
            "serviceName": "order-service",
            "startTime": "2024-01-15T10:00:00.100Z",
            "duration": 50,
            "tags": {
                "db.type": "postgresql",
                "db.statement": "SELECT * FROM orders WHERE id = ?"
            }
        }
    ]
}

Implementation

// USING OPENTELEMETRY (standard)

// 1. AUTOMATIC INSTRUMENTATION
// Add agent: -javaagent:opentelemetry-javaagent.jar
// Automatically instruments HTTP, DB, messaging

// 2. MANUAL SPANS
@Autowired
private Tracer tracer;

public Order processOrder(String orderId) {
    Span span = tracer.spanBuilder("processOrder")
        .setAttribute("order.id", orderId)
        .startSpan();

    try (Scope scope = span.makeCurrent()) {
        // Child spans automatically linked to this parent
        Order order = fetchOrder(orderId);
        validateOrder(order);
        processPayment(order);
        return order;
    } catch (Exception e) {
        span.setStatus(StatusCode.ERROR, e.getMessage());
        span.recordException(e);
        throw e;
    } finally {
        span.end();
    }
}

// 3. CONTEXT PROPAGATION (automatic with instrumentation)
// For manual propagation:
@RestController
class OrderController {

    @GetMapping("/orders/{id}")
    public Order getOrder(@PathVariable String id,
                          @RequestHeader HttpHeaders headers) {
        // Extract context from incoming request
        Context context = propagator.extract(Context.current(), headers,
            (carrier, key) -> carrier.getFirst(key));

        try (Scope scope = context.makeCurrent()) {
            // New spans are children of extracted context
            return orderService.getOrder(id);
        }
    }
}

// Inject context to outgoing request
webClient.get()
    .uri("/payments/" + orderId)
    .headers(headers -> propagator.inject(Context.current(), headers,
        (carrier, key, value) -> carrier.add(key, value)))
    .retrieve()
    .bodyToMono(Payment.class);

W3C Trace Context

W3C Trace Context


Tools & Architecture

Tracing Architecture


Tips & Tricks

Tips and Tricks


  • *