In this lesson, you will learn how to inject and propagate a unique identifier across service boundaries to trace the lifecycle of a single user request through a distributed system.
The Problem of Fragmented Logs
In a monolithic application, you can simply follow the stack trace. However, in a distributed architecture, a single user action may trigger a chain reaction of HTTP calls or message queue events across multiple services. When a failure occurs, searching for "OrderFailed" across ten different log streams leads to "needle in a haystack" debugging. Correlation IDs solve this by acting as a "thread of connection" that persists throughout the entire request flow.
Implementing Correlation IDs
To implement this, we rely on the Correlation-ID header. The entry-point service (usually an API Gateway or the initial Web API) generates a Guid, attaches it to the request context, and passes it downstream.
In ASP.NET Core, we typically use middleware to capture this ID at the start of the request:
public async Task InvokeAsync(HttpContext context)
{
// Try to get existing ID from header or generate a new one
if (!context.Request.Headers.TryGetValue("X-Correlation-ID", out var correlationId))
{
correlationId = Guid.NewGuid().ToString();
}
// Add to Logging Scope so all logs in this request include the ID
using (logger.BeginScope(new Dictionary<string, object> { ["CorrelationId"] = correlationId }))
{
context.Items["CorrelationId"] = correlationId;
await _next(context);
}
}
Propagation is Key
The pattern is only effective if every service respects the chain. When your service makes an outgoing call using HttpClient, you must manually inject that CorrelationId into the outgoing request headers. By ensuring every log entry is tagged with this ID, you can filter your log aggregator (like Seq, ELK, or Application Insights) for a single ID and see a perfect, chronological reconstruction of the request path, regardless of which server or microservice generated the log entry.
Key Takeaway: By binding a unique Correlation ID to a request scope, you transform disparate log entries into a unified, traceable timeline that bridges the gap between disconnected microservices.