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:
- Il codice dell'applicazione chiama metodi per incrementare counters, registrare gauge, scrivere histogram.
- Lo SDK OpenTelemetry Metrics raccoglie queste metriche (in memoria) e le invia periodicamente.
- L'exporter delle metriche le trasferisce nel sistema di monitoring scelto (Prometheus, Application Insights, Grafana Cloud, Datadog, ecc.).
- 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):
- Installare il package NuGet: OpenTelemetry.Exporter.Prometheus.
- Aggiungere .AddPrometheusExporter() nella registrazione delle metriche.
- 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.
GO TO FULL VERSION