Loki a escala: lecciones de logs a gran volumen
Actualizado: 2026-07-12
Loki indexa solo labels, no el contenido del log, lo que reduce el coste de almacenamiento frente a Elasticsearch. El principal riesgo en produccion es la cardinalidad explosiva cada combinacion unica de label-valores genera un stream y degrada las queries. Separar los paths de lectura y escritura garantiza que una consulta pesada no sature la ingesta.
Loki[1] de Grafana Labs ha ganado terreno como alternativa a Elasticsearch para logs. Su promesa: "como Prometheus pero para logs". Atractiva en la practica, solo indexar labels, no el contenido, reduce drasticamente el coste de almacenamiento e indexacion. Funciona muy bien para equipos medianos. A gran escala, los compromisos se notan. Este articulo recoge lecciones de operar Loki con volumenes reales (>1 TB/dia) y los patrones que evitan dolores de cabeza en produccion.
Puntos clave
-
Loki indexa solo labels, no contenido: cada combinacion unica de label-valores genera un stream.
-
La cardinalidad explosiva es el incidente numero uno. Revisar nuevos labels antes de mergear cualquier cambio.
-
Separar los paths de lectura y escritura es imprescindible en escala seria; una query gorda no debe saturar la ingesta.
-
Grafana Alloy reemplaza a Promtail para despliegues nuevos: un unico agente para logs, metricas y trazas.
-
Saber cuando Loki no es la herramienta adecuada (busqueda forense profunda, compliance estricto) es tan importante como saber como operarlo.
El modelo Loki en 30 segundos
Loki indexa solo labels (pares clave-valor 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 o filtros de texto.
Ventajas del modelo:
-
Storage barato (S3 mas compresion).
-
Ingestion rapida (sin pipeline de parsing pesado).
-
Labels compatibles con Prometheus[2].
Limites del modelo:
-
Queries no-label sobre mucho volumen son lentas.
-
La cardinalidad de labels es el coste: cada combinacion unica genera un stream.
Cardinalidad: el asesino silencioso
El error mas comun es labels de alta cardinalidad. Ejemplos que no deben ser labels:
-
user_id(millones de valores). -
request_id(unico por request). -
timestampo cualquier valor temporal. -
urlcompleta sin normalizar.
Cada valor unico genera un stream. 10 000 usuarios x 5 entornos x 3 servicios = 150 000 streams activos. El indice se infla, las queries se degradan, el coste de object store se dispara por el gran numero de ficheros pequenos.
La 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
Patron practico para equipos medianos:
-
{app, env, cluster, component}: eje fijo. 50-500 combinaciones tipicas. -
{level}: nivel del log (info/warn/error). -
Sin IDs unicos.
-
Sin valores libres del usuario.
Con este esquema, un entorno de 50 servicios x 3 environments x 2 clusters x 5 components x 4 levels = 6 000 streams. Perfectamente manejable.
Separar read y write paths
En escala seria, un mismo proceso no puede gestionar ingestion y queries sin que interfieran. El diseño recomendado:
-
Distributor + Ingester: pipeline de escritura. Recibe logs de Promtail/Alloy, los guarda en memoria y los escribe al object store en chunks.
-
Querier + Query-frontend: pipeline de lectura. Paraleliza queries, cachea y envia resultados.
-
Compactor: proceso batch que compacta chunks periodicamente.
-
Ruler: evalua reglas de alerta sobre logs.
-
Index Gateway: sirve el indice a las queries sin friccion si usas boltdb-shipper o TSDB.
Una query gorda no debe saturar la ingesta — esa separacion es la garantia.
Promtail -> Alloy
Promtail[3] era el shipper tradicional. Grafana Alloy[4] (antes Grafana Agent) lo reemplaza con un unico agente que envia logs, metricas y trazas. Para despliegues nuevos, Alloy es la eleccion correcta.
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, mas explicito que Promtail.
Queries LogQL: patrones utiles
LogQL es el lenguaje de consulta. Queries que ofrecen el mayor valor operativo:
# Top errores por servicio en 1h
sum by (app) (count_over_time({env="prod", level="error"}[1h]))
# Latencia extraida de logs (requiere stage de parseo)
histogram_quantile(0.95,
sum by (le) (rate(
{app="api"}
| json
| unwrap duration
| __error__=""
[5m]
))
)
# Buscar patron en una ventana
{app="api", env="prod"} |= "payment failed" | json | user_id = "12345"
Las queries eficientes siempre empiezan con un label matcher selectivo. Cada query sin label matcher actua sobre todos los streams y puede tumbar el cluster.
Alertas sobre logs
Loki soporta reglas estilo Prometheus[2] sobre metricas extraidas 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 cubre todo el rango de Elasticsearch/Kibana, pero cubre el 80% de los casos practicos. Las alertas de Loki se integran bien con las mismas politicas de SLOs y error budgets que se definen para metricas de latencia.
Arquitectura de observabilidad con Prometheus, Loki y Grafana mostrando los flujos de metricas y logs
Cuando Loki NO es la herramienta
Ser honesto sobre los limites ahorra frustraciones:
-
Busqueda full-text sofisticada: Elasticsearch gana.
-
Análisis forense profundo: Splunk y Elasticsearch tienen herramientas especificas para eso.
-
Compliance estricto con auditoria integrada: Splunk Enterprise esta disenado para eso.
-
Volumen masivo con necesidad de queries ad-hoc rapidas sobre periodos largos: Loki empieza a sufrir.
Loki es excelente para "monitoring-grade logs" (logs ingestados y consultados rutinariamente con patrones conocidos). Para investigacion forense de pasado profundo, hay opciones mejores.
Lecciones operacionales
Un año operando Loki en serio deja un conjunto de reglas claras:
-
Cardinalidad explosiva es el incidente numero uno. Revisar nuevos labels antes de mergear cualquier PR.
-
Query cap y timeouts. Un usuario con una query mal construida puede tumbar el cluster entero.
-
Backup del object store. Perder el bucket es perder todos los logs.
-
Monitorear Loki con Loki es circular. Usar Prometheus[2] mas las metricas que Loki expone.
-
Rate limiting en ingesta por tenant. Un servicio que genera spam de logs no debe afectar a los demas.
La observabilidad basada en eBPF puede complementar la capa de logs de Loki con visibilidad a nivel de kernel sin necesidad de instrumentacion adicional.
Conclusion
Loki es una eleccion solida para logs en la mayoria de contextos cloud-native. Su diseño basado en labels ofrece ventajas enormes pero requiere disciplina: la cardinalidad mal gestionada arruina la experiencia. A escala seria, separar read/write paths, invertir en cache y compactacion, y elegir bien el object store son decisiones clave. Para equipos que ya tienen Prometheus + Grafana, completar con Loki es uno de los cambios con mejor retorno en observabilidad.
Fuentes:
- Grafana Loki: documentacion oficial[5]
- Grafana Loki: repositorio en GitHub[6]
- Prometheus: documentacion y buenas practicas[7]