Project Oxygen & Ideo-LabIDEO LAB Dashboard 2026

đŸ¶ Partie 3 : Pilier 2 - APM (Traces DistribuĂ©es)

Deep dive sur l'Instrumentation, Traces, Spans, Flame Graphs, et Corrélation.

3.1 Moyen

Qu'est-ce que l'APM ?

Le "OĂč" et "Pourquoi" (la cause). (MĂ©triques = "Quoi"). Connecte les piliers.

APM Tracing
3.2 Moyen

Concepts : Trace & Span

Trace = Le voyage complet (requĂȘte). Span = L'Ă©tape (unitĂ© de travail).

Trace Span
3.3 Avancé

Auto-Instrumentation

Le "comment ça marche". "Monkey-patching" (Flask, Django, requests...).

Auto-Instrumentation dd-trace-run
3.4 Moyen

Setup Détaillé (Agent & Libs)

Étape 1 (Agent apm: true) + Étape 2 (Libs dd-trace-py, javaagent).

dd-trace-py dd-java-agent.jar
3.5 Avancé

Instrumentation Custom

@tracer.wrap(). Instrumenter la logique métier (ex: validate_cart).

@tracer.wrap Custom Spans
3.6 Avancé

Corrélation : Traces <-> Logs

Injection du trace_id. (Pivot "Trace" -> "Logs" et "Logs" -> "Trace").

trace_id Logs
3.7 Avancé

Corrélation : Traces <-> Métriques

Métriques auto-générées (trace.*.hits, .errors, .latency).

Metrics trace.
3.8 Moyen

Visualisation (UI)

Service Map (Dépendances), Service Page (Santé), Flame Graph (Latence).

Service Map Flame Graph
3.9 Moyen

Cheat-sheet (APM)

Setup (Python, Java, Node.js), Span Custom (Python).

cheat Setup
3.1 Qu'est-ce que l'APM (Application Performance Monitoring) ?

L'APM (ou "Tracing") est le processus qui suit une requĂȘte **Ă  travers** votre application et **Ă  travers** les microservices.

Alors que les MĂ©triques vous disent "Le service api-users est lent" (le Quoi), l'APM vous dit "Il est lent parce que le SELECT sur la table users_auth prend 3000ms" (le OĂč et le Pourquoi).

L'APM est le pilier central qui **connecte tous les autres piliers**.

3.2 Concepts Fondamentaux : Trace & Span

Pour comprendre l'APM, il faut comprendre ces deux concepts.

  • Trace : L'ensemble du "voyage" d'une requĂȘte. (Ex: le temps total d'un GET /api/checkout). Une Trace est un ensemble (un arbre) de Spans.
  • Span : Une "unitĂ© de travail" (une opĂ©ration) **Ă  l'intĂ©rieur** d'une Trace. (Ex: l'appel Ă  la base de donnĂ©es, la sĂ©rialisation JSON, l'appel Ă  une API externe).
Diagramme : Anatomie d'une Trace Distribuée

Imaginez une requĂȘte client qui touche 3 microservices (Frontend, API-Users, DB-Auth).

Client (RequĂȘte: 1200ms)
|
+--- [Trace ID: 123] -------------------------------------------------------------+
|                                                                                |
|   Service: Frontend (Span A: 1100ms)                                           |
|   |                                                                            |
|   +---- [Span B: Appel API /api/users (1000ms)] -------------------------------+
|   |                                                                         |  |
|   |                                Service: API-Users (Span C: 950ms)     |  |
|   |                                |                                      |  |
|   |                                +--- [Span D: Auth Check (500ms)] -----+  |
|   |                                |                                   |  |  |
|   |                                |    Service: DB-Auth (Span E: 450ms)|  |  |
|   |                                |    | (db.query(...))           |  |  |  |
|   |                                |    +---------------------------+  |  |  |
|   |                                |                                   |  |  |
|   |                                +--- [Span F: Business Logic (400ms)]  |  |
|   |                                |                                      |  |
|   |                                +--------------------------------------+  |
|   |                                                                         |
|   +-------------------------------------------------------------------------+
|   |                                                                            |
|   +---- [Span G: Rendu React (100ms)]                                          |
|                                                                                |
+--------------------------------------------------------------------------------+
            

DataDog propage le trace_id: 123 (via les headers HTTP) à travers tous les services et reconstruit ce "Flame Graph". Vous voyez instantanément que Span D (Auth Check) est le goulot d'étranglement, causé par Span E (la DB).

3.3 L'Instrumentation (Auto) : "Comment ça marche ?"

Comment DataDog capture-t-il les Spans (ex: db.query) sans que vous n'ayez Ă  modifier votre code ?

La réponse est l'Auto-Instrumentation.

Lorsque vous lancez votre application avec la bibliothÚque de tracing DataDog (ex: dd-trace-py), celle-ci "monkey-patche" (modifie à la volée) les bibliothÚques standards (Flask, Django, requests, psycopg2, Redis...) pour qu'elles "émettent" des Spans avant et aprÚs leurs opérations.

Diagramme : Auto-Instrumentation (Python/Flask)
Votre Code (myapp.py):
-------------------------
@app.route("/")
def hello():
    # (requests.get est "patché" !)
    r = requests.get("google.com")
    return "OK"
-------------------------
      |
      | (Vous exécutez:)
      ▌
$ dd-trace-run python myapp.py
      |
      ▌
Code Réel (Exécuté par dd-trace-run):
-------------------------
@app.route("/")
def hello():
    # (Code injecté par dd-trace)
    span = tracer.start_span("requests.get")
    span.set_tag("http.url", "google.com")
    
    r = requests.get("google.com")
    
    # (Code injecté par dd-trace)
    span.finish()
    
    return "OK"
-------------------------
            
3.4 Setup Détaillé (Agent & Librairies)

L'APM nécessite **deux** composants :
1. L'Agent DataDog (HÎte) : Doit avoir l'APM activé (il écoute sur le port 8126).
2. La BibliothĂšque (Client) : Doit ĂȘtre installĂ©e dans votre application (ex: dd-trace-py).

Étape 1 : Activer l'APM sur l'Agent

L'Agent (voir Partie 1) doit avoir ces configurations (datadog.yaml ou variables d'environnement).

