Loki a escala: lecciones de logs a gran volumen

Pantallas con logs de servidor en cascada sobre fondo oscuro

Loki de Grafana Labs ha ganado terreno como alternativa a Elasticsearch para logs. Su promesa — “como Prometheus pero para logs” — es atractiva: solo indexar labels, no el contenido, reduciendo brutalmente el coste de storage e indexing. Funciona muy bien para equipos medianos. A gran escala, los compromisos se notan. Este artículo recoge lecciones de operar Loki con volúmenes reales (>1TB/día) y los patrones que evitan dolores.

El modelo Loki en 30 segundos

Loki indexa solo labels (key-value pairs como {app="api", env="prod"}) y almacena el chunk de logs sin indexar en un object store (S3, GCS, MinIO). Las queries filtran primero por labels, luego escanean los chunks resultantes con regex/filtros.

Ventajas:

  • Storage barato (S3 + compresión).
  • Ingestión rápida — no hay pipeline de parsing pesado.
  • Labels compatibles con Prometheus.

Límites:

  • Queries no-label sobre mucho volumen son lentas.
  • Cardinalidad de labels es el coste: cada combinación única genera un stream.

Cardinalidad: el asesino silencioso

El error más común es labels de alta cardinalidad. Ejemplos malos:

  • user_id como label (millones de valores).
  • request_id (único por request).
  • timestamp o tiempo cualquiera.
  • url completa sin normalizar.

Cada valor único genera un stream. 10k usuarios × 5 entornos × 3 servicios = 150k streams activos. El índice se infla, las queries se degradan, el coste de object store se dispara por tanto fichero pequeño.

Regla de oro: labels para lo que filtras (app, env, cluster, severity, tenant si son pocos). Contenido para lo que buscas (user_id en el mensaje, consultable con regex).

Diseño de labels sano

Patrón práctico para equipos medianos:

  • {app, env, cluster, component} — eje fijo. 50-500 combinaciones típicas.
  • {level} — log level (info/warn/error).
  • NO IDs únicos.
  • NO valores libres del usuario.

Con este esquema, un entorno típico de 50 servicios × 3 environments × 2 clusters × 5 components × 4 levels = 6000 streams. Manejable.

Retention y compaction

Por defecto Loki guarda todo. En volumen grande, costoso y lento. Políticas típicas:

  • 30-90 días para logs generales.
  • 1 año para logs de auditoría/seguridad.
  • Archivo frío a S3 Glacier tras N días si hay compliance.

Retention se configura por tenant o por label en la config. Importante: Loki compacta chunks periódicamente — planificar I/O y CPU para eso.

Separar read y write paths

En escala seria, no tener un mismo proceso gestionando ingest y queries. El diseño recomendado:

  • Distributor + Ingester: pipeline de escritura. Recibe logs de Promtail/Alloy, los buffer en memoria, los escribe a object store en chunks.
  • Querier + Query-frontend: pipeline de lectura. Paraleliza queries, cache, enviá resultados.
  • Compactor: proceso batch para compactar chunks.
  • Ruler: evalúa reglas de alerta sobre logs.
  • Index Gateway: si usas boltdb-shipper o TSDB, sirve el índice a queries sin fricción.

Una query gorda no debe saturar la ingesta.

Promtail → Alloy

Promtail era el shipper tradicional. Grafana Alloy (antes Grafana Agent) lo reemplaza con un único agente que ships logs, metrics y traces. Para deployments nuevos, Alloy es la recomendación.

Config típica:

loki.source.file "logs" {
  targets = [{__path__ = "/var/log/app/*.log"}]
  forward_to = [loki.process.parse.receiver]
}

loki.process "parse" {
  forward_to = [loki.write.default.receiver]
  stage.regex {
    expression = `level=(?P<level>\w+)`
  }
  stage.labels {
    values = { level = "" }
  }
}

loki.write "default" {
  endpoint {
    url = "https://loki.example.com/loki/api/v1/push"
  }
}

Menos magia, más explícito que Promtail.

Storage: elegir bien

Para hobby o <100GB/día: single-binary Loki en un VM con disco local funciona. Para escala real:

  • Object storage: S3 (o compatible: MinIO, Wasabi, Cloudflare R2). Cheap + infinitamente escalable.
  • Indice: tsdb o boltdb-shipper. TSDB es el moderno.
  • Cache: Memcached o Redis para query results y chunks frecuentes.

La distribución multi-zonal o multi-región depende del object store elegido.

Queries LogQL: patrones útiles

LogQL es el query language. Ejemplos de los que más uso real hacemos:

# Top errores por servicio en 1h
sum by (app) (count_over_time({env="prod", level="error"}[1h]))

# Latencia extraída de logs (requiere stage de parseo)
histogram_quantile(0.95,
  sum by (le) (rate(
    {app="api"}
    | json
    | unwrap duration
    | __error__=""
    [5m]
  ))
)

# Buscar patrón en una ventana
{app="api", env="prod"} |= "payment failed" | json | user_id = "12345"

Queries eficientes siempre empiezan con label matcher selectivo.

Alertas sobre logs

Loki soporta reglas Prometheus-style sobre métricas extraídas de logs:

groups:
  - name: api-errors
    rules:
      - alert: HighErrorRate
        expr: |
          sum by (app) (rate({env="prod", level="error"}[5m])) > 10
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "High error rate on {{ $labels.app }}"

Esto convierte a Loki en herramienta de alerting también. No tiene todo el rango de Elasticsearch/Kibana, pero cubre el 80%.

Cuándo Loki NO es la herramienta

Ser honesto:

  • Búsqueda full-text sofisticada: Elasticsearch gana.
  • Análisis forense profundo: Splunk, ElasticSearch tienen herramientas específicas.
  • Compliance estricto con auditoría integrada: Splunk Enterprise está hecho para eso.
  • Volumen masivo con necesidad de queries ad-hoc rápidas sobre mucho tiempo: Loki empieza a sufrir.

Loki es excelente para “monitoring-grade logs” — logs ingestados y consultados rutinariamente con patrones. Para “investigación profunda de pasado profundo”, hay opciones mejores.

Lecciones operacionales

Un año operando Loki serio deja:

  • Cardinalidad explosiva es el incidente #1. Reviewar nuevos labels antes de merge.
  • Query cap y timeouts: un user loco puede tumbar el cluster con una query mala.
  • Backup del object store — la gente olvida que perder el bucket es perder todo.
  • Monitorear Loki con Loki es circular — usar Prometheus + métricas de Loki.
  • Rate limiting en ingesta por tenant — un servicio que spamea no debe afectar a los demás.

Conclusión

Loki es una elección sólida para logs en la mayoría de contextos cloud-native. Su diseño basado en labels ofrece ventajas enormes pero requiere disciplina — cardinalidad mal gestionada arruina la experiencia. A escala seria, separar read/write paths, invertir en cache y compactación, y elegir bien el object store son claves. Saber cuándo no es la herramienta correcta también es importante: para búsqueda forense profunda hay alternativas mejores. Para equipos que ya tienen Prometheus + Grafana, completar con Loki es de los cambios con mejor retorno en observabilidad.

Síguenos en jacar.es para más sobre observabilidad, Grafana stack y logs a escala.

Entradas relacionadas