Correlation ID Nedir? Mikroservislerde İstek Takibi
Correlation ID, tek bir kullanıcı isteğine verilen ve o isteğin değdiği her serviste aynı kalan iz numarasıdır. Gateway, ödeme API'si, kuyruk tüketicisi ve e-posta worker'ı aynı numarayı loguna yazar. "Ödeme alındı ama mail gitmedi" sorusu, saatlerce dosya gezmek yerine bir ID filtresiyle biter.
OpenTelemetry ve full tracing büyüdükçe correlation id "eski iş" sanılır. Değildir. Trace yokken de, kısmi sistemde de, destek ekranında da bu numara konuşulur. Ödeme geçidinde transaction id'nin neden hayati olduğunu ödeme yazısında anlatmıştım. Correlation id, o fikrin tüm HTTP ve mesaj sınırına yayılmış halidir.
Ne işe yarar?
- Bir isteğin hangi serviste, kaç ms'de kırıldığını bulmak
- Destek ekibine "şu numarayı söyleyin" demek
- Webhook, retry ve kuyruk yeniden işlemede aynı hikâyeyi bağlamak
- Hata alarmında ilgili log dilimini tek sorguda kesmek
Request ID ile karıştırmayın. Bazı ekipler her hop'ta yeni request id üretir, correlation id'yi sabit tutar. İkisi de tutulabilir. Sabit kalan şey correlation id'dir; değişen hop kimliği span / request id'dir. Tek alan kullanacaksanız sabit olanı seçin ve adını her serviste aynı yapın.
Kim üretir, kim taşır?
İstemci veya ilk gateway üretir. Yoksa sizin API üretir. Sonraki her çıkışta header olarak iletirsiniz. Yaygın header adı: X-Correlation-ID. W3C traceparent kullanıyorsanız onun trace-id'si de aynı rolü görür. İkisini birden üretip çakıştırmayın; birini kanonik seçin.
public sealed class CorrelationIdMiddleware
{
public const string HeaderName = "X-Correlation-ID";
private readonly RequestDelegate _next;
public CorrelationIdMiddleware(RequestDelegate next) => _next = next;
public async Task Invoke(HttpContext context)
{
var incoming = context.Request.Headers[HeaderName].FirstOrDefault();
var correlationId = string.IsNullOrWhiteSpace(incoming)
? Guid.NewGuid().ToString("N")
: incoming.Trim();
context.TraceIdentifier = correlationId;
context.Response.Headers[HeaderName] = correlationId;
using (Serilog.Context.LogContext.PushProperty("CorrelationId", correlationId))
{
await _next(context);
}
}
}
// Program.cs
app.UseMiddleware<CorrelationIdMiddleware>();
// Giden HttpClient çağrıları
services.AddHttpClient("payments")
.AddHttpMessageHandler<CorrelationIdHandler>();
public sealed class CorrelationIdHandler : DelegatingHandler
{
private readonly IHttpContextAccessor _http;
public CorrelationIdHandler(IHttpContextAccessor http) => _http = http;
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken ct)
{
var id = _http.HttpContext?.Request.Headers[
CorrelationIdMiddleware.HeaderName].FirstOrDefault()
?? _http.HttpContext?.TraceIdentifier;
if (!string.IsNullOrEmpty(id))
request.Headers.TryAddWithoutValidation(
CorrelationIdMiddleware.HeaderName, id);
return base.SendAsync(request, ct);
}
}
Cevap header'ına da yazın. Mobil ve SPA, hata ekranında bu numarayı gösterebilsin. Destek "saat kaçta ne oldu" diye sormasın; numarayı alsın.
Kuyruk ve webhook
HTTP bitince iz bitmez. Mesajı kuyruğa atarken correlation id'yi property / header olarak koyun. Consumer başlarken aynı property'yi log context'ine alsın. Aksi halde API logu ile worker logu iki ayrı evren olur.
// üretici
var props = channel.CreateBasicProperties();
props.Headers = new Dictionary<string, object>
{
["X-Correlation-ID"] = correlationId
};
// tüketici
var id = GetHeader(ea.BasicProperties, "X-Correlation-ID")
?? Guid.NewGuid().ToString("N");
using (LogContext.PushProperty("CorrelationId", id))
{
await HandleAsync(ea.Body);
}
Webhook tarafında sağlayıcı sizin ID'nizi bilmez. Gelen event_id'yi saklayın; kendi correlation id'nizi üretirken log satırına ikisini birden yazın. Böylece banka olay kimliği ile sizin iziniz birleşir. Webhook disiplini webhook yazısında.
Log formatı ve arama
Düz metin logda correlation id kaybolur. Yapılandırılmış log şarttır. Serilog + JSON sink ile her olayda CorrelationId property'si dursun. Loki, Elastic veya Seq'te CorrelationId = "..." filtresi tek tuş olsun.
Log.Information(
"Payment completed for {OrderId} in {ElapsedMs} ms",
orderId, elapsedMs);
// CorrelationId LogContext'ten otomatik eklenir
Kart numarası, CVV, TCKN yazmayın. İz numarası kişisel veri değildir; gövde çoğu zaman öyledir. KVKK tarafı kontrol listesinde.
OpenTelemetry ile birlikte
Activity / trace id varsa correlation id'yi yeniden icat etmeyin. Ya trace id'yi correlation olarak kullanın ya da ikisini logda yan yana tutup birini "destek numarası" ilan edin. Çift üretim, destek ekranında iki farklı numara doğurur; kimse hangisini arayacağını bilmez.
Küçük monolitde bile middleware koyun. Bugün tek process, yarın iki worker. Numara yoksa yarın geç kalırsınız. Distributed lock çatışmasını da aynı ID ile görürsünüz; kilit yazısı burada.
Kısa cevaplar
Correlation ID nedir?
Bir isteğin tüm servis ve worker loglarında aynı kalan iz numarasıdır. Hatanın hangi hop'ta olduğunu tek filtreyle buldurur.
Kim üretmeli?
İstemci veya API gateway. Gelmezse ilk ASP.NET Core middleware üretir ve hem response header'ına hem giden çağrılara yazar.
Hangi header kullanılır?
Çok ekip X-Correlation-ID kullanır. W3C Trace Context varsa traceparent içindeki trace-id kanonik seçilebilir. Önemli olan tek isimde anlaşmaktır.
Kuyrukta nasıl taşınır?
Mesaj header/property olarak yazılır; consumer log context'ine alınır. Taşınmazsa API ile worker logları birleşmez.
OpenTelemetry varken gerekir mi?
Evet, en azından destek ve eski servisler için. Trace yoksa correlation id tek bağdır. Trace varsa tek kanonik numara seçin; iki rakip ID üretmeyin.