TechLead
Leccion 28 de 30
6 min de lectura
Diseño de Sistemas

Monitoreo y Observabilidad

Domina el monitoreo y la observabilidad con los tres pilares: logs, métricas y trazas. Aprende sobre Prometheus, Grafana, alertas, SLOs y dashboards.

Monitoreo y Observabilidad

El monitoreo te dice cuándo algo está roto. La observabilidad te dice por qué. A medida que los sistemas crecen en complejidad con microservicios, contenedores y arquitecturas distribuidas, el monitoreo tradicional es insuficiente. La observabilidad es la capacidad de entender el estado interno de tu sistema examinando sus salidas externas: logs, métricas y trazas.

Monitoreo vs. Observabilidad

Aspecto Monitoreo Observabilidad
Enfoque Modos de fallo conocidos Desconocidos desconocidos
Enfoque Dashboards y alertas predefinidos Exploración ad-hoc y correlación
Pregunta "¿Está sano el sistema?" "¿Por qué esta solicitud es lenta?"
Datos Métricas agregadas Datos de alta cardinalidad y alta dimensionalidad

Los Tres Pilares

1. Logs

Los logs son registros discretos de eventos con timestamps. Proporcionan contexto detallado sobre lo que sucedió en un punto específico en el tiempo.

// Structured logging (JSON format)
interface LogEntry {
  timestamp: string;
  level: "DEBUG" | "INFO" | "WARN" | "ERROR";
  message: string;
  service: string;
  traceId?: string;
  spanId?: string;
  userId?: string;
  requestId?: string;
  duration?: number;
  error?: {
    message: string;
    stack: string;
    code: string;
  };
  [key: string]: unknown;
}

class Logger {
  private context: Record<string, unknown>;

  constructor(service: string) {
    this.context = { service };
  }

  withContext(ctx: Record<string, unknown>): Logger {
    const logger = new Logger(this.context.service as string);
    logger.context = { ...this.context, ...ctx };
    return logger;
  }

  info(message: string, data?: Record<string, unknown>): void {
    console.log(JSON.stringify({
      timestamp: new Date().toISOString(),
      level: "INFO",
      message,
      ...this.context,
      ...data,
    }));
  }

  error(message: string, error: Error, data?: Record<string, unknown>): void {
    console.error(JSON.stringify({
      timestamp: new Date().toISOString(),
      level: "ERROR",
      message,
      error: { message: error.message, stack: error.stack, name: error.name },
      ...this.context,
      ...data,
    }));
  }
}

// Usage
const logger = new Logger("order-service");
const reqLogger = logger.withContext({ requestId: "req_123", userId: "user_456" });
reqLogger.info("Order created", { orderId: "ord_789", amount: 99.99 });

2. Métricas

Las métricas son mediciones numéricas recolectadas a lo largo del tiempo. Son agregables y eficientes de almacenar, haciéndolas ideales para dashboards, alertas y análisis de tendencias.

// Four types of metrics (following Prometheus conventions)

// Counter: monotonically increasing value
// Use for: total requests, errors, bytes transferred
const httpRequestsTotal = new Counter({
  name: "http_requests_total",
  help: "Total HTTP requests",
  labelNames: ["method", "path", "status"],
});
httpRequestsTotal.labels("GET", "/api/orders", "200").inc();

// Gauge: value that goes up and down
// Use for: current connections, queue depth, temperature
const activeConnections = new Gauge({
  name: "active_connections",
  help: "Current active connections",
});
activeConnections.set(42);

// Histogram: distribution of values in buckets
// Use for: request duration, response size
const requestDuration = new Histogram({
  name: "http_request_duration_seconds",
  help: "Request duration in seconds",
  labelNames: ["method", "path"],
  buckets: [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10],
});
const end = requestDuration.labels("GET", "/api/orders").startTimer();
// ... handle request ...
end(); // Records the duration

3. Trazas

Las trazas distribuidas siguen una solicitud a medida que fluye a través de múltiples servicios. Cada traza está compuesta de spans, donde cada span representa una unidad de trabajo.

// Trace structure
interface Span {
  traceId: string;
  spanId: string;
  parentSpanId?: string;
  operationName: string;
  serviceName: string;
  startTime: number;
  duration: number;
  status: "OK" | "ERROR";
  tags: Record<string, string>;
  logs: SpanLog[];
}

// Example: API request -> Order Service -> Payment Service -> Database
// Trace: traceId = "abc"
//   Span 1: API Gateway (root span)
//     Span 2: Order Service - createOrder
//       Span 3: Payment Service - processPayment
//         Span 4: Stripe API call
//       Span 5: Database - insertOrder

