M5 — Observabilidad
Outcome del módulo: cuando el servicio falla, lo ves en un dashboard antes de que alguien te avise. Sin esto, operás a ciegas.
Diferencia clave: monitoring = ver métricas que definiste de antemano. Observabilidad = poder responder preguntas nuevas sobre el sistema sin re-deployar, a partir de lo que emite.
Concepto 1 — Los 3 pilares (logs, métricas, traces)
Idea núcleo: tres señales complementarias. Logs = qué pasó (evento). Métricas = cuánto/cuántos (agregado). Traces = por dónde pasó una request (recorrido).
Entender (capa 1)
Texto:
- Logs: eventos discretos con contexto. En prod: estructurados (JSON), no
printni texto libre — para poder filtrar/consultar. - Métricas: números agregados en el tiempo (requests/seg, latencia p95, error rate). Baratas de almacenar, ideales para dashboards y alertas.
- Traces: el camino de UNA request a través de servicios, con timing por tramo (span). Responde “¿dónde se fue el tiempo?”.
Visual:
flowchart TD
Q[¿qué pregunta respondo?] --> L[Logs<br/>¿qué pasó exactamente aquí?]
Q --> M[Métricas<br/>¿cuánto/con qué frecuencia?]
Q --> T[Traces<br/>¿por dónde pasó y dónde tardó?]
Video: Observability Explained | Logs, Metrics & Traces (The Three Pillars) — SystemDR
Ejemplo (log plano → estructurado):
# ❌ imposible de consultar/filtrar en agregado
print(f"user {uid} pagó {amount}")
# ✅ estructurado: cada campo es consultable
import structlog
log = structlog.get_logger()
log.info("payment_succeeded", user_id=uid, amount=amount, currency="COP")
Fijar (capa 2)
Nota atómica:
- Front: ¿cuándo mirás logs, métricas o traces?
- Back: métricas para detectar QUE algo anda mal (error rate sube); traces para ver DÓNDE (qué servicio/tramo tarda o falla); logs para el detalle de QUÉ pasó exactamente en ese punto. Se usan en cascada.
Feynman: “Las métricas son el tablero del auto (velocidad, temperatura): ves que algo sube. El trace es el GPS de tu viaje: por dónde fuiste y dónde te trabaste. Los logs son el diario detallado de cada parada.”
Aplicar (capa 3)
Convertí los print de un servicio a logs estructurados con structlog. Emití una métrica de contador (ej: pagos exitosos) y consultala.
Límites
- Logs son caros a volumen; no loguees todo en INFO en hot paths. Niveles + sampling.
- Nunca loguees secretos/PII (cruza con M4).
Concepto 2 — OpenTelemetry (el estándar)
Idea núcleo: OTel es el estándar vendor-neutral para instrumentar las 3 señales una sola vez y exportarlas a cualquier backend (Grafana, Jaeger, Datadog…).
Entender (capa 1)
Texto: instrumentás con la API de OpenTelemetry (spans, métricas, logs con contexto) y configurás un exporter. Cambiás de herramienta de visualización sin re-instrumentar. Un span representa una operación con inicio/fin; los spans se anidan formando el trace; el contexto se propaga entre servicios (trace-id) para seguir la request de punta a punta.
Visual:
flowchart LR
CODE[app instrumentada con OTel SDK] --> COL[OTel Collector] --> B1[Grafana/Tempo]
COL --> B2[Jaeger]
COL --> B3[otro backend]
Video: OpenTelemetry & Python: Manual Instrumentation for Beginners — Adam Gardner
Ejemplo:
from opentelemetry import trace
tracer = trace.get_tracer(__name__)
def checkout(cart):
with tracer.start_as_current_span("checkout") as span:
span.set_attribute("cart.items", len(cart))
with tracer.start_as_current_span("charge_payment"):
charge(cart) # span anidado: se ve su tiempo aparte
Fijar (capa 2)
Nota atómica:
- Front: ¿qué es un span y cómo se forma un trace?
- Back: un span = una operación con timing y atributos. Los spans se anidan (padre/hijo) y comparten un trace-id; ese árbol de spans = el trace, que muestra el recorrido y dónde se fue el tiempo en una request.
Feynman: “El trace es la factura detallada de un viaje: cada span es un tramo con su costo de tiempo. Ves que el viaje total fue 2s y que 1.8s se fueron en un solo tramo (la DB).”
Aplicar (capa 3)
Instrumentá un endpoint con OTel: un span padre + un span anidado para la llamada a DB. Verificá en el visualizador que ves el desglose de tiempo por tramo.
Límites
- Instrumentar todo genera overhead y ruido; empezá por los endpoints/servicios críticos y usá sampling en alto volumen.
Concepto 3 — Métricas: Prometheus, Grafana, Sentry
Idea núcleo: Prometheus recolecta métricas, Grafana las grafica, Sentry captura errores con contexto.
Entender (compacto):
- Prometheus: modelo pull (scrapea
/metrics); tipos: counter (sube), gauge (sube/baja), histogram (distribución, p50/p95/p99). - Grafana: dashboards sobre Prometheus (y otros); paneles de latencia, throughput, errores.
- Sentry: captura excepciones con stacktrace + contexto (request, user, release); ideal para el “qué se rompió” a nivel error.
- Las 4 golden signals (SRE): latencia, tráfico, errores, saturación.
from prometheus_client import Counter
payments = Counter("payments_total", "pagos", ["status"])
payments.labels(status="success").inc()
Video: How to set up Prometheus monitoring for your services — Google Cloud Tech Nota atómica: Front: ¿counter, gauge o histogram para latencia? Back: histogram — captura la distribución y permite calcular percentiles (p95/p99), que es lo que importa en latencia (el promedio miente). Counter para conteos monótonos, gauge para valores que suben y bajan (memoria, conexiones). Feynman: “El promedio de latencia es como decir ‘en promedio el restaurante atiende rápido’ mientras 1 de cada 20 clientes espera una hora. El p99 te muestra a ese cliente furioso.” Aplicar: exponé métricas Prometheus en un servicio, graficá latencia p95 y error rate en Grafana. Integrá Sentry y provocá una excepción para verla capturada. Límites: demasiadas labels de alta cardinalidad (ej: user_id) explotan Prometheus; labels acotadas.
Concepto 4 — SLO / SLI / error budget
Idea núcleo: definir “estar sano” con números. SLI = la medida. SLO = el objetivo. Error budget = cuánto podés fallar antes de frenar features.
Entender (capa 1)
Texto:
- SLI (Indicator): la métrica real, ej ”% de requests < 300ms” o ”% de requests sin error”.
- SLO (Objective): el target sobre el SLI, ej “99.9% de requests OK en 30 días”.
- Error budget: 100% − SLO. Con 99.9%, tu presupuesto de fallo es 0.1% (~43 min/mes). Si lo gastás, se frena el lanzamiento de features y se prioriza estabilidad.
Visual:
flowchart LR
SLI[SLI · % requests OK medido] --> SLO[SLO · objetivo 99.9%]
SLO --> EB[Error budget · 0.1% = 43min/mes]
EB --> DEC{¿budget gastado?}
DEC -->|sí| FREEZE[frenar features · estabilizar]
DEC -->|no| SHIP[seguir shippeando]
Video: SLO vs SLI vs SLA vs Error Budget | Google SRE in Plain English — Tech Tutorials with Piyush
Fijar (capa 2)
Nota atómica:
- Front: ¿qué es el error budget y para qué sirve?
- Back: es 100% − SLO: cuánta falla te podés permitir en el período. Sirve para decidir objetivamente: si aún hay budget, shippeás features; si lo gastaste, frenás y priorizás estabilidad. Convierte “confiabilidad” en una decisión con número, no discusión.
Feynman: “El error budget es el saldo de una tarjeta de ‘permiso para fallar’. Mientras te quede saldo, gastás en features nuevas. Si lo agotás, no comprás más hasta reponerlo estabilizando.”
Aplicar (capa 3)
Definí 1 SLI (ej: latencia < 300ms) y 1 SLO (99%) para un servicio. Calculá su error budget mensual y armá el cálculo de consumo actual.
Límites
- 100% de uptime es un anti-objetivo (imposible y carísimo); el SLO se fija según lo que el usuario realmente necesita, no “lo más alto posible”.
Concepto 5 — Alerting que sirve
Idea núcleo: alertar sobre síntomas del usuario, no sobre causas internas. Una alerta que no requiere acción es ruido que entrena a ignorar.
Entender (compacto):
- Alertá sobre SLO en riesgo (latencia/errores que ve el usuario), no sobre “CPU al 80%” (puede ser normal).
- Toda alerta debe ser accionable y linkear al runbook.
- Evitá alert fatigue: pocas alertas, importantes; el resto son dashboards.
flowchart TD
A{¿esto degrada la experiencia del usuario AHORA?} -->|sí| PAGE[alerta accionable + runbook]
A -->|no| DASH[dashboard, no alerta]
Nota atómica: Front: ¿sobre qué se debe alertar? Back: sobre síntomas que afectan al usuario (error rate, latencia, SLO en riesgo), no sobre causas internas (CPU, memoria) que no siempre implican problema. Cada alerta debe ser accionable y tener runbook; lo demás va a dashboard. Feynman: “Alertar por CPU alta es como que suene la alarma de incendio cada vez que prendés la estufa. Alertás cuando hay humo donde no debe (el usuario sufre), no por cada uso normal.” Aplicar: configurá 1 alerta sobre error rate (síntoma) con umbral y un runbook de 3 pasos. Provocá el fallo y verificá que dispara. Límites: umbrales muy sensibles = fatiga; muy laxos = te enterás tarde. Se calibran con datos reales.
Concepto 6 — Debugging y profiling en producción (cuando los 3 pilares no alcanzan)
Idea núcleo: logs/métricas/traces te dicen QUE algo anda mal y aproximadamente DÓNDE; profiling y debugging interactivo te dicen POR QUÉ, línea por línea, cuando hace falta bajar un nivel más.
Entender (capa 1)
Texto:
pdb/breakpoint(): debugger interactivo estándar de Python — parás la ejecución en un punto exacto, inspeccionás variables en vivo, avanzás línea por línea (n), entrás a una función (s), continuás (c). En producción real no lo usás en vivo (bloquea el proceso) — sirve para reproducir localmente un caso que viene de un reporte.cProfile: profiler determinista — mide tiempo exacto por función, cuántas veces se llamó cada una. Útil para “¿cuál función se está comiendo el tiempo?” en código CPU-bound.py-spy: profiler de muestreo (sampling) que se conecta a un proceso Python ya corriendo en producción sin reiniciarlo ni modificar código — genera un flamegraph de dónde está el tiempo real ahora mismo. Es lo que usás cuando no podés parar el servicio para debuggear.- Percentiles, no promedio: la latencia se mide en p50/p95/p99 (Concepto 3) — el promedio esconde a los usuarios que sufren la cola larga. Un servicio con p50=100ms y p99=5s tiene un problema real que el promedio (quizás 150ms) no muestra.
- Stack trace, de abajo hacia arriba: la última línea es donde explotó; subiendo ves la cadena de llamadas que llevó ahí. El error real a veces está más arriba (un dato mal formado que entró 3 funciones antes de explotar).
Visual:
flowchart TD
P[algo anda lento/falla] --> Q{¿puedo reproducir local?}
Q -->|sí| PDB[pdb/breakpoint — inspección línea por línea]
Q -->|no, es solo en prod| SPY[py-spy — attach a proceso vivo, sampling]
PDB --> CPU{¿es lento por cómputo?}
CPU -->|sí| PROF[cProfile — qué función consume el tiempo]
CPU -->|no, es I/O/red| TRACE[traces distribuidos — Concepto 2]
Video: Profiling and Optimizing Your Python Code — Real Python [verificar]
Ejemplo (postmortem, estructura mínima):
## Postmortem: caída endpoint /checkout, 2026-08-20, 14:32-15:01 UTC
**Impacto:** 29 min, 12% de checkouts fallaron (503)
**Timeline:** 14:32 primer 503 · 14:35 alerta dispara · 14:40 identificado (pool de conexiones DB agotado) · 15:01 mitigado (restart + límite de pool subido)
**Causa raíz:** un deploy a las 14:20 cambió el timeout de una query externa de 2s a 30s sin ajustar el pool — conexiones se acumularon hasta agotar el pool
**Qué falló en detectarlo antes:** no había alerta sobre pool de conexiones, solo sobre error rate del endpoint (llegó tarde)
**Fix:** revertido el timeout + agregada alerta de pool de conexiones al 80%
**Qué cambia:** checklist de deploy ahora incluye revisar timeouts que tocan recursos limitados (pools, rate limits)
Fijar (capa 2)
Nota atómica:
- Front: ¿cuándo usar
py-spyen vez decProfile? - Back: cuando el problema ocurre SOLO en producción y no podés parar/reiniciar el proceso para instrumentarlo —
py-spyse conecta a un proceso ya corriendo (sampling, bajo overhead) sin modificar código.cProfilees determinista y más preciso, pero requiere correr el código instrumentado, típicamente en local/staging.
Feynman: “cProfile es como grabar todo el partido en cámara lenta para analizarlo después — exacto pero tenés que planear la grabación antes. py-spy es como un dron que se posa sobre el partido que ya está en curso y toma fotos cada segundo — no tan exacto, pero no necesitás haber preparado nada.”
Aplicar (capa 3)
Tomá una función lenta tuya, medila con cProfile y encontrá qué línea/función se come el tiempo. Por separado, si tenés un proceso Python corriendo, instalá py-spy y generá un flamegraph sin tocar el código. Escribí un postmortem de 6 líneas (impacto, timeline, causa raíz, qué falló en detectar, fix, qué cambia) de un bug real que ya resolviste.
Límites
pdben un servidor con múltiples requests concurrentes bloquea TODO el proceso mientras estás parado en el breakpoint — nunca en producción con tráfico real.- Profiling agrega overhead — no dejarlo corriendo permanente en producción, solo durante una investigación puntual.
- Un postmortem sin “qué cambia” (acción concreta) es solo un relato — el valor está en la prevención, no en la narración del incidente.
Checklist de dominio M5
Marcás cuando podés explicar (Feynman) + aplicar:
- 3 pilares (logs estructurados / métricas / traces)
- OpenTelemetry (spans, traces, propagación de contexto)
- Prometheus + Grafana + Sentry (counter/gauge/histogram, golden signals)
- SLO / SLI / error budget
- Alerting accionable (síntomas, no causas)
- Debugging/profiling en producción (pdb, cProfile, py-spy, postmortem estructurado)
Salida verificable del módulo: un servicio instrumentado con OTel (logs estructurados + 1 métrica histogram + 1 trace con span anidado), un dashboard con latencia p95 y error rate, 1 SLO definido con su error budget, y 1 alerta accionable que dispara al provocar un fallo.