Monitoreo y observabilidad
Observabilidad es la capacidad de entender el estado interno de un sistema a partir de sus salidas externas. Un sistema observable te dice qué falla y por qué, sin necesidad de desplegar código nuevo. Este tutorial recorre las tres pilares — métricas, logs y trazas — con las herramientas más usadas en producción.
Pilares de la observabilidad
Toda estrategia de observabilidad se apoya en tres tipos de datos que responden a preguntas distintas. Juntos forman una imagen completa del comportamiento de tu sistema.
Métricas
Las métricas son valores numéricos agregados a lo largo del tiempo: requests_por_segundo, latencia_p99, errores_5xx. Son ligeras, baratas de almacenar y ideales para alertas y dashboards. No te dicen qué pasó con una petición concreta, pero sí cuántas están fallando y si la latencia sube.
Logs
Los logs son eventos discretos con contexto: cada línea describe qué ocurrió en un momento dado. Son la fuente más rica de detalle — un log puede incluir el ID de usuario, el endpoint y el tiempo de ejecución — pero también la más pesada. Sin centralización, buscar en logs de diez servidores es inviable.
Trazas distribuidas
Una traza sigue una petición desde que entra al sistema hasta que sale, cruzando servicios, colas de mensajes y bases de datos. Cada paso se llama span y contiene duración, metadatos y el ID de la traza padre. Las trazas son la única pista fiable para diagnosticar cuellos de botella en arquitecturas microservicio.
Métricas con Prometheus
Prometheus es el estándar de facto para métricas en entornos containerizados. Usa un modelo de pull: cada servicio expone un endpoint /metrics y Prometheus lo raspa cada cierto intervalo.
Instalación con Docker
Ejecuta Prometheus con Docker y un archivo de configuración que le diga qué servicios raspar:
# prometheus.yml
global:
scrape_interval: 15s # cada cuánto raspa targets
evaluation_interval: 15s # cada cuánto evalúa reglas
scrape_configs:
- job_name: "node-app"
static_configs:
- targets: ["host.docker.internal:3000"] # tu app Node.js
metrics_path: "/metrics"# Arrancar Prometheus
docker run -d --name prometheus \
-p 9090:9090 \
-v ./prometheus.yml:/etc/prometheus/prometheus.yml \
prom/prometheus
# Verificar que raspa correctamente
curl http://localhost:9090/api/v1/targetsFundamentos de PromQL
PromQL es el lenguaje de consulta de Prometheus. Permite filtrar, agregar y transformar métricas en tiempo real.
# Tasa de peticiones por segundo (últimos 5 min)
rate(http_requests_total[5m])
# Percentil 99 de latencia
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
# Errores 5xx agrupados por servicio
sum by (service) (
rate(http_requests_total{status=~"5.."}[5m])
)
# Disponibilidad: peticiones exitosas / total
sum(rate(http_requests_total{status!~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))Exponer métricas en Node.js
La librería prom-client expone métricas automáticamente y crea el endpoint /metrics:
import express from "express";
import { register, Counter, Histogram } from "prom-client";
const app = express();
const httpRequests = new Counter({
name: "http_requests_total",
help: "Total de peticiones HTTP",
labelNames: ["method", "route", "status"],
});
const httpDuration = new Histogram({
name: "http_request_duration_seconds",
help: "Duración de peticiones HTTP en segundos",
labelNames: ["method", "route"],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5],
});
app.use((req, res, next) => {
const end = httpDuration.startTimer({ method: req.method, route: req.path });
res.on("finish", () => {
end();
httpRequests.inc({
method: req.method,
route: req.path,
status: res.statusCode,
});
});
next();
});
app.get("/metrics", async (_req, res) => {
res.set("Content-Type", register.contentType);
res.end(await register.metrics());
});
app.listen(3000);rate() para contadores y histogram_quantile() para histograms. No uses increase() en dashboards de tiempo real: rate() suaviza mejor los picos.Dashboards con Grafana
Grafana conecta Prometheus como fuente de datos y te permite construir paneles visuales — gráficos, estadísticas y tablas — que actualizan en tiempo real.
Instalación con Docker
# Añadir Grafana al docker-compose
services:
grafana:
image: grafana/grafana:latest
ports:
- "3001:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: admin
volumes:
- grafana-data:/var/lib/grafana
volumes:
grafana-data:Añadir Prometheus como fuente
En Grafana ve a Configuration → Data Sources → Add data source y selecciona Prometheus. La URL será http://prometheus:9090 si usas Docker Compose.
Crear paneles
Grafana ofrece tres tipos de panel principales:
Graph:.series temporales — ideal para latencia, tasa de errores y throughput. Usa PromQL como query.
Stat:un solo valor grande con color condicional — útil para mostrar disponibilidad actual o peticiones activas.
Table:datos tabulares — perfecto para el top 5 de endpoints con mayor latencia.
# Ejemplo de PromQL para panel Graph — latencia P99
histogram_quantile(0.99,
sum by (le) (
rate(http_request_duration_seconds_bucket[5m])
)
)
# Ejemplo para panel Stat — disponibilidad actual
1 - (
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
)Variables para filtrar por servicio
Las variables permiten crear dashboards reutilizables. En Grafana ve a Dashboard Settings → Variables → New y crea una variable service con la query label_values(http_requests_total, service). Después usa $service en tus queries para filtrar.
Logging con Loki
Loki es el complemento de Prometheus para logs. No indexa el contenido completo de cada línea — solo las etiquetas — lo que lo hace mucho más ligero que Elasticsearch. Se integra directamente con Grafana.
Logging centralizado vs archivos sueltos
En un solo contenedor puedes leer logs con docker logs. Pero cuando tienes múltiples servicios o réplicas, necesitas centralizar: recopilar logs de todos los contenedores en un solo lugar, buscarlos y alertar sobre ellos. Eso es lo que hace Loki con Promtail.
Docker Compose con Loki + Promtail
services:
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
volumes:
- loki-data:/loki
promtail:
image: grafana/promtail:latest
volumes:
- /var/log:/var/log:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./promtail-config.yml:/etc/promtail/config.yml
command: -config.file=/etc/promtail/config.yml
grafana:
image: grafana/grafana:latest
ports:
- "3001:3000"
volumes:
- grafana-data:/var/lib/grafana
volumes:
loki-data:
grafana-data:# promtail-config.yml
server:
http_listen_port: 9080
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: docker
docker_sd_configs:
- host: unix:///var/run/docker.sock
relabel_configs:
- source_labels: ["__meta_docker_container_name"]
target_label: "container"Consultas con LogQL
LogQL se parece a PromQL. Filtras por etiquetas y luego aplicas operadores de texto:
# Todos los logs del contenedor "api"
{container="api"}
# Logs que contienen "error"
{container="api"} |= "error"
# Logs de errores 5xx en los últimos 5 min
{container="api"} |~ "5[0-9]{2}" [5m]
# Conteo de errores por minuto
rate({container="api"} |= "error" [1m])container o service), no el contenido de los logs. Usar demasiadas etiquetas de alta cardinalidad (como IDs de usuario) puede degradar el rendimiento. Mantén las etiquetas bajas y usa filtros de texto para el contenido.Trazas distribuidas con OpenTelemetry
OpenTelemetry (OTel) es el estándar de la CNCF para telemetría. Provee APIs unificadas para métricas, logs y trazas, con SDKs para la mayoría de lenguajes y un Collector que recibe, procesa y exporta datos a múltiples backends.
Qué son traces, spans y context propagation
Una trace representa el recorrido completo de una petición a través del sistema. Se compone de spans, cada uno representando una unidad de trabajo — una llamada HTTP, una query a la base de datos, una operación en cola. Cada span tiene un traceId, un spanId, un padre opcional, timestamps y atributos.
La context propagation es el mecanismo que transporta el ID de traza entre servicios. Cuando tu API llama a otro microservicio, el SDK OTel inyecta headers HTTP (traceparent) para que el siguiente servicio cree un span hijo en la misma traza.
Instalar el OTel Collector
# Añadir al docker-compose
services:
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
command: ["--config=/etc/otelcol/config.yml"]
volumes:
- ./otel-config.yml:/etc/otelcol/config.yml
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
- "8888:8888" # Métricas del propio collector
volumes:
loki-data:
grafana-data:# otel-config.yml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 5s
send_batch_size: 1024
exporters:
jaeger:
endpoint: jaeger:14250
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [jaeger]Instrumentar una app Node.js
import { NodeSDK } from "@opentelemetry/sdk-node";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-grpc";
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: "http://otel-collector:4317",
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
// Tu app Express/Koa/Fastify se instrumenta automáticamente
// Los spans se crean para cada request HTTP, query SQL, etc.Con las auto-instrumentaciones, cada petición HTTP entrante y saliente, cada query a base de datos y cada llamada a cola se convierte en un span automáticamente, sin que modifiques tu código de negocio.
Ver trazas en Jaeger UI
Jaeger es un backend de trazas que visualiza las trazas recogidas por el Collector. Añádelo al compose y accede a la UI en http://localhost:16686:
# Añadir Jaeger al docker-compose
services:
jaeger:
image: jaegertracing/all-in-one:latest
ports:
- "16686:16686" # UI
- "14250:14250" # gRPC
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
command: ["--config=/etc/otelcol/config.yml"]
volumes:
- ./otel-config.yml:/etc/otelcol/config.yml
ports:
- "4317:4317"
- "4318:4318"
depends_on:
- jaegerEn Jaeger UI selecciona tu servicio, busca trazas por duración o etiqueta, y explora el grafo de spans: verás exactamente cuánto tarda cada paso y dónde se acumula la latencia.
Observabilidad no es un lujo — es una inversión que reduce el tiempo de resolución de incidentes de horas a minutos. Con Prometheus, Grafana, Loki y OpenTelemetry tienes un stack completo y gratuito que escala desde un portátil hasta un cluster de producción. La clave está en empezar poco a poco y construir la cultura de observabilidad en tu equipo.