📍 Article principal : Observabilité applicative en 2026 : OpenTelemetry, traces distribuées et stack LGTM — pour le contexte conceptuel et l’architecture d’ensemble.
À quoi sert le Collector et pourquoi il n’est pas optionnel
Le réflexe naïf, quand on découvre OpenTelemetry, est d’exporter directement depuis l’application vers le backend final : SDK Node qui parle directement à Tempo, SDK Python qui parle directement à Mimir. C’est techniquement possible, mais opérationnellement médiocre. Une coupure réseau temporaire entre l’application et Tempo fait perdre les spans en attente. Un changement d’endpoint demande de redéployer toutes les applications. Une politique d’enrichissement (ajout d’un attribut cluster=eu-west-1 partout) doit être implémentée dans chaque SDK.
L’OpenTelemetry Collector est le composant qui résout ces problèmes en un seul endroit. C’est un binaire écrit en Go qui reçoit des données via plusieurs protocoles, les traite (filtre, enrichit, redacte, sample), puis les redistribue vers un ou plusieurs backends. Ce tutoriel le configure pas-à-pas pour la chaîne typique : recevoir OTLP, batcher, ajouter des attributs de ressource, et envoyer vers Tempo, Loki et Mimir.
Prérequis
- Une machine Linux ou macOS — Windows fonctionne aussi mais les exemples utilisent un shell Unix
- Docker installé (méthode la plus simple) ou possibilité d’exécuter un binaire Go
- Au moins un service applicatif instrumenté OTel pour tester (cf. tutoriels Node.js, Python, Go)
- Notions de YAML
- Temps estimé : 35 à 50 minutes
Étape 1 — Choisir une distribution
Il existe plusieurs distributions du Collector, qui ne diffèrent que par la liste des composants pré-compilés. La core distribue uniquement les composants stables maintenus dans le dépôt principal — minimaliste. La contrib ajoute des centaines de receivers, processors et exporters tiers (Loki, Tempo, Mimir, Kafka, etc.) maintenus par la communauté. Pour un usage typique avec la stack LGTM, la version otelcol-contrib est nécessaire car les exporters Loki et Tempo n’existent pas dans la core.
On peut soit télécharger un binaire depuis les releases GitHub, soit utiliser l’image Docker officielle. La voie Docker est plus simple à reproduire entre développeurs et environnements.
docker pull otel/opentelemetry-collector-contrib:latest
L’image est légère (autour de 200 Mo) et démarre en moins d’une seconde. Pour figer la version, remplacer :latest par une étiquette précise comme :0.151.0. En production, on fige toujours.
Étape 2 — Écrire la configuration minimale
La configuration du Collector est un fichier YAML structuré en quatre sections : receivers qui décrit comment les données entrent, processors qui décrit les transformations appliquées en chaîne, exporters qui décrit où les données sortent, et service qui orchestre tout en pipelines distincts pour traces, métriques et logs.
On commence par la version la plus simple qui valide la chaîne : on reçoit en OTLP, on batch, on logge en sortie. Cette config sert à confirmer que l’application instrumentée envoie bien quelque chose avant de connecter les vrais backends.
# otelcol.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 5s
send_batch_size: 1024
exporters:
debug:
verbosity: detailed
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [debug]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [debug]
logs:
receivers: [otlp]
processors: [batch]
exporters: [debug]
Cette configuration ouvre les deux ports OTLP standards (4317 gRPC, 4318 HTTP), accumule les batchs jusqu’à 1024 éléments ou 5 secondes, puis imprime tout ce qui passe sur stdout. C’est une boucle de validation idéale en début de mise en place : si on voit les spans dans les logs du Collector, c’est que l’application exporte correctement.
Étape 3 — Lancer le Collector
On lance le conteneur en montant la configuration et en exposant les ports OTLP. Le binaire interne du Collector lit la config au démarrage, vérifie sa validité (chaque composant doit être disponible et bien typé), puis démarre les pipelines.
docker run --rm \
-v "$(pwd)/otelcol.yaml":/etc/otelcol-contrib/config.yaml \
-p 4317:4317 -p 4318:4318 \
otel/opentelemetry-collector-contrib:latest \
--config=/etc/otelcol-contrib/config.yaml
Au démarrage, le Collector écrit en quelques lignes ce qu’il a chargé : ses pipelines, leurs composants, et les ports en écoute. Une fois prêt, on déclenche du trafic depuis une application instrumentée et on doit voir des blocs InstrumentationScope apparaître dans les logs avec le nom du service, les spans, et leurs attributs. Si rien n’arrive, vérifier dans cet ordre : connectivité réseau côté application, alignement gRPC/HTTP avec le port, et logs du Collector qui peuvent signaler une erreur de désérialisation OTLP.
Étape 4 — Ajouter des processors d’enrichissement
Le Collector devient utile quand on lui ajoute de la valeur côté processors. Le resource processor injecte des attributs communs à tout ce qui passe (par exemple l’environnement, le datacenter, le cluster), ce qui évite de demander à chaque application de connaître ces métadonnées. Le memory_limiter protège le binaire d’une explosion de mémoire si le débit dépasse ce que les exporters absorbent.
processors:
memory_limiter:
check_interval: 2s
limit_mib: 512
spike_limit_mib: 128
resource:
attributes:
- key: deployment.environment
value: production
action: upsert
- key: cluster
value: eu-west-1
action: upsert
batch:
timeout: 5s
send_batch_size: 1024
L’ordre des processors dans le pipeline est sémantique : on applique d’abord memory_limiter (qui rejette si saturé), puis resource (qui enrichit), puis batch (qui groupe pour l’export efficace). Cet ordre est typique et conseillé. On référence ces processors dans la section service :
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, resource, batch]
exporters: [debug]
Chaque trace reçue ressort enrichie de deployment.environment=production et cluster=eu-west-1. Cette homogénéisation est précieuse côté backend : on filtre par environnement sans devoir trust que chaque application l’ait positionné.
Étape 5 — Exporter vers les backends LGTM
Maintenant que la chaîne est validée, on remplace debug par les vrais exporters. Tempo, Loki et Mimir acceptent tous OTLP en entrée — ce qui rend la configuration directe. Adresses et tenant_id à adapter selon le déploiement.
exporters:
otlp/tempo:
endpoint: tempo:4317
tls:
insecure: true
otlphttp/loki:
endpoint: http://loki:3100/otlp
otlphttp/mimir:
endpoint: http://mimir:9009/otlp
headers:
X-Scope-OrgID: anonymous
Trois exporters distincts pour trois pipelines distincts. Tempo accepte OTLP gRPC nativement. Loki expose un endpoint OTLP HTTP qui ingère les LogRecords directement avec mapping automatique vers ses labels. Mimir accepte OTLP HTTP avec un en-tête X-Scope-OrgID qui identifie le tenant en mode multi-tenants ; sur un Mimir mono-tenant en dev, la valeur anonymous convient.
On câble ces exporters dans les pipelines :
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, resource, batch]
exporters: [otlp/tempo]
metrics:
receivers: [otlp]
processors: [memory_limiter, resource, batch]
exporters: [otlphttp/mimir]
logs:
receivers: [otlp]
processors: [memory_limiter, resource, batch]
exporters: [otlphttp/loki]
À ce stade, chaque type de signal va à son backend. Pour un troubleshooting facilité, on peut garder l’exporter debug en parallèle dans la liste — le Collector exporte vers tous les exporters d’un pipeline, on aura à la fois les vrais backends et le log stdout.
Étape 6 — Filtrage et redaction de données sensibles
La gateway Collector est l’endroit naturel pour appliquer les politiques de confidentialité : retirer les en-têtes Authorization, masquer les numéros de téléphone qui auraient pu se retrouver dans des attributs, supprimer entièrement certains attributs jamais souhaités hors du périmètre. Le processor attributes permet ces transformations.
processors:
attributes/redact:
actions:
- key: http.request.header.authorization
action: delete
- key: http.request.header.cookie
action: delete
- key: db.statement
action: hash
- key: user.email
action: extract
pattern: ^.+@(?P.+)$
L’action delete retire l’attribut. hash remplace la valeur par son SHA-256, ce qui permet d’identifier deux occurrences identiques sans révéler le contenu — utile pour les requêtes SQL où l’on veut grouper par template sans exposer les valeurs. extract avec une regex à groupes nommés ((?P<nom>...)) crée de nouveaux attributs à partir des captures (ici, user_email_domain contient le domaine de l’email sans la partie locale ; l’attribut original user.email n’est pas modifié — pour le supprimer ensuite, ajouter une action delete sur user.email). Ces transformations s’appliquent uniformément à toutes les données traversant le Collector, ce qui rend la politique vérifiable au niveau de l’infrastructure plutôt qu’auditée dans des dizaines de codebases.
Étape 7 — Échantillonnage côté Collector
Quand le volume de traces explose, on bascule vers un sampling tail-based, qui examine la trace complète avant de décider. Ce processor exige une topologie en deux couches dans une architecture distribuée (loadbalancer en amont qui groupe par trace_id, puis tail_sampling en aval). Pour un déploiement modeste mono-instance, on peut activer directement le processor.
processors:
tail_sampling:
decision_wait: 10s
num_traces: 100000
expected_new_traces_per_sec: 1000
policies:
- name: errors-policy
type: status_code
status_code:
status_codes: [ERROR]
- name: slow-traces-policy
type: latency
latency:
threshold_ms: 500
- name: sample-rest
type: probabilistic
probabilistic:
sampling_percentage: 5
Cette configuration garde 100 % des traces en erreur, 100 % des traces dépassant 500 ms, et 5 % des autres. L’effet sur le volume est typiquement une réduction de 80-90 % sans perte significative pour les investigations. Le tutoriel Tail-based sampling pour maîtriser les coûts creuse l’architecture en deux couches nécessaire à grande échelle.
Étape 8 — Vérifier la santé du Collector
Le Collector expose lui-même des métriques internes via une extension. On les active pour superviser la chaîne d’observabilité — un Collector en surcharge ou qui rejette des données doit alerter au même titre qu’une application en production.
extensions:
health_check:
endpoint: 0.0.0.0:13133
pprof:
endpoint: 0.0.0.0:1777
zpages:
endpoint: 0.0.0.0:55679
service:
extensions: [health_check, pprof, zpages]
telemetry:
metrics:
level: detailed
readers:
- pull:
exporter:
prometheus:
host: 0.0.0.0
port: 8888
L’endpoint :8888/metrics expose les compteurs internes au format Prometheus : otelcol_receiver_accepted_spans, otelcol_exporter_send_failed_spans, otelcol_processor_dropped_spans. Ces métriques alimentent un tableau de bord de supervision qui révèle une erreur d’export ou une saturation mémoire bien avant qu’elle ne fasse perdre des données.
Erreurs fréquentes
Utiliser la distribution core alors qu’il faut contrib
Erreur la plus fréquente. La core ne contient pas les exporters Loki, Tempo en mode HTTP, ni les processors avancés comme tail_sampling ou transform. Au démarrage, le Collector affiche unknown component et refuse de démarrer. Toujours utiliser otel/opentelemetry-collector-contrib pour la stack LGTM.
Pas de memory_limiter en production
Sans ce processor, un pic de débit ou un backend lent peut faire grossir la mémoire jusqu’à OOM. Le memory_limiter arrête le traitement quand la limite est atteinte et signale aux receivers de rejeter les nouvelles données plutôt que de les empiler en mémoire. C’est le garde-fou le plus important du Collector.
Confondre les ports gRPC et HTTP
4317 gRPC, 4318 HTTP. Pointer un client gRPC vers 4318 produit des erreurs de désérialisation. Vérifier le couple protocole-port côté SDK et côté receiver.
Mettre send_batch_size trop petit
Un batch de 100 sur du gros débit déclenche des milliers d’envois par seconde aux backends, ce qui sature les connexions et fait monter la latence de la chaîne. La valeur 1024 à 8192 est une zone confortable pour la majorité des charges. À très haut débit, monter encore avec un timeout ajusté pour ne pas garder les données trop longtemps.
Croire que le Collector est stateful sans le vouloir
Le Collector standard est sans état persistant : à un redémarrage, les données en buffer sont perdues. Pour une garantie de livraison sur incident, on active le file_storage extension qui persiste les batchs sur disque, ou on déploie une queue (Kafka, Redis Streams) entre le Collector et les backends. Sans ces options, accepter qu’un crash perde quelques secondes de données.
Tutoriels associés
- Envoyer les logs vers Loki via OTLP
- Envoyer les traces vers Tempo
- Envoyer les métriques vers Mimir/Prometheus
- 🔝 Retour à l’article principal — Observabilité applicative et stack LGTM
Ressources et références officielles
- Documentation officielle Collector : opentelemetry.io/docs/collector
- Releases Collector : github.com/open-telemetry/opentelemetry-collector/releases
- Releases contrib : github.com/open-telemetry/opentelemetry-collector-contrib/releases
- Liste des composants : github.com/open-telemetry/opentelemetry-collector-contrib
- Spec OTLP : opentelemetry.io/docs/specs/otlp
- Tail sampling processor : github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/tailsamplingprocessor
- Memory limiter : github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/memorylimiterprocessor
- OTLP receiver Loki : grafana.com/docs/loki/latest/send-data/otel
FAQ
Agent ou gateway ?
Les deux. L’agent tourne en sidecar du pod ou en daemonset sur l’hôte ; il décharge l’application du transport et enrichit avec les attributs locaux. La gateway centralise les politiques (sampling, redaction, routage). En production sérieuse, on déploie les deux en chaîne : application → agent → gateway → backends.
Le Collector remplace-t-il Fluent Bit pour les logs ?
De plus en plus, oui. Le Collector contrib a un filelog receiver mature qui suit les fichiers, gère les rotations, et peut parser le JSON. Pour des pipelines logs complexes, Fluent Bit reste plus rapide et plus léger sur du gros débit, mais le Collector unifie les pipelines et c’est souvent décisif pour la maintenance.
Comment tester une config sans la déployer ?
Le binaire accepte --dry-run qui valide la syntaxe et la disponibilité des composants sans démarrer. Combiné avec un test d’export debug en local, on attrape la majorité des erreurs avant déploiement.
Combien coûte le Collector en ressources ?
Pour un débit modeste (10k spans/s, 100k metrics/s, 50k logs/s), une instance avec 1 vCPU et 1 Go RAM suffit largement. À très grande échelle (100k+ spans/s), on passe à plusieurs réplicas derrière un load balancer pour le sharding par trace_id dans le sampling tail-based.
La config peut-elle être segmentée en plusieurs fichiers ?
Oui. Depuis 2024, le Collector accepte plusieurs --config et merge les YAML. C’est utile pour séparer les bouts génériques (receivers, processors communs) des bouts spécifiques (exporters par environnement). En Kubernetes, l’opérateur OTel facilite encore cette gestion via des CRD dédiés.