function tracingMiddleware(req: Request, res: Response, next: Function) {
  const traceId = req.headers["x-trace-id"] || generateTraceId();
  const parentSpanId = req.headers["x-span-id"];
  const spanId = generateSpanId();

  req.traceContext = { traceId, spanId, parentSpanId };
  res.setHeader("x-trace-id", traceId);

  const startTime = Date.now();
  res.on("finish", () => {
    recordSpan({
      traceId,
      spanId,
      parentSpanId,
      operationName: `${req.method} ${req.path}`,
      serviceName: "api-gateway",
      startTime,
      duration: Date.now() - startTime,
      status: res.statusCode < 400 ? "OK" : "ERROR",
      tags: { "http.method": req.method, "http.status": String(res.statusCode) },
      logs: [],
    });
  });

  next();
}

Panorama de Herramientas

Herramienta Pilar Propósito
Prometheus Métricas Recolección y consulta de métricas de series de tiempo (PromQL)
Grafana Visualización Dashboards para métricas, logs y trazas
ELK Stack Logs Elasticsearch + Logstash + Kibana para agregación de logs
Loki Logs Agregación de logs ligera por Grafana Labs
Jaeger Trazas Trazado distribuido (originalmente de Uber)
OpenTelemetry Los tres Estándar neutral de proveedor para recolección de telemetría
Datadog Los tres Plataforma de observabilidad todo-en-uno comercial

Estrategias de Alertas

Principios de Alertas Efectivas

  • Alerta sobre síntomas, no causas. Alerta sobre "tasa de error API > 1%" no "CPU > 80%"
  • Cada alerta debe ser accionable. Si la persona de guardia no puede hacer nada al respecto, no debería pagarles
  • Usa niveles de severidad: P1 (pagina inmediatamente), P2 (notificación Slack), P3 (ticket creado)
  • Incluye runbooks. Cada alerta debe vincular a un documento explicando qué verificar y cómo remediar
  • Evita la fatiga de alertas. Demasiadas alertas significan que los ingenieros empiezan a ignorarlas. Revisa y poda alertas regularmente

SLIs, SLOs y SLAs

Estos tres conceptos forman una jerarquía para definir y medir la fiabilidad del servicio.

Concepto Definición Ejemplo
SLI (Indicador de Nivel de Servicio) Una medida cuantitativa de la calidad del servicio 99.2% de solicitudes completan en menos de 200ms
SLO (Objetivo de Nivel de Servicio) Un valor objetivo para un SLI 99.9% de solicitudes deben completar en menos de 200ms
SLA (Acuerdo de Nivel de Servicio) Un contrato con consecuencias por no cumplir el SLO Si la disponibilidad cae debajo del 99.9%, el cliente recibe créditos
// Error budget calculation
interface SLOConfig {
  name: string;
  target: number;     // e.g., 0.999 (99.9%)
  window: number;     // Rolling window in days (e.g., 30)
}

function calculateErrorBudget(slo: SLOConfig, currentSLI: number) {
  const totalMinutesInWindow = slo.window * 24 * 60;
  const allowedDowntimeMinutes = totalMinutesInWindow * (1 - slo.target);
  const usedDowntimeMinutes = totalMinutesInWindow * (1 - currentSLI);
  const remainingBudgetMinutes = allowedDowntimeMinutes - usedDowntimeMinutes;
  const budgetRemainingPercent = (remainingBudgetMinutes / allowedDowntimeMinutes) * 100;

  return {
    totalBudgetMinutes: allowedDowntimeMinutes,
    usedBudgetMinutes: usedDowntimeMinutes,
    remainingBudgetMinutes: remainingBudgetMinutes,
    budgetRemainingPercent: budgetRemainingPercent,
    burnRate: usedDowntimeMinutes / allowedDowntimeMinutes,
  };
}

// Example: 99.9% SLO over 30 days
// Total budget: 30 * 24 * 60 * 0.001 = 43.2 minutes of downtime allowed
// If current SLI = 99.85%, used 64.8 minutes, budget is EXHAUSTED

Diseño de Dashboards

Principios de Dashboards Efectivos

  • Método USE: Para infraestructura: Utilización, Saturación, Errores para cada recurso (CPU, memoria, disco, red)
  • Método RED: Para servicios: Rate (solicitudes/seg), Errors (tasa de error), Duration (percentiles de latencia)
  • Capas de dashboards: Vista general de alto nivel con drill-down a servicios específicos, luego endpoints específicos
  • Muestra percentiles, no promedios. Las latencias p50, p95, p99 revelan la experiencia de usuarios en la cola que los promedios ocultan
  • Incluye contexto. Muestra marcadores de despliegue, umbrales de alertas y objetivos SLO en los gráficos
  • Consistencia de rango temporal. Todos los paneles en un dashboard deben mostrar el mismo rango de tiempo

Continuar Aprendiendo