RousselTMLEARNING

TP · OpenTelemetry: del descubrimiento a la experiencia

Práctica: Logs de OpenTelemetry correlacionados con las trazas y recogida de archivos con corrección

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

Lo que vas a aprender en este TP
  • Conectar un logger existente a OpenTelemetry
  • Correlacionar logs y trazas
  • Recoger archivos con filelog

Introducción #

Lo que vas a aprender en esta sección
  • Conectar un logger existente a OpenTelemetry
  • Correlacionar logs y trazas
  • Recoger archivos con filelog

Contexto. Los logs de facturación son archivos de texto que nadie vincula con las trazas. Conectas el servicio moderno a OpenTelemetry y recoges los logs de la aplicación heredada sin modificarla.

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: 45 min

Logs estructurados, correlacionados con las trazas, procedentes de las dos vías de recogida.

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. Conectar el módulo logging

    ¿Por qué? El LoggingHandler convierte cada llamada a logging en un LogRecord de OpenTelemetry, con el contexto de traza activo.

    Escribe facturation.py: un LoggerProvider con exportador OTLP, un handler añadido al logger raíz, un TracerProvider y logs INFO y ERROR emitidos dentro de spans.
    Ver la corrección
    pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
    cat > facturation.py <<'EOF'
    import logging, random, time
    from opentelemetry import trace
    from opentelemetry._logs import set_logger_provider
    from opentelemetry.sdk.resources import Resource
    from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
    from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
    from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
    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": "facturation", "service.version": "2.1.0"})
    logs = LoggerProvider(resource=resource)
    logs.add_log_record_processor(BatchLogRecordProcessor(OTLPLogExporter()))
    set_logger_provider(logs)
    logging.basicConfig(level=logging.INFO)
    logging.getLogger().addHandler(LoggingHandler(level=logging.INFO, logger_provider=logs))
    traces = TracerProvider(resource=resource)
    traces.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
    trace.set_tracer_provider(traces)
    
    log = logging.getLogger("facturation")
    tracer = trace.get_tracer("tp.facturation")
    for n in range(20):
        with tracer.start_as_current_span("emettre facture"):
            log.info("facture émise", extra={"shop.invoice.id": f"F-{n}", "shop.invoice.amount": random.randint(10, 900)})
            if n % 5 == 0:
                log.error("échec d'envoi au client", extra={"shop.invoice.id": f"F-{n}"})
        time.sleep(0.2)
    logs.shutdown() ; traces.shutdown()
    EOF
    python facturation.py
  3. 3. Consultar y correlacionar en Grafana

    ¿Por qué? El trace_id que lleva cada log permite pasar del log a la traza y viceversa.

    En Explore > Loki, muestra los errores del servicio, despliega una línea y localiza trace_id, span_id, la severidad y tus atributos. Abre la traza vinculada y, desde un span de Tempo, muestra sus logs.
    Ver la corrección
    {service_name="facturation"} | severity_text="ERROR"
    {service_name="facturation"} | shop_invoice_id="F-10"
    sum by (severity_text) (count_over_time({service_name="facturation"}[5m]))
  4. 4. Recoger los logs de una aplicación heredada

    ¿Por qué? Sin tocar el código, el receiver filelog lee los archivos y analiza el JSON, la severidad y el contexto de traza.

    Genera un archivo de logs JSON y lanza un Collector contrib en la misma red que la pila, con filelog, json_parser, trace_parser y un procesador resource que fije service.name.
    Ver la corrección
    mkdir -p logs
    for i in $(seq 1 30); do
      lvl=$([ $((i % 6)) -eq 0 ] && echo ERROR || echo INFO)
      printf '{"time":"%s","level":"%s","message":"traitement lot %d","trace_id":"%s","span_id":"%s"}\n' \
        "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$lvl" "$i" "$(openssl rand -hex 16)" "$(openssl rand -hex 8)" >> logs/app.log
    done
    cat > collector.yaml <<'EOF'
    receivers:
      filelog:
        include: [/logs/app.log]
        start_at: beginning
        operators:
          - type: json_parser
            severity: { parse_from: attributes.level }
          - type: trace_parser
            trace_id: { parse_from: attributes.trace_id }
            span_id: { parse_from: attributes.span_id }
          - type: move
            from: attributes.message
            to: body
    processors:
      resource:
        attributes: [{ key: service.name, value: facturation-historique, action: upsert }]
      batch: {}
    exporters:
      otlphttp: { endpoint: http://lgtm:4318 }
    service:
      pipelines:
        logs: { receivers: [filelog], processors: [resource, batch], exporters: [otlphttp] }
    EOF
    docker network create otel 2>/dev/null ; docker network connect otel lgtm
    docker run -d --name col-logs --network otel -v "$PWD/collector.yaml:/etc/otelcol-contrib/config.yaml" -v "$PWD/logs:/logs" otel/opentelemetry-collector-contrib:latest
    # Loki : {service_name="facturation-historique"} | severity_text="ERROR"
  5. 5. Reducir y enmascarar

    ¿Por qué? El Collector elimina el ruido y los datos personales antes del almacenamiento.

    Añade un procesador filter que elimine los logs INFO y un transform que enmascare las direcciones de correo en el cuerpo y compruébalo en Loki.
    Ver la corrección
    processors:
      filter/info:
        logs:
          log_record: ['severity_number < SEVERITY_NUMBER_WARN']
      transform/pii:
        log_statements:
          - context: log
            statements:
              - replace_pattern(body, "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+", "***@***")
    # pipeline : processors: [resource, filter/info, transform/pii, batch]
    # docker restart col-logs, puis nettoyage : docker rm -f col-logs

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

Mi seguimiento

  • ⏱ Duración estimada : ≈ 45 min

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

✉️ Hacer una pregunta

Artículos recomendados

Les logs avec OpenTelemetry : modèle de données, appenders, corrélation et collecte

Maîtriser le signal log : modèle de données LogRecord, Logs Bridge API et appenders (Logback, Log...

Grafana Alloy: Perfilado Continuo con Pyroscope

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