RousselTMLEARNING

TP · OpenTelemetry: del descubrimiento a la experiencia

Práctica: Instrumentar trazas a mano (spans, eventos, errores, enlaces, muestreo) con corrección

La corrección está integrada en el TP (Ver los recuadros verdes)

Lo que vas a aprender en este TP
  • Crear spans con el SDK
  • Señalar errores, eventos y enlaces
  • Consultar con TraceQL y muestrear

Introducción #

Lo que vas a aprender en esta sección
  • Crear spans con el SDK
  • Señalar errores, eventos y enlaces
  • Consultar con TraceQL y muestrear

Contexto. El servicio de pedidos no tiene ninguna biblioteca auto-instrumentada para su lógica de negocio. Lo instrumentas a mano para ver las etapas de un pedido, sus errores de pago y sus procesos por lotes.

Requisitos previos: Docker y Python 3.10 o superior; la pila grafana/otel-lgtm (Collector, Tempo, Loki, Prometheus, Pyroscope y Grafana en un solo contenedor)  ·  Duración estimada: 50 min

Trazas ricas y consultables, y un muestreo controlado.

Puesta en práctica #

Pasos guiados, con su corrección.

  1. 1. Arrancar la pila de observabilidad

    ¿Por qué? Una sola imagen proporciona un Collector OTLP y todos los backends: nos centramos en la señal.

    Lanza grafana/otel-lgtm, crea un entorno Python y apunta los exportadores al puerto OTLP/HTTP 4318.
    Ver la corrección
    docker run -d --name lgtm -p 3000:3000 -p 4317:4317 -p 4318:4318 -p 4040:4040 grafana/otel-lgtm
    python3 -m venv .venv && . .venv/bin/activate
    export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
    # Grafana : http://localhost:3000 (admin / admin)
  2. 2. Inicializar el SDK

    ¿Por qué? El TracerProvider lleva el recurso y el exportador; el BatchSpanProcessor envía por lotes.

    Instala el SDK y el exportador OTLP/HTTP y escribe otel_setup.py, que configura el recurso (service.name, service.version, deployment.environment.name) y la exportación.
    Ver la corrección
    pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
    cat > otel_setup.py <<'EOF'
    from opentelemetry import trace
    from opentelemetry.sdk.resources import Resource
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import BatchSpanProcessor
    from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
    
    resource = Resource.create({"service.name": "commandes", "service.version": "1.0.0",
                                "deployment.environment.name": "tp"})
    provider = TracerProvider(resource=resource)      # l'échantillonneur est lu dans OTEL_TRACES_SAMPLER
    provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
    trace.set_tracer_provider(provider)
    EOF
  3. 3. Crear una traza de negocio

    ¿Por qué? Spans con el nombre de cada etapa, con SpanKind y atributos conformes a las convenciones, hacen la traza legible por todas las herramientas.

    Escribe commande.py: un span raíz traiter commande y tres hijos (stock como CLIENT con atributos de base de datos, cálculo, pago), un evento y un error de pago una vez de cada tres con record_exception y estado ERROR.
    Ver la corrección
    cat > commande.py <<'EOF'
    import random, time
    from otel_setup import provider
    from opentelemetry import trace
    from opentelemetry.trace import SpanKind, Status, StatusCode
    
    tracer = trace.get_tracer("tp.commandes", "1.0.0")
    
    class PaiementRefuse(Exception):
        pass
    
    def traiter(numero):
        with tracer.start_as_current_span("traiter commande", kind=SpanKind.SERVER) as span:
            span.set_attribute("shop.order.id", numero)
            with tracer.start_as_current_span("SELECT stock", kind=SpanKind.CLIENT) as db:
                db.set_attribute("db.system.name", "postgresql")
                db.set_attribute("db.collection.name", "stock")
                db.set_attribute("db.operation.name", "SELECT")
                time.sleep(random.uniform(0.01, 0.05))
            with tracer.start_as_current_span("calculer total"):
                time.sleep(random.uniform(0.005, 0.02))
            with tracer.start_as_current_span("payer", kind=SpanKind.CLIENT) as pay:
                pay.add_event("fraude.verifiee", {"shop.fraud.score": round(random.random(), 2)})
                time.sleep(random.uniform(0.05, 0.3))
                try:
                    if numero % 3 == 0:
                        raise PaiementRefuse("carte refusée")
                except PaiementRefuse as exc:
                    pay.record_exception(exc)
                    pay.set_status(Status(StatusCode.ERROR, str(exc)))
                    span.set_status(Status(StatusCode.ERROR, "paiement refusé"))
                    span.set_attribute("error.type", "PaiementRefuse")
    
    for n in range(1, 31):
        traiter(n)
    provider.shutdown()      # vide le dernier lot
    EOF
    python commande.py
  4. 4. Explorar y consultar con TraceQL

    ¿Por qué? TraceQL filtra por la estructura y los atributos de los spans: una consulta encuentra las trazas importantes.

    En Grafana, abre Explore > Tempo. Ejecuta las consultas siguientes, abre una traza con error y encuentra el evento y la excepción del span payer.
    Ver la corrección
    { resource.service.name = "commandes" && status = error }
    { name = "payer" && duration > 200ms }
    { span.db.system.name = "postgresql" }
    { name = "traiter commande" } >> { name = "payer" && status = error }
  5. 5. Vincular un proceso por lotes con enlaces

    ¿Por qué? Un lote que procesa elementos de varias trazas no puede tener varios padres: se usan enlaces.

    Escribe lot.py: tres trazas «recepción» independientes y luego un span facturer lot enlazado a las tres. Encuentra los enlaces en el detalle del span.
    Ver la corrección
    cat > lot.py <<'EOF'
    from otel_setup import provider
    from opentelemetry import trace
    from opentelemetry.trace import Link
    
    tracer = trace.get_tracer("tp.lots")
    contextes = []
    for i in range(3):
        with tracer.start_as_current_span("recevoir message") as span:
            span.set_attribute("messaging.message.id", f"msg-{i}")
            contextes.append(span.get_span_context())
    with tracer.start_as_current_span("facturer lot", links=[Link(c) for c in contextes]) as lot:
        lot.set_attribute("messaging.batch.message_count", len(contextes))
    provider.shutdown()
    EOF
    python lot.py
  6. 6. Muestrear

    ¿Por qué? El muestreo por ratio reduce el volumen; parentbased garantiza que una traza se conserve o se descarte entera.

    Vuelve a lanzar commande.py con un muestreo del 20 % y compara el número de trazas recibidas; comprueba que ninguna traza está incompleta. Explica por qué el tail sampling del Collector sería preferible para conservar todos los errores.
    Ver la corrección
    OTEL_TRACES_SAMPLER=parentbased_traceidratio OTEL_TRACES_SAMPLER_ARG=0.2 python commande.py
    # Grafana : { resource.service.name = "commandes" } sur les 5 dernières minutes : ~6 nouvelles traces au lieu de 30
    # le head sampling jette aussi ~80 % des traces en erreur : le tail sampling (politique status_code) les garderait toutes

Nivel de dificultad : ●●●○○ (3/5)

Mi seguimiento

  • ⏱ Duración estimada : ≈ 50 min

Inicia sesión para seguir tu progreso y tomar notas. Iniciar sesión

✉️ Hacer una pregunta

Artículos recomendados

Les traces OpenTelemetry en profondeur : spans, statuts, événements, liens et échantillonnage

Maîtriser le signal trace : anatomie d'une trace distribuée, span et SpanKind, attributs, statut ...

Tracing distribué : la propagation du contexte (W3C Trace Context et baggage)

Comprendre et mettre en œuvre la propagation de contexte OpenTelemetry : en-tête traceparent, tra...

Grafana Alloy: Perfilado Continuo con Pyroscope

Descubra cómo configurar el perfilado continuo (Continuous Profiling) en sus entornos utilizando ...