
OpenTelemetry (OTel) est le standard open source pour instrumenter vos applications : vous générez traces, métriques et logs une seule fois, puis vous les envoyez vers n'importe quel backend. Pas de vendor lock-in, pas de ré-instrumentation si vous changez de stack d'observabilité.
Ce guide s'adresse aux profils débutants et intermédiaires qui veulent rendre leurs services observables. Vous irez de votre première trace jusqu'à une configuration prête pour la production : quickstart, auto-instrumentation, spans métier, configuration et sampling.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Comprendre le modèle en couches d'OpenTelemetry (API, SDK, Collector).
- Voir passer une première trace avec un Collector de test.
- Instrumenter une application en auto puis en manuel.
- Configurer OTel proprement par variables d'environnement.
- Préparer la production : sampling, propagation, conventions.
Pourquoi OpenTelemetry ?
Section intitulée « Pourquoi OpenTelemetry ? »Avant OpenTelemetry, chaque outil d'observabilité imposait son propre agent et son propre format. Changer de backend obligeait à réinstrumenter toutes les applications. OTel casse ce couplage : une instrumentation unique, un protocole unique (OTLP), et le choix du backend devient une simple décision d'infrastructure. C'est ce qui en a fait le standard de fait.
| Question | Réponse |
|---|---|
| C'est quoi ? | Un ensemble d'APIs, SDKs et outils pour générer de la télémétrie (traces, métriques, logs) |
| Qui le maintient ? | CNCF (Cloud Native Computing Foundation), statut Incubating, graduation en cours |
| Pourquoi l'utiliser ? | Standard unique, multi-langage, multi-backend, communauté active |
| Quels backends ? | Jaeger, Tempo, Prometheus, Mimir, Loki, Datadog, New Relic, Splunk... |
Mental model : comment les briques s'assemblent
Section intitulée « Mental model : comment les briques s'assemblent »Avant de coder, comprenez les 5 couches :
| Couche | Ce qu'elle fait | Où elle vit |
|---|---|---|
| API | Interface pour créer spans, métriques, logs | Dans votre code |
| SDK | Implémentation : buffering, sampling, export | Runtime de l'app |
| Auto-instrumentation | Instrumente automatiquement les frameworks | Agent/wrapper autour de l'app |
| Exporter OTLP | Envoie les signaux en OTLP | Dans l'app (SDK) |
| Collector | Reçoit, transforme, route | Infrastructure (container/daemon) |
Quickstart : votre première trace (10 min)
Section intitulée « Quickstart : votre première trace (10 min) »Objectif : voir une trace passer avant de coder quoi que ce soit.
-
Lancez un Collector avec export debug
Fenêtre de terminal docker run --rm -p 4317:4317 -p 4318:4318 \otel/opentelemetry-collector-contrib:0.155.0 \--config='receivers:otlp:protocols:grpc:endpoint: 0.0.0.0:4317http:endpoint: 0.0.0.0:4318exporters:debug:verbosity: detailedservice:pipelines:traces:receivers: [otlp]exporters: [debug]'Le Collector affiche les traces reçues dans la console.
-
Envoyez une trace de test
Avec
telemetrygen(outil de test OTel) :Fenêtre de terminal docker run --rm --network host ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:0.155.0 \traces --otlp-insecure --traces 1 -
Vérifiez la sortie
Vous devez voir dans les logs du Collector :
Trace ID: ...Span #0Name: otelgenKind: SPAN_KIND_INTERNAL
Vous avez un pipeline fonctionnel. Maintenant, instrumentons une vraie application.
Choisir son mode : auto vs manuel
Section intitulée « Choisir son mode : auto vs manuel »| Mode | Quand l'utiliser | Ce que ça couvre |
|---|---|---|
| Auto-instrumentation | Démarrage rapide, legacy, frameworks standards | HTTP, SQL, Redis, Kafka, gRPC... |
| Instrumentation manuelle | Logique métier, spans personnalisés | Checkout, paiement, génération PDF... |
| Les deux | Production réelle | Auto = "plomberie", manuel = "métier" |
Auto-instrumentation par langage
Section intitulée « Auto-instrumentation par langage »L'auto-instrumentation injecte des traces sans modifier votre code pour les frameworks courants (HTTP, DB, messaging).
# Installer la distro (inclut SDK + instrumentations courantes)pip install opentelemetry-distroopentelemetry-bootstrap -a install
# Lancer en auto-instrumentationopentelemetry-instrument python app.pyConfiguration via env vars (recommandé) :
export OTEL_SERVICE_NAME=checkout-apiexport OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317# Optionnel : debug localexport OTEL_TRACES_EXPORTER=console# Télécharger l'agent (vérifiez la dernière version sur opentelemetry.io/docs/languages/java/)curl -L -o opentelemetry-javaagent.jar \ https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
# Lancer avec l'agentjava -javaagent:opentelemetry-javaagent.jar \ -jar myapp.jarConfiguration via env vars :
export OTEL_SERVICE_NAME=order-serviceexport OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317L'agent instrumente automatiquement : Spring Boot, Quarkus, JAX-RS, JDBC, Kafka, Redis...
const { NodeSDK } = require('@opentelemetry/sdk-node');const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
const sdk = new NodeSDK({ traceExporter: new OTLPTraceExporter(), instrumentations: [getNodeAutoInstrumentations()],});
sdk.start();# Installernpm install @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node @opentelemetry/exporter-trace-otlp-grpc
# LancerOTEL_SERVICE_NAME=frontend \OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 \node --require ./tracing.js app.jsusing OpenTelemetry.Trace;using OpenTelemetry.Resources;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenTelemetry() .ConfigureResource(r => r.AddService("my-service")) .WithTracing(tracing => tracing .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddOtlpExporter());Configuration via env vars :
export OTEL_SERVICE_NAME=payment-apiexport OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317Go n'a pas d'auto-instrumentation "magique", vous devez wrapper les packages manuellement. Utilisez les instrumentations officielles pour net/http, database/sql, gRPC, etc.
import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
// Wrapper HTTP clientclient := &http.Client{Transport: otelhttp.NewTransport(http.DefaultTransport)}
// Wrapper HTTP serverhandler := otelhttp.NewHandler(myHandler, "my-server")Instrumentation manuelle : spans métier
Section intitulée « Instrumentation manuelle : spans métier »L'auto couvre les "tuyaux" (HTTP, DB). Pour tracer la logique métier, ajoutez des spans manuels.
from opentelemetry import trace
tracer = trace.get_tracer(__name__)
def process_checkout(cart_id: str, user_id: str): with tracer.start_as_current_span("checkout.process") as span: # Attributs métier span.set_attribute("cart.id", cart_id) span.set_attribute("user.id", user_id) span.set_attribute("cart.items_count", 5)
# Span enfant with tracer.start_as_current_span("checkout.validate_payment"): validate_payment()
with tracer.start_as_current_span("checkout.reserve_stock"): reserve_stock()import io.opentelemetry.api.GlobalOpenTelemetry;import io.opentelemetry.api.trace.Span;import io.opentelemetry.api.trace.Tracer;
public class CheckoutService { private static final Tracer tracer = GlobalOpenTelemetry.getTracer("checkout-service");
public void processCheckout(String cartId) { Span span = tracer.spanBuilder("checkout.process").startSpan(); try (var scope = span.makeCurrent()) { span.setAttribute("cart.id", cartId); validatePayment(); reserveStock(); } finally { span.end(); } }}const opentelemetry = require('@opentelemetry/api');
const tracer = opentelemetry.trace.getTracer('checkout-service');
async function processCheckout(cartId, userId) { return tracer.startActiveSpan('checkout.process', async (span) => { span.setAttribute('cart.id', cartId); span.setAttribute('user.id', userId);
await tracer.startActiveSpan('checkout.validate_payment', async (paymentSpan) => { await validatePayment(); paymentSpan.end(); });
span.end(); });}import ( "context" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute")
var tracer = otel.Tracer("checkout-service")
func ProcessCheckout(ctx context.Context, cartID, userID string) error { ctx, span := tracer.Start(ctx, "checkout.process") defer span.End()
span.SetAttributes( attribute.String("cart.id", cartID), attribute.String("user.id", userID), )
if err := validatePayment(ctx); err != nil { span.RecordError(err) return err }
return reserveStock(ctx)}Gérer les erreurs
Section intitulée « Gérer les erreurs »from opentelemetry.trace import Status, StatusCode
try: result = process_payment()except Exception as e: span.set_status(Status(StatusCode.ERROR, str(e))) span.record_exception(e) raiseConfiguration universelle (env vars)
Section intitulée « Configuration universelle (env vars) »Ne hardcodez pas les endpoints. Utilisez les variables d'environnement standard OTel :
# Identité du service (obligatoire)OTEL_SERVICE_NAME=checkout-apiOTEL_RESOURCE_ATTRIBUTES=service.version=1.2.0,deployment.environment=production
# Export OTLPOTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317OTEL_EXPORTER_OTLP_PROTOCOL=grpc # ou http/protobuf
# Sampling (production)OTEL_TRACES_SAMPLER=parentbased_traceidratioOTEL_TRACES_SAMPLER_ARG=0.1 # 10% des traces
# PropagationOTEL_PROPAGATORS=tracecontext,baggage
# Debug (dev uniquement)OTEL_TRACES_EXPORTER=console # affiche dans stdoutOTEL_LOG_LEVEL=debug # logs détaillés du SDKProduction : les 5 points critiques
Section intitulée « Production : les 5 points critiques »1. Sampling : ne tracez pas 100%
Section intitulée « 1. Sampling : ne tracez pas 100% »En production, tracer 100% des requêtes = coûts explosifs et overhead.
| Sampler | Description |
|---|---|
always_on | Tout (dev uniquement) |
traceidratio | X% des traces |
parentbased_traceidratio | X% sauf si parent a décidé (recommandé) |
OTEL_TRACES_SAMPLER=parentbased_traceidratioOTEL_TRACES_SAMPLER_ARG=0.1 # 10%2. Propagation : W3C Trace Context
Section intitulée « 2. Propagation : W3C Trace Context »La propagation transmet le trace-id entre services. Sans elle, vos traces sont fragmentées.
traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01Par défaut, OTel utilise W3C Trace Context. Si vous avez du legacy B3 (Zipkin) :
OTEL_PROPAGATORS=tracecontext,baggage,b3multi3. Semantic Conventions : attributs standards
Section intitulée « 3. Semantic Conventions : attributs standards »Utilisez les Semantic Conventions pour des attributs interopérables :
| Domaine | Attributs |
|---|---|
| HTTP | http.request.method, url.path, http.response.status_code |
| Database | db.system, db.name, db.operation, db.statement |
| Messaging | messaging.system, messaging.destination.name |
4. Logs : corrélation avec les traces
Section intitulée « 4. Logs : corrélation avec les traces »Injectez trace_id et span_id dans vos logs pour les corréler :
{ "timestamp": "2026-02-09T10:30:00Z", "level": "ERROR", "message": "Payment failed: insufficient funds", "trace_id": "abc123def456...", "span_id": "789xyz...", "service.name": "payment-service"}5. Collector : découplage et gouvernance
Section intitulée « 5. Collector : découplage et gouvernance »En production, ne pas exporter directement vers les backends. Passez par un Collector :
App (SDK) → OTLP → Collector → BackendsPourquoi ?
- Découplage : changez de backend sans recoder
- Résilience : le Collector bufferise si backend down
- Gouvernance : filtrage, redaction PII, enrichissement K8s
- Multi-destinations : traces → Tempo, métriques → Mimir
Anti-patterns (ce qu'il ne faut PAS faire)
Section intitulée « Anti-patterns (ce qu'il ne faut PAS faire) »| Anti-pattern | Conséquence | Solution |
|---|---|---|
| IDs uniques en attributs | Explosion cardinalité, coûts | Utiliser des catégories (status, region) |
| Tracer 100% en prod | Overhead, coûts stockage | Sampling 10-20% |
| PII/secrets en attributs | Fuite de données | Redaction via Collector |
| Exporter direct vers backend | Vendor lock-in, pas de gouvernance | Passer par Collector |
Pas de service.name | Traces anonymes, inutilisables | Toujours définir OTEL_SERVICE_NAME |
| Ignorer la propagation | Spans orphelins, traces fragmentées | Vérifier propagators + headers |
| Spans trop verbeux | Bruit, difficile à exploiter | 5-10 attributs max par span |
Dépannage
Section intitulée « Dépannage »Checklist "je ne vois pas mes traces"
Section intitulée « Checklist "je ne vois pas mes traces" »-
Le SDK génère-t-il des spans ?
Fenêtre de terminal OTEL_TRACES_EXPORTER=console python app.py# → Vous devez voir des spans dans stdout -
Le Collector reçoit-il les données ?
Activez le debug exporter dans le Collector :
exporters:debug:verbosity: detailed -
Le réseau est-il OK ?
Fenêtre de terminal # Depuis le container de l'appcurl -v http://otel-collector:4318/v1/traces# → Doit répondre (même en erreur 405) -
Les ports sont-ils corrects ?
- 4317 = gRPC
- 4318 = HTTP
Vérifiez que
OTEL_EXPORTER_OTLP_PROTOCOLcorrespond. -
La propagation est-elle configurée ?
Sans propagation, les services ne partagent pas le
trace-id.Fenêtre de terminal OTEL_PROPAGATORS=tracecontext,baggage
Erreurs courantes
Section intitulée « Erreurs courantes »| Symptôme | Cause probable | Solution |
|---|---|---|
Connection refused :4317 | Collector non démarré ou mauvais port | Vérifier le container/service |
| Spans orphelins (pas de parent) | Propagation manquante | Vérifier OTEL_PROPAGATORS |
| Traces fragmentées | Context perdu entre appels async | Utiliser context.with_span() |
| "Unknown service" dans le backend | OTEL_SERVICE_NAME non défini | Définir la variable |
Métriques (bref aperçu)
Section intitulée « Métriques (bref aperçu) »OTel supporte aussi les métriques. Les types de base :
| Type | Usage | Exemple |
|---|---|---|
| Counter | Valeurs cumulatives | Requêtes, erreurs |
| Gauge | Valeur instantanée | Mémoire, connexions actives |
| Histogram | Distribution | Latence (p50, p95, p99) |
from opentelemetry import metrics
meter = metrics.get_meter(__name__)
# Counterrequest_counter = meter.create_counter("http.server.requests")request_counter.add(1, {"method": "GET", "status": "200"})
# Histogram (latence)latency_histogram = meter.create_histogram("http.server.duration", unit="ms")latency_histogram.record(45.2, {"method": "GET", "route": "/api/orders"})FAQ : questions fréquentes
Section intitulée « FAQ : questions fréquentes »OTEL_EXPORTER_OTLP_ENDPOINT, puis on ajoute des spans manuels pour la logique métier que l'auto ne capture pas.À retenir
Section intitulée « À retenir »- OTel = standard CNCF pour traces, métriques, logs, choisissez-le par défaut
- Auto-instrumentation couvre 80% sans modifier le code
- Spans manuels pour la logique métier (checkout, paiement...)
- Configurez via env vars (
OTEL_*), pas en dur dans le code - Passez par un Collector en production (découplage, gouvernance)
- Sampling + redaction = obligatoires en prod
Prochaines étapes
Section intitulée « Prochaines étapes »Ressources
Section intitulée « Ressources »- Documentation officielle
- SDK Configuration (env vars)
- OTLP Exporter Configuration
- Semantic Conventions
- Specification Status, maturité des signaux par langage
- Registry (instrumentations)