Grafana, Loki y todo el stack de observabilidad: guía completa
Aprende a montar un stack de observabilidad con Grafana, Loki, Promtail y Prometheus: logs, métricas, dashboards y alertas en un solo lugar.
Grafana, Loki y todo el stack de observabilidad: guía completa
Un sistema moderno (microservicios, contenedores, Kubernetes) genera enormes cantidades de logs, métricas y trazas. Para no volverte loco buscando un error o entendiendo el comportamiento de la aplicación, necesitas un stack de observabilidad que centralice todo y permita correlacionar datos. Grafana es la pieza que visualiza; Loki es el almacén de logs pensado para integrarse con Grafana y con Prometheus (métricas). En esta guía verás qué es cada componente, cómo encajan entre sí, cómo desplegarlos y cómo usarlos en el día a día.
Los tres pilares de la observabilidad
La observabilidad suele describirse con tres pilares:
| Pilar | Qué responde | Herramientas típicas |
|---|---|---|
| Logs | Qué pasó (eventos, errores, mensajes) | Loki, Elasticsearch, Splunk |
| Métricas | Cuánto, cuándo, tendencias | Prometheus, InfluxDB |
| Trazas | Cómo fluye una petición entre servicios | Jaeger, Tempo, Zipkin |
Grafana no almacena datos por sí sola: se conecta a datasources (Loki para logs, Prometheus para métricas, Tempo o Jaeger para trazas) y te permite construir dashboards y alertas sobre ellos. El stack “Grafana + Loki + Prometheus” cubre logs y métricas de forma muy integrada; si añades Tempo, tienes también trazas y puedes saltar de un error en un log a la traza y a las métricas del mismo servicio.
¿Qué es Grafana?
Grafana es una aplicación open source de visualización y análisis de datos. Permite:
- Conectar múltiples fuentes de datos (Prometheus, Loki, MySQL, InfluxDB, Elasticsearch, APIs, etc.)
- Crear dashboards con gráficos, tablas, mapas de calor, indicadores (gauges) y paneles personalizados
- Definir alertas que evalúan consultas periódicamente y envían notificaciones (email, Slack, PagerDuty, webhooks)
- Explorar datos ad hoc con el explorador de datos (Explore) sin tener que crear un panel
- Compartir dashboards y enlazar entre ellos (drill-down)
Grafana no sustituye a Prometheus ni a Loki: los usa como backend. Tú configuras en Grafana las URLs de Prometheus y Loki (y opcionalmente Tempo), y desde la misma interfaz consultas logs, métricas y trazas.
¿Qué es Loki?
Loki es un sistema de agregación y almacenamiento de logs creado por Grafana Labs. Su filosofía es “Prometheus, pero para logs”:
- No indexa el contenido de las líneas de log (como hace Elasticsearch). Solo indexa metadatos: etiquetas (labels) como
job,app,env,level. Así reduce coste y complejidad. - Usa las mismas etiquetas que Prometheus cuando tiene sentido (
job,instance, etc.), de modo que en Grafana puedes pasar de una métrica a los logs del mismojob/instancecon un clic. - Almacena los logs en chunks en object storage (S3, GCS, Azure Blob) o en el sistema de archivos local, y los comprime bien.
- Expone una API compatible con la sintaxis de Prometheus para consultas; el lenguaje de consulta se llama LogQL.
Loki se compone de varios procesos (modos “monolítico” o “microservicios”): ingester (recibe y escribe), querier (lee), distributor, compactor, etc. En entornos pequeños suele usarse el binario único loki en modo monolítico; en producción grande se separan los componentes y se escalan por separado.
Promtail: enviar logs a Loki
Promtail es el agente que recoge logs de máquinas o contenedores y los envía a Loki. Es el equivalente a un “log shipper”: lee archivos de log (o recibe logs por push), les asigna etiquetas (por ejemplo, nombre del archivo, directorio, labels de Kubernetes) y hace push a Loki.
Características principales:
- Descubrimiento de objetivos: detecta archivos de log (por rutas, por anotaciones en pods en Kubernetes)
- Pipeline de etapas: puede parsear, extraer campos, modificar etiquetas y filtrar líneas antes de enviar
- Posición: guarda hasta dónde leyó cada archivo para no duplicar ni perder líneas al reiniciar
- Integración con Kubernetes: lee metadatos del pod (namespace, label, nombre del contenedor) y los añade como labels en Loki
Sin Promtail (o otro agente compatible con Loki, como Fluent Bit con el plugin de Loki o Grafana Alloy), Loki no tendría logs que mostrar. La configuración típica es: aplicación escribe en stdout/archivo → Promtail (o Alloy) lee → Loki almacena → Grafana consulta.
Nota: Promtail llegó a su fin de vida (EOL) en marzo de 2026. El reemplazo oficial es Grafana Alloy, un colector unificado basado en OpenTelemetry que puede hacer la misma función. Para proyectos nuevos se recomienda usar Alloy; si ya usas Promtail, la configuración se puede migrar con alloy convert --source-format=promtail.
Promtail: pipelines de procesamiento
Promtail no se limita a reenviar líneas en bruto: puedes definir pipelines con etapas que parsean, extraen campos y modifican etiquetas antes de enviar a Loki. Así en LogQL podrás filtrar por level="error" o status_code="500" si esas etiquetas se extraen en el pipeline.
Etapas útiles:
json: parsea la línea como JSON y extrae campos a etiquetas o datos. Ideal para aplicaciones que escriben logs en JSON.logfmt: parsea formato logfmt (key=value key2=value2) y extrae pares clave-valor.regex: extrae grupos con una expresión regular.labels: convierte datos extraídos en labels de Loki (solo para valores con cardinalidad baja).labelallow/labeldrop: permiten o eliminan labels para no enviar demasiados a Loki.
Ejemplo de pipeline que parsea JSON y expone level y service como labels:
scrape_configs:
- job_name: api
static_configs:
- targets: [localhost]
labels:
job: api
__path__: /var/log/api/*.log
pipeline_stages:
- json:
expressions:
level: level
service: service
- labels:
level:
service:
- output:
source: message
Así en Grafana podrás usar {job="api", level="error"} para filtrar solo errores.
Ejemplo para Kubernetes: Promtail puede descubrir pods automáticamente y usar sus etiquetas como labels en Loki. Un scrape_config típico usa kubernetes_sd_configs con roles pod y relabel_configs para mapear __meta_kubernetes_pod_label_app, __meta_kubernetes_namespace, etc. a labels como app y namespace. Así en Grafana podrás filtrar por {namespace="production", app="api"} igual que en Prometheus.
Prometheus: métricas
Prometheus es el estándar de facto para métricas en entornos cloud nativos. No forma parte del mismo “producto” que Loki, pero se usa casi siempre junto con Grafana:
- Scrape de targets (HTTP endpoints que exponen métricas en formato Prometheus)
- Almacenamiento de series temporales en disco
- PromQL como lenguaje de consulta
- Alertmanager para agrupar y enviar alertas (Grafana también puede alertar sobre Prometheus)
En Grafana añades Prometheus como datasource y creas paneles con consultas PromQL. La correlación con Loki viene porque muchas apps exponen las mismas etiquetas en sus logs (por ejemplo, app=api) y en sus métricas; así puedes en un dashboard tener un gráfico de latencia (Prometheus) y un panel de logs (Loki) del mismo servicio.
Arquitectura del stack: cómo encaja todo
Flujo típico:
- Aplicaciones escriben logs (stdout o archivos) y exponen métricas (endpoint
/metricsen formato Prometheus). - Promtail lee los logs, les pone labels y los envía a Loki.
- Prometheus hace scrape de los endpoints de métricas y almacena las series.
- Grafana se conecta a Loki (datasource “Logs”) y a Prometheus (datasource “Metrics”). El usuario crea dashboards que combinan ambos.
- (Opcional) Tempo recibe trazas (por OTLP o Jaeger) y Grafana se conecta también a Tempo; así puedes ir de una traza a los logs y métricas del mismo request.
┌─────────────┐ ┌──────────┐ ┌──────┐ ┌─────────┐
│ Apps / Pods │────▶│ Promtail │────▶│ Loki │◀────│ Grafana │
└─────────────┘ └──────────┘ └──────┘ └────┬────┘
│ │
│ scrape │ query
▼ │
┌─────────────┐ │
│ Prometheus │─────────────────────────────────────────┘
└─────────────┘
Desplegar el stack con Docker Compose
Un ejemplo mínimo para tener Grafana, Loki y Promtail en tu máquina o en un servidor:
docker-compose.yml:
services:
loki:
image: grafana/loki:3.6
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
volumes:
- ./loki-config.yaml:/etc/loki/local-config.yaml
- loki-data:/loki
networks:
- obs
promtail:
image: grafana/promtail:2.9.2
volumes:
- ./promtail-config.yaml:/etc/promtail/config.yaml
- /var/log:/var/log:ro
command: -config.file=/etc/promtail/config.yaml
depends_on:
- loki
networks:
- obs
grafana:
image: grafana/grafana:11.0
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana-data:/var/lib/grafana
depends_on:
- loki
networks:
- obs
volumes:
grafana-data:
loki-data:
networks:
obs:
driver: bridge
En Docker Compose actual (Compose Specification) el campo version ya no se usa: se puede omitir y el archivo es válido. Las etiquetas de imagen (p. ej. grafana/loki:3.6, grafana/grafana:11.0) son orientativas; en producción conviene fijar una versión concreta según la documentación oficial. Para nuevos despliegues, en lugar de Promtail puedes usar Grafana Alloy como colector de logs hacia Loki.
loki-config.yaml (configuración simple en modo monolítico):
auth_enabled: false
server:
http_listen_port: 3100
ingester:
lifecycler:
ring:
kvstore:
store: inmemory
replication_factor: 1
chunk_idle_period: 5m
chunk_retain_period: 30s
schema_config:
configs:
- from: 2020-10-24
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
storage_config:
filesystem:
directory: /loki/chunks
tsdb:
directory: /loki/tsdb-index
compactor:
working_directory: /loki/compactor
compaction_interval: 10m
limits_config:
reject_old_samples: true
reject_old_samples_max_age: 168h
chunk_store_config:
max_look_back_period: 0s
table_manager:
retention_deletes_enabled: false
promtail-config.yaml (ejemplo leyendo /var/log y enviando a Loki):
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: system
static_configs:
- targets:
- localhost
labels:
job: varlogs
__path__: /var/log/*.log
Para producción, en la configuración de Loki conviene activar retención. Añade o ajusta:
# En limits_config (junto al resto):
limits_config:
reject_old_samples: true
reject_old_samples_max_age: 168h
retention_period: 720h # 30 días
# En compactor:
compactor:
working_directory: /loki/compactor
compaction_interval: 10m
retention_enabled: true
retention_delete_delay: 2h
Para S3/GCS sustituye object_store: filesystem y storage_config.filesystem por la configuración de tu proveedor (p. ej. s3 con storage_config.aws) y ejecuta el compactor como instancia única (singleton).
Pasos:
- Crea los tres archivos en un directorio.
docker compose up -d.- Abre
http://localhost:3000, usuarioadmin, contraseñaadmin. - En Grafana: Configuration → Data sources → Add data source → Loki. URL:
http://loki:3100, Save & test. - En Explore (icono de brújula), elige Loki y ejecuta una consulta como
{job="varlogs"}.
Si añades Prometheus al compose (con un prometheus.yml que haga scrape de algún target), también lo configuras como datasource en Grafana y tendrás métricas y logs en el mismo sitio.
LogQL: consultar logs en Loki
LogQL es el lenguaje de consulta de Loki. Tiene dos partes: un selector de streams (por labels) y opcionalmente un filtro de línea o pipeline de etapas.
Ejemplos:
-
Todos los logs del job
varlogs:
{job="varlogs"} -
Solo líneas que contienen la palabra “error”:
{job="varlogs"} |= "error" -
Excluir “debug”:
{job="varlogs"} != "debug" -
Expresión regular:
{job="api"} |~ "status=[45][0-9][0-9]" -
Rate de líneas por stream (métrica derivada):
rate({job="api"}[5m]) -
Contar líneas por nivel si has parseado un campo
level:
sum by (level) (count_over_time({job="api"} | json | level="error" [1h]))
Parsing y extracción (logfmt, json):
-
Logs en logfmt con filtro por campo:
{job="api"} | logfmt | duration > 100ms -
Logs en JSON filtrando por nivel:
{job="api"} | json | level="error" -
Regex para extraer un campo y usarlo:
{job="nginx"} | regexp "(?P<status>\\d{3})" | status="500"
Métricas derivadas y agregaciones:
-
Suma de rate por etiqueta:
sum by (level) (rate({job="api"} | json [5m])) -
Contar líneas que contienen “panic” en la última hora:
count_over_time({job="api"} |= "panic" [1h]) -
Operadores binarios (ej. doble del rate):
sum(rate({app="foo"}[1m])) * 2
En Grafana Explore, eliges Loki, escribes la query y ves el resultado en tiempo real o en un rango de tiempo. Luego puedes “pin” esa query a un panel en un dashboard.
Grafana Alloy: el sucesor de Promtail
Grafana Alloy es el colector de telemetría unificado de Grafana Labs (sucede a Grafana Agent y absorbe las funciones de Promtail). Está basado en el OpenTelemetry Collector y en un solo proceso puede:
- Enviar logs a Loki (sustituyendo a Promtail)
- Enviar métricas a Prometheus o Mimir
- Enviar trazas a Tempo
La configuración se escribe en el lenguaje Alloy (declarativo, en bloques). Para migrar desde Promtail puedes usar:
alloy convert --source-format=promtail --output=alloy-config.alloy promtail-config.yaml
Si estás empezando un proyecto nuevo, usa Alloy desde el principio; si ya tienes Promtail desplegado (en LTS hasta EOL marzo 2026), puedes migrar con la herramienta de conversión cuando convenga.
Alertas en Grafana
Grafana incluye un motor de alertas unificado (Unified Alerting) que permite definir reglas sobre cualquier datasource: Prometheus, Loki, etc.
- Contact points: dónde enviar las notificaciones (email, Slack, PagerDuty, webhook, etc.).
- Reglas de alerta: nombre, consulta (por ejemplo una expresión LogQL o PromQL), condición (ej. “es mayor que X”) y evaluación cada cierto intervalo.
- Políticas: quién notifica, tiempo de espera entre alertas, agrupación por etiquetas.
Ejemplo de idea para una regla basada en Loki: “si en los últimos 5 minutos hay más de 10 líneas con la palabra ‘panic’ en el job api, disparar alerta”. La consulta sería algo como count_over_time({job="api"} |= "panic" [5m]) > 10. Creas la regla en Alerting → Alert rules, asignas un contact point y listo.
Las alertas de Grafana complementan (o sustituyen) a las de Prometheus Alertmanager cuando quieres centralizar todo en la misma UI.
Correlación entre trazas, logs y métricas
Cuando tienes Tempo (o Jaeger) como datasource de trazas en Grafana, puedes saltar de un span a los logs del mismo request y a las métricas del servicio. Para que la correlación funcione:
- TraceID en los logs: las aplicaciones deben incluir el
trace_id(y opcionalmentespan_id) en cada línea de log, normalmente inyectado desde el contexto de OpenTelemetry. Así Grafana puede buscar en Loki por esetrace_idy mostrar solo los logs de esa petición. - Configuración en Loki: si los logs llegan con un campo
trace_id(o similar), en la configuración del datasource de Loki en Grafana puedes definir un derived field: “cuando encuentres este campo en el log, genera un enlace a Tempo con este trace ID”. Así, al hacer clic en un log, se abre la traza correspondiente. - Mismo orden de labels: que los servicios usen las mismas etiquetas en logs (Promtail/Alloy), métricas (Prometheus) y trazas (service.name) facilita filtrar por
joboserviceen todos los pilares.
Sin trace_id en los logs, la correlación automática no funciona; hay que instrumentar la aplicación (por ejemplo con el SDK de OpenTelemetry y un bridge para el logger).
Retención de logs en Loki
Por defecto Loki no borra logs antiguos. Para limitar cuánto tiempo se conservan:
- Compactor: en
loki-config.yamlactiva la retención en el compactor:compactor.retention_enabled: truecompactor.retention_delete_delay: 2h(opcional)
- Límites: en
limits_configdefineretention_period, por ejemploretention_period: 30dpara 30 días.
La retención se aplica en cada ciclo de compactación; los chunks que superen la antigüedad se marcan y se eliminan (en backend con S3/GCS suele usarse lifecycle o el borrado que hace el compactor). El compactor debe correr como instancia única (singleton), por ejemplo un solo pod en Kubernetes, con almacenamiento persistente para su estado.
En instalaciones pequeñas con almacenamiento en filesystem, conviene fijar una retención moderada (p. ej. 7–14 días) para no llenar el disco.
Alternativas y complementos
| Componente | Alternativa | Notas |
|---|---|---|
| Promtail | Grafana Alloy, Fluent Bit, Fluentd, Vector | Alloy es el reemplazo oficial; Fluent Bit tiene plugin out_loki. |
| Loki | Elasticsearch, OpenSearch, ClickHouse | Loki destaca por bajo coste y buena integración con Grafana/Prometheus. |
| Prometheus | VictoriaMetrics, Thanos, Mimir | Para métricas a gran escala o larga retención. |
| Trazas | Jaeger, Zipkin, SigNoz | Tempo se integra nativamente con Grafana; Jaeger es muy usado. |
Para entornos Kubernetes, Helm ofrece charts oficiales (grafana/loki-stack, grafana/loki, grafana/promtail, etc.) que desplegan todo el stack con valores por defecto razonables.
Problemas habituales y soluciones
- “No data” en Loki en Grafana: comprueba que la URL del datasource sea accesible desde el navegador (o desde Grafana si está en otro contenedor). En Docker, usa el nombre del servicio (
http://loki:3100). Verifica que Promtail (o Alloy) esté enviando logs y que el selector de la query coincida con las etiquetas que envía (por ejemplo{job="varlogs"}). - Demasiadas series / cardinalidad alta: si usas labels con valores muy variables (IDs, emails, etc.), Loki crea muchas series y el rendimiento se degrada. Usa solo labels con cardinalidad baja (app, env, level) y filtra el contenido con el pipeline o en la query.
- Logs duplicados: suele deberse a varios Promtail leyendo los mismos archivos o a reinicios sin
positionspersistente. Asegura un único Promtail por flujo y que el archivo de posiciones esté en un volumen persistente. - Correlación traza–log no funciona: la causa más común es que los logs no incluyan
trace_id. Hay que instrumentar la aplicación para que el logger reciba el contexto de la traza (p. ej. OpenTelemetry log bridge) y que ese campo se envíe a Loki como label o en el cuerpo y esté configurado como derived field en Grafana. - Loki lento o OOM: en modo monolítico, limita la memoria o pasa a despliegue por componentes y escala queriers/ingesters. Revisa retención y compactor para no acumular datos indefinidamente.
Buenas prácticas
- Labels en Loki: usar pocas labels y que sean dimensiones por las que realmente vayas a filtrar (app, env, level). Evitar poner el mensaje completo como label; Loki está pensado para indexar por metadata, no por contenido.
- Promtail: en Kubernetes, usar el descubrimiento de pods y que las etiquetas del pod (app, namespace) lleguen a Loki; así correlacionas con métricas de Prometheus que tengan las mismas labels.
- Retención: en
loki-config.yamlpuedes definir retención por tiempo; en producción suele usarse object storage (S3) y compactor con retención para no llenar disco. - Alertas: definir alertas en Grafana sobre LogQL (por ejemplo, “si rate de líneas con ‘panic’ > 0”) o sobre Prometheus, y canalizarlas a Slack o email para no depender solo de revisar dashboards.
- Dashboards: crear un dashboard por servicio o por equipo, con paneles de métricas (Prometheus) y un panel de logs (Loki) con el selector
{job="nombre-servicio"}para tener contexto junto a las gráficas. - Retención: en producción activar retención en el compactor y definir
retention_periodenlimits_config; usar object storage (S3/GCS) para no depender del disco local. - Correlación: si usas Tempo, incluir
trace_iden los logs desde la aplicación y configurar los derived fields en el datasource de Loki para enlazar a la traza desde cada línea de log.
Resumen
| Componente | Función principal |
|---|---|
| Grafana | Visualización, dashboards, alertas, exploración; conecta a Loki, Prometheus, Tempo, etc. |
| Loki | Almacén de logs indexado por labels; consulta con LogQL. |
| Promtail (EOL marzo 2026) | Agente que recogía logs y los enviaba a Loki; reemplazado por Alloy. |
| Prometheus | Almacén de métricas y PromQL; scrape de targets. |
| Tempo (opcional) | Trazas distribuidas; correlación con logs y métricas en Grafana. |
| Grafana Alloy | Colector unificado recomendado para logs (y métricas/trazas); reemplazo de Promtail. |
Con Grafana + Loki + Alloy (o Promtail en instalaciones existentes) ya tienes logs centralizados y visualizados; añadiendo Prometheus cubres métricas y con Tempo (o Jaeger) cierras el círculo de los tres pilares. Configurando retención en Loki y alertas en Grafana tendrás el stack listo para producción. Es escalable, open source y muy habitual en entornos Kubernetes y en servidores tradicionales.