Distributed Tracing
Definition

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


Tips & Tricks
