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

  1. Los tres pilares de la observabilidad
  2. Logging estructurado
  3. Métricas: las metodologías RED y USE
  4. Trazas distribuidas y correlación
  5. OpenTelemetry: el estándar unificador
  6. Errores comunes y consejos
  7. Ejercicios

  1. 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.

  1. 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:

2026-06-30 10:15:32 ERROR Usuario ana no pudo pagar el pedido 123 (tarjeta rechazada)

¿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 hilos

El 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).

  1. 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"} 48190

Explicació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.

  1. 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.

  1. 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_id en 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

  1. 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.

  2. 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"

  3. 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

  1. (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.

  2. 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ía trace_id.

  3. (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

Módulo 2: Principios y Tácticas de Diseño

Módulo 3: Estilos y Patrones Arquitectónicos

Módulo 4: Arquitecturas Distribuidas y Microservicios

Módulo 5: Arquitecturas Dirigidas por Eventos y Mensajería

Módulo 6: Diseño Dirigido por el Dominio (DDD)

Módulo 7: Datos y Persistencia

Módulo 8: Arquitectura en la Nube y Despliegue

Módulo 9: Calidad, Seguridad y Observabilidad

Módulo 10: Evolución, Gobernanza y Casos Prácticos

© Copyright 2026. Todos los derechos reservados