Logs — El Registro del Comportamiento

Por: Artiko
logsloggingstructured-logsobservabilidaddebugging

Logs — El Registro del Comportamiento

¿Qué son los logs?

Los logs son registros de eventos discretos que ocurrieron en un sistema en un momento específico en el tiempo. Son la forma más antigua y ubicua de instrumentación de software — prácticamente todo programa desde los años 60 ha emitido algún tipo de log.

Un log es, en su forma más simple, una línea de texto que describe algo que ocurrió:

2026-04-04 14:23:45 ERROR Failed to connect to database

Pero los logs modernos son mucho más ricos que esto, y la diferencia entre logs bien y mal diseñados puede significar la diferencia entre resolver un incidente en 5 minutos o en 5 horas.


El problema con los logs tradicionales: texto no estructurado

La mayoría de los sistemas legacy emiten logs como texto plano no estructurado. El problema es inmediato cuando intentas buscar en ellos:

Apr  4 14:23:45 prod-server-01 myapp[1234]: User johndoe logged in from 192.168.1.100
Apr  4 14:23:46 prod-server-01 myapp[1234]: Processing payment for order #98765, amount: $149.99
Apr  4 14:23:47 prod-server-01 myapp[1234]: ERROR: Payment gateway timeout after 3000ms
Apr  4 14:23:47 prod-server-01 myapp[1234]: Retrying payment for order #98765, attempt 2/3

Para buscar todos los pagos fallidos de un usuario específico en el último mes, necesitas:

  1. Acceder a los archivos de log de todos los servidores
  2. Parsear el formato con expresiones regulares frágiles
  3. Hacer joins manuales entre diferentes líneas
  4. Esperar minutos u horas si el volumen es grande

Esto no escala. En un sistema con 50 servidores emitiendo 10,000 líneas por segundo, el log unstructured es prácticamente innavegable.


Logs estructurados: la solución moderna

Los logs estructurados son logs emitidos en un formato que puede ser parseado automáticamente, típicamente JSON. Cada campo es explícito, tipado y consultable.

{
  "timestamp": "2026-04-04T14:23:47.423Z",
  "level": "error",
  "service": "payment-service",
  "version": "2.3.1",
  "environment": "production",
  "trace_id": "a1b2c3d4e5f6g7h8",
  "span_id": "i9j0k1l2",
  "user_id": "usr_johndoe_12345",
  "order_id": "ord_98765",
  "event": "payment_gateway_timeout",
  "gateway": "stripe",
  "timeout_ms": 3000,
  "attempt": 2,
  "max_attempts": 3,
  "message": "Payment gateway timeout, retrying"
}

Ahora puedes hacer consultas como:

Esta es la diferencia entre logs como texto plano y logs como datos.

graph LR
    subgraph "Logs No Estructurados"
        UL[Texto plano\nnov 4 14:23 ERROR payment failed] --> UP[Parse con regex\nfrágil y lento]
        UP --> US[Búsqueda limitada\ny manual]
    end

    subgraph "Logs Estructurados"
        SL[JSON/Key-Value\ntimestamp, level, user_id, event] --> SP[Indexado automático\npor cada campo]
        SP --> SS[Búsqueda rápida\npor cualquier campo]
    end

Los niveles de severidad

Los niveles de log permiten filtrar el ruido y enfocarse en lo importante. El estándar más común sigue la convención de syslog/RFC 5424:

NivelUsoEjemplo
TRACEInformación extremadamente detallada, solo para debugging intensivoEntrada/salida de cada función
DEBUGInformación útil durante desarrollo y debuggingEstado de variables, flujo de control
INFOEventos normales del negocioUsuario se registró, pedido completado
WARNSituaciones anómalas que no son errores pero merecen atenciónRetry exitoso, uso alto de recursos
ERRORErrores que impactan una operación específicaPago fallido, request timeout
FATAL/CRITICALErrores que impiden que el sistema funcioneNo puede conectarse a la base de datos, crash

Regla de oro para los niveles

