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 duration3. 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 EXHAUSTEDDiseñ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