
OpenTelemetry (OTel) est le standard 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.
Cette page vous guide de votre première trace jusqu’à une configuration production-ready.
Pourquoi OpenTelemetry ?
Section intitulée « Pourquoi OpenTelemetry ? »| 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:latest \--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:latest \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"})À 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)