intermediate Por Mathias Paulenko

Agregación de Logs

Guía práctica sobre agregación de logs: logs estructurados, estrategias de envío, políticas de retención y construcción de pipelines de logs consultables con ELK, Loki y soluciones nativas de la nube.

Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.

Descripción General

La agregación de logs recolecta logs de todos los servicios, sistemas e infraestructura en una plataforma centralizada y consultable. Transforma archivos de texto dispersos en una señal de observabilidad consultable, permitiendo depuración rápida, auditoría de seguridad y visibilidad operativa en sistemas distribuidos.

A continuación: logging estructurado, estrategias de envío, optimización de almacenamiento y selección de plataformas.

Cuándo Usar

  • For alternatives, see Complete Guide to Observability with the Grafana Stack.

  • Operas más de 5 servicios y necesitas correlacionar logs entre ellos

  • Depurar requiere buscar a través de múltiples servidores o contenedores

  • Necesitas alertas basadas en logs para errores y anomalías

  • Seguridad o cumplimiento requiere logs de auditoría centralizados

  • Tu logging actual es ad-hoc e inconsistente entre equipos

Conceptos Clave

ConceptoDescripción
Logging EstructuradoEmitir logs como JSON o pares clave-valor en lugar de texto libre
Log ShipperAgente que lee logs locales y los reenvía a un almacén central
ÍndicePartición de almacenamiento consultable organizada por tiempo o fuente
Política de RetenciónReglas que determinan cuánto tiempo se conservan los logs antes de eliminarse
Parsing de LogsExtraer campos de líneas de log en bruto al ingerir o consultar
Almacenamiento Hot/Warm/ColdAlmacenamiento por niveles basado en frecuencia de acceso y antigüedad

Arquitecturas de Agregación de Logs

┌──────────┐   ┌──────────┐   ┌──────────┐
│  App 1   │   │  App 2   │   │  App N   │
│ (stdout) │   │ (stdout) │   │ (stdout) │
└────┬─────┘   └────┬─────┘   └────┬─────┘
     │              │              │
     └──────────────┼──────────────┘
                    │
           ┌────────▼────────┐
           │  Log Shipper    │  (Filebeat, Fluent Bit, Vector)
           │  (Parse + Enrich)│
           └────────┬────────┘
                    │
         ┌──────────┴──────────┐
         │                     │
   ┌─────▼─────┐        ┌─────▼─────┐
   │  Indexer  │        │  Object   │
   │(Elasticsearch│      │  Storage  │
   │   Loki)    │        │  (S3/GCS) │
   └─────┬─────┘        └───────────┘
         │
   ┌─────▼─────┐
   │ Dashboard │
   │(Kibana/Grafana)
   └───────────┘

Configuración de Agregación de Logs Paso a Paso

1. Adopta Logging Estructurado

Haz que los logs sean parseables por máquinas desde la fuente:

# Ejemplo: Logging estructurado en Python con structlog
import structlog
import logging
import sys

structlog.configure(
    processors=[
        structlog.stdlib.filter_by_level,
        structlog.stdlib.add_logger_name,
        structlog.stdlib.add_log_level,
        structlog.stdlib.PositionalArgumentsFormatter(),
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.processors.StackInfoRenderer(),
        structlog.processors.format_exc_info,
        structlog.processors.UnicodeDecoder(),
        structlog.processors.JSONRenderer()
    ],
    context_class=dict,
    logger_factory=structlog.stdlib.LoggerFactory(),
    wrapper_class=structlog.stdlib.BoundLogger,
    cache_logger_on_first_use=True,
)

logger = structlog.get_logger()

# Log estructurado
logger.info(
    "payment_processed",
    payment_id="pay-123",
    amount=99.99,
    currency="USD",
    user_id="user-456",
    duration_ms=145
)
# Salida: {"event": "payment_processed", "payment_id": "pay-123", "amount": 99.99, ...}
// Ejemplo: Logging estructurado en Node.js con pino
const pino = require('pino');
const logger = pino({ level: 'info' });

logger.info({
  msg: 'payment_processed',
  paymentId: 'pay-123',
  amount: 99.99,
  currency: 'USD',
  userId: 'user-456',
  durationMs: 145
});

