Lección 1 de 214 min
Métricas, logs y trazas: Prometheus y Grafana
Los tres pilares de la observabilidad, PromQL, paneles, alertas y objetivos de nivel de servicio.
Son las tres de la mañana y la tienda online va lenta. ¿Desde cuándo? ¿A cuántos clientes afecta? ¿Es la base de datos, el servicio de pagos o el último despliegue? Si lo único que tienes es una terminal abierta en el servidor, las respuestas llegan tarde y a ciegas.
La observabilidad es la capacidad de responder a esas preguntas desde fuera, a partir de los datos que emite el propio sistema. En esta lección verás sus tres fuentes de datos clásicas (métricas, logs y trazas), las dos herramientas de código abierto más extendidas para trabajar con ellas, Prometheus y Grafana, y cómo decidir con números cuánta fiabilidad es suficiente.
Los tres pilares: métricas, logs y trazas
Cada pilar responde a una pregunta distinta. Las métricas te dicen que algo va mal, los logs qué pasó exactamente y las trazas dónde se fue el tiempo. Se complementan: casi nunca basta con uno solo.
Métricas
Números medidos a lo largo del tiempo: baratos de guardar y perfectos para alertar.
- Responde a:
- ¿Va algo mal?
- Volumen:
- Bajo
Una métrica es un valor numérico con marca de tiempo: peticiones por segundo, porcentaje de CPU, milisegundos de respuesta. Cada serie se identifica por un nombre y unas etiquetas (labels), como http_requests_total{ruta="/pagar", codigo="500"}, que te permiten filtrar y agrupar.
Como solo guardan números agregados, ocupan poco aunque recibas millones de peticiones y se pueden conservar durante meses. Son la base de los paneles y de las alertas: «avísame si la tasa de errores supera el 2 % durante cinco minutos».
Úsalo cuando
Para vigilar la salud del sistema de un vistazo, ver tendencias a lo largo de semanas y disparar alertas.
Ten en cuenta
Una métrica agrega: te dice que el 3 % de los pagos falla, pero no cuáles ni por qué. Para eso necesitas los logs y las trazas.
# HELP http_requests_total Peticiones HTTP atendidas.
# TYPE http_requests_total counter
http_requests_total{ruta="/pagar",codigo="200"} 18234
http_requests_total{ruta="/pagar",codigo="500"} 41
# HELP pedidos_en_cola Pedidos pendientes de procesar.
# TYPE pedidos_en_cola gauge
pedidos_en_cola 7Logs
El diario del sistema: un registro de cada suceso, con todo su detalle.
- Responde a:
- ¿Qué pasó exactamente?
- Volumen:
- Alto
Un log es un mensaje que la aplicación escribe cuando ocurre algo: una petición atendida, un pago rechazado, una excepción. Cada línea lleva al menos la hora, un nivel (debug, info, warn, error) y un mensaje.
Los logs estructurados se escriben como objetos JSON en lugar de frases sueltas, así que una herramienta puede filtrar por campo (nivel = error y pedido = 8812) en vez de buscar texto a ciegas. Si además cada petición lleva un id de correlación (correlation id), que se crea al entrar y se pasa a cada servicio, puedes reunir todas las líneas de una misma petición aunque vengan de cinco servidores distintos.
Úsalo cuando
Para investigar un caso concreto: qué le pasó al pedido 8812, qué error exacto lanzó el servicio de pagos, qué hizo el usuario justo antes del fallo.
Ten en cuenta
Son el pilar más caro: ocupan mucho y se acumulan rápido. No registres cada paso con nivel info y nunca escribas contraseñas, tokens ni datos personales.
import pino from "pino";
const log = pino(); // escribe cada línea como JSON
app.use((req, res, next) => {
// Reutiliza el id si llega de otro servicio; si no, crea uno
const id = req.get("x-request-id") ?? crypto.randomUUID();
req.log = log.child({ correlacion: id });
res.set("x-request-id", id);
next();
});
app.post("/pedidos", async (req, res) => {
req.log.info({ pedido: req.body.id }, "Pedido recibido");
// {"level":30,…,"correlacion":"c7f3…","pedido":8812,"msg":"Pedido recibido"}
});Trazas
El recorrido completo de una petición por todos los servicios, paso a paso.
- Responde a:
- ¿Dónde se va el tiempo?
- Unidad:
- Span
Cuando una petición atraviesa la pasarela, el servicio de pedidos, el de inventario y la base de datos, una traza la sigue de punta a punta. Se compone de spans: cada span es una operación con nombre, hora de inicio, duración y atributos, como «consulta a la base de datos, 12 ms».
Todos los spans de una petición comparten un trace id y cada uno apunta a su span padre, así que se pueden dibujar como una cascada. Para que el id viaje entre servicios se envía en una cabecera HTTP; el estándar es traceparent, de W3C Trace Context. Es la versión automática y precisa del id de correlación.
Úsalo cuando
Cuando el sistema está repartido en varios servicios y necesitas saber cuál de ellos hace lenta una petición o dónde empieza un error.
Ten en cuenta
Guardar la traza de cada petición sale caro, así que se suele hacer muestreo (sampling): se conserva solo una parte, por ejemplo todas las que fallan y un porcentaje pequeño del resto. Y si un servicio no propaga la cabecera, la traza se corta en dos.
POST /pedidos pasarela ████████████████████ 820 ms
└─ crear-pedido pedidos ███████████████████ 805 ms
├─ SELECT clientes postgres █ 11 ms
├─ reservar-stock inventario ████████████████ 760 ms ← aquí
└─ INSERT pedidos postgres █ 14 msMonitorizar no es lo mismo que observar
La monitorización clásica vigila problemas que ya conoces: «avísame si el disco pasa del 90 %». La observabilidad busca que puedas investigar también los que no esperabas, cruzando los tres pilares: una alerta de métricas te lleva a las trazas lentas de ese minuto y, de ahí, a los logs de esa petición concreta.
Prometheus: recoger métricas y alertar
Prometheus es el sistema de métricas de código abierto más extendido, sobre todo en entornos con Kubernetes. Guarda las métricas como series temporales, las consulta con su propio lenguaje, PromQL, y evalúa reglas de alerta.
Prometheus y el modelo pull
Un servidor que pasa cada pocos segundos a recoger las métricas de tus servicios.
- Origen:
- SoundCloud
- Fundación:
- CNCF
- Puerto:
- 9090
En lugar de esperar a que las aplicaciones le envíen datos, Prometheus va a buscarlos: cada cierto intervalo hace una petición HTTP a la ruta /metrics de cada objetivo (target) y guarda lo que lee. Esto es el modelo pull y cada lectura se llama scrape. Si un objetivo no responde, su métrica up vale 0, lo que ya es una alerta en sí misma.
Tu aplicación expone sus métricas con una librería cliente (las hay para Node.js, Java, Go, Python…). Para software que no habla el formato de Prometheus, como una base de datos o el propio sistema operativo, se usa un exporter: un pequeño programa que traduce sus estadísticas a /metrics. Dos muy conocidos son node_exporter, para la máquina, y postgres_exporter, para PostgreSQL.
Úsalo cuando
Para métricas de servicios e infraestructura que funcionan de forma continua, sobre todo en Kubernetes, donde Prometheus descubre solo los objetivos nuevos.
Ten en cuenta
El modelo pull encaja mal con tareas de pocos segundos, que pueden terminar antes del siguiente scrape; para ellas existe el Pushgateway. Y un servidor de Prometheus guarda los datos en local: para conservarlos años o tener alta disponibilidad se combina con sistemas como Thanos o Grafana Mimir.
global:
scrape_interval: 15s
scrape_configs:
- job_name: tienda-api
static_configs:
- targets: ["api:3000"]
- job_name: maquinas
static_configs:
- targets: ["web-1:9100", "web-2:9100"] # node_exporter
rule_files:
- alertas.yml
alerting:
alertmanagers:
- static_configs:
- targets: ["alertmanager:9093"]Counter, gauge e histogram
Los tipos de métrica: algo que solo sube, algo que sube y baja, y una distribución.
- Tipos:
- counter, gauge, histogram, summary
Un counter (contador) solo aumenta, salvo cuando el proceso se reinicia y vuelve a cero: peticiones atendidas, errores, bytes enviados. Su valor absoluto apenas dice nada; lo interesante es cuánto crece por segundo. Un gauge (indicador) sube y baja libremente: memoria en uso, usuarios conectados, pedidos en cola.
Un histogram (histograma) reparte las observaciones en cubos (buckets): cuántas peticiones tardaron menos de 0,1 s, menos de 0,3 s, menos de 1 s… Con eso calculas percentiles como el p95, el tiempo por debajo del cual queda el 95 % de las peticiones. Existe un cuarto tipo, el summary, que calcula los percentiles en la propia aplicación, pero no permite combinarlos entre varias instancias.
Úsalo cuando
Counter para todo lo que se cuenta, gauge para fotos del estado actual e histogram para latencias y tamaños, donde la media engaña.
Ten en cuenta
No midas la latencia con una media: si 99 peticiones tardan 100 ms y una tarda 10 s, la media sale unos 200 ms y el usuario que esperó 10 s desaparece. Por convención, los counters terminan en _total y la unidad va en el nombre (_seconds, _bytes).
import client from "prom-client";
const peticiones = new client.Counter({
name: "http_requests_total",
help: "Peticiones HTTP atendidas",
labelNames: ["ruta", "codigo"],
});
const enCola = new client.Gauge({ name: "pedidos_en_cola", help: "Pedidos pendientes" });
const duracion = new client.Histogram({
name: "http_request_duration_seconds",
help: "Duración de las peticiones",
buckets: [0.1, 0.3, 0.5, 1, 2.5],
});
const terminar = duracion.startTimer();
peticiones.inc({ ruta: "/pagar", codigo: "200" });
enCola.set(7);
terminar(); // registra los segundos transcurridos en su cuboPromQL y rate()
El lenguaje para consultar series temporales: filtrar, calcular tasas y agregar.
Con PromQL seleccionas series por nombre y etiquetas (http_requests_total{codigo="500"}) y las transformas con funciones. Un rango entre corchetes, como [5m], toma los valores de los últimos cinco minutos de cada serie.
La función estrella es rate(): convierte un counter, que solo crece, en cuánto crece por segundo de media en ese rango, y corrige sola los reinicios a cero. rate(http_requests_total[5m]) son las peticiones por segundo; si lo agregas con sum by (ruta) obtienes el tráfico de cada ruta y, dividiendo errores entre total, la tasa de errores.
Úsalo cuando
Para construir cualquier panel o regla de alerta sobre datos de Prometheus, ya sea en el propio Prometheus o en Grafana.
Ten en cuenta
No apliques rate() a un gauge ni mires un counter «a pelo»: su valor bruto depende de cuándo arrancó el proceso. Y elige un rango de varias veces el intervalo de scrape, o la curva saldrá a saltos o vacía.
# Peticiones por segundo de cada ruta (media de los últimos 5 min)
sum by (ruta) (rate(http_requests_total[5m]))
# Porcentaje de respuestas 5xx
100 * sum(rate(http_requests_total{codigo=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m]))
# Percentil 95 de la latencia, a partir del histograma
histogram_quantile(0.95,
sum by (le) (rate(http_request_duration_seconds_bucket[5m])))Alertmanager
El componente que agrupa, silencia y reparte las alertas que dispara Prometheus.
- Puerto:
- 9093
Las reglas de alerta viven en Prometheus: una expresión PromQL y un tiempo for que debe cumplirse antes de disparar, para no avisar por un pico de un segundo. Cuando una regla salta, Prometheus la envía a Alertmanager, que decide qué hacer con ella.
Alertmanager deduplica (la misma alerta desde varias réplicas llega una sola vez), agrupa (las cuarenta alertas que provoca la caída de un nodo salen en un único aviso), enruta según las etiquetas (lo crítico a la persona de guardia, el resto a un canal de chat) y permite silenciar alertas durante un mantenimiento planificado. Sabe enviar a correo, Slack, PagerDuty o cualquier webhook.
Úsalo cuando
Siempre que uses alertas de Prometheus: sin Alertmanager las reglas se evalúan, pero los avisos no llegan a nadie.
Ten en cuenta
Una alerta que salta a menudo sin que haya nada que hacer acaba ignorada, y con ella las importantes. Alerta sobre síntomas que sufre el usuario (errores, latencia) más que sobre causas (CPU al 80 %).
groups:
- name: tienda
rules:
- alert: MuchosErroresEnPagos
expr: |
sum(rate(http_requests_total{ruta="/pagar",codigo=~"5.."}[5m]))
/ sum(rate(http_requests_total{ruta="/pagar"}[5m])) > 0.02
for: 5m
labels:
severidad: critica
annotations:
resumen: "Más del 2 % de los pagos falla desde hace 5 minutos"Cuidado con la cardinalidad
Cada combinación distinta de etiquetas es una serie temporal nueva. Si pones el id de usuario o la URL completa en una etiqueta, un millón de usuarios son un millón de series y Prometheus se queda sin memoria. Usa etiquetas con pocos valores posibles (ruta, codigo, metodo) y deja los detalles para los logs y las trazas.
Grafana: paneles, fuentes de datos y alertas
Prometheus guarda y consulta, pero su interfaz es muy básica. Grafana es donde el equipo mira de verdad los datos: junta en un mismo sitio métricas, logs y trazas que vienen de sistemas distintos.
Grafana
Visualiza y alerta sobre datos de muchas fuentes, todo en los mismos paneles.
- Desarrolla:
- Grafana Labs
- Licencia:
- AGPLv3
Grafana no guarda los datos: se conecta a fuentes de datos (data sources) como Prometheus, Loki, Tempo, Elasticsearch, PostgreSQL o los servicios de monitorización de las nubes, y les lanza consultas en el lenguaje de cada una. Con los resultados construyes dashboards: paneles con gráficas, indicadores y tablas que se refrescan solos.
Los dashboards admiten variables (un desplegable para elegir entorno o servicio) y se guardan como JSON, así que puedes versionarlos con Git. Grafana Alerting te deja definir reglas sobre cualquier fuente, no solo Prometheus, y enviar avisos por correo, Slack o PagerDuty con políticas de notificación parecidas a las de Alertmanager.
Úsalo cuando
Para los paneles del equipo y de guardia, y siempre que quieras ver en una misma pantalla datos que vienen de herramientas distintas.
Ten en cuenta
Un dashboard con cuarenta gráficas no lo mira nadie. Empieza por pocos paneles que respondan a preguntas concretas, como las «cuatro señales doradas» que propone Google: latencia, tráfico, errores y saturación.
# Grafana carga estas fuentes de datos al arrancar
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
url: http://prometheus:9090
isDefault: true
- name: Loki
type: loki
url: http://loki:3100
- name: Tempo
type: tempo
url: http://tempo:3200Loki y Tempo
Los almacenes de logs y de trazas de Grafana Labs, pensados para ir con Prometheus.
- Lenguajes:
- LogQL y TraceQL
Loki guarda logs con la filosofía de Prometheus: solo indexa unas pocas etiquetas (servicio, entorno) y no el texto completo de cada línea, lo que lo hace mucho más barato que un buscador de texto completo. Se consulta con LogQL, que se parece a PromQL. Tempo guarda trazas, acepta los formatos de OpenTelemetry, Jaeger y Zipkin, y se consulta con TraceQL.
La gracia está en conectarlos: desde un pico en una gráfica de Prometheus saltas a las trazas lentas de ese minuto en Tempo y, desde un span, a los logs de Loki con el mismo trace id. Junto con Grafana y Mimir (métricas a largo plazo) forman lo que Grafana Labs llama el stack LGTM.
Úsalo cuando
Si ya usas Prometheus y Grafana y quieres añadir logs y trazas con la misma filosofía, en vez de montar aparte sistemas como Elasticsearch o Jaeger.
Ten en cuenta
Como Loki no indexa el texto, una búsqueda que no filtra bien por etiquetas obliga a leer muchos datos y va lenta. Y sus etiquetas siguen las mismas reglas de cardinalidad que las de Prometheus.
# LogQL: errores del servicio de pagos que mencionan la pasarela
{servicio="pagos", entorno="produccion"} |= "pasarela" | json | nivel="error"
# LogQL: errores por minuto, como si fuera una métrica
sum(count_over_time({servicio="pagos"} | json | nivel="error" [1m]))
# TraceQL: trazas del servicio de pedidos que tardaron más de 1 s
{ resource.service.name = "pedidos" && duration > 1s }Pruébalo
Si quieres trastear sin instalar nada, play.grafana.org es una instancia pública de Grafana con dashboards de ejemplo. Abre uno, edita un panel y mira la consulta que hay detrás.
SLI, SLO y presupuesto de errores
Ningún sistema está disponible el 100 % del tiempo, y perseguirlo sale carísimo. La pregunta útil es: ¿cuánto fallo es aceptable para nuestros usuarios? Estas ideas, popularizadas por el libro de SRE (Site Reliability Engineering) de Google, convierten esa conversación en números.
SLI y SLO
Qué mides de la experiencia del usuario y qué valor te comprometes a alcanzar.
Un SLI (Service Level Indicator, indicador de nivel de servicio) mide lo bien que funciona el servicio desde el punto de vista del usuario, casi siempre como proporción: peticiones correctas entre peticiones totales, o peticiones que responden en menos de 300 ms entre el total.
Un SLO (Service Level Objective, objetivo de nivel de servicio) es el valor que quieres que alcance ese SLI en una ventana de tiempo: «el 99,9 % de las peticiones de pago, correctas en los últimos 30 días». No lo confundas con el SLA (acuerdo de nivel de servicio), que es un contrato con el cliente con penalizaciones si no se cumple; el SLO interno suele ser más exigente que el SLA, para tener margen.
Úsalo cuando
Para decidir con datos si el servicio va «bien» y para alertar de lo que de verdad afecta a los usuarios, en lugar de vigilar decenas de métricas técnicas.
Ten en cuenta
Cada nueve extra cuesta mucho más que el anterior: con un 99,99 % te quedan unos 4 minutos de margen al mes. Fija el SLO que tus usuarios necesitan, no el más alto que se te ocurra.
# SLI de disponibilidad: proporción de peticiones sin error 5xx en 30 días
sum(rate(http_requests_total{codigo!~"5.."}[30d]))
/ sum(rate(http_requests_total[30d]))
# SLI de latencia: proporción de peticiones servidas en menos de 0,3 s
sum(rate(http_request_duration_seconds_bucket{le="0.3"}[30d]))
/ sum(rate(http_request_duration_seconds_count[30d]))Presupuesto de errores
El margen de fallo que deja el SLO: se puede gastar, pero no sobrepasar.
- Fórmula:
- 1 − SLO
Si tu SLO es del 99,9 %, el 0,1 % restante es tu presupuesto de errores (error budget). En una ventana de 30 días son unos 43 minutos de caída total o su equivalente: un 1 % de peticiones fallidas durante 72 horas gasta lo mismo.
El presupuesto convierte la eterna discusión entre ir rápido y no romper nada en una regla: mientras quede presupuesto, el equipo despliega y experimenta; si se agota, se congelan los despliegues de funciones nuevas y el esfuerzo va a la fiabilidad hasta recuperar margen. Lo habitual es dejarlo por escrito en una política acordada con el equipo de producto.
Úsalo cuando
Para decidir cuándo arriesgar y cuándo frenar, y para alertar por velocidad de consumo (burn rate): avisar si, al ritmo actual, el presupuesto se agotará en pocos días.
Ten en cuenta
Si el presupuesto sobra siempre, quizá el SLO es más laxo de lo que esperan tus usuarios; si se agota cada mes, es demasiado exigente o el sistema necesita trabajo. Y sin una política acordada de antemano, el congelado no se cumple cuando llega el momento.
SLO: 99,9 % de peticiones correctas, ventana de 30 días
Presupuesto: 0,1 % × 30 días × 24 h × 60 min = 43,2 min
Incidentes
Caída total de 20 min ................ 20 min
5 % de errores durante 2 h ........... 6 min (5 % × 120 min)
Gastado: 26 min (60 %) Quedan: 17,2 minInteractivo
Calculadora de presupuesto de errores
Elige un SLO y una ventana, añade incidentes y decide si el equipo puede seguir desplegando.
SLO de disponibilidad
Ventana
Caída total que admite el SLO
43 min 12 sen 30 días
30 días × 24 h × 60 min = 43.200 min · × 0,1 % = 43,2 min
Añade un incidente
Seguir desplegando: tienes todo el presupuesto intacto. Añade incidentes para ver cómo se gasta.
Simplificación: suponemos tráfico constante, así que un 5 % de errores durante 2 h gasta lo mismo que 6 min de caída total. Prueba a cambiar el SLO con los mismos incidentes.
Interactivo
Métricas, logs y trazas cara a cara
Activa o desactiva opciones para compararlas cara a cara.
| Característica | Métricas | Logs | Trazas |
|---|---|---|---|
| Qué guardan | Números agregados a lo largo del tiempo | Un registro por suceso, con todo el detalle | El recorrido de una petición, en spans |
| Pregunta que responden | ¿Va algo mal? ¿Desde cuándo? | ¿Qué pasó exactamente? | ¿Dónde se va el tiempo? |
| Volumen y coste | Bajo | Alto | Alto; se suele muestrear |
| Ideal para | Paneles y alertas | Investigar un caso concreto | Sistemas con varios servicios |
| En el ecosistema de Grafana | Prometheus o Mimir | Loki | Tempo |
Ponlo en práctica
Saber qué es cada cosa es la mitad del trabajo. La otra mitad es reconocer cuándo usarla.
Interactivo
¿Qué usarías?
Lee cada situación y elige la opción que mejor la resuelve. Fallar aquí no cuenta: es para practicar.
Una clienta escribe a soporte: su pedido 8812 dio error al pagar ayer a las 18:40. Necesitas el mensaje exacto que devolvió la pasarela de pago.
Para recordar
- Las métricas dicen que algo va mal, los logs qué pasó exactamente y las trazas dónde se fue el tiempo: se usan juntas, enlazadas por identificadores comunes.
- Prometheus recoge métricas en modelo pull desde
/metrics; los exporters traducen el software que no habla su formato y Alertmanager reparte las alertas. - Los counters se consultan con
rate()y las latencias, con histogramas y percentiles, nunca con medias. - Grafana no guarda datos: reúne en sus paneles y alertas fuentes como Prometheus, Loki y Tempo.
- Un SLO deja un presupuesto de errores: mientras quede, despliegas; si se agota, toca congelar y reforzar la fiabilidad.
Comprueba lo aprendido
Cuestionario
Responde las 5 preguntas para completar la lección.
1.¿Qué significa que Prometheus siga un modelo pull?
2.
http_errors_totales un counter. ¿Por qué se consulta conrate()en lugar de mirar su valor?3.Tu SLO es del 99,9 % de disponibilidad en una ventana de 30 días. ¿Cuánta caída total admite el presupuesto de errores?
4.Una petición pasa por cinco servicios. ¿Qué te permite reunir después todos sus logs y spans?
5.¿En qué se diferencian un SLO y un SLA?