Loki
Le système de centralisation de logs de Grafana Labs. Pensé comme « Prometheus, mais pour les logs » : léger, peu coûteux, basé sur des labels, et requêté avec LogQL depuis Grafana.
À quoi ça sert
Quand tu as plus d'un service qui tourne — une API FastAPI, des workers
Celery, des conteneurs Docker — tes logs sont éparpillés. SSH sur chaque
machine, tail -f sur chaque fichier… ça ne tient pas. Loki
règle ça :
- Tous les logs au même endroit, requêtables depuis Grafana.
- Corrélation avec les métriques Prometheus (même timeline, mêmes labels).
- Alertes sur les logs (« plus de 5 erreurs en 1 min ») via les mêmes règles que Prometheus.
- Coût bas : Loki indexe seulement les labels, pas le contenu — beaucoup moins de stockage qu'Elasticsearch.
L'approche ELK (Elasticsearch + Logstash + Kibana) indexe chaque mot de chaque log : très rapide à requêter, mais demande beaucoup de RAM et de disque. Loki indexe uniquement les labels (job, host, level) et stocke le reste compressé. Tu paies en vitesse de recherche full-text, mais tu divises souvent le coût par 5 ou 10. Pour des projets perso / MLOps moyenne taille, c'est nettement plus adapté.
L'architecture en 3 briques
-
L'agent (Promtail, Grafana Alloy, Fluent Bit…) — tourne
à côté de tes applis, lit leurs fichiers de logs ou les sorties Docker,
ajoute des labels (
job,host,container), et expédie le tout à Loki via HTTP. - Loki — le serveur : reçoit les logs, stocke les labels dans un index, compresse les blocs de logs, et répond aux requêtes LogQL.
- Grafana — la UI. On y ajoute Loki comme « datasource » et on requête les logs avec LogQL dans l'explorateur ou les dashboards.
Promtail est l'agent historique de Grafana, simple et spécialisé pour Loki. Grafana Alloy est le successeur (multi-usage : logs, métriques, traces) et remplace progressivement Promtail. Fluent Bit est un agent universel qui parle à Loki et Elasticsearch et CloudWatch — utile si tu veux rester portable. Pour démarrer : Promtail.
Un exemple d'usage
Un stack Loki + Promtail + Grafana en local avec Docker Compose, qui lit les logs de tous les conteneurs Docker de la machine :
services:
loki:
image: grafana/loki:latest
ports: ["3100:3100"]
command: -config.file=/etc/loki/local-config.yaml
promtail:
image: grafana/promtail:latest
volumes:
- /var/log:/var/log
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./promtail-config.yml:/etc/promtail/config.yml
command: -config.file=/etc/promtail/config.yml
grafana:
image: grafana/grafana:latest
ports: ["3000:3000"]
Dans Grafana (localhost:3000) tu ajoutes une datasource Loki
pointant http://loki:3100, et tu peux requêter en LogQL.
How-to : envoyer ses logs Python à Loki
-
Écrire les logs au format JSON
Loki préfère des logs structurés : Promtail saura extraire les champs et en faire des labels ou des fields requêtables. Avec Loguru c'est trivial :
pythonfrom loguru import logger logger.add("logs/app.jsonl", serialize=True) logger.bind(user="olivier").info("action")Une ligne par log, format JSON, prête à être ingérée.
-
Configurer Promtail pour lire ce fichier
yaml (promtail-config.yml)server: http_listen_port: 9080 clients: - url: http://loki:3100/loki/api/v1/push scrape_configs: - job_name: app static_configs: - targets: [localhost] labels: job: app __path__: /var/log/app/*.jsonlLe label
job=appidentifiera tous les logs dans LogQL. On évite de mettre trop de labels (chaque combinaison de labels crée un « stream » indexé séparément — c'est la principale source de problèmes de perf). -
Alternative : driver Docker direct
Si tes applis tournent en Docker, tu peux te passer de Promtail et utiliser le driver de logs Loki de Docker directement :
bashdocker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissionsyamlservices: mon-api: image: monimage logging: driver: loki options: loki-url: "http://localhost:3100/loki/api/v1/push" loki-batch-size: "400"Tous les
stdoutdu conteneur partent vers Loki, sans fichier intermédiaire ni agent. -
Requêter avec LogQL dans Grafana
LogQL ressemble à PromQL : on commence par un sélecteur de labels entre
{}, puis on enchaîne des filtres.logql# Tous les logs du job "app" {job="app"} # Ceux qui contiennent "ERROR" {job="app"} |= "ERROR" # Exclure une chaîne {job="app"} |= "ERROR" != "healthcheck" # Parser le JSON et filtrer sur un champ {job="app"} | json | level="ERROR" # Compter les erreurs par minute (métrique tirée des logs !) sum(rate({job="app"} |= "ERROR" [1m])) -
Définir une alerte sur les logs
Loki s'intègre à Alertmanager (celui de Prometheus). On peut déclencher une alerte si une requête LogQL renvoie un résultat — par exemple plus de 10 erreurs en 5 min :
yaml (rules.yml)groups: - name: app-errors rules: - alert: TooManyErrors expr: | sum(rate({job="app"} |= "ERROR" [5m])) > 0.1 for: 2m annotations: summary: "Beaucoup d'erreurs sur app"
Aide-mémoire
{job="app"} # sélecteur de base
{job="app", level="error"} # plusieurs labels
{job="app"} |= "texte" # contient
{job="app"} != "texte" # ne contient pas
{job="app"} |~ "err.*" # regex
{job="app"} | json # extrait champs JSON
{job="app"} | logfmt # format key=value
{job="app"} | json | level="ERROR"
rate({job="app"}[5m]) # lignes/sec
count_over_time({job="app"}[5m])
sum by(level) (rate({job="app"} | json [5m]))
Loki et le reste de l'écosystème
- Grafana — l'interface unique pour explorer Loki. Mode « Explore » pour les recherches ad-hoc, panels « Logs » dans les dashboards à côté des métriques.
-
Prometheus — Loki et
Prometheus partagent les mêmes labels
(
job,instance) : on passe d'un graphe de latence à ses logs correspondants en un clic dans Grafana. - Loguru / logging — sources naturelles des logs Python à pousser vers Loki, idéalement en format JSON.
-
Docker — le driver
lokide Docker peut envoyer les logs des conteneurs directement sans Promtail. - Kubernetes — Promtail ou Grafana Alloy se déploient en DaemonSet : un agent par nœud, lit les logs de tous les pods avec les labels Kubernetes injectés automatiquement.
Ne jamais mettre en label une valeur qui change souvent (ID utilisateur, request_id, timestamp précis). Chaque combinaison unique de labels crée un stream indexé : si tu as 1 million d'IDs, tu auras 1 million de streams et Loki s'écroule. Les champs JSON dans le message sont parfaits pour ce genre d'info : on les requête, on ne les indexe pas.
Pour aller plus loin
- Site officiel : grafana.com/oss/loki
- Documentation : grafana.com/docs/loki
- Référence LogQL : grafana.com/docs/loki/query
- Grafana Alloy (successeur de Promtail) : grafana.com/docs/alloy