CodeGym /Kurse /C# SELF /Performance-Monitoring und Metrik-Sammlung

Performance-Monitoring und Metrik-Sammlung

C# SELF
Level 64 , Lektion 3
Verfügbar

1. Einführung

Stell dir vor, du bist der Betreiber einer Webseite und jeden Tag kommen tausend Nutzer. In den Logs siehst du, dass alles funktioniert, fast keine Errors (höchstens vergisst mal jemand das Passwort oder hat Probleme mit Captcha). „Alles super!“ denkst du.

Und plötzlich schreibt jemand den Support an: die Seite ist extrem langsam, Seiten laden in 10 Sekunden. Du schaust in die Logs — keine Errors! Hurra? Nein, denn Logs erzählen was passiert ist (oder nicht), aber sie sagen nicht, wie schnell oder langsam es passiert ist, wieviel Ressourcen dafür nötig waren und wie sich dieses Verhalten mit wachsender Last verändert hat.

Hier kommen Metriken ins Spiel — messbare Kennzahlen der Anwendung. Das sind nicht nur Fehlerzahlen, sondern auch durchschnittliche Antwortzeiten, Speicherverbrauch, Requests pro Sekunde und andere Indikatoren, anhand derer man die Gesundheit des Systems beurteilen kann.

Vergleich:
Logs — das ist "was passiert ist".
Metriken — das ist "wie gut/schlecht das System arbeitet".
Tracing — das ist "warum das System so arbeitet (im Detail)".

Welche Metriken gibt es und was sollte man sammeln?

Hauptarten von Metriken:

Metric-Typ Beispiel Wofür nützlich
Counters (Zähler) Anzahl Requests, Errors, Failures Trends, Alerts, Last
Histograms Antwortzeit, Paketgröße Wertverteilung, Percentiles
Gauges Speicherverbrauch, CPU Aktueller Zustand einer Resource
Sum Gesamtdatenvolumen, Bytes Gesamtvolumen an Operationen über einen Zeitraum

Beispiele:

  • Durchschnitts- und 95-ter Perzentil der Antwortzeit auf GET-Requests.
  • Anzahl der aktuell online befindlichen Nutzer.
  • Speichernutzung (Private Bytes, Working Set).
  • Häufigkeit von Errors vom Typ 500/503.
  • DB-Queries pro Minute.

Diese Kennzahlen erlauben nicht nur, Probleme zu finden, sondern sie auch zu verhindern — erhöhte Serverlast oder ein „schleichender“ Anstieg der Antwortzeiten können auf zukünftige Ausfälle hinweisen.

2. Wie Metrik-Sammlung in .NET und der OpenTelemetry-Ökosystem funktioniert

Allgemeine Architektur

In modernem .NET (ab .NET 6, besonders in .NET 8/9) gibt es ein Standard-System zur Metrik-Sammlung, basierend auf OpenTelemetry.

So funktioniert das:

  1. Der Application-Code ruft Methoden auf, um Counters zu inkrementieren, Gauges zu registrieren, Histograms zu schreiben.
  2. Das OpenTelemetry Metrics SDK sammelt diese Metriken (im Speicher) und sendet sie periodisch.
  3. Ein Metrics-Exporter überträgt sie an ein Monitoring-System (Prometheus, Application Insights, Grafana Cloud, Datadog etc.).
  4. Das Monitoring-Backend aggregiert, speichert, visualisiert, erstellt Alerts und Dashboards.
Schema-Blockdiagramm:

[Deine Anwendung] 
       ⬇ 
 [Metrik-Sammlung (OpenTelemetry SDK)]
       ⬇
 [Metrics-Exporter (Prometheus, AI, Datadog, ...)]
       ⬇
 [Monitoring-System/Dashboards/Alerts]

3. Praxis: Grundlagen der Arbeit mit Metriken in C#

Einfache interne Metriken: System.Diagnostics.Metrics

.NET stellt einen eingebauten Mechanismus für Metriken bereit — System.Diagnostics.Metrics.

Hauptakteure: Meter, Counter<T>, Histogram<T>, ObservableGauge<T>.

Beispiel: Page-Visit-Counter


// Erstellen eines Meter (typischerweise ein Meter für die ganze Anwendung)
using System.Diagnostics.Metrics;

static Meter meter = new Meter("MyCompany.MyApp", "1.0");

// Registrieren eines Counters
static Counter<long> homePageVisits = meter.CreateCounter<long>("HomePageVisits");

// Irgendwo im Controller oder Service...
public void HomePageRequested()
{
    homePageVisits.Add(1);
    // Restliche Seitenverarbeitung
}

Kommentare:

  • Meter ist die «Fabrik» für Metriken, mit einem eindeutigen Namen (Namespace der App/Firma).
  • CreateCounter<long> erstellt einen Counter; Inkrement via Add(1).

Beispiel: Messung der Antwortzeit


static Histogram<double> pageLoadTime = meter.CreateHistogram<double>("PageLoadTimeMs");

// Im Request-Handler:
public void OnRequest()
{
    var stopwatch = System.Diagnostics.Stopwatch.StartNew();

    // ...der eigentliche Request...

    stopwatch.Stop();
    pageLoadTime.Record(stopwatch.Elapsed.TotalMilliseconds);
}

Gauges für dynamische Werte sammeln

Ein Gauge ist ein Wert, der sich über die Zeit ändert: Anzahl verbundener Nutzer, aktueller Speicherverbrauch, usw.