# (datadog.yaml)
apm_config:
  enabled: true

# (Variables d'environnement Docker/K8s)
# DD_APM_ENABLED="true"
# (Pour le tracing non-local, ex: Lambda)
# DD_APM_NON_LOCAL_TRAFFIC="true" 
            
Étape 2 : Installer les Librairies (Application)

Ceci dépend de votre langage applicatif.

Python (dd-trace-py)

Lancement via dd-trace-run (auto-instrumentation).

# 1. Installer la librairie
pip install dd-trace

# 2. Configurer (Tags unifiés)
export DD_ENV="production"
export DD_SERVICE="api-users"
export DD_VERSION="1.2.0"

# 3. Lancer (Ex: app Flask)
# (NE PAS lancer "python app.py")
dd-trace-run gunicorn myapp:app
                
Java (dd-java-agent.jar)

Lancement via -javaagent (auto-instrumentation de la JVM).

# (Télécharger dd-java-agent.jar)

# Lancer l'application .jar
java -javaagent:./dd-java-agent.jar \
     -Ddd.env="production" \
     -Ddd.service="api-payments" \
     -Ddd.version="1.2.0" \
     -jar /path/to/my-app.jar
                
Node.js (dd-trace)

Importation (init()) au tout début du code.

# 1. Installer la librairie
npm install dd-trace

# 2. Initialiser (index.js - TOUT EN HAUT)
const tracer = require('dd-trace');
tracer.init({
    env: 'production',
    service: 'api-frontend',
    version: '1.2.0'
});

// (Reste du code... ex: require('express'))
const express = require('express');
const app = express();
// (Express est auto-instrumenté)
                
3.5 Instrumentation Custom (Le "Deep Dive")

L'auto-instrumentation est parfaite pour l'infrastructure (http.request, db.query), mais elle ne voit pas votre **logique métier**.

Exemple : Une requĂȘte /checkout (Span A) prend 500ms. L'auto-instrumentation vous montre un db.query (Span B) de 50ms. OĂč sont passĂ©es les 450ms restantes ?

Elles sont dans votre code (validation, calcul de TVA...). Nous devons ajouter des Spans custom.

Exemple (Python - Décorateur @tracer.wrap())

On utilise le décorateur @tracer.wrap() pour créer un nouveau Span autour d'une fonction métier.

from ddtrace import tracer

# (Auto-instrumentation de Flask)
@app.route("/checkout")
def handle_checkout():
    # ...
    
    # (Appel Ă  notre fonction custom)
    # (Sans le décorateur, ce temps est "perdu")
    validation_result = validate_cart(cart)
    
    # ...
    return "OK"


# 1. Instrumentation Custom
@tracer.wrap(
    name="ecommerce.validation",  // (Nom du Span)
    service="api-checkout"        // (Tag 'service')
)
def validate_cart(cart):
    
    # 2. Récupérer le Span (optionnel)
    span = tracer.current_span()
    
    # 3. Ajouter des Tags Custom (CRUCIAL)
    # (Permet de rechercher/filtrer les traces)
    span.set_tag("cart.id", cart.id)
    span.set_tag("user.id", cart.user_id)
    
    try:
        # (Logique métier...)
        if cart.value > 10000:
            span.set_tag("cart.review_required", True)
            
        # ... (Logique lente) ...
        
    except Exception as e:
        # 4. Reporter les erreurs
        span.set_tag("error", True)
        span.set_exc_info(type(e), e, e.__traceback__)
        raise
        
    return True
            
