Ana içeriğe geç

Jaeger ile Dağıtık Tracing

Jaeger, dağıtık sistemlerde istek izleme ve performans analizi sağlar; yanlış konfigürasyon trace kaybına ve analiz zorluğuna yol açar.


1. Jaeger’ı Doğrudan Exporter ile Kullanmak

Yanlış Kullanım: Her servisten doğrudan Jaeger’a trace göndermek.

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddJaegerExporter(o =>
        {
            o.AgentHost = "jaeger";
            o.AgentPort = 6831;
        }));
// Her servis ayrı bağlantı kurar, Jaeger agent yükü artar

İdeal Kullanım: OTLP Collector üzerinden Jaeger’a gönderin.

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .SetResourceBuilder(ResourceBuilder.CreateDefault()
            .AddService("order-service", serviceVersion: "1.0.0")
            .AddAttributes(new Dictionary<string, object>
            {
                ["deployment.environment"] = builder.Environment.EnvironmentName
            }))
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddOtlpExporter(o => o.Endpoint = new Uri("http://otel-collector:4317")));
# otel-collector-config.yml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317

exporters:
  jaeger:
    endpoint: jaeger:14250
    tls:
      insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [jaeger]

2. Service Bağımlılık Grafiğini Oluşturamamak

Yanlış Kullanım: Servis adlarını tutarsız tanımlamak.

// Servis 1
.AddService("OrderSvc")
// Servis 2
.AddService("order-service")
// Servis 3
.AddService("Order_Service")
// Jaeger'da 3 farklı servis görünür, dependency graph bozulur

İdeal Kullanım: Tutarlı isimlendirme convention’ı kullanın.

public static class ServiceInfo
{
    public const string Name = "order-service";
    public const string Version = "1.0.0";
}

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .SetResourceBuilder(ResourceBuilder.CreateDefault()
            .AddService(ServiceInfo.Name, serviceVersion: ServiceInfo.Version)));

3. Span Adlarını Yanlış Vermek

Yanlış Kullanım: Span adlarında dinamik değer kullanmak.

using var activity = Source.StartActivity($"GET /api/orders/{orderId}");
// Her orderId için farklı span adı → Jaeger'da gruplama yapılamaz

İdeal Kullanım: Span adını sabit tutup detayları attribute olarak ekleyin.

using var activity = Source.StartActivity("GET /api/orders/{id}");
activity?.SetTag("order.id", orderId);
activity?.SetTag("http.method", "GET");
activity?.SetTag("http.route", "/api/orders/{id}");
// Jaeger'da aynı endpoint'in tüm çağrıları gruplanır

4. Trace Verilerini Saklama Süresini Ayarlamamak

Yanlış Kullanım: Varsayılan retention ile disk alanı tükenmesi.

# docker-compose.yml
jaeger:
  image: jaegertracing/all-in-one:latest
  # Varsayılan in-memory storage, restart'ta veri kaybolur

İdeal Kullanım: Production-ready storage ve retention ayarlayın.

# docker-compose.yml
jaeger:
  image: jaegertracing/all-in-one:latest
  environment:
    - SPAN_STORAGE_TYPE=elasticsearch
    - ES_SERVER_URLS=http://elasticsearch:9200
    - ES_MAX_SPAN_AGE=168h  # 7 gün
    - ES_NUM_SHARDS=3
    - ES_NUM_REPLICAS=1

elasticsearch:
  image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
  environment:
    - discovery.type=single-node
    - xpack.security.enabled=false
  volumes:
    - es-data:/usr/share/elasticsearch/data

5. Trace’leri Loglarla İlişkilendirmemek

Yanlış Kullanım: Log ve trace ayrı sistemlerde, ilişki yok.

_logger.LogError("Sipariş oluşturulamadı: {OrderId}", orderId);
// Log'dan trace'e, trace'den log'a geçiş yapılamaz

İdeal Kullanım: Trace ID’yi loglara otomatik ekleyin.

builder.Logging.AddOpenTelemetry(options =>
{
    options.IncludeScopes = true;
    options.IncludeFormattedMessage = true;
    options.AddOtlpExporter();
});

// Serilog ile
builder.Host.UseSerilog((context, config) => config
    .Enrich.WithProperty("ServiceName", "order-service")
    .Enrich.With<ActivityEnricher>()
    .WriteTo.Console(outputTemplate:
        "[{Timestamp:HH:mm:ss} {Level}] {Message} TraceId={TraceId} SpanId={SpanId}{NewLine}"));

public class ActivityEnricher : ILogEventEnricher
{
    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory factory)
    {
        var activity = Activity.Current;
        if (activity is null) return;

        logEvent.AddPropertyIfAbsent(factory.CreateProperty("TraceId", activity.TraceId));
        logEvent.AddPropertyIfAbsent(factory.CreateProperty("SpanId", activity.SpanId));
    }
}