static ObservableGauge<int> onlineUserGauge = meter.CreateObservableGauge(
    "OnlineUsers", 
    () => GetOnlineUserCount());

// Wo GetOnlineUserCount eine Methode ist, die den aktuellen Wert zurückgibt
static int GetOnlineUserCount()
{
    // Hier muss deine echte Logik stehen!
    return ActiveUserList.Count;
}

Im echten Einsatz läuft das asynchron: die App liefert Metrikwerte, der Exporter nimmt sie und gibt sie weiter (z.B. scrapt Prometheus den Endpoint "/metrics").

Metriken in einer modernen ASP.NET Core-Anwendung hinzufügen

Für ASP.NET Core ist vieles out-of-the-box verfügbar. Es reicht, das Paket OpenTelemetry.Instrumentation.AspNetCore hinzuzufügen, dann gibt es HTTP-Metriken, Antwortzeiten, Error-Zahlen usw.

Beispiel-Konfiguration in Program.cs:


using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics =>
    {
        metrics
            .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MyApp"))
            .AddAspNetCoreInstrumentation() // HTTP-Metriken
            .AddRuntimeInstrumentation()    // .NET CLR Runtime-Metriken
            .AddProcessInstrumentation()    // CPU/Memory des Prozesses
            .AddMeter("MyCompany.MyApp")    // deine Metriken
            .AddPrometheusExporter();       // Export nach Prometheus
    });

var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

Deine Anwendung liefert jetzt Metriken unter dem Pfad /metrics, die von Prometheus oder anderen Systemen gescrapt werden können.

4. Praktische Beispiele für den Einsatz von Metriken

Performance-Monitoring in realen Projekten

Wir fügen Metriken hinzu, um herauszufinden:

  • Welchen durchschnittlichen und maximalen RPS (requests per second) hält die API aus?
  • Wo sind die Bottlenecks: ein Endpoint braucht 300 ms, ein anderer — 2000 ms?
  • Wie viel Zeit geht für DB-Calls drauf? (eigene Histograms hinzufügen)

Beispiel: DB-Response-Time überwachen


static Histogram<double> dbQueryDuration = meter.CreateHistogram<double>("DbQueryDurationMs");

public async Task<List<Product>> GetProductsAsync()
{
    var sw = Stopwatch.StartNew();
    var result = await _db.Products.ToListAsync();
    sw.Stop();
    dbQueryDuration.Record(sw.Elapsed.TotalMilliseconds);
    return result;
}

Beispiel: Anzahl der Errors zählen


static Counter<long> apiErrors = meter.CreateCounter<long>("ApiErrors");

public IActionResult SomeEndpoint()
{
    try
    {
        // irgendeine Aktion
        return Ok();
    }
    catch (Exception)
    {
        apiErrors.Add(1);
        throw;
    }
}

Arbeiten mit Labels (Tags) für Metriken

Wichtig ist, Daten nach sinnvollen Merkmalen zu gruppieren: Endpoint, Error-Typ, User-Type usw.


homePageVisits.Add(
    1, 
    KeyValuePair.Create<string, object>("UserType", "Admin"));

Oder für ein Histogram:


dbQueryDuration.Record(
    sw.Elapsed.TotalMilliseconds, 
    KeyValuePair.Create<string, object>("QueryType", "GetProducts"));

Dank Tags kann man in Grafana nicht nur App-weite Grafiken bauen, sondern auch für spezifische Segmente filtern.

5. Integration mit Prometheus, Application Insights, Datadog, Grafana

Exporter und Integration

  • Prometheus — populäres Open-Source-Monitoring, de-facto Standard für Cloud und Kubernetes.
  • Application Insights — Cloud-Integration für Azure.
  • Datadog, Grafana Cloud — für professionelle Infrastrukturen.

All diese Systeme können Metriken aus .NET via OpenTelemetry-Exporter sammeln. Dokumentation zu OTel Exportern

Prometheus (Schritte):

  1. NuGet-Paket hinzufügen: OpenTelemetry.Exporter.Prometheus.
  2. .AddPrometheusExporter() zu den Metric-Registrierungen hinzufügen.
  3. In Grafana den Datasource auf Prometheus konfigurieren und Dashboards bauen.

Nützliche Links:

6. Besonderheiten, Fallstricke und typische Fehler

Ein häufiger Fehler ist übermäßige Detailtiefe bei Tags. Wenn Tags zu viele einzigartige Werte bekommen (z. B. User-ID/Order-ID), explodiert die Anzahl der Time-Series — das überlastet das Metrik-Storage und treibt die Kosten in die Höhe (so genanntes cardinality explosion). Halte Tags „grobkörnig“.

Entwickler ignorieren manchmal System.Diagnostics.Metrics und vorhandene Tools und bauen stattdessen eine „Fahrrad“-Lösung mit Logs und Timern. Am Ende ist das Monitoring schlechter integriert und schwerer zu warten. Nutze Standardtools und automatische Instrumentation.

Ein weiterer Fehler ist, dass Metriken gesammelt, aber nicht exportiert werden. Die Konfiguration des Exporters ist Pflicht: füge z. B. .AddPrometheusExporter() hinzu und vergewissere dich, dass der Endpoint /metrics für Scraping erreichbar ist.

Und schließlich die Verwechslung der Metriktypen: die durchschnittliche Antwortzeit wird über einen Counter berechnet, obwohl man ein Histogram verwenden sollte — sonst siehst du keine Spitzen und Verteilungen. Counters sind für Mengen; Histograms für Zeiten/Größen/Verteilungen; Gauges für aktuelle Zustände.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION