← Blog
#observabilidad#devops#métricas#opentelemetry

Observabilidad no es monitoreo: métricas, logs y trazas son tres cosas distintas

June 2, 2026

Son las 3am. La latencia de tu API subió un 400%. La alerta llegó al celular. Abrís Grafana, ves el pico en el gráfico, y entonces empieza la parte difícil: ¿por qué?

Si tu sistema solo tiene monitoreo, sabés qué está mal. Si tiene observabilidad, podés responder por qué. La diferencia entre esas dos preguntas es la diferencia entre 10 minutos de debugging y 3 horas de revisar logs a ciegas.

Monitoreo vs observabilidad: no son sinónimos

El monitoreo es reactivo. Definís umbrales conocidos — CPU > 80%, error rate > 1%, latencia > 500ms — y cuando se superan, alertás. Es útil, pero tiene un límite fundamental: solo te avisa sobre las fallas que ya anticipaste.

La observabilidad es la capacidad de entender el estado interno de un sistema a partir de sus outputs externos, sin necesidad de saber de antemano qué pregunta vas a hacer. Viene del control de sistemas: un sistema es observable si podés inferir su estado interno sin instrumentación adicional.

El monitoreo te dice que el paciente tiene fiebre. La observabilidad te deja hacerle preguntas hasta entender por qué.

La observabilidad se construye sobre tres tipos de señales, conocidas como los tres pilares: métricas, logs y trazas. Cada una responde una pregunta distinta. Usarlas bien — y entender cuándo usar cada una — es lo que hace debuggeable a un sistema en producción.

Métricas: ¿qué está pasando ahora?

Las métricas son mediciones numéricas agregadas en el tiempo. Son la señal más eficiente en términos de storage y la más rápida de consultar. Responden a la pregunta: ¿qué está pasando en el sistema ahora mismo?

Hay tres tipos fundamentales de métricas en Prometheus (el estándar de facto):

Counter: solo sube. Requests totales, errores totales, bytes enviados. Nunca se resetea (salvo reinicio). Se usa con rate() para calcular variación en el tiempo.

from prometheus_client import Counter

requests_total = Counter(
    'http_requests_total',
    'Total de requests HTTP',
    ['method', 'endpoint', 'status']
)

# En el handler:
requests_total.labels(method='GET', endpoint='/orders', status='200').inc()

Gauge: sube y baja. Conexiones activas, memoria usada, jobs en cola. Representa un valor puntual en el tiempo.

Histogram: distribuye observaciones en buckets. Fundamental para medir latencia. Permite calcular percentiles (p50, p95, p99) que son mucho más útiles que un promedio.

from prometheus_client import Histogram
import time

request_latency = Histogram(
    'http_request_duration_seconds',
    'Latencia de requests HTTP',
    ['endpoint'],
    buckets=[0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0]
)

# Uso:
with request_latency.labels(endpoint='/orders').time():
    result = process_order()

El error más común con métricas es confiar solo en el promedio de latencia. Un promedio de 200ms puede esconder que el 1% de los requests tarda 10 segundos. El p99 es siempre más honesto.

Logs: ¿qué pasó exactamente?

Los logs son registros de eventos discretos. Son la señal más antigua y la más mal usada. Responden a la pregunta: ¿qué pasó exactamente en este momento?

El problema con los logs no es que sean inútiles — es que la mayoría de los equipos los usan mal. Dos antipatrones que se repiten siempre:

Logs no estructurados: strings de texto libre imposibles de parsear a escala.

# MAL: imposible de filtrar o agregar
print(f"Error procesando orden {order_id} para usuario {user_id}: {error}")

# BIEN: structured logging
import structlog
log = structlog.get_logger()

log.error("order_processing_failed",
    order_id=order_id,
    user_id=user_id,
    error_type=type(error).__name__,
    error_message=str(error)
)

Logging de todo: loggear cada request en DEBUG genera tal volumen que los logs importantes se pierden y los costos de storage se disparan. La regla práctica: ERROR para fallas reales, WARN para degradación, INFO solo para eventos de negocio relevantes, DEBUG solo en desarrollo.

En el stack moderno, los logs se centralizan con herramientas como Loki (si usás Grafana), Elasticsearch (stack ELK), o CloudWatch/Cloud Logging en entornos managed. La clave es que sean buscables y correlacionables con las otras señales.

Trazas: ¿por dónde pasó el request?

Las trazas distribuidas son la señal más poderosa y la menos implementada. Responden a la pregunta: ¿por dónde pasó este request específico, cuánto tardó cada parte, y dónde se atascó?

En una arquitectura de microservicios, un request puede pasar por el API Gateway, el servicio de autenticación, el servicio de órdenes, la base de datos, el servicio de notificaciones y el proveedor de emails. Las métricas te dicen que algo tardó. Los logs de cada servicio tienen fragmentos de la historia. Solo las trazas te dan el cuadro completo.