flowchart TD
    Q1{¿Detuvo el sistema?} -->|Sí| FATAL[FATAL/CRITICAL]
    Q1 -->|No| Q2{¿Falló una operación\npara el usuario?}
    Q2 -->|Sí| ERROR[ERROR]
    Q2 -->|No| Q3{¿Algo inusual\npero recuperable?}
    Q3 -->|Sí| WARN[WARN]
    Q3 -->|No| Q4{¿Es un evento\nimportante del negocio?}
    Q4 -->|Sí| INFO[INFO]
    Q4 -->|No| Q5{¿Útil para\ndebugging?}
    Q5 -->|Sí| DEBUG[DEBUG]
    Q5 -->|No| TRACE[TRACE]

Errores comunes con los niveles

Over-logging en ERROR: El error más frecuente. Si cada request que devuelve 404 es un ERROR, tu tablero de errores se convierte en ruido. Un 404 es un INFO o ni siquiera eso — es el comportamiento esperado.

Under-logging en INFO: Muchos sistemas no logean eventos de negocio importantes. “Usuario completó checkout”, “Suscripción cancelada”, “Archivo procesado” son eventos que tienen valor para negocio y debugging.

DEBUG en producción: Dejar DEBUG activado en producción puede aumentar el volumen de logs 10-100x, saturar el storage y ocultar los eventos importantes.


¿Qué incluir en un log?

Un log bien diseñado responde las preguntas: ¿Qué? ¿Quién? ¿Cuándo? ¿Dónde? ¿Con qué resultado?

Campos obligatorios

{
  "timestamp": "2026-04-04T14:23:47.423Z",  // ISO 8601, siempre UTC
  "level": "error",                           // Nivel de severidad
  "service": "payment-service",               // Nombre del servicio
  "environment": "production",                // prod / staging / dev
  "message": "Descripción humano-legible"    // Qué pasó
}

Campos de trazabilidad

{
  "trace_id": "a1b2c3d4e5f6g7h8",   // Para correlacionar con trazas
  "span_id": "i9j0k1l2",             // Span actual
  "request_id": "req_xyz789"         // ID único de la request
}

Campos de contexto de negocio

{
  "user_id": "usr_12345",
  "tenant_id": "acme-corp",
  "order_id": "ord_98765",
  "session_id": "sess_abc"
}

Campos de contexto técnico

{
  "host": "prod-server-01",
  "pod": "payment-service-7d9b-xkp4z",
  "version": "2.3.1",
  "duration_ms": 234
}

Logging de errores: la anatomía completa

Cuando logueas un error, la información mínima debe permitir a alguien que nunca vio el sistema entender qué falló, en qué contexto, y con qué consecuencia:

{
  "timestamp": "2026-04-04T14:23:47.423Z",
  "level": "error",
  "service": "payment-service",
  "event": "payment_failed",
  "message": "Payment processing failed after 3 attempts",

  "error": {
    "type": "PaymentGatewayException",
    "message": "Connection timeout to Stripe API",
    "stack": "PaymentService.processPayment (payment.service.ts:234)...",
    "code": "GATEWAY_TIMEOUT"
  },

  "context": {
    "user_id": "usr_12345",
    "order_id": "ord_98765",
    "amount_cents": 14999,
    "currency": "USD",
    "gateway": "stripe",
    "attempts": 3
  },

  "impact": {
    "user_facing": true,
    "order_lost": false,
    "retry_scheduled": true,
    "retry_at": "2026-04-04T14:28:47.423Z"
  },

  "trace_id": "a1b2c3d4e5f6g7h8"
}

Logs y privacidad: qué NO loguear

Los logs son a menudo el vector de más fugas de datos sensibles. Reglas fundamentales:

graph TD
    D[Dato a loguear] --> Q{¿Es sensible?}
    Q -->|Sí| NEVER[NUNCA loguear]
    Q -->|No| LOG[Loguear normalmente]

    NEVER --> S1[Contraseñas / tokens]
    NEVER --> S2[Números de tarjeta]
    NEVER --> S3[SSN / documentos de identidad]
    NEVER --> S4[Datos médicos]
    NEVER --> S5[Claves privadas / secrets]
    NEVER --> S6[PII sin justificación legal]