Lo que funciona para logging estructurado:

  • Siempre loguea como JSON en producción
  • Usa nombres de campo consistentes (snake_case recomendado)
  • Incluye trace_id, span_id y request_id en cada log
  • Añade campos contextuales (user_id, tenant_id, request_path) al inicio de la petición
  • Nunca loguees PII o secretos

2. Envía Logs al Almacén Central

Elige y configura un log shipper:

ShipperMejor ParaProsContras
FilebeatStack ELKMaduro, módulos ricosConsumo de recursos elevado
Fluent BitKubernetes, embebidoLigero, rápidoMenos maduro que Fluentd
VectorAlto rendimientoBasado en Rust, performanteEcosistema más pequeño
PromtailLokiIntegración nativa con LokiSolo Loki
# Ejemplo: Configuración de Fluent Bit para Kubernetes
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluent-bit-config
data:
  fluent-bit.conf: |
    [INPUT]
        Name              tail
        Tag               kube.*
        Path              /var/log/containers/*.log
        Parser            docker
        DB                /var/log/flb_kube.db

    [FILTER]
        Name              kubernetes
        Match             kube.*
        Merge_Log         On
        Keep_Log          Off

    [OUTPUT]
        Name              loki
        Match             kube.*
        Host              loki.monitoring.svc
        Labels            job=fluentbit
# Ejemplo: Configuración de Filebeat para Elasticsearch
filebeat.inputs:
  - type: log
    paths:
      - /var/log/myapp/*.log
    fields:
      service: myapp
      environment: production
    fields_under_root: true
    json.keys_under_root: true
    json.add_error_key: true

output.elasticsearch:
  hosts: ["https://elasticsearch:9200"]
  index: "myapp-logs-%{+yyyy.MM.dd}"

Lo que funciona para el envío:

  • Usa shippers conscientes de backpressure que no fallen el host
  • Añade metadatos (host, servicio, entorno) a nivel del shipper
  • Almacena en buffer local para sobrevivir cortes de red temporales
  • Usa TLS para todo el transporte de logs

3. Diseña Retención y Almacenamiento

Equilibra costo con capacidad de consulta:

Nivel de AlmacenamientoRetenciónVelocidad de ConsultaCosto
Hot1-7 díasInstantáneaAlto
Warm7-30 díasSegundosMedio
Cold (S3/GCS)30-365 díasMinutosBajo
Archivo1-7 añosSolo batchMuy bajo
# Ejemplo: Política ILM de Elasticsearch
PUT _ilm/policy/logs_policy
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_size": "50GB",
            "max_age": "1d"
          }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "shrink": { "number_of_shards": 1 },
          "forcemerge": { "max_num_segments": 1 }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "freeze": {},
          "allocate": { "require": { "data": "cold" } }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": { "delete": {} }
      }
    }
  }
}

Reglas de retención:

  • Logs de error: Conservar más tiempo (90+ días) que logs de acceso (30 días)
  • Logs de seguridad/auditoría: Conservar 1-7 años según requisitos de cumplimiento
  • Logs de debug: Conservar solo en almacenamiento hot (1-3 días)
  • Archiva a almacenamiento de objetos antes de eliminar para cumplimiento

4. Consulta y Analiza Logs

Haz que tus logs agregados sean útiles:

# Ejemplo: KQL (Lenguaje de Consulta de Kibana)

# Encuentra errores en un servicio específico
service.name:orders-service and level:error

# Encuentra peticiones lentas (>1s)
duration_ms > 1000

# Encuentra peticiones para un usuario específico
user_id:user-123

# Cuenta errores por servicio
service.name:* and level:error | stats count() by service.name

# Encuentra excepciones en rango de tiempo
@timestamp:[now-1h TO now] and exception.class:*
# Ejemplo: LogQL (Grafana Loki)

# Busca errores en un servicio
{job="orders-service"} |= "ERROR"

# Cuenta errores por minuto
sum(rate({job="orders-service"} |= "ERROR" [1m]))

# Encuentra consultas lentas a base de datos
{job="orders-service"} |= "duration_ms" | json | duration_ms > 500

# Extrae y grafica montos de pagos
{job="payment-service"} |= "payment_processed" | json | line_format "{{.amount}}"

Lo que funciona para consultar:

  • Aprende el lenguaje de consulta de tu plataforma elegida (KQL, LogQL, SPL)
  • Guarda consultas comunes como dashboards o alertas
  • Usa métricas basadas en logs para dashboards (más rápido que consultas de logs en bruto)
  • Correlaciona logs con trazas usando campos trace_id

5. Construye Alertas Basadas en Logs

Detecta problemas desde patrones de logs:

# Ejemplo: Regla de alerta de Grafana para tasa de error
apiVersion: 1
groups:
  - name: log_alerts
    rules:
      - alert: HighErrorRate
        expr: |
          sum(rate({job=~".*"} |= "ERROR" [5m])) by (job)
          /
          sum(rate({job=~".*"} [5m])) by (job)
          > 0.01
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "High error rate detected"

      - alert: SlowPayments
        expr: |
          avg_over_time(
            {job="payment-service"} |= "payment_processed" | json | duration_ms [10m]
          ) > 500
        labels:
          severity: warning

Patrones de alerta:

  • Pico en tasa de error: % de logs de error / total de logs > umbral
  • Nuevo patrón de error: Cantidad de tipos únicos de excepción aumentó
  • Logs faltantes: Volumen de logs cayó por debajo de la línea base esperada
  • Evento de seguridad: Coincidencia de patrones conocidos de ataque

Lo que funciona

  • Estandariza niveles de log. Usa ERROR, WARN, INFO, DEBUG consistentemente en todos los servicios.
  • Incluye IDs de correlación. Cada log debe tener trace_id, span_id o request_id para depuración entre servicios.
  • Evita loguear en bucles ajustados. Agrupa o saltea logs de bucle para prevenir inundación de logs.
  • Muestrea logs de alto volumen. No cada petición necesita logging debug completo.
  • Monitoriza el pipeline. Alerta si el envío de logs se retrasa o el almacenamiento se llena.
  • Documenta tu esquema. Los equipos necesitan saber qué campos están disponibles para consultas.

Errores Comunes

  • Logs no estructurados en todas partes. Parsear logs de texto al ingerir es frágil y lento.
  • Sin estrategia de retención. Los costos de almacenamiento crecen exponencialmente sin políticas de ciclo de vida.
  • Sobre-logueo. Logs de nivel debug en producción abruman el pipeline y ocultan problemas reales.
  • Contexto faltante. Logs sin nombre de servicio, entorno o trace IDs son casi inútiles.
  • Ignorar backpressure. Log shippers que fallan bajo carga crean puntos ciegos.

Variantes

  • Nativo de nube: AWS CloudWatch Logs, Google Cloud Logging, Azure Monitor Logs (gestionado, pero específico de proveedor)
  • Stack open-source: ELK (Elasticsearch, Logstash, Kibana) o PLG (Promtail, Loki, Grafana)
  • Empresarial: Splunk, Datadog, Sumo Logic (amplias capacidades, mayor costo)
  • Agregación edge: Agregación de logs local antes del envío central (reduce costo de red)

Conclusión

La agregación de logs transforma la salida dispersa de aplicaciones en una plataforma unificada de depuración y auditoría. Al adoptar logging estructurado, elegir la estrategia de envío correcta y diseñar políticas de retención inteligentes, construyes una base de observabilidad que escala con tu infraestructura.

Preguntas frecuentes

¿Cómo empiezo con esto en un proyecto existente?
Empieza con una parte pequeña y aislada de tu codebase. Aplica los conceptos de esta guía a un módulo o servicio. Mide el impacto, luego expande a otras áreas.
¿Qué herramientas necesito?
Las herramientas mencionadas throughout esta guía se listan en cada sección. La mayoría son open-source y ampliamente adoptadas. Consulta los recursos relacionados para instrucciones de setup.
¿Cómo mido el éxito después de implementar esto?
Define métricas claras antes de empezar: benchmarks de rendimiento, tasas de error o indicadores de mantenibilidad. Compara antes y después. Itera basándote en datos, no en suposiciones.