RousselTMLEARNING

Observabilité

Collecter des logs vers Loki : Alloy, Promtail, Docker et OpenTelemetry

Envoyer des logs vers Loki : Grafana Alloy, Promtail, driver Docker, OTLP, bons labels, parsing et enrichissement, collecte Kubernetes et journald.

1. Choisir son agent #

Alloy est le standard actuel.

Les options #

Sources de logs et collecteur
Alloy unifie logs, métriques, traces et profils.
AgentStatutRemarque
Grafana AlloyRecommandéPipelines loki.source.*, loki.process, loki.write
PromtailDéprécié (fin de vie annoncée)À migrer avec alloy convert --source-format=promtail
Docker Loki driverPratique en localPlugin Docker, bloque si Loki est indisponible sans réglage
OpenTelemetry CollectorAlternatifExporter otlphttp vers l'endpoint OTLP de Loki (/otlp)
Fluent Bit / VectorAlternatifsPlugins de sortie Loki
# Migrer une configuration Promtail existante
alloy convert --source-format=promtail --output=config.alloy /etc/promtail/config.yml

2. Pipelines Alloy courants #

Fichiers, journald, Docker et Kubernetes.

Fichiers de logs #

local.file_match "app" {
  path_targets = [{"__path__" = "/var/log/myapp/*.log", "job" = "myapp", "env" = "prod"}]
}

loki.source.file "app" {
  targets    = local.file_match.app.targets
  forward_to = [loki.process.app.receiver]
}

loki.process "app" {
  forward_to = [loki.write.default.receiver]
  stage.json   { expressions = { level = "level", msg = "message" } }
  stage.labels { values = { level = "" } }
}

loki.write "default" {
  endpoint {
    url = "http://loki:3100/loki/api/v1/push"
  }
}

journald et conteneurs Docker #

loki.source.journal "system" {
  forward_to    = [loki.write.default.receiver]
  relabel_rules = loki.relabel.journal.rules
  labels        = { job = "systemd-journal" }
}

loki.relabel "journal" {
  forward_to = []
  rule {
    source_labels = ["__journal__systemd_unit"]
    target_label  = "unit"
  }
}

discovery.docker "local" { host = "unix:///var/run/docker.sock" }

discovery.relabel "containers" {
  targets = discovery.docker.local.targets
  rule {
    source_labels = ["__meta_docker_container_name"]
    regex         = "/(.*)"
    target_label  = "container"
  }
}

loki.source.docker "containers" {
  host       = "unix:///var/run/docker.sock"
  targets    = discovery.relabel.containers.output
  forward_to = [loki.write.default.receiver]
}

Pods Kubernetes #

discovery.kubernetes "pods" { role = "pod" }

discovery.relabel "pod_logs" {
  targets = discovery.kubernetes.pods.targets
  rule { source_labels = ["__meta_kubernetes_namespace"]           target_label = "namespace" }
  rule { source_labels = ["__meta_kubernetes_pod_name"]            target_label = "pod" }
  rule { source_labels = ["__meta_kubernetes_pod_container_name"]  target_label = "container" }
  rule { source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_name"] target_label = "app" }
}

loki.source.kubernetes "pods" {
  targets    = discovery.relabel.pod_logs.output
  forward_to = [loki.write.default.receiver]
}

3. Les bons labels #

La règle d'or de Loki.

Peu de labels, à faible cardinalité #

Chaque combinaison unique de labels crée un flux distinct. Trop de valeurs différentes (identifiant utilisateur, adresse IP, trace ID) provoquent une explosion du nombre de flux : ingesters saturés, requêtes lentes, coûts élevés.

✅ À utiliser comme labels❌ À éviter comme labels
app, namespace, env, clusteruser_id, session_id, trace_id
level (quelques valeurs)ip, url complète, request_id
job, containerHorodatages, identifiants de commande
# Mettre les valeurs à forte cardinalité en "structured metadata" plutôt qu'en label
loki.process "app" {
  forward_to = [loki.write.default.receiver]
  stage.json         { expressions = { trace_id = "trace_id" } }
  stage.structured_metadata { values = { trace_id = "" } }
}
# Recherche : {app="api"} | trace_id="4bf92f3577b34da6a3ce929d0e0e4736" 
Erreur Courante : Un label dont le nombre de valeurs dépasse quelques centaines est un signal d'alarme : déplacez-le vers la structured metadata ou le contenu de la ligne.

4. OTLP natif #

Envoyer directement des logs OpenTelemetry.

Endpoint OTLP de Loki #

# Collecteur OpenTelemetry ou Alloy : exporter OTLP HTTP vers Loki
otelcol.exporter.otlphttp "loki" {
  client { endpoint = "http://loki:3100/otlp" }
}

# SDK d'application
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://loki:3100/otlp/v1/logs
export OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/protobuf
À savoir : Loki convertit les attributs de ressource OTel (service.name, k8s.namespace.name...) en labels indexés et le reste en structured metadata.

Pour aller plus loin