CodeGym /Corsi /C# SELF /Monitoraggio delle prestazioni e raccolta delle metriche

Monitoraggio delle prestazioni e raccolta delle metriche

C# SELF
Livello 64 , Lezione 3
Disponibile

1. Introduzione

Immaginiamo che tu sia l'amministratore di un sito e ogni giorno arrivino mille utenti. Nei log vedi che tutto funziona, quasi nessun errore (al massimo qualcuno dimentica la password o sbaglia la captcha). "Tutto ok!" — pensi.

E all'improvviso qualcuno scrive al supporto: il sito rallenta terribilmente, le pagine si aprono in 10 secondi. Vai a guardare i log — non ci sono errori! Evviva? No, perché i log dicono cosa è successo (o non è successo), ma non dicono quanto velocemente o lentamente è successo, quante risorse ha richiesto e come questo comportamento è cambiato con l'aumentare del carico.

Ed è qui che entrano in gioco le metriche — caratteristiche misurabili del funzionamento dell'applicazione. Non sono solo il numero di errori, ma anche il tempo medio di risposta, la quantità di memoria consumata, il numero di richieste al secondo e altri indicatori che permettono di valutare la salute del sistema.

Confronto:
I log sono "cosa è successo".
Le metriche sono "quanto bene/ male funziona il sistema".
La trace è "perché il sistema si comporta così (nei dettagli)".

Che tipi di metriche esistono e cosa val la pena raccogliere?

Tipologie principali di metriche:

Tipo di metrica Esempio Per cosa serve
Contatori (Counters) Numero di richieste, errori, fallimenti Tendenze, alert, carico
Istogrammi Tempo di risposta, dimensione del pacchetto Distribuzione dei valori, percentili
Gauge Utilizzo memoria, CPU Stato corrente della risorsa
Somme (Sum) Volume totale dei dati, byte Volume totale delle operazioni in un periodo

Esempi:

  • Tempo medio e il 95° percentile della latenza delle richieste GET.
  • Numero di utenti online in questo momento.
  • Utilizzo della memoria (Private Bytes, Working Set).
  • Frequenza di errori tipo 500/503.
  • Query al DB al minuto.

Questi indicatori permettono non solo di trovare problemi, ma anche di prevenire — un aumento del carico sul server o una "deriva" del tempo di risposta possono segnalare futuri malfunzionamenti.

2. Come è strutturata la raccolta delle metriche in .NET e nell'ecosistema OpenTelemetry

Architettura generale

Nel .NET moderno (a partire da .NET 6, specialmente in .NET 8/9) esiste un sistema standard per la raccolta delle metriche basato su OpenTelemetry.

Ecco come funziona:

  1. Il codice dell'applicazione chiama metodi per incrementare counters, registrare gauge, scrivere histogram.
  2. Lo SDK OpenTelemetry Metrics raccoglie queste metriche (in memoria) e le invia periodicamente.
  3. L'exporter delle metriche le trasferisce nel sistema di monitoring scelto (Prometheus, Application Insights, Grafana Cloud, Datadog, ecc.).
  4. Il backend di monitoring aggrega, memorizza, visualizza, costruisce alert e dashboard.
Schema semplificato:

[La tua applicazione] 
       ⬇ 
 [Raccolta metriche (OpenTelemetry SDK)]
       ⬇
 [Exporter metriche (Prometheus, AI, Datadog, ...)]
       ⬇
 [Sistema di monitoring/dashboard/alert]

3. Pratica: Basi del lavoro con le metriche in C#

Metriche semplici interne: System.Diagnostics.Metrics

.NET fornisce un meccanismo integrato per le metriche — System.Diagnostics.Metrics.

Giocatori principali: Meter, Counter<T>, Histogram<T>, ObservableGauge<T>.

Esempio: contatore visite alla pagina


// Creiamo un Meter (di solito uno per tutta l'applicazione)
using System.Diagnostics.Metrics;

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

// Registriamo il counter
static Counter<long> homePageVisits = meter.CreateCounter<long>("HomePageVisits");

// Da qualche parte nel controller o servizio...
public void HomePageRequested()
{
    homePageVisits.Add(1);
    // Il resto del codice di gestione della pagina
}

Commenti:

  • Meter è la "fabbrica" per le metriche, con un nome unico (namespace dell'applicazione/azienda).
  • CreateCounter<long> crea un counter; incremento tramite Add(1).

Esempio: misurare il tempo di risposta


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

// Nel handler della richiesta:
public void OnRequest()
{
    var stopwatch = System.Diagnostics.Stopwatch.StartNew();

    // ...qui la richiesta vera e propria...

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

Raccogliere gauge per valori dinamici

Il gauge è un valore che cambia nel tempo: numero di utenti connessi, volume di memoria corrente, ecc.


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

// Dove GetOnlineUserCount è un metodo che ritorna il valore attuale
static int GetOnlineUserCount()
{
    // Qui va la tua logica reale!
    return ActiveUserList.Count;
}

Nel mondo reale tutto questo funziona in modo asincrono: l'applicazione segnala i valori delle metriche e l'exporter li raccoglie e li espone (per esempio Prometheus fa scrape dell'endpoint "/metrics").

Aggiungere metriche in un'app ASP.NET Core moderna

Per ASP.NET Core molte cose sono già pronte. Basta aggiungere il package OpenTelemetry.Instrumentation.AspNetCore, e otterrai metriche sulle richieste HTTP, tempi di risposta, numero di errori ecc.

Esempio di configurazione 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() // metriche HTTP
            .AddRuntimeInstrumentation()    // metriche runtime .NET CLR
            .AddProcessInstrumentation()    // CPU/memoria del processo
            .AddMeter("MyCompany.MyApp")    // tue metriche
            .AddPrometheusExporter();       // export verso Prometheus
    });

var app = builder.Build();

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

app.Run();

Ora la tua applicazione esporrà le metriche all'indirizzo /metrics, che possono essere scrape-ate da Prometheus o raccolte da altri sistemi.

4. Esempi pratici di utilizzo delle metriche

Monitoraggio delle prestazioni in progetti reali

Colleghiamo le metriche per capire:

  • Qual è il RPS (requests per second) medio e di picco che l'API regge?
  • Dove sono i colli di bottiglia: un endpoint è a 300 ms, un altro a 2000 ms?
  • Quanto tempo si spende nelle chiamate al DB? (aggiungiamo histogram custom)

Esempio: monitoriamo il tempo delle query al DB


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;
}

Esempio: contiamo gli errori


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

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

Lavorare con label (tag) per le metriche

È importante raggruppare i dati per attributi utili: endpoint, tipo di errore, tipo di utente, ecc.


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

O per un histogram:


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

Grazie ai tag in Grafana puoi costruire grafici non solo per tutta l'app, ma anche per segmenti specifici.

5. Integrazione con Prometheus, Application Insights, Datadog, Grafana

Exporter e integrazioni

  • Prometheus — monitoring open-source popolare, de-facto standard per cloud e Kubernetes.
  • Application Insights — integrazione cloud per Azure.
  • Datadog, Grafana Cloud — per infrastrutture professionali.

Tutti questi sistemi possono raccogliere metriche da .NET tramite exporter di OpenTelemetry. Documentazione sugli exporter OTel

Prometheus (passi):

  1. Installare il package NuGet: OpenTelemetry.Exporter.Prometheus.
  2. Aggiungere .AddPrometheusExporter() nella registrazione delle metriche.
  3. In Grafana configurare il datasource verso Prometheus e costruire le dashboard.

Link utili:

6. Peculiarità, insidie e errori tipici

Uno degli errori comuni è la dettaglio eccessivo dei tag. Se dai ai tag troppi valori unici (per esempio ID utente/ordine), il numero di time series esploderà — questo porta al sovraccarico dello storage delle metriche e all'aumento dei costi (la cosiddetta cardinality explosion). Mantieni i tag "coarse-grained".

Gli sviluppatori a volte ignorano System.Diagnostics.Metrics e gli strumenti pronti, reinventando la ruota con log e timer. Alla fine il monitoring è meno integrato e più difficile da mantenere. Usa gli strumenti standard e l'instrumentazione automatica.

Un altro errore è che le metriche vengono raccolte ma non esportate. La configurazione dell'exporter è obbligatoria: aggiungi, per esempio, .AddPrometheusExporter() e assicurati che l'endpoint /metrics sia raggiungibile per lo scraping.

Infine, confusione tra i tipi di metriche: la latenza media viene contata con Counter, mentre dovrebbe essere usato Histogram — altrimenti non vedrai i picchi e la distribuzione. I counters sono per i conteggi; gli histogram per tempi/dimensioni/distribuzioni; i gauge per stati correnti.

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