Ana içeriğe geç

Distributed Tracing ile Hata Takibi

Distributed tracing, dağıtık sistemlerde isteklerin servisler arası yolculuğunu izler; eksik veya yanlış tracing hata tespitini zorlaştırır ve MTTR’ı artırır.


1. Trace Context Propagation Yapmamak

Yanlış Kullanım: Servisler arası çağrılarda trace context’i taşımamak.

public class OrderService
{
    public async Task<Order> CreateAsync(CreateOrderDto dto)
    {
        var order = await SaveOrderAsync(dto);
        await _httpClient.PostAsJsonAsync("/api/inventory/reserve", order);
        await _httpClient.PostAsJsonAsync("/api/payment/charge", order);
        // Her servis bağımsız trace oluşturur, ilişkilendirme yok
    }
}

İdeal Kullanım: W3C Trace Context ile otomatik propagation sağlayın.

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddSqlClientInstrumentation()
        .SetResourceBuilder(ResourceBuilder.CreateDefault()
            .AddService("order-service"))
        .AddOtlpExporter(o => o.Endpoint = new Uri("http://collector:4317")));

// HttpClient otomatik olarak traceparent header'ı ekler
// traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01

2. Span’lara Yeterli Bilgi Eklememek

Yanlış Kullanım: Boş span’lar oluşturmak.

using var activity = ActivitySource.StartActivity("ProcessOrder");
var result = await ProcessOrderAsync(order);
// Span'da hiçbir detay yok, hata analizi yapılamaz

İdeal Kullanım: Span’lara tag ve event ekleyin.

private static readonly ActivitySource Source = new("OrderService");

public async Task<Order> ProcessOrderAsync(Order order)
{
    using var activity = Source.StartActivity("ProcessOrder", ActivityKind.Internal);
    activity?.SetTag("order.id", order.Id);
    activity?.SetTag("order.total", order.TotalAmount);
    activity?.SetTag("customer.id", order.CustomerId);

    try
    {
        activity?.AddEvent(new ActivityEvent("ValidatingOrder"));
        await ValidateAsync(order);

        activity?.AddEvent(new ActivityEvent("ChargingPayment"));
        await ChargePaymentAsync(order);

        activity?.SetTag("order.status", "completed");
        return order;
    }
    catch (Exception ex)
    {
        activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
        activity?.RecordException(ex);
        throw;
    }
}

3. Sampling Stratejisi Belirlememek

Yanlış Kullanım: Tüm istekleri trace etmek.

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .SetSampler(new AlwaysOnSampler()));
// Production'da her istek trace edilir, yüksek maliyet ve performans kaybı

İdeal Kullanım: Akıllı sampling ile maliyeti kontrol edin.

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .SetSampler(new ParentBasedSampler(new TraceIdRatioBasedSampler(0.1)))
        .AddAspNetCoreInstrumentation(options =>
        {
            options.Filter = context =>
            {
                // Health check ve static file isteklerini hariç tut
                var path = context.Request.Path.Value;
                return !path!.Contains("/health") && !path.Contains("/static");
            };
        }));

4. Baggage Kullanmamak

Yanlış Kullanım: İş bilgilerini her serviste tekrar sorgulamak.

// Payment Service - müşteri bilgisi için Order Service'e geri sorgu
public async Task ChargeAsync(int orderId)
{
    var order = await _orderClient.GetOrderAsync(orderId);
    var customer = await _customerClient.GetCustomerAsync(order.CustomerId);
    // Gereksiz ağ çağrısı
}

İdeal Kullanım: Baggage ile iş bağlamını servisler arası taşıyın.

// Order Service - baggage ekle
Baggage.SetBaggage("customer.tier", customer.Tier);
Baggage.SetBaggage("order.priority", order.Priority.ToString());

// Payment Service - baggage oku
var customerTier = Baggage.GetBaggage("customer.tier");
if (customerTier == "Premium")
    await PriorityChargeAsync(orderId);

5. Trace Backend Olmadan Çalışmak

Yanlış Kullanım: Trace verilerini sadece konsola yazdırmak.

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddConsoleExporter()); // Trace verileri kaybolur, arama/analiz yapılamaz

İdeal Kullanım: OTLP exporter ile merkezi backend’e gönderin.

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddSqlClientInstrumentation()
        .AddOtlpExporter(options =>
        {
            options.Endpoint = new Uri("http://otel-collector:4317");
            options.Protocol = OtlpExportProtocol.Grpc;
        }))
    .WithMetrics(metrics => metrics
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddOtlpExporter());