Ana içeriğe geç

Prometheus ile Metrik Toplama

Prometheus, zaman serisi verileri toplar ve uyarı mekanizmaları sağlar; yanlış metrik tasarımı anlamsız dashboardlara ve gözden kaçan sorunlara yol açar.


1. Metrik Tanımlamamak

Yanlış Kullanım: Uygulama metrikleri olmadan çalışmak.

app.MapGet("/api/orders", async (IOrderService service) =>
{
    return await service.GetAllAsync();
    // İstek sayısı, süre, hata oranı bilinmiyor
});

İdeal Kullanım: Custom metrikler tanımlayıp kaydedin.

builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics => metrics
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddRuntimeInstrumentation()
        .AddPrometheusExporter());

app.MapPrometheusScrapingEndpoint();

// Custom metrikler
public class OrderMetrics
{
    private readonly Counter<long> _ordersCreated;
    private readonly Histogram<double> _orderProcessingDuration;
    private readonly UpDownCounter<long> _activeOrders;

    public OrderMetrics(IMeterFactory meterFactory)
    {
        var meter = meterFactory.Create("OrderService");
        _ordersCreated = meter.CreateCounter<long>("orders.created", "order", "Toplam oluşturulan sipariş");
        _orderProcessingDuration = meter.CreateHistogram<double>("orders.processing.duration", "ms");
        _activeOrders = meter.CreateUpDownCounter<long>("orders.active");
    }

    public void RecordOrderCreated(string status) =>
        _ordersCreated.Add(1, new KeyValuePair<string, object?>("status", status));

    public void RecordDuration(double ms) => _orderProcessingDuration.Record(ms);
}

2. High-Cardinality Label Kullanmak

Yanlış Kullanım: Benzersiz değerleri label olarak eklemek.

_requestCounter.Add(1,
    new("user_id", userId),        // Milyonlarca benzersiz değer
    new("request_id", requestId),  // Her istekte farklı
    new("timestamp", DateTime.Now.ToString()));
// Prometheus belleği tükenir, sorgu performansı çöker

İdeal Kullanım: Düşük kardinaliteli, anlamlı label’lar kullanın.

_requestCounter.Add(1,
    new("endpoint", "/api/orders"),
    new("method", "GET"),
    new("status_code", "200"),
    new("customer_tier", "premium"));
// Sınırlı sayıda benzersiz kombinasyon

3. RED Metodunu Uygulamamak

Yanlış Kullanım: Anlamsız metrikler toplamak.

var meter = meterFactory.Create("App");
meter.CreateCounter<long>("app.started");
meter.CreateCounter<long>("app.requests");
// Rate, Error, Duration bilgisi eksik

İdeal Kullanım: RED (Rate, Errors, Duration) metodunu uygulayın.

public class HttpMetrics
{
    private readonly Counter<long> _requestCount;    // Rate
    private readonly Counter<long> _errorCount;      // Errors
    private readonly Histogram<double> _duration;    // Duration

    public HttpMetrics(IMeterFactory meterFactory)
    {
        var meter = meterFactory.Create("HttpServer");
        _requestCount = meter.CreateCounter<long>("http.server.requests");
        _errorCount = meter.CreateCounter<long>("http.server.errors");
        _duration = meter.CreateHistogram<double>("http.server.duration", "ms");
    }

    public void RecordRequest(string endpoint, string method, int statusCode, double durationMs)
    {
        var tags = new TagList
        {
            { "endpoint", endpoint },
            { "method", method },
            { "status_code", statusCode.ToString() }
        };

        _requestCount.Add(1, tags);
        _duration.Record(durationMs, tags);

        if (statusCode >= 500)
            _errorCount.Add(1, tags);
    }
}

4. Histogram Bucket’larını Ayarlamamak

Yanlış Kullanım: Varsayılan bucket sınırları ile ölçüm yapmak.

var histogram = meter.CreateHistogram<double>("request.duration");
// Varsayılan bucket'lar iş yüküne uygun olmayabilir

İdeal Kullanım: İş yüküne uygun bucket sınırları belirleyin.

builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics => metrics
        .AddView("http.server.duration", new ExplicitBucketHistogramConfiguration
        {
            Boundaries = new double[] { 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000 }
        })
        .AddView("db.query.duration", new ExplicitBucketHistogramConfiguration
        {
            Boundaries = new double[] { 1, 5, 10, 25, 50, 100, 500 }
        }));

5. Alert Kuralları Tanımlamamak

Yanlış Kullanım: Metrikleri toplamak ama alert oluşturmamak.

# prometheus.yml - sadece scrape, alert yok
scrape_configs:
  - job_name: 'dotnet-app'
    static_configs:
      - targets: ['app:8080']

İdeal Kullanım: Anlamlı alert kuralları tanımlayın.

# alert_rules.yml
groups:
  - name: dotnet-app-alerts
    rules:
      - alert: HighErrorRate
        expr: rate(http_server_errors_total[5m]) / rate(http_server_requests_total[5m]) > 0.05
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Yüksek hata oranı (> 5%)"

      - alert: HighLatency
        expr: histogram_quantile(0.95, rate(http_server_duration_bucket[5m])) > 1000
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "P95 latency 1 saniyeyi aştı"

      - alert: HighMemoryUsage
        expr: dotnet_gc_heap_size_bytes / 1024 / 1024 > 500
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Heap boyutu 500MB üzerinde"