Centraliser ses logs finit presque toujours par une facture de stockage qui double tous les trimestres. La cause la plus fréquente n’est pas le volume de logs lui-même : c’est un choix d’outil qui indexe le contenu de chaque ligne comme s’il s’agissait d’une base de recherche plein texte, alors que la plupart des questions qu’on pose à ses logs commencent par « quel service, sur quelle période » et non par une recherche libre dans le texte.

Ce que Loki n’indexe pas, et pourquoi ça change tout

Elasticsearch indexe le contenu de chaque log ligne par ligne : chaque mot devient cherchable, ce qui coûte cher en CPU d’ingestion et en espace disque, souvent plusieurs fois la taille des logs bruts. Loki part d’un principe différent, inspiré directement de Prometheus : il n’indexe que les labels associés à un flux de logs (le nom du service, l’environnement, le pod), jamais le contenu textuel des lignes elles-mêmes.

# Exemple de labels attachés à un flux de logs par Promtail/Alloy
labels:
  app: payment-api
  namespace: production
  pod: payment-api-7d9f8c-x2k1p

Le contenu brut des lignes est compressé et stocké tel quel dans des chunks, sur un stockage objet bon marché (S3, GCS, ou un équivalent self-hosted comme MinIO). Une requête LogQL commence toujours par filtrer sur les labels pour identifier le petit nombre de chunks concernés, puis grep ce sous-ensemble réduit :

{app="payment-api", namespace="production"} |= "timeout"

Cette étape de filtrage par labels avant la recherche textuelle est ce qui permet à Loki de coûter une fraction du stockage d’un système à indexation complète, pour le même volume de logs.

Le piège de cardinalité : les labels ne sont pas des champs de recherche

L’erreur qui casse le modèle économique de Loki consiste à traiter les labels comme des colonnes de base de données et à y mettre des valeurs à haute cardinalité : un identifiant de requête HTTP, un identifiant utilisateur, un timestamp précis. Chaque combinaison unique de valeurs de labels crée un flux distinct, et Loki construit un index séparé par flux. Avec un request_id en label, chaque requête HTTP crée son propre flux : l’index explose, la mémoire d’ingestion explose avec lui, et le problème que Loki devait éviter (un index qui grossit indéfiniment) revient par la porte de derrière.

La règle pratique : les labels servent à identifier quel flux de logs, pas à rendre chaque ligne individuellement cherchable par un champ précis. Un identifiant de requête, un user-agent, un montant de transaction restent dans le contenu de la ligne, filtrables par |= ou par une expression LogQL, jamais en label.

# Label à éviter : request_id="a3f9c2e1..." (cardinalité illimitée)
# Bon réflexe : garder request_id dans le texte de la ligne, le chercher au besoin
{app="payment-api"} | json | request_id="a3f9c2e1-..."

LogQL : structuré au moment de la lecture, pas de l’écriture

Loki accepte des logs non structurés (texte brut) aussi bien que structurés (JSON), et l’analyse de structure se fait au moment de la requête plutôt qu’à l’ingestion. L’opérateur | json dans une requête LogQL parse chaque ligne à la volée et expose ses champs comme des attributs filtrables, sans avoir eu besoin de les déclarer en label à l’avance :

{app=~"api-.*"} | json | duration > 500ms and status >= 500

Cette approche déplace le coût du parsing de l’ingestion (payé une fois, pour toujours, même si personne ne consulte jamais ces logs) vers la lecture (payé uniquement quand quelqu’un pose réellement la question). Pour un volume de logs qui dépasse largement ce qu’on consulte activement, chaque jour, c’est l’inversion qui rend le modèle économique tenable.

Où ça se range dans une stack d’observabilité

Loki complète les golden signals exposés par Prometheus plutôt qu’il ne les remplace : les métriques disent qu’un problème existe et depuis quand, les logs disent ce qui s’est réellement passé ligne par ligne à ce moment précis. Le principe qui traverse toute la stack d’observabilité reste le même que pour un dashboard bien construit : mesurer et structurer en fonction des questions qu’on pose réellement, pas de ce qu’il est techniquement possible de collecter.

À retenir

Loki coûte moins cher parce qu’il n’indexe que les labels, jamais le contenu des lignes ; le filtrage par labels réduit d’abord l’espace de recherche, la recherche textuelle s’applique ensuite sur un petit sous-ensemble. Le piège qui annule cet avantage est d’y mettre des valeurs à haute cardinalité en label plutôt que dans le contenu de la ligne. Mettre en place une stack de logs qui reste utilisable et abordable à mesure que le volume grandit fait partie de ce qui se traite dans une mission de fiabilité et observabilité.