En un sistema monolítico sencillo bastaba con revisar un fichero de log cuando algo fallaba. Pero en arquitecturas modernas (microservicios, contenedores efímeros, cientos de instancias) una sola petición puede atravesar diez servicios distintos. ¿Cómo sabes qué ocurrió realmente? La respuesta es la observabilidad: la capacidad de entender el estado interno de un sistema a partir de las señales que emite hacia el exterior. No se trata solo de monitorizar (saber si algo va mal), sino de poder preguntar al sistema por qué va mal, incluso ante problemas que no habías previsto. En esta lección dominarás los tres pilares de la observabilidad (logs, métricas y trazas), los formatos y metodologías que los hacen útiles (logs estructurados, RED, USE), la trazabilidad distribuida con correlación, y el estándar que lo unifica todo: OpenTelemetry.
Contenido
- Los tres pilares de la observabilidad
- Logging estructurado
- Métricas: las metodologías RED y USE
- Trazas distribuidas y correlación
- OpenTelemetry: el estándar unificador
- Errores comunes y consejos
- Ejercicios
- Los tres pilares de la observabilidad
La observabilidad se apoya en tres tipos de señales (telemetry signals) complementarias:
| Pilar | Qué responde | Naturaleza | Ejemplo |
|---|---|---|---|
| Logs | ¿Qué ocurrió exactamente? | Eventos discretos con detalle | "Error al cobrar pedido 123: tarjeta rechazada" |
| Métricas | ¿Cuánto/cómo de bien va? | Números agregados en el tiempo | "Latencia p95 = 240 ms; 1.200 req/s" |
| Trazas | ¿Por dónde pasó la petición? | El recorrido de una petición entre servicios | "API -> Pedidos -> Pago -> Banco (380 ms total)" |
Son complementarios: las métricas te avisan de que algo va mal (la latencia ha subido), las trazas te dicen dónde (qué servicio es lento), y los logs te dicen por qué (qué error concreto se produjo). Usados juntos, te permiten pasar de "el sistema va lento" a "la causa exacta" en minutos.
- Logging estructurado
El logging tradicional escribe texto libre, fácil de leer para un humano pero muy difícil de procesar por una máquina:
¿Cómo buscas "todos los errores del pedido 123" entre millones de líneas así? Imposible de forma fiable. La solución es el logging estructurado: emitir los logs como datos (normalmente JSON) con campos bien definidos.
{
"timestamp": "2026-06-30T10:15:32Z",
"level": "ERROR",
"service": "pagos",
"message": "Pago rechazado",
"trace_id": "abc123",
"order_id": 123,
"user_id": "ana",
"reason": "card_declined"
}Cada campo es consultable: ahora puedes filtrar por order_id = 123, agrupar por reason, o seguir el trace_id para enlazar con la traza completa (lo veremos más adelante). Esto convierte los logs en datos analizables a escala.
// Logging estructurado con SLF4J + MDC (Mapped Diagnostic Context)
MDC.put("trace_id", traceId); // Añade contexto a TODOS los logs de este hilo
MDC.put("order_id", "123");
log.error("Pago rechazado", kv("reason", "card_declined"));
MDC.clear(); // Limpiar al terminar para no contaminar otros hilosEl MDC (Mapped Diagnostic Context) permite "adjuntar" campos de contexto (como el trace_id) que se incluirán automáticamente en cada línea de log emitida en ese hilo, sin tener que pasarlos a mano en cada llamada. Es fundamental limpiarlo (MDC.clear()) al final para no arrastrar datos a otras peticiones.
Buenas prácticas de logging:
- Usa niveles correctamente (DEBUG, INFO, WARN, ERROR) y no inundes con logs irrelevantes.
- Nunca registres datos sensibles (contraseñas, tarjetas, datos personales). Esto es una obligación legal además de una buena práctica.
- Incluye siempre identificadores de correlación (
trace_id).
- Métricas: las metodologías RED y USE
Las métricas son valores numéricos medidos a lo largo del tiempo: contadores (peticiones totales), medidores (memoria usada) e histogramas (distribución de latencias). Como puedes medir miles de cosas, dos metodologías te ayudan a elegir qué medir.
RED se centra en el servicio desde el punto de vista del usuario:
| Métrica RED | Significado | Pregunta |
|---|---|---|
| Rate | Peticiones por segundo | ¿Cuánto tráfico recibo? |
| Errors | Tasa de errores | ¿Cuántas peticiones fallan? |
| Duration | Latencia (distribución) | ¿Cuánto tardan? |
USE se centra en los recursos (CPU, memoria, disco, red):
| Métrica USE | Significado | Pregunta |
|---|---|---|
| Utilization | Porcentaje de uso | ¿Cómo de ocupado está? |
| Saturation | Trabajo en cola esperando | ¿Hay más demanda de la que puede atender? |
| Errors | Errores del recurso | ¿Está fallando el recurso? |
Regla práctica: usa RED para los servicios (lo que ve el usuario) y USE para los recursos (la infraestructura subyacente). Juntas te dan una visión completa.
# Formato de exposición de métricas Prometheus
# HELP http_requests_total Total de peticiones HTTP
# TYPE http_requests_total counter
http_requests_total{service="pagos",status="200"} 48210
http_requests_total{service="pagos",status="500"} 37
# HELP http_request_duration_seconds Latencia de las peticiones
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.3"} 47100
http_request_duration_seconds_bucket{le="1.0"} 48190Explicación: el primer bloque es un contador (counter) de peticiones, etiquetado (labels) por servicio y código de estado; permite calcular Rate y Errors de RED. El segundo es un histograma que cuenta cuántas peticiones cayeron bajo cada umbral de latencia (le = less or equal); con él se calculan percentiles como el p95 (Duration de RED). Las etiquetas ({service=...}) permiten filtrar y agrupar las métricas.
- Trazas distribuidas y correlación
Cuando una petición atraviesa varios servicios, ¿cómo sigues su rastro completo? Con trazas distribuidas. Una traza (trace) representa el viaje completo de una petición; se compone de spans, donde cada span es una operación (una llamada a un servicio, una consulta a la base de datos).
El mecanismo clave es la propagación del contexto: el primer servicio genera un trace_id único y lo pasa a cada servicio que llama, normalmente en cabeceras HTTP. Así, todos los logs y spans de esa petición comparten el mismo trace_id y se pueden correlacionar.
graph TD
A["Span: API Gateway (trace_id=abc, 400ms)"] --> B["Span: Servicio Pedidos (120ms)"]
A --> C["Span: Servicio Pago (250ms)"]
C --> D["Span: Llamada al Banco (230ms)"]Este árbol de spans muestra de un vistazo dónde se va el tiempo: de los 400 ms totales, el servicio de Pago consume 250 ms, y casi todos (230 ms) los gasta esperando al Banco. Sin trazas, solo sabrías que "la petición tardó 400 ms"; con ellas, sabes exactamente dónde está el cuello de botella.
# Propagación del contexto de traza vía cabecera estándar W3C Trace Context curl https://api.miempresa.com/pedidos \ -H "traceparent: 00-abc123def456...-0011223344556677-01" # versión-trace_id-----------------span_id---------flags
La cabecera traceparent (estándar W3C) transporta el trace_id (identifica toda la traza) y el span_id (identifica el span padre). Cada servicio lee esta cabecera, crea su propio span hijo y la reenvía al siguiente servicio, manteniendo la cadena unida.
- OpenTelemetry: el estándar unificador
Históricamente, cada herramienta de observabilidad tenía su propio formato e instrumentación, atándote a un proveedor. OpenTelemetry (OTel) es el estándar abierto (de la CNCF) que unifica la generación de las tres señales (logs, métricas y trazas) bajo una sola API y un solo protocolo (OTLP). Instrumentas tu código una vez y puedes enviar los datos a cualquier backend (Prometheus, Jaeger, Grafana, etc.).
graph LR
APP[Tu aplicación + SDK OTel] -->|OTLP| COL[OpenTelemetry Collector]
COL --> M[Métricas: Prometheus]
COL --> T[Trazas: Jaeger]
COL --> L[Logs: Loki]La pieza central es el Collector: la aplicación, instrumentada con el SDK de OTel, envía toda su telemetría al Collector mediante el protocolo OTLP, y este la procesa y la reenvía a cada backend especializado. La gran ventaja: tu código no sabe ni le importa qué backend hay detrás; puedes cambiarlos sin tocar la aplicación.
// Crear un span manualmente con la API de OpenTelemetry
Span span = tracer.spanBuilder("procesar-pago").startSpan();
try (Scope scope = span.makeCurrent()) {
span.setAttribute("order_id", 123); // Atributos consultables en el span
procesarPago(); // El trabajo real
} catch (Exception e) {
span.recordException(e); // Registra el error en la traza
throw e;
} finally {
span.end(); // SIEMPRE cerrar el span
}Este código crea un span llamado procesar-pago que mide el tiempo de la operación. makeCurrent() lo hace "activo" para que las llamadas anidadas se cuelguen de él automáticamente. Si hay un error, recordException lo adjunta a la traza (verás el fallo en el span). El span.end() en el finally garantiza que el span se cierre y se registre su duración pase lo que pase.
Errores Comunes y Consejos
- Confundir monitorización con observabilidad. Monitorizar es vigilar métricas conocidas; observabilidad es poder investigar problemas que no anticipaste. Necesitas ambas.
- Logs en texto libre. Imposibles de analizar a escala. Usa logging estructurado (JSON) desde el principio.
- No propagar el
trace_id. Sin correlación, los logs de cada servicio son islas inconexas. Propaga siempre el contexto de traza. - Registrar datos sensibles. Es un fallo de seguridad y de cumplimiento normativo. Filtra contraseñas, tarjetas y datos personales antes de loguear.
- Demasiadas métricas de alta cardinalidad. Etiquetas con valores ilimitados (como
user_iden cada métrica) hacen explotar el coste de almacenamiento. Usa cardinalidad acotada. - Trazar el 100% del tráfico sin muestreo. En sistemas de alto volumen genera un coste enorme. Aplica muestreo (sampling) inteligente.
- Consejo: instrumenta pensando en las preguntas que querrás hacer cuando algo falle a las 3 de la madrugada.
Ejercicios
-
Elegir el pilar adecuado. Para cada situación, indica qué pilar (logs, métricas o trazas) usarías primero: (a) un panel muestra que la tasa de errores ha subido al 5%; (b) quieres saber por qué exactamente falló el pedido 987; (c) una petición tarda 3 segundos y quieres saber qué servicio la está ralentizando.
-
Mejorar un log. Reescribe este log en texto libre como un log estructurado en JSON con campos útiles para diagnóstico:
"ERROR: fallo al conectar con la base de datos tras 3 intentos" -
RED vs USE. Clasifica cada métrica según la metodología más apropiada: (a) peticiones por segundo de la API; (b) porcentaje de uso de CPU del servidor; (c) longitud de la cola de disco; (d) tasa de respuestas HTTP 500.
Soluciones
-
(a) Métricas (te alertan de que algo va mal: la tasa de errores). (b) Logs (te dan el detalle exacto del fallo de ese pedido concreto). (c) Trazas (te muestran el recorrido de la petición y qué span/servicio consume el tiempo). El flujo natural de investigación suele ser métrica -> traza -> log.
-
Ejemplo de log estructurado:
{ "timestamp": "2026-06-30T10:20:00Z", "level": "ERROR", "service": "pedidos", "message": "Fallo de conexión a base de datos", "component": "db_pool", "retries": 3, "db_host": "db-primary", "trace_id": "abc123" }Ahora se puede filtrar por
component, contar reintentos o enlazar con la traza víatrace_id. -
(a) RED (Rate, métrica de servicio). (b) USE (Utilization de un recurso). (c) USE (Saturation: trabajo encolado en el disco). (d) RED (Errors, métrica de servicio). En resumen: a y d miden el servicio (RED); b y c miden recursos (USE).
Conclusión
Has aprendido que la observabilidad se sostiene sobre tres pilares complementarios: logs (qué pasó), métricas (cuánto/cómo de bien) y trazas (por dónde pasó). Has visto cómo los logs estructurados los hacen analizables, cómo RED y USE te guían sobre qué medir, cómo la correlación mediante trace_id une todas las señales de una petición, y cómo OpenTelemetry estandariza toda la instrumentación. Un sistema observable es uno que puedes interrogar para entender cualquier problema, previsto o no.
Las métricas que hemos aprendido a recoger (latencia, throughput, percentiles) son precisamente las que mediremos al someter el sistema a carga. En la última lección del módulo, Rendimiento y Pruebas de Carga, veremos cómo medir y validar que el sistema aguanta lo que el negocio necesita.
Curso de Arquitectura de Aplicaciones
Módulo 1: Fundamentos de la Arquitectura de Aplicaciones
- ¿Qué es la Arquitectura de Aplicaciones?
- El Rol del Arquitecto de Software
- Atributos de Calidad y Requisitos No Funcionales
- Decisiones Arquitectónicas y Compromisos (Trade-offs)
- Documentación de Arquitectura: Vistas y el Modelo C4
Módulo 2: Principios y Tácticas de Diseño
- Acoplamiento, Cohesión y Separación de Responsabilidades
- Principios SOLID Aplicados a la Arquitectura
- DRY, KISS, YAGNI y Otros Principios de Diseño
- Tácticas Arquitectónicas para los Atributos de Calidad
- Gestión de la Deuda Técnica
Módulo 3: Estilos y Patrones Arquitectónicos
- Arquitectura Monolítica
- Arquitectura en Capas (N-Tier)
- Arquitectura Cliente-Servidor
- Arquitectura Hexagonal (Puertos y Adaptadores)
- Arquitectura Limpia y Cebolla (Clean & Onion)
Módulo 4: Arquitecturas Distribuidas y Microservicios
- Introducción a los Sistemas Distribuidos
- Arquitectura de Microservicios
- Descomposición de Servicios y Bounded Contexts
- API Gateway, Service Discovery y Comunicación entre Servicios
- Patrones de Resiliencia: Circuit Breaker, Retry y Bulkhead
- El Teorema CAP y la Consistencia de Datos
Módulo 5: Arquitecturas Dirigidas por Eventos y Mensajería
- Fundamentos de la Arquitectura Orientada a Eventos
- Mensajería Asíncrona: Colas y Brokers
- Patrones de Eventos: Event Sourcing y CQRS
- Gestión de Transacciones Distribuidas: Patrón Saga
- Streaming de Datos en Tiempo Real
Módulo 6: Diseño Dirigido por el Dominio (DDD)
- Conceptos Fundamentales del DDD
- Diseño Estratégico: Bounded Contexts y Lenguaje Ubicuo
- Diseño Táctico: Entidades, Agregados y Repositorios
- Mapeo de Contextos (Context Mapping)
Módulo 7: Datos y Persistencia
- Estrategias de Persistencia: SQL vs NoSQL
- Patrones de Acceso a Datos: Repository, Unit of Work y DAO
- Base de Datos por Servicio y Gestión de Datos Distribuidos
- Caché y Estrategias de Invalidación
Módulo 8: Arquitectura en la Nube y Despliegue
- Fundamentos del Cloud Computing (IaaS, PaaS, SaaS)
- Contenedores y Orquestación con Docker y Kubernetes
- Arquitectura Serverless
- Patrones de Diseño Cloud-Native
- Infraestructura como Código (IaC)
Módulo 9: Calidad, Seguridad y Observabilidad
- Escalabilidad: Horizontal vs Vertical y Balanceo de Carga
- Alta Disponibilidad y Tolerancia a Fallos
- Seguridad por Diseño y Autenticación/Autorización
- Observabilidad: Logging, Métricas y Trazabilidad
- Rendimiento y Pruebas de Carga