3.6 Corrélation : Traces <-> Logs

C'est la "magie" de DataDog. L'APM (Trace) est le pivot qui relie tout.

Lorsque l'APM (ex: dd-trace-py) est activé, il **injecte automatiquement** le trace_id et span_id actuels dans vos logs (surtout si vous loguez en JSON, ou via DD_LOGS_INJECTION=true).

Exemple (Log Python JSON)
{
  "timestamp": "2024-10-27T10:30:05Z",
  "level": "ERROR",
  "message": "Payment failed for user 123",
  "service": "api-payment",
  "env": "production",
  
  // (Injecté par dd-trace)
  "dd": {
    "trace_id": "4567890123456",
    "span_id": "9876543210987"
  }
}
            

Résultat (Pivot) :
1. (Vue "Trace" -> "Logs") : Vous regardez un Flame Graph (Trace APM) d'une requĂȘte /checkout lente. L'onglet "Logs" vous montre uniquement les logs (y compris l'erreur "Payment failed") de *cette requĂȘte prĂ©cise*.
2. (Vue "Log" -> "Trace") : Vous trouvez ce Log d'erreur. Vous cliquez sur le trace_id. DataDog vous amÚne au Flame Graph complet, vous montrant ce qui s'est passé *avant* et *aprÚs* l'erreur.

3.7 Corrélation : Traces <-> Métriques

L'APM génÚre **automatiquement** des métriques (DISTRIBUTION) à partir des Traces.

Pour chaque service et chaque resource (endpoint, ex: flask.request ou db.query), DataDog génÚre :

  • trace.<NOM_SPAN>.request.hits (COUNT) : Nb de requĂȘtes (Hits).
  • trace.<NOM_SPAN>.request.errors (COUNT) : Nb d'erreurs.
  • trace.<NOM_SPAN>.request.latency (DISTRIBUTION) : La latence.

Ces métriques sont la base des "Service Pages" et des "Monitors" (Alertes).

Exemple (Alerte sur Latence DB)

Cela vous permet de créer des Monitors (Partie 6) basés sur vos Spans :

# (Alerte si la latence p95 des requĂȘtes DB (auto-instrumentĂ©es)
# du service 'db-auth' dépasse 500ms)

p95(last_5m):trace.db.query.latency{env:prod,service:db-auth} > 500
            
3.8 Visualisation (UI APM)

Une fois l'APM configuré, vous passez votre temps dans ces 3 écrans (APM > Services) :

ÉcranDescriptionUsage
Service MapCarte auto-générée des dépendances (basée sur les traces).
"Quels services (API, DB, Kafka) mon api-users appelle-t-il ?"
Service PageLe "Dashboard" d'un microservice (service:api-users)."Quel est le Taux d'Erreur, la Latence p95, et le Nb de Hits de ce service ?"
Traces (Flame Graph)La vue "Waterfall" (diagramme de Gantt) d'une requĂȘte unique.
"Pourquoi *cette* requĂȘte spĂ©cifique (trace_id: 456...) a-t-elle pris 3 secondes ?"
3.9 Cheat-sheet (APM)
Setup (Agent)
# (datadog.yaml)
apm_config:
  enabled: true

# (Docker Env)
# DD_APM_ENABLED="true"
                    
Setup (Python)
# 1. Install
pip install dd-trace

# 2. Set Env
export DD_ENV="prod"
export DD_SERVICE="my-api"

# 3. Run
dd-trace-run gunicorn app:main
                    
Setup (Java)
java -javaagent:./dd-java-agent.jar \
     -Ddd.env="prod" \
     -Ddd.service="my-api" \
     -jar my-app.jar
                    
Span Custom (Python)
from ddtrace import tracer

@tracer.wrap(name="my.custom.span")
def ma_fonction_lente(param1):
    
    # (Optionnel: get span)
    span = tracer.current_span()
    
    # (Ajouter des tags)
    span.set_tag("mon.tag", param1)
    
    # (Marquer une erreur)
    if error:
        span.set_tag("error", True)
        span.set_exc_info(...)
        
    return ...
                    
Corrélation Logs (Python JSON)
# (Activer l'injection)
export DD_LOGS_INJECTION="true"

# (Log JSON contiendra:)
# "dd": {
#   "trace_id": "...",
#   "span_id": "..."
# }