# Fichier: python_cheats/cheatsheets/opentelemetry.txt

```
# Cheatsheet OpenTelemetry - Guide Ultra-Détaillé pour Grands Débutants


[OK] CONCEPTS FONDAMENTAUX (EXPLICATIONS TRÈS DÉTAILLÉES)

# === QU'EST-CE QUE OPENTELEMETRY ? ===

# Imagine que tu as créé une application Python (API, site web, microservice)
# Elle fonctionne bien... jusqu'à ce qu'un problème apparaisse:
# - "Pourquoi cette page prend 10 secondes à charger?"
# - "Où est le bug qui fait planter l'app?"
# - "Quelle partie du code consomme le plus de ressources?"

# Sans observabilité, tu es AVEUGLE:
# - Tu ne sais pas ce qui se passe dans ton app
# - Tu ne peux pas tracer les requêtes
# - Tu ne vois pas les erreurs cachées
# - Déboguer = deviner au hasard

# OPENTELEMETRY = Framework standard pour observer tes applications
# Il collecte automatiquement des données sur ce qui se passe:
# - Traces: Suivi du parcours d'une requête dans ton système
# - Métriques: Compteurs, temps de réponse, utilisation CPU/RAM
# - Logs: Messages texte avec contexte enrichi

# OpenTelemetry = "Télémétrie ouverte"
# = Standard universel pour instrumenter n'importe quelle app
# = Compatible avec tous les langages et tous les backends d'observabilité

# Analogie simple:
# Ton app = Une voiture
# OpenTelemetry = Le tableau de bord qui affiche:
#   - Vitesse (métriques)
#   - Trajet GPS (traces)
#   - Alertes moteur (logs)


# === VOCABULAIRE OPENTELEMETRY (TRÈS IMPORTANT!) ===

# OBSERVABILITÉ (Observability)
# = Capacité à comprendre l'état interne de ton système
# = En observant ses sorties (traces, métriques, logs)
# Analogie: Un médecin observe tes symptômes pour comprendre ta santé

# INSTRUMENTATION
# = Ajouter du code pour collecter des données d'observabilité
# Types:
#   - Automatique: OpenTelemetry ajoute le code pour toi
#   - Manuelle: Tu ajoutes des annotations dans ton code
# Exemple: Mesurer le temps d'exécution d'une fonction

# TRACES (Traçage distribué)
# = Suivi du parcours complet d'une requête dans ton système
# Une trace = Ensemble de "spans" (segments)
# Exemple: Une requête HTTP traverse:
#   1. API Gateway (span 1)
#   2. Service authentification (span 2)
#   3. Base de données (span 3)
#   4. Service email (span 4)
# La trace montre TOUT ce parcours avec les temps

# SPAN (Segment de trace)
# = Une opération individuelle dans une trace
# = A un début, une fin, et des attributs (métadonnées)
# Exemple: "Requête SQL SELECT users", durée: 45ms
# Attributs: table="users", query="SELECT * FROM users WHERE id=1"

# SPAN CONTEXT (Contexte de span)
# = Identifiants uniques pour relier les spans entre eux
# Contient:
#   - trace_id: ID unique de la trace complète
#   - span_id: ID unique de ce span
#   - parent_span_id: ID du span parent (si existe)
# Permet de reconstruire l'arbre complet d'une requête

# METRICS (Métriques)
# = Mesures numériques agrégées dans le temps
# Types:
#   - Counter: Compteur qui augmente (ex: nombre de requêtes)
#   - Gauge: Valeur qui monte/descend (ex: RAM utilisée)
#   - Histogram: Distribution de valeurs (ex: temps de réponse)
# Exemple: "api_requests_total = 1543 requêtes"

# LOGS (Journaux enrichis)
# = Messages texte avec contexte d'observabilité
# OpenTelemetry ajoute automatiquement:
#   - trace_id: Pour relier le log à sa trace
#   - span_id: Pour relier le log à son span
#   - Timestamp précis
# Permet de chercher tous les logs d'UNE requête spécifique

# EXPORTER (Exporteur)
# = Composant qui envoie les données vers un backend
# Exemples:
#   - Jaeger: Visualisation de traces
#   - Prometheus: Stockage de métriques
#   - Elasticsearch: Stockage de logs
#   - Console: Affichage dans le terminal (debug)

# BACKEND D'OBSERVABILITÉ
# = Système qui reçoit, stocke et visualise les données
# Exemples populaires:
#   - Jaeger: Traces distribuées
#   - Prometheus + Grafana: Métriques et dashboards
#   - Elastic Stack (ELK): Logs et recherche
#   - Datadog, New Relic: Solutions commerciales all-in-one

# BAGGAGE (Bagage)
# = Métadonnées propagées à travers tous les spans d'une trace
# Exemple: user_id, session_id, request_id
# Utile pour filtrer et analyser par utilisateur ou session

# RESOURCE (Ressource)
# = Informations sur l'entité qui produit les données
# Exemples:
#   - service.name = "api-users"
#   - service.version = "1.2.0"
#   - host.name = "server-prod-01"
#   - cloud.provider = "AWS"


# === POURQUOI UTILISER OPENTELEMETRY? ===

# PROBLÈME SANS OPENTELEMETRY:
# 1. Tu as 5 microservices différents
# 2. Un utilisateur signale: "La page prend 30 secondes!"
# 3. Où est le problème?
#    - Service A? Service B? Base de données? API externe?
# 4. Tu regardes les logs... mais ils sont séparés par service
# 5. Tu ne peux pas relier les logs entre les services
# 6. Tu passes 2 heures à déboguer = PERTE DE TEMPS

# SOLUTION AVEC OPENTELEMETRY:
# 1. OpenTelemetry trace AUTOMATIQUEMENT la requête complète
# 2. Tu vois instantanément:
#    - Service A: 200ms (OK)
#    - Service B: 50ms (OK)
#    - Base de données: 100ms (OK)
#    - API externe: 29 secondes (PROBLÈME!)
# 3. Tu identifies le problème en 10 secondes
# 4. GAIN DE TEMPS ÉNORME

# Autres avantages:
# - Standard universel (pas de vendor lock-in)
# - Instrumentations automatiques pour frameworks populaires
# - Corrélation automatique traces/métriques/logs
# - Open source et gratuit
# - Communauté active et nombreuses intégrations


# === COMMENT ÇA MARCHE? (FLUX COMPLET) ===

# ÉTAPE 1: INSTRUMENTATION
# Tu ajoutes OpenTelemetry à ton code:
from opentelemetry import trace
tracer = trace.get_tracer(__name__)

@app.route('/api/users')
def get_users():
    with tracer.start_as_current_span("get_users"):
        # Ton code ici
        users = db.query("SELECT * FROM users")
        return users

# OpenTelemetry enregistre automatiquement:
# - Quand la fonction commence
# - Quand elle se termine
# - Combien de temps ça prend
# - Si une erreur se produit


# ÉTAPE 2: COLLECTE
# OpenTelemetry collecte les données pendant l'exécution:
# - Chaque requête = nouvelle trace
# - Chaque opération = nouveau span
# - Chaque métrique = nouveau point de données


# ÉTAPE 3: EXPORTATION
# Les données sont envoyées vers un backend:
# Traces -> Jaeger (pour visualisation)
# Métriques -> Prometheus (pour graphiques)
# Logs -> Elasticsearch (pour recherche)


# ÉTAPE 4: VISUALISATION
# Tu ouvres Jaeger dans ton navigateur:
# - Tu vois TOUTES les traces
# - Tu filtres par service, durée, erreur
# - Tu cliques sur une trace lente
# - Tu vois l'arbre complet avec tous les spans
# - Tu identifies instantanément le goulot d'étranglement


# === ARCHITECTURE OPENTELEMETRY ===

# OpenTelemetry se compose de plusieurs parties:

# 1. API (Interface de programmation)
# = Définit comment ton code crée des traces/métriques/logs
# = Stable, ne change pas
# = Tu codes CONTRE l'API

# 2. SDK (Kit de développement)
# = Implémentation de l'API
# = Gère la collecte et l'exportation
# = Configurable

# 3. INSTRUMENTATIONS
# = Bibliothèques qui instrumentent automatiquement ton code
# Exemples:
#   - opentelemetry-instrumentation-flask (pour Flask)
#   - opentelemetry-instrumentation-requests (pour requests)
#   - opentelemetry-instrumentation-sqlalchemy (pour bases de données)

# 4. EXPORTERS
# = Envoient les données vers des backends
# Exemples:
#   - opentelemetry-exporter-jaeger (vers Jaeger)
#   - opentelemetry-exporter-prometheus (vers Prometheus)
#   - opentelemetry-exporter-otlp (protocole standard)

# 5. COLLECTOR (Optionnel mais recommandé)
# = Service qui reçoit, traite et redistribue les données
# Avantages:
#   - Découple ton app du backend
#   - Traitement et filtrage centralisé
#   - Un seul point de config


[OK] INSTALLATION SUPER DÉTAILLÉE

# === PRÉREQUIS ===

# 1. Python installé (version 3.8+)
python --version
# Doit afficher: Python 3.8.x ou supérieur

# 2. pip installé (gestionnaire de packages Python)
pip --version
# Doit afficher: pip 20.x.x ou supérieur

# 3. Environnement virtuel (FORTEMENT RECOMMANDÉ)
# Pourquoi? Isole les packages de ton projet
# Évite les conflits entre projets

# Créer un environnement virtuel:
# Linux/macOS:
python3 -m venv venv
source venv/bin/activate

# Windows:
python -m venv venv
venv\Scripts\activate

# Doit afficher: (venv) $ ou (venv) C:\...


# === INSTALLATION DES PACKAGES DE BASE ===

# 1. CORE API et SDK
# = Composants essentiels d'OpenTelemetry

pip install opentelemetry-api
pip install opentelemetry-sdk

# Explications:
# opentelemetry-api = Interface pour créer traces/métriques
# opentelemetry-sdk = Implémentation concrète


# 2. INSTRUMENTATION AUTOMATIQUE (Bootstrap)
# = Outil qui installe automatiquement les instrumentations

pip install opentelemetry-distro
pip install opentelemetry-instrumentation

# Puis, découvre et installe les instrumentations nécessaires:
opentelemetry-bootstrap -a install

# Cette commande:
# 1. Scanne tes dépendances (Flask, requests, etc.)
# 2. Installe automatiquement les instrumentations correspondantes
# 3. Exemple: Si tu as Flask, installe opentelemetry-instrumentation-flask


# 3. EXPORTERS (Selon tes besoins)

# Pour Jaeger (traces):
pip install opentelemetry-exporter-jaeger

# Pour Prometheus (métriques):
pip install opentelemetry-exporter-prometheus

# Pour OTLP (protocole standard, recommandé):
pip install opentelemetry-exporter-otlp

# Pour Console (debug local):
pip install opentelemetry-exporter-otlp-proto-http


# 4. INSTRUMENTATIONS SPÉCIFIQUES (Exemples)

# Flask:
pip install opentelemetry-instrumentation-flask

# Requests (bibliothèque HTTP):
pip install opentelemetry-instrumentation-requests

# SQLAlchemy (ORM base de données):
pip install opentelemetry-instrumentation-sqlalchemy

# Django:
pip install opentelemetry-instrumentation-django

# FastAPI:
pip install opentelemetry-instrumentation-fastapi

# Redis:
pip install opentelemetry-instrumentation-redis

# PostgreSQL (via psycopg2):
pip install opentelemetry-instrumentation-psycopg2


# === INSTALLATION COMPLÈTE RECOMMANDÉE ===

# Pour un projet Flask typique avec base de données:

pip install \
    opentelemetry-api \
    opentelemetry-sdk \
    opentelemetry-instrumentation-flask \
    opentelemetry-instrumentation-requests \
    opentelemetry-instrumentation-sqlalchemy \
    opentelemetry-exporter-jaeger \
    opentelemetry-exporter-otlp

# Sauvegarder dans requirements.txt:
pip freeze > requirements.txt


# === VÉRIFICATION DE L'INSTALLATION ===

# Vérifier que les packages sont installés:
pip list | grep opentelemetry

# Doit afficher:
# opentelemetry-api                1.20.0
# opentelemetry-sdk                1.20.0
# opentelemetry-instrumentation    0.41b0
# ...


# === INSTALLATION DE BACKENDS POUR TESTS LOCAUX ===

# JAEGER (Pour visualiser les traces)

# Avec Docker (recommandé):
docker run -d --name jaeger \
  -p 16686:16686 \
  -p 6831:6831/udp \
  jaegertracing/all-in-one:latest

# Sans Docker (téléchargement binaire):
# Linux/macOS:
curl -LO https://github.com/jaegertracing/jaeger/releases/download/v1.50.0/jaeger-1.50.0-linux-amd64.tar.gz
tar -xzf jaeger-1.50.0-linux-amd64.tar.gz
cd jaeger-1.50.0-linux-amd64
./jaeger-all-in-one

# Interface web: http://localhost:16686


# PROMETHEUS (Pour les métriques)

# Avec Docker:
docker run -d --name prometheus \
  -p 9090:9090 \
  prom/prometheus

# Interface web: http://localhost:9090


# OPENTELEMETRY COLLECTOR (Recommandé pour production)

# Avec Docker:
docker run -d --name otel-collector \
  -p 4317:4317 \
  -p 4318:4318 \
  otel/opentelemetry-collector:latest

# Le collector reçoit les données sur le port 4317 (gRPC) ou 4318 (HTTP)


[OK] PREMIER EXEMPLE: APPLICATION FLASK SIMPLE

# === ÉTAPE 1: CRÉER L'APPLICATION FLASK ===

# Fichier: app.py

from flask import Flask, jsonify
import time
import random

app = Flask(__name__)

@app.route('/')
def home():
    """Page d'accueil simple"""
    return jsonify({"message": "Bienvenue sur l'API OpenTelemetry!"})

@app.route('/api/users')
def get_users():
    """Simule une requête base de données"""
    # Simule un délai aléatoire (50-200ms)
    time.sleep(random.uniform(0.05, 0.2))
    
    users = [
        {"id": 1, "name": "Alice"},
        {"id": 2, "name": "Bob"},
        {"id": 3, "name": "Charlie"}
    ]
    return jsonify(users)

@app.route('/api/slow')
def slow_endpoint():
    """Endpoint intentionnellement lent"""
    time.sleep(2)  # 2 secondes
    return jsonify({"message": "Cette requête est lente!"})

@app.route('/api/error')
def error_endpoint():
    """Endpoint qui génère une erreur"""
    raise Exception("Erreur simulée pour démonstration")

if __name__ == '__main__':
    app.run(debug=True, port=5000)


# === ÉTAPE 2: AJOUTER OPENTELEMETRY (VERSION MINIMALE) ===

# Fichier: app_instrumented.py

from flask import Flask, jsonify
import time
import random

# IMPORTS OPENTELEMETRY
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, BatchSpanProcessor
from opentelemetry.instrumentation.flask import FlaskInstrumentor

# === CONFIGURATION OPENTELEMETRY ===

# 1. Créer un TracerProvider (gère la création de traces)
provider = TracerProvider()

# 2. Ajouter un exporter (envoie vers console pour debug)
console_exporter = ConsoleSpanExporter()
processor = BatchSpanProcessor(console_exporter)
provider.add_span_processor(processor)

# 3. Définir le provider comme provider global
trace.set_tracer_provider(provider)

# 4. Créer l'app Flask
app = Flask(__name__)

# 5. Instrumenter Flask AUTOMATIQUEMENT
FlaskInstrumentor().instrument_app(app)

# === ROUTES (IDENTIQUES) ===

@app.route('/')
def home():
    return jsonify({"message": "Bienvenue sur l'API OpenTelemetry!"})

@app.route('/api/users')
def get_users():
    time.sleep(random.uniform(0.05, 0.2))
    users = [
        {"id": 1, "name": "Alice"},
        {"id": 2, "name": "Bob"},
        {"id": 3, "name": "Charlie"}
    ]
    return jsonify(users)

@app.route('/api/slow')
def slow_endpoint():
    time.sleep(2)
    return jsonify({"message": "Cette requête est lente!"})

@app.route('/api/error')
def error_endpoint():
    raise Exception("Erreur simulée pour démonstration")

if __name__ == '__main__':
    app.run(debug=True, port=5000)


# === ÉTAPE 3: TESTER L'APPLICATION ===

# 1. Lancer l'app:
python app_instrumented.py

# 2. Dans un autre terminal, faire des requêtes:
curl http://localhost:5000/
curl http://localhost:5000/api/users
curl http://localhost:5000/api/slow

# 3. Observer la sortie console:
# OpenTelemetry affiche automatiquement les traces!

# Exemple de sortie:
"""
{
    "name": "GET /api/users",
    "context": {
        "trace_id": "0x1234567890abcdef",
        "span_id": "0xabcdef123456",
        "trace_state": "[]"
    },
    "kind": "SpanKind.SERVER",
    "parent_id": null,
    "start_time": "2024-01-15T10:30:00.000000Z",
    "end_time": "2024-01-15T10:30:00.150000Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {
        "http.method": "GET",
        "http.url": "http://localhost:5000/api/users",
        "http.status_code": 200
    }
}
"""

# Explications:
# - name: Nom de l'opération (route Flask)
# - trace_id: ID unique de la trace
# - span_id: ID unique de ce span
# - start_time/end_time: Début et fin de l'opération
# - attributes: Métadonnées (méthode HTTP, URL, code status)


# === ÉTAPE 4: AJOUTER DES SPANS MANUELS ===

# Modifier la fonction get_users pour ajouter des spans personnalisés:

from opentelemetry import trace

tracer = trace.get_tracer(__name__)

@app.route('/api/users')
def get_users():
    # Span automatique créé par FlaskInstrumentor
    
    with tracer.start_as_current_span("simulate_db_query"):
        # Span manuel pour simuler une requête DB
        time.sleep(random.uniform(0.05, 0.2))
        
        # Ajouter des attributs au span
        current_span = trace.get_current_span()
        current_span.set_attribute("db.system", "postgresql")
        current_span.set_attribute("db.statement", "SELECT * FROM users")
    
    with tracer.start_as_current_span("format_response"):
        # Span manuel pour formater la réponse
        users = [
            {"id": 1, "name": "Alice"},
            {"id": 2, "name": "Bob"},
            {"id": 3, "name": "Charlie"}
        ]
    
    return jsonify(users)

# Maintenant, chaque requête vers /api/users génère:
# 1. Span parent: "GET /api/users" (automatique)
# 2. Span enfant 1: "simulate_db_query" (manuel)
# 3. Span enfant 2: "format_response" (manuel)

# Cela crée une hiérarchie de spans (arbre)


[OK] TRACES DISTRIBUÉES EN DÉTAIL

# === QU'EST-CE QU'UNE TRACE? ===

# Une trace = Enregistrement du parcours complet d'une requête
# Composée de plusieurs spans (segments)
# Chaque span = Une opération

# Exemple concret:
# 1. Utilisateur visite: https://monapp.com/profile
# 2. Cela déclenche:
#    a. Requête HTTP vers API Gateway
#    b. API Gateway appelle Service Auth (vérifier token)
#    c. Service Auth interroge Redis (cache session)
#    d. API Gateway appelle Service Users (récupérer profil)
#    e. Service Users interroge PostgreSQL
#    f. Service Users appelle Service Images (récupérer avatar)
#    g. Service Images interroge S3
#    h. Réponse retourne à l'utilisateur

# SANS OpenTelemetry:
# - Chaque service log séparément
# - Impossible de relier les logs
# - Tu ne peux pas voir le parcours complet
# - Déboguer = cauchemar

# AVEC OpenTelemetry:
# - UNE trace avec 8 spans
# - Tu vois TOUT le parcours
# - Tu vois QUELLES parties sont lentes
# - Tu vois QUELS services ont des erreurs


# === ANATOMIE D'UN SPAN ===

# Un span contient:

# 1. IDENTIFIANTS
trace_id = "1234567890abcdef1234567890abcdef"  # ID de la trace complète
span_id = "abcdef1234567890"                    # ID unique de ce span
parent_span_id = "fedcba0987654321"             # ID du span parent (si existe)

# 2. TIMESTAMPS
start_time = "2024-01-15T10:30:00.000000Z"     # Début de l'opération
end_time = "2024-01-15T10:30:00.150000Z"       # Fin de l'opération
duration = 150                                   # Durée en millisecondes

# 3. MÉTADONNÉES
name = "GET /api/users"                         # Nom descriptif de l'opération
kind = "SERVER"                                 # Type: SERVER, CLIENT, INTERNAL...
status = "OK"                                   # Status: OK, ERROR

# 4. ATTRIBUTS (métadonnées personnalisées)
attributes = {
    "http.method": "GET",
    "http.url": "http://localhost:5000/api/users",
    "http.status_code": 200,
    "db.system": "postgresql",
    "db.statement": "SELECT * FROM users",
    "user.id": "12345"
}

# 5. ÉVÉNEMENTS (logs dans le span)
events = [
    {
        "name": "Cache miss",
        "timestamp": "2024-01-15T10:30:00.050000Z",
        "attributes": {"cache.key": "users:all"}
    },
    {
        "name": "Database query executed",
        "timestamp": "2024-01-15T10:30:00.120000Z",
        "attributes": {"rows.returned": 3}
    }
]

# 6. LIENS (vers d'autres traces)
links = [
    {
        "trace_id": "fedcba9876543210fedcba9876543210",
        "span_id": "1234567890abcdef"
    }
]


# === CRÉER DES SPANS MANUELLEMENT ===

from opentelemetry import trace

# Obtenir un tracer (fabrique de spans)
tracer = trace.get_tracer(__name__)

# MÉTHODE 1: Context manager (recommandé)
def ma_fonction():
    with tracer.start_as_current_span("operation_importante"):
        # Ton code ici
        # Le span se ferme automatiquement à la sortie du with
        result = faire_quelque_chose()
        return result

# MÉTHODE 2: Manuel (pour contrôle total)
def ma_fonction_2():
    span = tracer.start_span("operation_importante")
    try:
        result = faire_quelque_chose()
        span.set_status(trace.Status(trace.StatusCode.OK))
        return result
    except Exception as e:
        span.set_status(trace.Status(trace.StatusCode.ERROR, str(e)))
        span.record_exception(e)
        raise
    finally:
        span.end()


# === AJOUTER DES ATTRIBUTS AUX SPANS ===

from opentelemetry import trace

tracer = trace.get_tracer(__name__)

def process_order(order_id, user_id):
    with tracer.start_as_current_span("process_order") as span:
        # Ajouter des attributs
        span.set_attribute("order.id", order_id)
        span.set_attribute("user.id", user_id)
        span.set_attribute("order.total", 99.99)
        
        # Attributs sémantiques (conventions OpenTelemetry)
        span.set_attribute("db.system", "postgresql")
        span.set_attribute("db.name", "orders")
        span.set_attribute("db.operation", "INSERT")
        
        # Ton code métier
        db.insert_order(order_id, user_id)

# Attributs sémantiques recommandés:
# HTTP: http.method, http.url, http.status_code
# Database: db.system, db.name, db.statement, db.operation
# RPC: rpc.system, rpc.service, rpc.method
# Messaging: messaging.system, messaging.destination


# === AJOUTER DES ÉVÉNEMENTS (LOGS) AUX SPANS ===

def transfer_money(from_account, to_account, amount):
    with tracer.start_as_current_span("transfer_money") as span:
        span.set_attribute("from_account", from_account)
        span.set_attribute("to_account", to_account)
        span.set_attribute("amount", amount)
        
        # Événement: Vérification du solde
        span.add_event("Checking balance", {
            "account": from_account,
            "current_balance": 1000.00
        })
        
        if balance < amount:
            # Événement: Solde insuffisant
            span.add_event("Insufficient balance", {
                "required": amount,
                "available": balance
            })
            raise ValueError("Solde insuffisant")
        
        # Événement: Début du transfert
        span.add_event("Transfer started")
        
        # Effectuer le transfert
        db.debit(from_account, amount)
        db.credit(to_account, amount)
        
        # Événement: Transfert réussi
        span.add_event("Transfer completed", {
            "transaction_id": "TXN123456"
        })


# === GÉRER LES ERREURS DANS LES SPANS ===

def risky_operation():
    with tracer.start_as_current_span("risky_operation") as span:
        try:
            # Code qui peut lever une exception
            result = might_fail()
            
            # Marquer le span comme réussi
            span.set_status(trace.Status(trace.StatusCode.OK))
            return result
            
        except Exception as e:
            # Marquer le span comme erreur
            span.set_status(
                trace.Status(
                    trace.StatusCode.ERROR,
                    description=str(e)
                )
            )
            
            # Enregistrer l'exception avec traceback
            span.record_exception(e)
            
            # Ajouter des attributs d'erreur
            span.set_attribute("error", True)
            span.set_attribute("error.type", type(e).__name__)
            span.set_attribute("error.message", str(e))
            
            # Re-lever l'exception
            raise


# === SPANS IMBRIQUÉS (NESTED) ===

def operation_complexe():
    with tracer.start_as_current_span("operation_complexe"):
        # Span parent
        
        with tracer.start_as_current_span("etape_1"):
            # Span enfant 1
            etape_1()
        
        with tracer.start_as_current_span("etape_2"):
            # Span enfant 2
            with tracer.start_as_current_span("etape_2_sous_tache"):
                # Span petit-enfant
                etape_2_sous_tache()
            etape_2()
        
        with tracer.start_as_current_span("etape_3"):
            # Span enfant 3
            etape_3()

# Hiérarchie créée:
# operation_complexe (parent)
# ├─ etape_1
# ├─ etape_2
# │  └─ etape_2_sous_tache
# └─ etape_3


[OK] INSTRUMENTATION AUTOMATIQUE DÉTAILLÉE

# === QU'EST-CE QUE L'INSTRUMENTATION AUTOMATIQUE? ===

# Au lieu d'ajouter manuellement des spans partout:
with tracer.start_as_current_span("..."):
    # code

# L'instrumentation automatique:
# 1. Détecte les frameworks/bibliothèques utilisés
# 2. Injecte automatiquement du code de tracing
# 3. Crée des spans sans que tu touches ton code!

# Avantages:
# - Gain de temps énorme
# - Couverture complète automatique
# - Pas besoin de modifier ton code existant
# - Mise à jour facile


# === INSTRUMENTER FLASK AUTOMATIQUEMENT ===

# Méthode1: Instrumentation programmatique

from flask import Flask
from opentelemetry.instrumentation.flask import FlaskInstrumentor

app = Flask(__name__)

# Instrumenter Flask AVANT de définir les routes
FlaskInstrumentor().instrument_app(app)

@app.route('/')
def home():
    return "Hello!"  # Tracé automatiquement!

# Maintenant, TOUTES les routes sont tracées automatiquement:
# - Nom du span = "HTTP GET /route"
# - Attributs HTTP automatiques (method, url, status_code)
# - Durée mesurée automatiquement
# - Erreurs capturées automatiquement


# Méthode 2: Instrumentation via ligne de commande

# Au lieu de modifier ton code, lance:
opentelemetry-instrument \
    --traces_exporter console \
    --metrics_exporter console \
    python app.py

# Cela:
# 1. Détecte Flask automatiquement
# 2. Instrum ente Flask
# 3. Lance ton app
# 4. Affiche les traces dans la console


# === INSTRUMENTER REQUESTS (BIBLIOTHÈQUE HTTP) ===

from opentelemetry.instrumentation.requests import RequestsInstrumentor
import requests

# Instrumenter requests AVANT de faire des requêtes
RequestsInstrumentor().instrument()

# Maintenant, TOUTES les requêtes HTTP sont tracées:
response = requests.get('https://api.example.com/users')
# Crée automatiquement un span:
# - Nom: "HTTP GET"
# - Attributs: http.url, http.method, http.status_code
# - Durée: temps de la requête

# Utile pour tracer les appels vers APIs externes!


# === INSTRUMENTER SQLALCHEMY (ORM) ===

from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
from sqlalchemy import create_engine

# Créer le moteur SQLAlchemy
engine = create_engine('postgresql://user:pass@localhost/db')

# Instrumenter SQLAlchemy
SQLAlchemyInstrumentor().instrument(
    engine=engine,
    tracer_provider=trace.get_tracer_provider()
)

# Maintenant, TOUTES les requêtes SQL sont tracées:
result = session.query(User).filter(User.id == 1).first()
# Crée automatiquement un span:
# - Nom: "SELECT users"
# - Attributs: db.statement (la requête SQL complète)
# - Durée: temps de la requête


# === INSTRUMENTER REDIS ===

from opentelemetry.instrumentation.redis import RedisInstrumentor
import redis

# Instrumenter Redis
RedisInstrumentor().instrument()

# Toutes les commandes Redis sont tracées:
client = redis.Redis(host='localhost', port=6379)
client.set('key', 'value')  # Tracé!
client.get('key')           # Tracé!


# === INSTRUMENTER DJANGO ===

# Dans settings.py:

INSTALLED_APPS = [
    # ... autres apps
]

# Ajouter middleware OpenTelemetry:
MIDDLEWARE = [
    'opentelemetry.instrumentation.django.middleware.OpenTelemetryMiddleware',
    # ... autres middlewares
]

# Dans manage.py ou wsgi.py:
from opentelemetry.instrumentation.django import DjangoInstrumentor

DjangoInstrumentor().instrument()

# Maintenant toutes les vues Django sont tracées automatiquement!


# === INSTRUMENTER FASTAPI ===

from fastapi import FastAPI
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor

app = FastAPI()

# Instrumenter FastAPI
FastAPIInstrumentor.instrument_app(app)

@app.get('/users')
def get_users():
    return [{"id": 1, "name": "Alice"}]

# Toutes les routes sont tracées automatiquement!


# === LISTE DES INSTRUMENTATIONS DISPONIBLES ===

# HTTP Clients:
# - requests: opentelemetry-instrumentation-requests
# - httpx: opentelemetry-instrumentation-httpx
# - aiohttp: opentelemetry-instrumentation-aiohttp
# - urllib3: opentelemetry-instrumentation-urllib3

# Web Frameworks:
# - flask: opentelemetry-instrumentation-flask
# - django: opentelemetry-instrumentation-django
# - fastapi: opentelemetry-instrumentation-fastapi
# - pyramid: opentelemetry-instrumentation-pyramid
# - tornado: opentelemetry-instrumentation-tornado

# Databases:
# - sqlalchemy: opentelemetry-instrumentation-sqlalchemy
# - psycopg2: opentelemetry-instrumentation-psycopg2
# - mysql: opentelemetry-instrumentation-mysql
# - pymongo: opentelemetry-instrumentation-pymongo

# Caches:
# - redis: opentelemetry-instrumentation-redis
# - memcached: opentelemetry-instrumentation-memcache

# Message Queues:
# - celery: opentelemetry-instrumentation-celery
# - kafka: opentelemetry-instrumentation-kafka-python
# - pika (RabbitMQ): opentelemetry-instrumentation-pika

# Cloud Services:
# - boto3 (AWS): opentelemetry-instrumentation-boto3
# - botocore: opentelemetry-instrumentation-botocore

# Autres:
# - logging: opentelemetry-instrumentation-logging
# - grpc: opentelemetry-instrumentation-grpc


# === DÉCOUVERTE AUTOMATIQUE ===

# Au lieu d'installer manuellement chaque instrumentation:

# 1. Installer opentelemetry-distro:
pip install opentelemetry-distro opentelemetry-instrumentation

# 2. Découvrir et installer automatiquement:
opentelemetry-bootstrap -a install

# Cela:
# - Scanne requirements.txt
# - Détecte Flask, requests, sqlalchemy, etc.
# - Installe automatiquement les instrumentations correspondantes

# 3. Lancer avec instrumentation automatique:
opentelemetry-instrument python app.py

# Toutes les bibliothèques supportées sont instrumentées!


[OK] EXPORTERS: ENVOYER LES DONNÉES VERS DES BACKENDS

# === QU'EST-CE QU'UN EXPORTER? ===

# Ton app génère des traces/métriques/logs
# L'exporter = Composant qui ENVOIE ces données vers un backend
# Exemples de backends:
# - Jaeger: Visualisation de traces
# - Prometheus: Métriques et alertes
# - Elasticsearch: Logs et recherche
# - Datadog, New Relic: Solutions commerciales


# === CONSOLE EXPORTER (DEBUG) ===

# Affiche les données directement dans le terminal
# Utile pour développement et tests

from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, BatchSpanProcessor

provider = TracerProvider()
console_exporter = ConsoleSpanExporter()
processor = BatchSpanProcessor(console_exporter)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)

# Maintenant toutes les traces s'affichent dans la console!


# === JAEGER EXPORTER (TRACES) ===

# Jaeger = Backend open-source pour visualiser les traces

# Installation:
pip install opentelemetry-exporter-jaeger

# Configuration:
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

# Créer l'exporter Jaeger
jaeger_exporter = JaegerExporter(
    agent_host_name="localhost",  # Hôte Jaeger
    agent_port=6831,               # Port Jaeger agent
)

# Configurer le provider
provider = TracerProvider()
processor = BatchSpanProcessor(jaeger_exporter)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)

# Lancer Jaeger avec Docker:
docker run -d --name jaeger \
  -p 5775:5775/udp \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 14268:14268 \
  jaegertracing/all-in-one:latest

# Interface web: http://localhost:16686
# Tu verras toutes tes traces dans une belle interface graphique!


# === OTLP EXPORTER (PROTOCOLE STANDARD) ===

# OTLP = OpenTelemetry Protocol
# = Protocole standard pour envoyer traces/métriques/logs
# = Compatible avec de nombreux backends

# Installation:
pip install opentelemetry-exporter-otlp

# Configuration:
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

# Créer l'exporter OTLP
otlp_exporter = OTLPSpanExporter(
    endpoint="http://localhost:4317",  # Endpoint du collector
    insecure=True                       # Pas de TLS (dev only!)
)

# Configurer le provider
provider = TracerProvider()
processor = BatchSpanProcessor(otlp_exporter)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)

# OTLP supporte:
# - gRPC (port 4317)
# - HTTP/JSON (port 4318)


# === PROMETHEUS EXPORTER (MÉTRIQUES) ===

# Prometheus = Backend pour stocker et visualiser les métriques

# Installation:
pip install opentelemetry-exporter-prometheus

# Configuration:
from opentelemetry import metrics
from opentelemetry.exporter.prometheus import PrometheusMetricReader
from opentelemetry.sdk.metrics import MeterProvider
from prometheus_client import start_http_server

# Démarrer le serveur HTTP Prometheus
start_http_server(port=8000, addr="0.0.0.0")

# Créer le reader Prometheus
reader = PrometheusMetricReader()

# Configurer le provider
provider = MeterProvider(metric_readers=[reader])
metrics.set_meter_provider(provider)

# Maintenant Prometheus peut scraper les métriques sur:
# http://localhost:8000/metrics


# === CONFIGURATION VIA VARIABLES D'ENVIRONNEMENT ===

# Au lieu de coder en dur, utilise des variables d'env:

# Fichier .env:
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
OTEL_SERVICE_NAME=mon-api
OTEL_TRACES_EXPORTER=otlp
OTEL_METRICS_EXPORTER=otlp
OTEL_LOGS_EXPORTER=otlp

# Code Python (lit automatiquement .env):
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter

provider = TracerProvider()
processor = BatchSpanProcessor(OTLPSpanExporter())  # Utilise OTEL_EXPORTER_OTLP_ENDPOINT
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)


# === MULTIPLE EXPORTERS ===

# Tu peux envoyer vers PLUSIEURS backends simultanément!

from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter

provider = TracerProvider()

# Exporter 1: Console (debug)
console_processor = BatchSpanProcessor(ConsoleSpanExporter())
provider.add_span_processor(console_processor)

# Exporter 2: Jaeger (visualisation)
jaeger_processor = BatchSpanProcessor(JaegerExporter(
    agent_host_name="localhost",
    agent_port=6831
))
provider.add_span_processor(jaeger_processor)

# Exporter 3: OTLP (collector)
otlp_processor = BatchSpanProcessor(OTLPSpanExporter(
    endpoint="http://collector:4317"
))
provider.add_span_processor(otlp_processor)

trace.set_tracer_provider(provider)

# Toutes les traces sont envoyées vers les 3 destinations!


[OK] MÉTRIQUES DÉTAILLÉES

# === QU'EST-CE QU'UNE MÉTRIQUE? ===

# Métrique = Mesure numérique agrégée dans le temps
# Exemples:
# - Nombre de requêtes par seconde
# - Temps de réponse moyen
# - Utilisation CPU/RAM
# - Nombre d'erreurs
# - Nombre d'utilisateurs actifs

# Différence avec traces:
# - Trace = Parcours d'UNE requête spécifique
# - Métrique = Agrégation sur TOUTES les requêtes

# Exemple:
# Trace: "La requête #12345 a pris 150ms"
# Métrique: "Le temps de réponse moyen est 120ms"


# === TYPES DE MÉTRIQUES ===

# 1. COUNTER (Compteur)
# = Valeur qui AUGMENTE seulement
# Exemples:
# - Nombre total de requêtes
# - Nombre d'erreurs
# - Octets envoyés
# Ne peut que croître (jamais diminuer)

# 2. UP DOWN COUNTER (Compteur bidirectionnel)
# = Valeur qui peut augmenter OU diminuer
# Exemples:
# - Nombre d'utilisateurs connectés (augmente/diminue)
# - Nombre de tâches en queue
# - Taille d'un cache

# 3. HISTOGRAM (Distribution)
# = Enregistre la distribution de valeurs
# Exemples:
# - Temps de réponse (50ms, 150ms, 200ms...)
# - Taille des requêtes (1KB, 5KB, 10KB...)
# Permet de calculer:
#   - Min/Max
#   - Moyenne
#   - Percentiles (p50, p95, p99)

# 4. GAUGE (Jauge)
# = Valeur instantanée qui peut changer
# Exemples:
# - Utilisation CPU actuelle
# - RAM disponible
# - Température
# Note: Rarement utilisé en OpenTelemetry (préférer UpDownCounter)


# === CRÉER ET UTILISER DES MÉTRIQUES ===

from opentelemetry import metrics
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader, ConsoleMetricExporter

# 1. Créer un MeterProvider
reader = PeriodicExportingMetricReader(
    ConsoleMetricExporter(),
    export_interval_millis=5000  # Exporte toutes les 5 secondes
)
provider = MeterProvider(metric_readers=[reader])
metrics.set_meter_provider(provider)

# 2. Obtenir un Meter (fabrique de métriques)
meter = metrics.get_meter(__name__)

# 3. Créer un Counter
request_counter = meter.create_counter(
    name="http.server.requests",
    description="Nombre total de requêtes HTTP",
    unit="requests"
)

# 4. Incrémenter le counter
request_counter.add(1, {"http.method": "GET", "http.route": "/api/users"})
request_counter.add(1, {"http.method": "POST", "http.route": "/api/users"})

# 5. Créer un Histogram
response_time_histogram = meter.create_histogram(
    name="http.server.duration",
    description="Durée des requêtes HTTP",
    unit="ms"
)

# 6. Enregistrer des valeurs
response_time_histogram.record(150, {"http.method": "GET", "http.route": "/api/users"})
response_time_histogram.record(250, {"http.method": "GET", "http.route": "/api/users"})
response_time_histogram.record(100, {"http.method": "POST", "http.route": "/api/users"})


# === EXEMPLE COMPLET: INSTRUMENTER FLASK AVEC MÉTRIQUES ===

from flask import Flask, request
from opentelemetry import metrics
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader, ConsoleMetricExporter
import time

# Configuration métriques
reader = PeriodicExportingMetricReader(ConsoleMetricExporter())
provider = MeterProvider(metric_readers=[reader])
metrics.set_meter_provider(provider)

meter = metrics.get_meter(__name__)

# Définir les métriques
request_counter = meter.create_counter(
    name="http.server.requests.total",
    description="Nombre total de requêtes HTTP",
    unit="requests"
)

response_time_histogram = meter.create_histogram(
    name="http.server.duration",
    description="Durée des requêtes HTTP",
    unit="ms"
)

error_counter = meter.create_counter(
    name="http.server.errors.total",
    description="Nombre total d'erreurs HTTP",
    unit="errors"
)

# Créer l'app Flask
app = Flask(__name__)

@app.before_request
def before_request():
    # Enregistrer le début de la requête
    request.start_time = time.time()

@app.after_request
def after_request(response):
    # Calculer la durée
    duration_ms = (time.time() - request.start_time) * 1000
    
    # Attributs communs
    attributes = {
        "http.method": request.method,
        "http.route": request.endpoint or "unknown",
        "http.status_code": response.status_code
    }
    
    # Incrémenter le compteur de requêtes
    request_counter.add(1, attributes)
    
    # Enregistrer la durée
    response_time_histogram.record(duration_ms, attributes)
    
    # Compter les erreurs (5xx)
    if response.status_code >= 500:
        error_counter.add(1, attributes)
    
    return response

@app.route('/')
def home():
    return "Hello!"

@app.route('/slow')
def slow():
    time.sleep(2)
    return "Slow response"

@app.route('/error')
def error():
    raise Exception("Erreur simulée")

if __name__ == '__main__':
    app.run(debug=True)


# === MÉTRIQUES AVEC PROMETHEUS ===

from opentelemetry import metrics
from opentelemetry.exporter.prometheus import PrometheusMetricReader
from opentelemetry.sdk.metrics import MeterProvider
from prometheus_client import start_http_server

# Démarrer serveur HTTP pour Prometheus
start_http_server(port=8000)

# Configurer le reader Prometheus
reader = PrometheusMetricReader()
provider = MeterProvider(metric_readers=[reader])
metrics.set_meter_provider(provider)

# Créer métriques
meter = metrics.get_meter(__name__)
request_counter = meter.create_counter("requests_total")
request_counter.add(1)

# Prometheus peut maintenant scraper:
# http://localhost:8000/metrics


# === MÉTRIQUES PERSONNALISÉES AVANCÉES ===

# Observable Gauge (valeur calculée à la demande)
def get_memory_usage():
    import psutil
    return psutil.virtual_memory().percent

memory_gauge = meter.create_observable_gauge(
    name="system.memory.usage",
    description="Pourcentage de mémoire utilisée",
    unit="%",
    callbacks=[lambda options: [metrics.Observation(get_memory_usage())]]
)

# Observable Counter (compteur calculé)
def get_total_disk_usage():
    import psutil
    return psutil.disk_usage('/').used

disk_counter = meter.create_observable_counter(
    name="system.disk.used",
    description="Espace disque utilisé",
    unit="bytes",
    callbacks=[lambda options: [metrics.Observation(get_total_disk_usage())]]
)


# === CONVENTIONS DE NOMMAGE ===

# OpenTelemetry recommande des noms sémantiques:

# HTTP Server:
# - http.server.duration (Histogram)
# - http.server.request.size (Histogram)
# - http.server.response.size (Histogram)
# - http.server.active_requests (UpDownCounter)

# HTTP Client:
# - http.client.duration (Histogram)
# - http.client.request.size (Histogram)
# - http.client.response.size (Histogram)

# Database:
# - db.client.connections.usage (UpDownCounter)
# - db.client.connections.idle.max (UpDownCounter)
# - db.client.operations.duration (Histogram)

# System:
# - system.cpu.utilization (Gauge)
# - system.memory.usage (Gauge)
# - system.network.io (Counter)
# - system.disk.io (Counter)

# Process:
# - process.runtime.jvm.memory.usage (Gauge)
# - process.runtime.jvm.threads.count (UpDownCounter)


[OK] LOGS STRUCTURÉS AVEC OPENTELEMETRY

# === QU'EST-CE QU'UN LOG OPENTELEMETRY? ===

# Log classique (sans OpenTelemetry):
print("User 123 logged in")
# Problème:
# - Pas de contexte (quelle requête? quel service?)
# - Impossible de relier aux traces
# - Format texte non structuré

# Log OpenTelemetry:
logger.info("User logged in", extra={
    "user.id": 123,
    "trace_id": "abc123",
    "span_id": "def456"
})
# Avantages:
# - Corrélation automatique avec traces
# - Format structuré (JSON)
# - Recherche et filtrage faciles
# - Contexte complet


# === INSTRUMENTER LES LOGS PYTHON ===

from opentelemetry._logs import set_logger_provider
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogRecordExporter
import logging

# 1. Créer un LoggerProvider
logger_provider = LoggerProvider()

# 2. Ajouter un exporter
console_exporter = ConsoleLogRecordExporter()
processor = BatchLogRecordProcessor(console_exporter)
logger_provider.add_log_record_processor(processor)

# 3. Définir comme provider global
set_logger_provider(logger_provider)

# 4. Créer un handler OpenTelemetry
handler = LoggingHandler(
    level=logging.NOTSET,
    logger_provider=logger_provider
)

# 5. Configurer le logger Python standard
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.addHandler(handler)

# 6. Utiliser le logger normalement
logger.info("Application démarrée")
logger.warning("Attention: cache presque plein")
logger.error("Erreur de connexion à la base de données")

# Les logs sont maintenant enrichis automatiquement avec:
# - trace_id (si dans un span)
# - span_id (si dans un span)
# - service.name
# - Timestamp précis


# === CORRÉLATION AUTOMATIQUE LOGS <-> TRACES ===

from opentelemetry import trace
from opentelemetry._logs import set_logger_provider
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogRecordExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
import logging

# Configuration traces
trace_provider = TracerProvider()
trace_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
trace.set_tracer_provider(trace_provider)
tracer = trace.get_tracer(__name__)

# Configuration logs
logger_provider = LoggerProvider()
logger_provider.add_log_record_processor(BatchLogRecordProcessor(ConsoleLogRecordExporter()))
set_logger_provider(logger_provider)

handler = LoggingHandler(logger_provider=logger_provider)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.addHandler(handler)

# Utilisation
def process_order(order_id):
    with tracer.start_as_current_span("process_order") as span:
        span.set_attribute("order.id", order_id)
        
        # Ce log est AUTOMATIQUEMENT corrélé au span!
        logger.info("Processing order", extra={"order.id": order_id})
        
        # Simuler traitement
        import time
        time.sleep(0.5)
        
        logger.info("Order processed successfully")

process_order(12345)

# Dans Jaeger ou autre backend:
# - Tu vois la trace avec le span "process_order"
# - Tu peux cliquer sur le span
# - Tu vois AUTOMATIQUEMENT les logs associés!
# - Corrélation via trace_id et span_id


# === LOGS STRUCTURÉS (JSON) ===

# Les logs OpenTelemetry sont structurés (pas juste du texte)

logger.info("User logged in", extra={
    "user.id": 123,
    "user.email": "alice@example.com",
    "user.role": "admin",
    "session.id": "abc-123-def",
    "ip.address": "192.168.1.100"
})

# Résultat (format interne OpenTelemetry):
{
    "timestamp": "2024-01-15T10:30:00.000000Z",
    "severity": "INFO",
    "body": "User logged in",
    "attributes": {
        "user.id": 123,
        "user.email": "alice@example.com",
        "user.role": "admin",
        "session.id": "abc-123-def",
        "ip.address": "192.168.1.100"
    },
    "trace_id": "1234567890abcdef1234567890abcdef",
    "span_id": "abcdef1234567890",
    "service.name": "api-users"
}

# Permet de chercher facilement:
# - Tous les logs de l'utilisateur 123
# - Tous les logs de la session abc-123-def
# - Tous les logs INFO du service api-users


# === NIVEAUX DE SÉVÉRITÉ ===

# OpenTelemetry supporte les niveaux standard:

logger.debug("Détails de débogage")       # DEBUG
logger.info("Information générale")       # INFO
logger.warning("Avertissement")           # WARNING
logger.error("Erreur non critique")       # ERROR
logger.critical("Erreur critique")        # CRITICAL

# Conventions:
# - DEBUG: Infos détaillées pour le développement
# - INFO: Événements normaux (démarrage, arrêt, opérations)
# - WARNING: Situations anormales mais gérables
# - ERROR: Erreurs nécessitant attention
# - CRITICAL: Erreurs graves compromettant le système


# === EXPORTER LES LOGS VERS BACKENDS ===

# OTLP (recommandé):
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter

logger_provider = LoggerProvider()
logger_provider.add_log_record_processor(
    BatchLogRecordProcessor(
        OTLPLogExporter(endpoint="http://localhost:4317")
    )
)

# Elasticsearch:
# Utiliser OTLP Collector avec exporter Elasticsearch

# Fichier JSON:
from opentelemetry.sdk._logs.export import SimpleLogRecordProcessor
import json

class JSONFileExporter:
    def __init__(self, filename):
        self.file = open(filename, 'a')
    
    def export(self, batch):
        for record in batch:
            self.file.write(json.dumps(record.to_json()) + '\n')
            self.file.flush()
        return True

logger_provider = LoggerProvider()
logger_provider.add_log_record_processor(
    SimpleLogRecordProcessor(JSONFileExporter('logs.json'))
)


[OK] OPENTELEMETRY COLLECTOR EN DÉTAIL

# === QU'EST-CE QUE LE COLLECTOR? ===

# Collector = Service qui:
# 1. REÇOIT les données d'observabilité (traces/métriques/logs)
# 2. TRAITE les données (filtrage, enrichissement, agrégation)
# 3. EXPORTE vers un ou plusieurs backends

# Pourquoi utiliser un Collector?
# - Découple ton app du backend final
# - Un seul endpoint pour toutes les apps
# - Traitement centralisé
# - Routage flexible vers multiples backends
# - Pas besoin de redéployer les apps pour changer de backend


# === ARCHITECTURE AVEC COLLECTOR ===

# SANS Collector:
# App 1 -> Jaeger
# App 2 -> Jaeger
# App 3 -> Jaeger
# Problème: Chaque app doit connaître Jaeger

# AVEC Collector:
# App 1 ->
# App 2 -> Collector -> Jaeger
# App 3 ->           -> Prometheus
#                   -> Elasticsearch
# Avantages:
# - Apps envoient vers un seul endpoint (Collector)
# - Collector route vers multiples backends
# - Changer de backend = modifier SEULEMENT le Collector


# === INSTALLER LE COLLECTOR ===

# Méthode 1: Docker (recommandé)

docker run -d --name otel-collector \
  -p 4317:4317 \     # gRPC receiver
  -p 4318:4318 \     # HTTP receiver
  -p 8888:8888 \     # Metrics endpoint
  -p 8889:8889 \     # Prometheus exporter
  -p 13133:13133 \   # Health check
  -p 55679:55679 \   # zPages
  -v $(pwd)/otel-collector-config.yaml:/etc/otel-collector-config.yaml \
  otel/opentelemetry-collector:latest \
  --config=/etc/otel-collector-config.yaml

# Méthode 2: Binaire (Linux)

# Télécharger:
wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.90.0/otelcol_0.90.0_linux_amd64.tar.gz

# Extraire:
tar -xzf otelcol_0.90.0_linux_amd64.tar.gz

# Lancer:
./otelcol --config=config.yaml


# === CONFIGURATION DU COLLECTOR ===

# Fichier: otel-collector-config.yaml

# Le Collector se configure en 4 sections:
# 1. receivers: Comment recevoir les données
# 2. processors: Comment traiter les données
# 3. exporters: Où envoyer les données
# 4. service: Pipeline complet

# Configuration minimale:

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317  # Recevoir via gRPC
      http:
        endpoint: 0.0.0.0:4318  # Recevoir via HTTP

processors:
  batch:
    timeout: 10s        # Grouper par lots toutes les 10s
    send_batch_size: 100  # Ou dès que 100 spans

exporters:
  logging:
    loglevel: debug  # Afficher dans les logs (debug)
  
  jaeger:
    endpoint: jaeger:14250  # Envoyer vers Jaeger
    tls:
      insecure: true
  
  prometheus:
    endpoint: "0.0.0.0:8889"  # Exposer pour Prometheus

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [logging, jaeger]
    
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [logging, prometheus]


# === CONFIGURATION AVANCÉE ===

# Fichier: otel-collector-config-advanced.yaml

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318
  
  # Recevoir métriques Prometheus (scraping)
  prometheus:
    config:
      scrape_configs:
        - job_name: 'my-app'
          scrape_interval: 10s
          static_configs:
            - targets: ['app:8000']

processors:
  # Grouper par lots
  batch:
    timeout: 10s
    send_batch_size: 100
  
  # Ajouter des attributs à toutes les données
  resource:
    attributes:
      - key: environment
        value: production
        action: upsert
      - key: cluster.name
        value: k8s-prod-01
        action: upsert
  
  # Filtrer certains spans
  filter:
    traces:
      span:
        - 'attributes["http.url"] == "/health"'  # Ignorer /health
  
  # Enrichir avec des métadonnées
  attributes:
    actions:
      - key: user.id
        from_attribute: http.request.header.x-user-id
        action: insert

exporters:
  # Console (debug)
  logging:
    loglevel: info
  
  # Jaeger (traces)
  jaeger:
    endpoint: jaeger:14250
    tls:
      insecure: true
  
  # Prometheus (métriques)
  prometheus:
    endpoint: "0.0.0.0:8889"
  
  # OTLP vers un autre collector ou backend
  otlp:
    endpoint: backend.example.com:4317
    tls:
      insecure: false
      cert_file: /certs/client.crt
      key_file: /certs/client.key
  
  # Elasticsearch (logs)
  elasticsearch:
    endpoints: ["http://elasticsearch:9200"]
    index: "otel-logs"

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch, resource, filter]
      exporters: [logging, jaeger, otlp]
    
    metrics:
      receivers: [otlp, prometheus]
      processors: [batch, resource]
      exporters: [logging, prometheus]
    
    logs:
      receivers: [otlp]
      processors: [batch, resource]
      exporters: [logging, elasticsearch]


# === UTILISER LE COLLECTOR DEPUIS TON APP ===

# Au lieu d'exporter directement vers Jaeger:

from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

# Exporter vers le Collector (pas directement Jaeger)
otlp_exporter = OTLPSpanExporter(
    endpoint="http://localhost:4317",  # Collector endpoint
    insecure=True
)

provider = TracerProvider()
processor = BatchSpanProcessor(otlp_exporter)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)

# Maintenant:
# 1. Ton app envoie vers Collector (port 4317)
# 2. Collector traite et enrichit
# 3. Collector envoie vers Jaeger, Prometheus, etc.


# === MONITORING DU COLLECTOR ===

# Le Collector expose ses propres métriques:

# zPages (debug web):
# http://localhost:55679/debug/tracez
# http://localhost:55679/debug/pipelinez

# Prometheus metrics:
# http://localhost:8888/metrics

# Health check:
# http://localhost:13133/

# Métriques intéressantes:
# - otelcol_receiver_accepted_spans: Spans reçus
# - otelcol_receiver_refused_spans: Spans refusés
# - otelcol_processor_batch_batch_send_size: Taille des lots
# - otelcol_exporter_sent_spans: Spans exportés
# - otelcol_exporter_send_failed_spans: Échecs d'export


[OK] EXEMPLE COMPLET: APPLICATION MICROSERVICES

# === ARCHITECTURE ===

# 3 microservices:
# 1. API Gateway (Flask) - Point d'entrée
# 2. User Service (Flask) - Gestion utilisateurs
# 3. Order Service (Flask) - Gestion commandes

# + Base de données PostgreSQL
# + OpenTelemetry Collector
# + Jaeger pour visualisation


# === DOCKER COMPOSE ===

# Fichier: docker-compose.yml

version: '3.8'

services:
  # OpenTelemetry Collector
  otel-collector:
    image: otel/opentelemetry-collector:latest
    command: ["--config=/etc/otel-collector-config.yaml"]
    volumes:
      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
    ports:
      - "4317:4317"   # OTLP gRPC receiver
      - "4318:4318"   # OTLP HTTP receiver
      - "8889:8889"   # Prometheus exporter
      - "13133:13133" # Health check

  # Jaeger
  jaeger:
    image: jaegertracing/all-in-one:latest
    ports:
      - "16686:16686"  # UI
      - "14250:14250"  # gRPC

  # PostgreSQL
  postgres:
    image: postgres:15
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: password
      POSTGRES_DB: microservices
    ports:
      - "5432:5432"

  # API Gateway
  api-gateway:
    build: ./api-gateway
    ports:
      - "5000:5000"
    environment:
      OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4317
      OTEL_SERVICE_NAME: api-gateway
      USER_SERVICE_URL: http://user-service:5001
      ORDER_SERVICE_URL: http://order-service:5002
    depends_on:
      - otel-collector
      - user-service
      - order-service

  # User Service
  user-service:
    build: ./user-service
    ports:
      - "5001:5001"
    environment:
      OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4317
      OTEL_SERVICE_NAME: user-service
      DATABASE_URL: postgresql://admin:password@postgres:5432/microservices
    depends_on:
      - otel-collector
      - postgres

  # Order Service
  order-service:
    build: ./order-service
    ports:
      - "5002:5002"
    environment:
      OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4317
      OTEL_SERVICE_NAME: order-service
      DATABASE_URL: postgresql://admin:password@postgres:5432/microservices
    depends_on:
      - otel-collector
      - postgres


# === API GATEWAY (api-gateway/app.py) ===

from flask import Flask, jsonify
import requests
import os
from opentelemetry import trace
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.resources import Resource

# Configuration OpenTelemetry
resource = Resource(attributes={
    "service.name": os.getenv("OTEL_SERVICE_NAME", "api-gateway"),
    "service.version": "1.0.0",
    "deployment.environment": "production"
})

provider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(
    OTLPSpanExporter(endpoint=os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT"))
)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)

tracer = trace.get_tracer(__name__)

# Créer l'app Flask
app = Flask(__name__)

# Instrumenter Flask et Requests
FlaskInstrumentor().instrument_app(app)
RequestsInstrumentor().instrument()

# URLs des services
USER_SERVICE_URL = os.getenv("USER_SERVICE_URL", "http://localhost:5001")
ORDER_SERVICE_URL = os.getenv("ORDER_SERVICE_URL", "http://localhost:5002")


@app.route('/api/user/<int:user_id>/orders')
def get_user_orders(user_id):
    """
    Endpoint qui:
    1. Appelle User Service pour récupérer l'utilisateur
    2. Appelle Order Service pour récupérer ses commandes
    3. Combine les résultats
    """
    with tracer.start_as_current_span("get_user_orders") as span:
        span.set_attribute("user.id", user_id)
        
        # Appel au User Service
        with tracer.start_as_current_span("call_user_service"):
            user_response = requests.get(f"{USER_SERVICE_URL}/api/users/{user_id}")
            
            if user_response.status_code != 200:
                span.set_status(trace.Status(trace.StatusCode.ERROR))
                return jsonify({"error": "User not found"}), 404
            
            user = user_response.json()
        
        # Appel au Order Service
        with tracer.start_as_current_span("call_order_service"):
            orders_response = requests.get(f"{ORDER_SERVICE_URL}/api/orders?user_id={user_id}")
            orders = orders_response.json() if orders_response.status_code == 200 else []
        
        # Combiner les résultats
        result = {
            "user": user,
            "orders": orders,
            "total_orders": len(orders)
        }
        
        span.set_attribute("orders.count", len(orders))
        
        return jsonify(result)


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)


# === USER SERVICE (user-service/app.py) ===

from flask import Flask, jsonify
import os
from opentelemetry import trace
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.resources import Resource
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

# Configuration OpenTelemetry
resource = Resource(attributes={
    "service.name": os.getenv("OTEL_SERVICE_NAME", "user-service"),
    "service.version": "1.0.0",
    "deployment.environment": "production"
})

provider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(
    OTLPSpanExporter(endpoint=os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT"))
)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)

tracer = trace.get_tracer(__name__)

# Base de données
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://admin:password@localhost:5432/microservices")
engine = create_engine(DATABASE_URL)
Base = declarative_base()
Session = sessionmaker(bind=engine)

# Instrumenter SQLAlchemy
SQLAlchemyInstrumentor().instrument(
    engine=engine,
    tracer_provider=provider
)

# Modèle User
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(100))
    email = Column(String(100))

Base.metadata.create_all(engine)

# Flask app
app = Flask(__name__)
FlaskInstrumentor().instrument_app(app)


@app.route('/api/users/<int:user_id>')
def get_user(user_id):
    with tracer.start_as_current_span("get_user") as span:
        span.set_attribute("user.id", user_id)
        
        session = Session()
        user = session.query(User).filter(User.id == user_id).first()
        session.close()
        
        if not user:
            span.set_status(trace.Status(trace.StatusCode.ERROR, "User not found"))
            return jsonify({"error": "User not found"}), 404
        
        return jsonify({
            "id": user.id,
            "name": user.name,
            "email": user.email
        })


if __name__ == '__main__':
    # Ajouter des utilisateurs de test
    session = Session()
    if session.query(User).count() == 0:
        session.add(User(id=1, name="Alice", email="alice@example.com"))
        session.add(User(id=2, name="Bob", email="bob@example.com"))
        session.commit()
    session.close()
    
    app.run(host='0.0.0.0', port=5001)


# === ORDER SERVICE (order-service/app.py) ===

from flask import Flask, jsonify, request
import os
from opentelemetry import trace
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.resources import Resource

# Configuration OpenTelemetry
resource = Resource(attributes={
    "service.name": os.getenv("OTEL_SERVICE_NAME", "order-service"),
    "service.version": "1.0.0",
    "deployment.environment": "production"
})

provider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(
    OTLPSpanExporter(endpoint=os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT"))
)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)

tracer = trace.get_tracer(__name__)

# Flask app
app = Flask(__name__)
FlaskInstrumentor().instrument_app(app)

# Données de test (en mémoire)
ORDERS = [
    {"id": 1, "user_id": 1, "product": "Laptop", "amount": 999.99},
    {"id": 2, "user_id": 1, "product": "Mouse", "amount": 29.99},
    {"id": 3, "user_id": 2, "product": "Keyboard", "amount": 79.99},
]


@app.route('/api/orders')
def get_orders():
    user_id = request.args.get('user_id', type=int)
    
    with tracer.start_as_current_span("get_orders") as span:
        if user_id:
            span.set_attribute("user.id", user_id)
            orders = [o for o in ORDERS if o['user_id'] == user_id]
        else:
            orders = ORDERS
        
        span.set_attribute("orders.count", len(orders))
        
        return jsonify(orders)


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5002)


# === TESTER L'APPLICATION ===

# 1. Lancer tout:
docker-compose up -d

# 2. Faire une requête:
curl http://localhost:5000/api/user/1/orders

# 3. Ouvrir Jaeger:
# http://localhost:16686

# 4. Dans Jaeger:
# - Sélectionner "api-gateway" dans le dropdown
# - Cliquer "Find Traces"
# - Tu vois LA TRACE COMPLÈTE!

# 5. Cliquer sur une trace:
# Tu vois l'arbre complet:
# api-gateway: GET /api/user/1/orders (200ms)
# ├─ get_user_orders (195ms)
# │  ├─ call_user_service (100ms)
# │  │  └─ HTTP GET http://user-service:5001/api/users/1 (95ms)
# │  │     └─ user-service: GET /api/users/1 (90ms)
# │  │        └─ get_user (85ms)
# │  │           └─ SELECT users WHERE id=1 (80ms)
# │  └─ call_order_service (50ms)
# │     └─ HTTP GET http://order-service:5002/api/orders?user_id=1 (45ms)
# │        └─ order-service: GET /api/orders (40ms)
# │           └─ get_orders (35ms)

# Tu vois EXACTEMENT:
# - Quels services ont été appelés
# - Dans quel ordre
# - Combien de temps chaque partie a pris
# - Si des erreurs se sont produites


[OK] BONNES PRATIQUES

# === NOMMAGE DES SPANS ===

# [X] MAUVAIS:
with tracer.start_as_current_span("function"):
    ...

# [OK] BON:
with tracer.start_as_current_span("process_payment"):
    ...

# [OK] EXCELLENT (avec opération):
with tracer.start_as_current_span("http.get /api/users"):
    ...

# Conventions:
# - Être descriptif et spécifique
# - Inclure le type d'opération (http.get, db.query, cache.get)
# - Utiliser lowercase avec underscores
# - Éviter les valeurs dynamiques dans le nom


# === ATTRIBUTS SÉMANTIQUES ===

# Utilise les conventions OpenTelemetry:

# HTTP:
span.set_attribute("http.method", "GET")
span.set_attribute("http.url", "https://api.example.com/users")
span.set_attribute("http.status_code", 200)
span.set_attribute("http.request.header.content-type", "application/json")

# Database:
span.set_attribute("db.system", "postgresql")
span.set_attribute("db.name", "production")
span.set_attribute("db.statement", "SELECT * FROM users WHERE id = $1")
span.set_attribute("db.operation", "SELECT")

# RPC:
span.set_attribute("rpc.system", "grpc")
span.set_attribute("rpc.service", "UserService")
span.set_attribute("rpc.method", "GetUser")


# === GESTION DES ERREURS ===

# [X] MAUVAIS (ignore l'erreur):
with tracer.start_as_current_span("risky"):
    try:
        might_fail()
    except:
        pass

# [OK] BON (enregistre l'erreur):
with tracer.start_as_current_span("risky") as span:
    try:
        might_fail()
    except Exception as e:
        span.set_status(trace.Status(trace.StatusCode.ERROR, str(e)))
        span.record_exception(e)
        raise


# === PERFORMANCE ===

# [X] MAUVAIS (trop de spans):
for i in range(1000):
    with tracer.start_as_current_span(f"process_item_{i}"):
        process(i)

# [OK] BON (un span global + compteur):
with tracer.start_as_current_span("process_items") as span:
    span.set_attribute("items.count", 1000)
    for i in range(1000):
        process(i)


# === SAMPLING (ÉCHANTILLONNAGE) ===

# En production, ne pas tracer 100% des requêtes (trop de données!)
# Utiliser l'échantillonnage:

from opentelemetry.sdk.trace.sampling import TraceIdRatioBased

# Tracer seulement 10% des requêtes:
sampler = TraceIdRatioBased(0.1)

provider = TracerProvider(sampler=sampler)

# Autres stratégies:
# - AlwaysOn: Tracer tout (dev)
# - AlwaysOff: Ne rien tracer
# - ParentBased: Suivre la décision du parent


# === SÉCURITÉ ===

# [X] MAUVAIS (expose des secrets):
span.set_attribute("db.password", "secret123")
span.set_attribute("api.key", "sk-1234567890")

# [OK] BON (masque les secrets):
span.set_attribute("db.user", "admin")  # OK
span.set_attribute("has_auth", True)    # OK, booléen

# Utiliser un processor pour filtrer:
class SensitiveDataFilter:
    def on_start(self, span, parent_context):
        pass
    
    def on_end(self, span):
        # Retirer les attributs sensibles
        if span.attributes:
            sensitive_keys = ["password", "api_key", "token", "secret"]
            for key in list(span.attributes.keys()):
                if any(s in key.lower() for s in sensitive_keys):
                    del span.attributes[key]


# === CONTEXTE UTILISATEUR ===

# Ajouter l'ID utilisateur à tous les spans:

from opentelemetry import baggage

def authenticate(user_id):
    # Ajouter dans le baggage (propagé automatiquement)
    ctx = baggage.set_baggage("user.id", user_id)
    return ctx

# Puis dans tes spans:
user_id = baggage.get_baggage("user.id")
if user_id:
    span.set_attribute("user.id", user_id)


[OK] DÉBOGAGE ET TROUBLESHOOTING

# === VÉRIFIER LA CONFIGURATION ===

# 1. Afficher la config actuelle:
from opentelemetry import trace

provider = trace.get_tracer_provider()
print(f"Tracer Provider: {provider}")
print(f"Resource: {provider.resource.attributes}")

# 2. Tester avec Console Exporter:
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, BatchSpanProcessor

provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))

# 3. Vérifier les variables d'environnement:
import os
print(f"OTEL_EXPORTER_OTLP_ENDPOINT: {os.getenv('OTEL_EXPORTER_OTLP_ENDPOINT')}")
print(f"OTEL_SERVICE_NAME: {os.getenv('OTEL_SERVICE_NAME')}")


# === PROBLÈMES COURANTS ===

# PROBLÈME: Aucune trace n'apparaît
# SOLUTIONS:
# 1. Vérifier que l'instrumentation est activée
# 2. Vérifier l'endpoint du collector
# 3. Vérifier les logs du collector
# 4. Utiliser Console Exporter pour debug

# PROBLÈME: Spans non liés (pas d'arbre)
# SOLUTIONS:
# 1. Vérifier la propagation du contexte
# 2. S'assurer que le contexte est passé entre threads/processus
# 3. Utiliser context managers (with)

# PROBLÈME: Performance dégradée
# SOLUTIONS:
# 1. Réduire le sampling (0.1 au lieu de 1.0)
# 2. Utiliser BatchSpanProcessor (pas SimpleSpanProcessor)
# 3. Réduire le nombre de spans

# PROBLÈME: Attributs manquants
# SOLUTIONS:
# 1. Vérifier que les instrumentations sont installées
# 2. Vérifier les versions des packages
# 3. Ajouter manuellement les attributs manquants


# === LOGS DE DEBUG ===

# Activer les logs OpenTelemetry:
import logging

logging.basicConfig(level=logging.DEBUG)
logging.getLogger("opentelemetry").setLevel(logging.DEBUG)

# Affiche tous les détails internes d'OpenTelemetry


[OK] RESSOURCES ET LIENS

# Documentation officielle OpenTelemetry:
# https://opentelemetry.io/docs/

# Spécification complète:
# https://github.com/open-telemetry/opentelemetry-specification

# Python SDK:
# https://opentelemetry-python.readthedocs.io/

# Instrumentations disponibles:
# https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation

# Semantic Conventions:
# https://github.com/open-telemetry/semantic-conventions

# Collector:
# https://opentelemetry.io/docs/collector/

# Jaeger:
# https://www.jaegertracing.io/

# Prometheus:
# https://prometheus.io/

# Exemples complets:
# https://github.com/open-telemetry/opentelemetry-python/tree/main/docs/examples

# Community Slack:
# https://cloud-native.slack.com (channel #otel-python)

# Blog et tutoriels:
# https://opentelemetry.io/blog/

# Awesome OpenTelemetry:
# https://github.com/magsther/awesome-opentelemetry
```