Anterior
Observabilidad Logging Tracing Engineering

Observabilidad primero: construir sistemas que puedas depurar a las 3 AM

Dawlin Peña
Dawlin Peña
18 de abril de 2026 9 min de lectura

El problema real

Es la 1:47 AM. Tu teléfono vibra. PagerDuty: “checkout-service: 5xx rate > 2%”. Te conectas, abres tu dashboard, ves un gráfico subiendo. ¿Qué hiciste hace 30 segundos para entender qué pasa?

Si la respuesta involucra grep sobre logs sin estructura, no tienes observabilidad. Tienes archivos.

La observabilidad no es una herramienta. Es la propiedad de un sistema de poder responder preguntas que no anticipaste sobre su comportamiento, sin tener que parar el sistema o adivinarle.

Los tres pilares (y por qué no bastan separados)

Logs

Eventos discretos con contexto. Excelente para entender una transacción individual a fondo.

log.info({
  event: 'ecf.submitted',
  comprobanteId: 'E310000000123',
  rnc: '101099090',
  tipo: '31',
  monto: 15600.00,
  trackingId: 'trk_01HXY...',
  durationMs: 847,
  dgiiEndpoint: 'https://ecf.dgii.gov.do/...'
}, 'e-CF submitted to DGII')

Punto clave: logs estructurados. Nada de console.log('something happened: ' + x). Cada log es un objeto JSON con campos consistentes que puedes filtrar y agregar.

Métricas

Valores numéricos agregables a lo largo del tiempo. Excelente para detectar tendencias.

ecf.submitted.duration.histogram({ tipo: '31' }).record(847)
ecf.submitted.counter({ tipo: '31', result: 'aceptado' }).inc()

Punto clave: dimensiones bajas. Si tu métrica tiene 50 etiquetas únicas, no tienes una métrica — tienes un log mal disfrazado.

Traces

La historia completa de una request distribuida. Excelente para entender por qué algo es lento.

trace_id: 8f3d2c9a-7b1e-4a5f
├─ ecf-api (847ms)
│  ├─ validate-xml (12ms)
│  ├─ check-cert (45ms)
│  ├─ dgii.submit (782ms)  ◀── el cuello de botella
│  │  └─ network.wait (758ms)
│  └─ build-response (8ms)

Punto clave: el trace_id se propaga. Si tu trace se corta en el segundo servicio, no es trace — es un log con esperanzas.

El error común

Los equipos eligen uno. “Tenemos Datadog”. “Tenemos Grafana”. “Tenemos OpenTelemetry”. Pero usan logs sin métricas, o métricas sin trazas. Los tres pilares son complementarios:

  • Métricas te dicen QUÉ está mal (tasa de 5xx alta).
  • Traces te dicen DÓNDE está mal (en el llamado a DGII).
  • Logs te dicen POR QUÉ está mal (certificado expirado).

Si te falta uno, tu MTTR (mean time to recovery) sube.

Reglas que aplicamos en cada servicio

1. Contexto se propaga, no se duplica

Cada request tiene un request_id. Si llama a otro servicio, ese request_id viaja en un header. Si el segundo servicio loguea, ese request_id aparece en cada log. Si todo esto vive en un trace, todo se enlaza por trace_id y span_id.

import { trace, context } from '@opentelemetry/api'

app.use((req, res, next) => {
  const requestId = req.headers['x-request-id'] || crypto.randomUUID()
  res.setHeader('x-request-id', requestId)

  const tracer = trace.getTracer('checkout-service')
  const span = tracer.startSpan('http.request', {
    attributes: { 'http.method': req.method, 'http.url': req.url, 'request.id': requestId }
  })

  context.with(trace.setSpan(context.active(), span), () => {
    res.on('finish', () => {
      span.setAttributes({ 'http.status_code': res.statusCode })
      span.end()
    })
    next()
  })
})

2. Loguea decisiones, no condiciones

Mal:

log.info('user.email is ' + user.email)
log.info('user.verified is ' + user.verified)

Bien:

log.info({ userId: user.id, verified: user.verified }, 'login.allowed')
// o
log.warn({ userId: user.id, reason: 'unverified' }, 'login.denied')

El segundo te permite preguntar “cuántos logins fueron denegados por unverified” en segundos. El primero te obliga a procesar texto.

3. SLO antes que dashboard

Antes de construir 50 paneles, define qué te importa. Tres números por servicio crítico:

  • Disponibilidad (% de requests exitosas en X tiempo).
  • Latencia (p95 o p99 a un umbral).
  • Tasa de error (% de requests que fallan).

Cada uno tiene un objetivo (SLO) y una alerta cuando se rompe. El resto del dashboard es exploración — bonito de tener, no esencial.

4. Alerts: pocas, accionables

Una alerta que no es accionable es ruido. Si tu PagerDuty suena 14 veces al día por cosas que se autoresolverían, en un mes nadie va a contestar.

Regla simple: si la alerta no requiere intervención humana inmediata, no es una alerta. Es una métrica. Vive en un dashboard, no en tu teléfono.

5. Sample tracing inteligentemente

100% de las trazas es caro y, sobre todo, inútil: las 999 trazas exitosas no te dicen nada. Estrategia que usamos:

  • Tail-based sampling: capturamos al 100% las trazas que tienen errores o son lentas (>p95). El resto se samplea al 5%.
  • Head-based sampling con override: si una request entra con header x-debug: 1, esa traza captura todo, sin importar el resultado.

Errores típicos que vemos en clientes

“Loguear todo por si acaso”

10 GB/día de logs, 99.9% sin valor. Costo en storage, costo en consulta. Loguea los eventos importantes, no las variables internas de cada función.

“Métricas con cardinalidad alta”

http_requests_total{user_id="abc123", path="/api/x"}. Si tienes 1M usuarios, tienes 1M series. Tu Prometheus se desploma.

Mejor:

http_requests_total{path="/api/x"}  # agregado
auth_failures_total{reason="bad_password"}  # categoría, no identidad

Si necesitas saber qué usuario falló, eso vive en un log o trace.

“Tracing pero los spans no se enlazan”

Olvidaron propagar el traceparent header entre servicios. Cada span vive solo. No es tracing distribuido; es logging caro.

“Dashboards que nadie mira”

Si tu dashboard de “performance global” no lo abre nadie en una semana normal, no es un dashboard — es un cuadro decorativo. Mata los dashboards que no usas.

Stack que recomendamos hoy

No hay respuesta universal. Pero un punto de partida razonable:

NecesidadOpción simpleOpción empresarial
Logs estructuradosLokiDatadog Logs
MétricasPrometheus + GrafanaDatadog Metrics
TracingTempo + OpenTelemetryDatadog APM
AlertasGrafana + PagerDutyDatadog Monitors

Lo importante no es la herramienta — es que estén integradas. Si ves una métrica subiendo y no puedes saltar directamente a las trazas correspondientes, vas a tardar 10 minutos donde tardarías 30 segundos.

Cierre

Observabilidad es disciplina, no presupuesto. Una startup con pino + Grafana + OpenTelemetry y reglas claras de qué loguear va a depurar producción más rápido que una empresa con Datadog y console.log por todos lados.

La pregunta que define si tu sistema es observable: “¿puedo responder en 5 minutos por qué la última hora se vio diferente a la hora anterior?” Si la respuesta es no, hay trabajo que hacer antes de la próxima alerta.

Dawlin