Una traza se compone de spans. Cada span representa una operación — un llamado HTTP, una query SQL, una llamada a función. Los spans tienen inicio, duración, atributos, y se encadenan con un trace ID que los conecta a través de servicios.

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider

tracer = trace.get_tracer(__name__)

def process_order(order_id: str):
    with tracer.start_as_current_span("process_order") as span:
        span.set_attribute("order.id", order_id)
        
        with tracer.start_as_current_span("validate_inventory"):
            result = inventory_service.check(order_id)
            
        with tracer.start_as_current_span("charge_payment") as payment_span:
            payment_span.set_attribute("payment.method", result.payment_method)
            charge = payment_service.charge(order_id)
            
        return charge

El trace ID debe propagarse en los headers HTTP entre servicios. El estándar hoy es el header W3C traceparent. Si un servicio intermedio no lo propaga, la cadena se rompe y perdés visibilidad en ese tramo.

OpenTelemetry: el estándar que unificó el caos

Antes de OpenTelemetry, cada vendor tenía su SDK propio. Si usabas Datadog, te casabas con Datadog. Si querías cambiar a New Relic, reinstrumentabas todo. OpenTelemetry (OTel) resolvió eso: es un estándar abierto, vendor-neutral, para emitir métricas, logs y trazas desde tu aplicación.

La arquitectura es simple: tu app instrumentada con el OTel SDK emite telemetría al OTel Collector, que la procesa, filtra y exporta al backend que elijas — Jaeger, Tempo, Prometheus, Datadog, Honeycomb, o cualquier otro compatible. Cambiás de backend sin tocar el código de tu app.

# otel-collector.yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

exporters:
  prometheus:
    endpoint: "0.0.0.0:8889"
  otlp/tempo:
    endpoint: tempo:4317
    tls:
      insecure: true
  loki:
    endpoint: http://loki:3100/loki/api/v1/push

service:
  pipelines:
    metrics:
      receivers: [otlp]
      exporters: [prometheus]
    traces:
      receivers: [otlp]
      exporters: [otlp/tempo]
    logs:
      receivers: [otlp]
      exporters: [loki]

El stack moderno: Grafana LGTM

El stack open source más adoptado hoy para los tres pilares es el que Grafana llama LGTM:

Loki para logs — indexa solo los labels, no el contenido completo. Mucho más barato que Elasticsearch a escala. Grafana para visualización — dashboards, alertas, exploración. Tempo para trazas — almacenamiento de trazas distribuidas, integrado nativamente con Grafana. Mimir (o Prometheus) para métricas — series de tiempo con PromQL.

La killer feature de este stack es la correlación entre señales. Desde un spike en una métrica podés saltar directamente a los logs de ese período. Desde un log de error podés abrir la traza del request que lo generó. Eso es observabilidad real.

Cuándo cada señal te salva

Escenario 1 — La latencia subió pero no hay errores: empezás por las métricas. ¿Es un endpoint específico? ¿Coincide con mayor volumen de requests? Luego vas a las trazas para ver qué span está tardando — casi siempre es una query SQL sin índice o una llamada externa que se degradó.

Escenario 2 — Hay errores 500 intermitentes: las métricas te dan la tasa de error. Los logs te dan el stack trace. Las trazas te dicen en qué servicio ocurre y si hay un patrón — solo cuando un servicio upstream está involucrado, solo para ciertos usuarios, solo bajo carga.

Escenario 3 — Memoria subiendo sostenidamente hasta OOM: las métricas te muestran la tendencia. Los logs pueden tener pistas si loggeas estadísticas de uso. Las trazas te ayudan a encontrar qué tipo de requests acumula más memoria — un leak generalmente está ligado a un flujo específico.

El error más común: usar logs para todo

La mayoría de los sistemas que dicen tener observabilidad tienen logs. Solo logs. Y cuando algo falla, el proceso es siempre el mismo: SSH al servidor, grep, leer miles de líneas, intentar reconstruir la secuencia de eventos en la cabeza.

Los logs no escalan como herramienta de diagnóstico primario. Son caros de almacenar, lentos de buscar a volumen, y no tienen contexto de causalidad entre servicios. Son indispensables, pero como complemento de las métricas y las trazas, no como reemplazo.

Conclusión

Las métricas te dicen que algo está mal. Los logs te dicen qué evento ocurrió. Las trazas te dicen por dónde pasó el flujo. Necesitás las tres, correlacionadas, para poder debuggear un sistema distribuido sin depender de la suerte o de reproducir el problema.

El costo de instrumentar bien es bajo comparado con el costo de una noche debuggeando a ciegas. Y con OpenTelemetry como estándar, nunca fue más fácil hacerlo sin atarse a un vendor. La observabilidad no es un lujo de empresas grandes — es lo mínimo que le debés a tu sistema en producción.

Un sistema observable no es el que nunca falla. Es el que cuando falla, te dice exactamente por qué.

FungiCode.io

Software Development · Data Analysis

Work with me