1. Anatomie d'une trace #
Trace, span, contexte.
Trace et spans #
Une trace représente le parcours complet d'une requête. Elle se compose de spans : des opérations nommées, avec un début, une fin, un parent, des attributs, des événements et un statut. Tous les spans partagent le même trace_id (16 octets) ; chacun a son span_id (8 octets).
| Champ | Rôle |
|---|---|
| name | opération, à faible cardinalité : GET /users/{id} |
| trace_id, span_id, parent_span_id | structure de l'arbre |
| start / end | durée |
| kind | rôle du span dans l'échange |
| attributes | contexte (semconv) |
| events | faits horodatés pendant le span |
| links | relations avec d'autres traces |
| status | Unset, Ok ou Error |
SpanKind #
| Kind | Quand | Exemple |
|---|---|---|
| SERVER | réception d'une requête synchrone | handler HTTP, méthode gRPC |
| CLIENT | appel synchrone sortant | requête HTTP, requête SQL |
| PRODUCER | envoi d'un message asynchrone | publication Kafka, RabbitMQ |
| CONSUMER | traitement d'un message reçu | consommateur Kafka |
| INTERNAL | opération interne | calcul, étape métier |
2. Instrumenter manuellement #
Attributs, statut, événements, liens.
Créer des spans #
from opentelemetry import trace
from opentelemetry.trace import SpanKind, Status, StatusCode, Link
tracer = trace.get_tracer("shop.checkout", "1.4.0")
def payer(commande):
with tracer.start_as_current_span("payer commande", kind=SpanKind.INTERNAL) as span:
span.set_attribute("shop.order.id", commande.id) # attribut métier
span.set_attribute("shop.order.amount", commande.total)
span.add_event("fraude.verifiee", {"shop.fraud.score": 0.12})
try:
passerelle.debiter(commande)
except PaiementRefuse as exc:
span.record_exception(exc) # événement "exception"
span.set_status(Status(StatusCode.ERROR, "paiement refusé"))
raiseStatut et erreurs #
Le statut vaut Unset par défaut. On le passe à Error quand l'opération a échoué ; on ne met Ok que pour forcer un succès malgré une règle contraire. Pour HTTP, les conventions disent : côté serveur, seules les réponses 5xx sont des erreurs ; côté client, 4xx et 5xx le sont.
Erreur Courante : N'utilisez pas les attributs pour stocker des données volumineuses ou personnelles (corps de requête, e-mails) : coût, cardinalité et RGPD.
Liens #
Un lien relie un span à un ou plusieurs autres contextes sans relation parent-enfant : un traitement par lots qui agrège 100 messages venant de 100 traces différentes, ou une tâche asynchrone déclenchée par une requête. tracer.start_as_current_span("lot", links=[Link(ctx1), Link(ctx2)]).
3. Le pipeline des spans dans le SDK #
Processors, exportateurs, limites.
Span processors #
| Composant | Rôle |
|---|---|
| SimpleSpanProcessor | exporte chaque span à sa fin : uniquement pour le débogage |
| BatchSpanProcessor | regroupe et exporte par lots : à utiliser en production |
| Limites | OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, OTEL_SPAN_EVENT_COUNT_LIMIT protègent contre l'explosion de taille |
Arrêt propre #
Bonne Pratique : Appelezprovider.shutdown()(ouforce_flush()) à l'arrêt d'un processus court (job, fonction serverless) : sinon le dernier lot de spans est perdu.
4. Échantillonnage #
Garder les bonnes traces sans payer pour toutes.
Head et tail #
| Échantillonneur (SDK) | Comportement |
|---|---|
always_on / always_off | tout ou rien |
traceidratio | garde un ratio, calculé sur le trace_id (décision identique partout) |
parentbased_traceidratio | suit la décision du parent ; ratio pour les traces qui démarrent ici (recommandé) |
processors:
tail_sampling:
decision_wait: 10s
policies:
- { name: erreurs, type: status_code, status_code: { status_codes: [ERROR] } }
- { name: lentes, type: latency, latency: { threshold_ms: 1000 } }
- { name: reste, type: probabilistic, probabilistic: { sampling_percentage: 5 } }Erreur Courante : Le tail sampling exige que tous les spans d'une trace arrivent sur le même Collector : placez devant un Collector avec l'exportateur loadbalancing routé par TraceID.Interroger les traces #
{ resource.service.name = "checkout" && status = error }
{ span.http.route = "/orders" && duration > 500ms }
{ span.db.system.name = "postgresql" } | count() > 20 # traces qui font plus de 20 requêtes SQL (N+1)
{ resource.service.name = "frontend" } >> { resource.service.name = "payment" && status = error }Mon suivi
Connectez-vous pour suivre votre progression et prendre des notes. Se connecter
Articles recommandés
Les SLO Dynatrace : Comprendre et Gérer les Objectifs de Niveau de Service
Découvrez comment utiliser les SLO (Service Level Objectives) dans Dynatrace pour définir et surv...
Apdex vs Core Web Vitals
Découvrez les différences entre Apdex et Core Web Vitals, deux indicateurs de performance essenti...
Pourquoi collecter des métriques
Découvrez les raisons clés pour collecter des métriques avec des exemples concrets pour chaque cas.
Les types de métriques dans Prometheus
Découvrez en détail les quatre types de métriques supportés par Prometheus (Counter, Gauge, Histo...
PromQL : introduction au langage de requête de Prometheus
Apprendre PromQL, le langage de requête de Prometheus : sélecteurs, rate, sum by, histogrammes et...
Dynatrace OneAgent : tags, props, vars CLI
Pense‑bête des commandes CLI/API pour gérer tags, propriétés, variables d'environnement, versions...
Grafana Alloy : Collecte et Transformation de Télémétrie
Apprenez à utiliser Grafana Alloy pour collecter, transformer et acheminer logs, métriques et tra...
Grafana Alloy : Collecter les métriques système et les logs locaux
Découvrez comment configurer Grafana Alloy pour superviser le serveur sur lequel il s'exécute, av...
Grafana Alloy : L'importance de l'auto-supervision (Self-Monitoring)
Découvrez pourquoi et comment configurer Grafana Alloy pour qu'il se supervise lui-même, en colle...