En lugar de loguear datos sensibles, logea referencias:

No: "password": "mysecret"
Sí: No lo loguees bajo ninguna circunstancia.


Arquitectura de un sistema de logs

En producción, los logs fluyen desde la aplicación hasta un sistema de almacenamiento y búsqueda:

flowchart LR
    subgraph Aplicación
        A1[Service A] --> CL[Log Collector\ne.g. Fluentd/Logstash]
        A2[Service B] --> CL
        A3[Service C] --> CL
    end

    CL --> |Transporte seguro\nTLS| AGG[Aggregator]
    AGG --> STORE[(Log Storage\nElasticsearch/Loki/S3)]
    STORE --> SEARCH[Search & Analysis\nKibana/Grafana]
    STORE --> ALERT[Alert System]
    STORE --> ARCHIVE[Cold Storage\nS3/GCS para retención larga]

Consideraciones de volumen

Un servicio mediano puede generar entre 1MB y 1GB de logs por hora dependiendo del nivel de verbosidad. A escala, el costo del almacenamiento y procesamiento de logs es significativo. Estrategias:

Sampling: Para eventos de alta frecuencia y baja importancia, loguea solo una muestra. Si procesas 1M de pagos exitosos al día, quizás logear 1% de los exitosos y 100% de los fallidos es suficiente.

Log levels by environment: En producción, INFO. En staging, DEBUG. En desarrollo, TRACE.

Retención diferenciada: Logs de ERROR → 90 días. Logs de INFO → 30 días. Logs de DEBUG → 7 días.


Correlación: el superpoder de los logs estructurados

El mayor beneficio de los logs estructurados es la capacidad de correlacionar eventos a través de servicios. El campo trace_id es el hilo que conecta todo.

Cuando un usuario reporta “mi pago no funcionó a las 3PM”, puedes:

  1. Encontrar el trace_id asociado a esa request
  2. Buscar todos los logs de todos los servicios con ese trace_id
  3. Reconstruir exactamente qué pasó, en qué servicio, en qué orden
trace_id: a1b2c3d4e5f6g7h8

14:23:45.100 [api-gateway] INFO  Request received: POST /checkout
14:23:45.150 [order-service] INFO  Order created: ord_98765
14:23:45.200 [inventory-service] INFO  Stock reserved for ord_98765
14:23:45.250 [payment-service] INFO  Payment processing started
14:23:47.423 [payment-service] ERROR Payment gateway timeout (3000ms)
14:23:47.450 [payment-service] WARN  Retry 1/3 scheduled
14:23:50.450 [payment-service] ERROR Payment gateway timeout (3000ms)
14:23:50.500 [order-service] INFO  Order ord_98765 marked as pending
14:23:50.520 [notification-service] INFO  Email queued: payment_pending

En segundos tienes toda la historia de lo que pasó, sin grep manual, sin SSH, sin reuniones.


Mejores prácticas

mindmap
  root((Mejores Prácticas\nde Logging))
    Estructura
      JSON siempre
      Campos consistentes
      No interpolación de strings
    Contexto
      trace_id en cada log
      user_id cuando aplica
      service y version
    Contenido
      Mensajes descriptivos
      No datos sensibles
      Error completo con stack
    Operación
      Sampling en alta frecuencia
      Niveles correctos
      Retención por nivel

Anti-patrones comunes

Log and throw: No loguees un error Y lo relances hacia arriba — terminarás con el mismo error logueado múltiples veces en diferentes capas, dificultando el diagnóstico.

Logging en bucles: Si procesas 10,000 items, no loguees un INFO por item. Loguea el inicio, el fin y los errores.

Mensajes sin contexto: "Error processing request" no le dice nada a nadie. "Error processing payment for order ord_98765: timeout after 3000ms" sí.

Timestamps en timezone local: Siempre UTC, siempre ISO 8601. Los logs de múltiples servidores en diferentes zonas horarias con timestamps locales son una pesadilla.


Herramientas populares

Recopilación y transporte:

Almacenamiento y búsqueda:

SaaS:


Referencias