1. Introducción
Imagina que eres el administrador de un sitio y cada día te llegan mil usuarios. En los logs ves que todo funciona, casi no hay errores (a lo sumo alguien olvida la contraseña o falla el captcha de vez en cuando). «¡Todo perfecto!» — piensas.
Y de repente alguien escribe al soporte: el sitio va muy lento, las páginas tardan 10 segundos en cargar. Revisas los logs — ¡no hay errores! ¿Genial? No, porque los logs cuentan qué pasó (o no pasó), pero no dicen qué tan rápido o lento ocurrió, cuántos recursos se necesitaron, ni cómo cambió ese comportamiento con el aumento de carga.
Aquí entran en escena las métricas — características medibles del funcionamiento de la aplicación. No es solo el número de errores, sino el tiempo medio de respuesta, el volumen de memoria consumida, las peticiones por segundo y otros indicadores con los que se juzga la salud del sistema.
Comparación:
Logs — son "qué pasó".
Métricas — son "qué tan bien/mal funciona el sistema".
Trazas — son "por qué el sistema funciona así (en detalle)".
¿Qué tipos de métricas hay y qué conviene recopilar?
Tipos principales de métricas:
| Tipo de métrica | Ejemplo | Para qué sirve |
|---|---|---|
| Counters (Contadores) | Número de peticiones, errores, fallos | Tendencias, alertas, carga |
| Histograms | Tiempo de respuesta, tamaño de paquete | Distribución de valores, percentiles |
| Gauges | Uso de memoria, CPU | Estado actual del recurso |
| Sum | Volumen total de datos, bytes | Volumen total de operaciones en un periodo |
Ejemplos:
- El tiempo medio y el percentil 95 de respuesta a una GET.
- El número de usuarios online ahora mismo.
- Uso de memoria (Private Bytes, Working Set).
- Frecuencia de errores tipo 500/503.
- Consultas a la BD por minuto.
Estos indicadores permiten no solo encontrar problemas, sino también prevenirlos — un aumento de carga en el servidor o un crecimiento gradual del tiempo de respuesta pueden avisar de fallos futuros.
2. Cómo funciona la recopilación de métricas en .NET y el ecosistema OpenTelemetry
Arquitectura general
En el .NET moderno (desde .NET 6, y especialmente en .NET 8/9) existe un sistema estándar de recopilación de métricas basado en OpenTelemetry.
Así es como funciona:
- El código de la aplicación llama a métodos para incrementar contadores, registrar gauges, y escribir histogramas.
- El SDK de OpenTelemetry Metrics recoge esas métricas (en memoria) y las envía periódicamente.
- El exportador de métricas las transmite al sistema de monitorización elegido (Prometheus, Application Insights, Grafana Cloud, Datadog, etc.).
- El backend de monitorización agrega, almacena, visualiza, crea alertas y dashboards.
Esquema simplificado:
[Tu aplicación]
⬇
[Recopilación de métricas (OpenTelemetry SDK)]
⬇
[Exportador de métricas (Prometheus, AI, Datadog, ...)]
⬇
[Sistema de monitorización/dashboards/alertas]
3. Práctica: Fundamentos de trabajo con métricas en C#
Métricas internas simples: System.Diagnostics.Metrics
.NET ofrece un mecanismo incorporado para métricas — System.Diagnostics.Metrics.
Jugadores principales: Meter, Counter<T>, Histogram<T>, ObservableGauge<T>.
Ejemplo: contador de visitas a la página
// Creamos un Meter (normalmente uno por toda la aplicación)
using System.Diagnostics.Metrics;
static Meter meter = new Meter("MyCompany.MyApp", "1.0");
// Registramos el contador
static Counter<long> homePageVisits = meter.CreateCounter<long>("HomePageVisits");
// En algún lugar del controlador o servicio...
public void HomePageRequested()
{
homePageVisits.Add(1);
// El resto del código que maneja la página
}
Comentarios:
- Meter — es la "fábrica" de métricas, con un nombre único (espacio de nombres de la aplicación/compañía).
- CreateCounter<long> — crea el contador; se incrementa con Add(1).
Ejemplo: medir el tiempo de respuesta
static Histogram<double> pageLoadTime = meter.CreateHistogram<double>("PageLoadTimeMs");
// En el handler de la petición:
public void OnRequest()
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
// ...aquí va la propia petición...
stopwatch.Stop();
pageLoadTime.Record(stopwatch.Elapsed.TotalMilliseconds);
}
Recopilar gauges para valores dinámicos
Un gauge es una métrica que cambia con el tiempo: número de usuarios conectados, volumen de memoria actual, etc.
static ObservableGauge<int> onlineUserGauge = meter.CreateObservableGauge(
"OnlineUsers",
() => GetOnlineUserCount());
// Donde GetOnlineUserCount - método que devuelve el valor actual
static int GetOnlineUserCount()
{
// Aquí debería ir tu lógica real!
return ActiveUserList.Count;
}
En el mundo real todo esto funciona de forma asíncrona: la aplicación reporta valores de métricas y el exportador los recoge y los expone (por ejemplo, Prometheus scrapea el endpoint "/metrics").
Añadir métricas en una aplicación ASP.NET Core moderna
Para ASP.NET Core muchas cosas están listas para usar. Basta con añadir el paquete OpenTelemetry.Instrumentation.AspNetCore y aparecerán métricas de HTTP, tiempos de respuesta, número de errores, etc.
Ejemplo de configuración en 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() // métricas HTTP
.AddRuntimeInstrumentation() // métricas del runtime .NET CLR
.AddProcessInstrumentation() // CPU/memoria del proceso
.AddMeter("MyCompany.MyApp") // tus métricas
.AddPrometheusExporter(); // exportar a Prometheus
});
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Ahora tu aplicación expondrá métricas en /metrics, que Prometheus u otros sistemas pueden scrappear.
4. Ejemplos prácticos de uso de métricas
Monitorización del rendimiento en proyectos reales
Conectamos métricas para saber:
- Cuál es el RPS medio y máximo (requests per second) que aguanta la API?
- Dónde están los "cuellos de botella": un endpoint responde en 300 ms y otro en 2000 ms?
- Cuánto tiempo se pasa en llamadas a la BD? (añadimos nuestros histogramas)
Ejemplo: controlar el tiempo de respuesta a la base de datos
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;
}
Ejemplo: contar errores
static Counter<long> apiErrors = meter.CreateCounter<long>("ApiErrors");
public IActionResult SomeEndpoint()
{
try
{
// alguna acción
return Ok();
}
catch (Exception)
{
apiErrors.Add(1);
throw;
}
}
Trabajar con labels (tags) para métricas
Es importante agrupar datos por atributos útiles: endpoint, tipo de error, tipo de usuario, etc.
homePageVisits.Add(
1,
KeyValuePair.Create<string, object>("UserType", "Admin"));
O para un histograma:
dbQueryDuration.Record(
sw.Elapsed.TotalMilliseconds,
KeyValuePair.Create<string, object>("QueryType", "GetProducts"));
Gracias a los tags, en Grafana puedes crear gráficas no solo para toda la app, sino para segmentos específicos.
5. Integración con Prometheus, Application Insights, Datadog, Grafana
Exportadores e integración
- Prometheus — monitoring open-source muy popular, estándar de facto en cloud y Kubernetes.
- Application Insights — integración cloud para Azure.
- Datadog, Grafana Cloud — para infraestructuras profesionales.
Todos estos sistemas pueden recoger métricas de .NET a través de los exportadores de OpenTelemetry. Documentación sobre exportadores OTel
Prometheus (pasos):
- Añadir el paquete NuGet: OpenTelemetry.Exporter.Prometheus.
- Añadir .AddPrometheusExporter() en el registro de métricas.
- Configurar en Grafana un datasource apuntando a Prometheus y crear dashboards.
Enlaces útiles:
6. Particularidades, trampas y errores típicos
Uno de los errores comunes es la excesiva granularidad de los tags. Si das a los tags demasiados valores únicos (por ejemplo, ID de usuario/pedido), el número de series temporales se disparará — esto sobrecargará el almacenamiento de métricas y aumentará los costos (lo que se conoce como cardinality explosion). Mantén los tags "coarse-grained".
Los desarrolladores a veces ignoran System.Diagnostics.Metrics y las herramientas listas, implementando soluciones caseras con logs y timers. Al final el monitoring queda peor integrado y es más difícil de mantener. Usa las herramientas estándar y la instrumentación automática.
Otro fallo es que las métricas se recopilan pero no se exportan. La configuración del exportador es obligatoria: añade, por ejemplo, .AddPrometheusExporter() y asegúrate de que el endpoint /metrics esté disponible para el scraping.
Y, por último, confundir los tipos de métricas: calcular el tiempo medio de respuesta con un Counter cuando deberías usar un Histogram — si haces eso no verás picos ni la distribución. Counters — para recuentos; histograms — para tiempos/tamaños/distribuciones; gauges — para estados actuales.
GO TO FULL VERSION