Habilitación del seguimiento distribuido de OpenTelemetry con Durable Task Scheduler

El seguimiento distribuido proporciona visibilidad de extremo a extremo en la ejecución de la orquestación. Al habilitar OpenTelemetry con Durable Task Scheduler, cada orquestación, actividad y suborquestación genera segmentos vinculados que muestran el tiempo, el orden y los errores en todo el flujo de trabajo. Puede exportar estos seguimientos a cualquier back-end compatible con OpenTelemetry, como Azure Monitor Application Insights, Jaeger o Zipkin.

Durable Functions y los SDK de tareas independientes Durable Task admiten el seguimiento distribuido de OpenTelemetry al usar Durable Task Scheduler como back-end.

Cómo funciona

Los SDK de Durable Task instrumentan automáticamente orquestaciones y actividades con intervalos de OpenTelemetry. El SDK crea un intervalo primario para cada orquestación e intervalos secundarios para cada llamada a actividad, suborquestación y temporizador. El contexto de seguimiento se propaga automáticamente en todas estas operaciones, por lo que se obtiene un único seguimiento correlacionado para todo el flujo de trabajo.

El árbol de seguimiento resultante tiene el siguiente aspecto:

create_orchestration (client)
  └─ orchestration (server)
       ├─ activity:Step1
       ├─ activity:Step2
       └─ activity:Step3

No es necesario agregar instrumentación personalizada al orquestador o al código de actividad. Registre el Microsoft.DurableTask origen de actividad con la configuración de OpenTelemetry y el SDK controla el resto.

Prerrequisitos

  • Un proyecto de Azure Functions con la extensión Durable Functions versión 2.13.0 o posterior.
  • Programador de tareas duraderas configurado como back-end de almacenamiento para la aplicación de funciones.
  • Un back-end compatible con OpenTelemetry para ver seguimientos (Application Insights, Jaeger u otro recopilador OTLP).
  • SDK de .NET 8 o posterior.
  • Los paquetes NuGet Microsoft.DurableTask.Worker.AzureManaged y Microsoft.DurableTask.Client.AzureManaged.
  • Los OpenTelemetry, OpenTelemetry.Extensions.Hosting y OpenTelemetry.Exporter.OpenTelemetryProtocol paquetes NuGet.
  • Un back-end compatible con OpenTelemetry para ver seguimientos, como Application Insights para producción o Jaeger para desarrollo local.

Habilitación del seguimiento distribuido

Para habilitar el seguimiento distribuido en Durable Functions, actualice el host.json y configure un back-end de telemetría compatible con OpenTelemetry.

Actualización de host.json

Agregue la sección tracing dentro de durableTask en tu archivo host.json:

{
  "version": "2.0",
  "extensions": {
    "durableTask": {
      "tracing": {
        "DistributedTracingEnabled": true,
        "Version": "V2"
      }
    }
  }
}

Configurar Application Insights

Establezca la variable de entorno APPLICATIONINSIGHTS_CONNECTION_STRING en su aplicación de función.

Para el desarrollo local, agréguelo a local.settings.json:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
    "APPLICATIONINSIGHTS_CONNECTION_STRING": "<your-connection-string>"
  }
}

Para las aplicaciones hospedadas Azure, agréguela como una configuración de aplicación en Configuration en el portal de Azure.

Nota:

Si ha usado APPINSIGHTS_INSTRUMENTATIONKEYanteriormente , cambie a APPLICATIONINSIGHTS_CONNECTION_STRING para las funcionalidades más recientes.

Reducción del ruido de telemetría

Para evitar que Application Insights muestree datos de seguimiento, excluya Request las reglas de muestreo en host.json:

{
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "excludedTypes": "Request"
      }
    }
  }
}

Registre el origen de la actividad Microsoft.DurableTask con la configuración de OpenTelemetry. El SDK de Durable Task crea automáticamente intervalos para orquestaciones y actividades al registrar este origen.

En el archivo Program.cs del trabajo, agregue el seguimiento de OpenTelemetry con el origen de actividad de Durable Task.

using Microsoft.DurableTask;
using Microsoft.DurableTask.Worker;
using Microsoft.DurableTask.Worker.AzureManaged;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using OpenTelemetry;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

var builder = Host.CreateApplicationBuilder(args);

// Configure OpenTelemetry tracing
builder.Services.AddOpenTelemetry()
    .ConfigureResource(resource => resource.AddService("durable-worker"))
    .WithTracing(tracing =>
    {
        tracing
            .AddSource("Microsoft.DurableTask")
            .AddOtlpExporter(opts =>
            {
                opts.Endpoint = new Uri(
                    Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT")
                    ?? "http://localhost:4317");
            });
    });

// Build connection string from environment variables
string endpoint = Environment.GetEnvironmentVariable("ENDPOINT") ?? "http://localhost:8080";
string taskHub = Environment.GetEnvironmentVariable("TASKHUB") ?? "default";
string connectionString = endpoint.Contains("localhost")
    ? $"Endpoint={endpoint};TaskHub={taskHub};Authentication=None"
    : $"Endpoint={endpoint};TaskHub={taskHub};Authentication=DefaultAzure";

// Configure Durable Task worker
builder.Services.AddDurableTaskWorker()
    .AddTasks(tasks =>
    {
        tasks.AddOrchestratorFunc<string, string>(
            "OrderProcessingOrchestration", async (ctx, input) =>
        {
            var validated = await ctx.CallActivityAsync<string>("ValidateOrder", input);
            var payment = await ctx.CallActivityAsync<string>("ProcessPayment", validated);
            var shipment = await ctx.CallActivityAsync<string>("ShipOrder", payment);
            var result = await ctx.CallActivityAsync<string>("SendNotification", shipment);
            return result;
        });

        tasks.AddActivityFunc<string, string>("ValidateOrder", (ctx, input) =>
            Task.FromResult($"Validated({input})"));
        tasks.AddActivityFunc<string, string>("ProcessPayment", (ctx, input) =>
            Task.FromResult($"Paid({input})"));
        tasks.AddActivityFunc<string, string>("ShipOrder", (ctx, input) =>
            Task.FromResult($"Shipped({input})"));
        tasks.AddActivityFunc<string, string>("SendNotification", (ctx, input) =>
            Task.FromResult($"Notified({input})"));
    })
    .UseDurableTaskScheduler(connectionString);

var host = builder.Build();
await host.RunAsync();

La línea de clave es .AddSource("Microsoft.DurableTask"), que indica a OpenTelemetry que capture intervalos que emite el SDK de Durable Task.

Configuración del punto de conexión de OTLP

Los fragmentos de código anteriores hacen referencia a la OTEL_EXPORTER_OTLP_ENDPOINT variable de entorno para establecer el destino de los datos de seguimiento. Establezca esta variable en función del back-end:

Backend Valor del punto de conexión Protocolo
Jaeger (local) http://localhost:4317 gRPC
Jaeger (local, HTTP) http://localhost:4318 HTTP/protobuf
Recopilador de OpenTelemetry http://<collector-host>:4317 gRPC
Azure Monitor (a través de OTLP) Use el exportador de Azure Monitor en su lugar. N/A

Para el desarrollo local con Jaeger, el valor predeterminado http://localhost:4317 funciona cuando Jaeger se ejecuta con OTLP gRPC habilitado (puerto 4317). El SDK de JavaScript usa HTTP/protobuf de forma predeterminada, por lo que se dirige al puerto 4318 con la ruta /v1/traces.

Visualizar trazas localmente con la interfaz Jaeger

Para el desarrollo local, utilice el emulador del programador de tareas duraderas junto con Jaeger para ver los seguimientos. Utilice un docker-compose.yml para iniciar ambos servicios:

services:
  dts-emulator:
    image: mcr.microsoft.com/dts/dts-emulator:latest
    ports:
      - "8080:8080"  # gRPC
      - "8082:8082"  # Dashboard
  jaeger:
    image: jaegertracing/jaeger:latest
    ports:
      - "16686:16686"  # Jaeger UI
      - "4317:4317"    # OTLP gRPC
      - "4318:4318"    # OTLP HTTP

Inicie la infraestructura:

docker compose up -d

Después de ejecutar la aplicación, abra la interfaz de usuario de Jaeger en http://localhost:16686 y busque el nombre del servicio (por ejemplo, durable-worker) para ver los seguimientos.

Para el desarrollo local con Durable Functions, los datos de seguimiento distribuidos se envían a Application Insights de forma predeterminada. Para ver los seguimientos localmente sin implementar, puede agregar un exportador de OTLP junto con Application Insights en Program.cs de la aplicación de funciones:

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing
            .AddSource("Microsoft.DurableTask")
            .AddOtlpExporter(opts =>
            {
                opts.Endpoint = new Uri("http://localhost:4317");
            });
    });

A continuación, ejecute Jaeger localmente con docker run -d -p 16686:16686 -p 4317:4317 jaegertracing/jaeger:latest y abra la interfaz de usuario de Jaeger en http://localhost:16686.

Visualización de rastros en Application Insights

En el caso de las cargas de trabajo de producción, Application Insights es el back-end de telemetría recomendado.

Una vez que DistributedTracingEnabled se establece en true y Version se establece en V2 en host.json, la aplicación Durable Functions emite intervalos correlacionados a Application Insights. Para ver el seguimiento completo de la orquestación en el portal de Azure:

  1. Vaya al recurso de Application Insights en el portal de Azure.
  2. Abra la búsqueda de transacciones y busque su orquestación por nombre o identificador de instancia.
  3. Seleccione un seguimiento para ver la transacción completa con todos los intervalos correlacionados.

El seguimiento muestra la orquestación como un intervalo principal con intervalos secundarios para cada llamada a actividad, suborquestación y espera de temporizador. Los siguientes patrones producen formas de trazado distintas.

Modelo Forma de seguimiento
Diagrama de encadenamiento de funciones Intervalos de actividades secuenciales anidados bajo el intervalo del orquestador.
Distribución ramificada de salida y de entrada Intervalos de actividad paralela que se solapan en el tiempo.
Interacción humana Un intervalo de orquestados con una espera larga para un evento externo.
Monitor Intervalos de actividad repetidos con esperas de temporizador entre iteraciones.

Configura el exportador de OTLP para enviar trazas a Application Insights mediante el exportador de OpenTelemetry de Azure Monitor, o exporta a través de OTLP a un recopilador de OpenTelemetry que reenvía a Application Insights.

Instale el paquete NuGet Azure.Monitor.OpenTelemetry.Exporter y reemplace el exportador de OTLP por:

builder.Services.AddOpenTelemetry()
    .ConfigureResource(resource => resource.AddService("durable-worker"))
    .WithTracing(tracing =>
    {
        tracing
            .AddSource("Microsoft.DurableTask")
            .AddAzureMonitorTraceExporter(opts =>
            {
                opts.ConnectionString = Environment.GetEnvironmentVariable(
                    "APPLICATIONINSIGHTS_CONNECTION_STRING");
            });
    });

Qué muestra los datos de rastreo

Los datos de seguimiento generados por los SDK de Durable Task incluyen:

Tipo de intervalo Descripción
create_orchestration El intervalo del lado cliente emitido al programar una nueva orquestación
orchestration El intervalo de orquestación del lado servidor que cubre el ciclo de vida de ejecución completo
activity:<name> Intervalo para cada invocación de actividad, que muestra el tiempo y el resultado
sub_orchestration:<name> Un intervalo para cada llamada a la suborchestración
timer Un intervalo para esperas de temporizador duradero

Cada intervalo incluye atributos como durabletask.type, durabletask.task.name, durabletask.task.instance_idy durabletask.task.task_id. Las actividades y orquestaciones con errores incluyen detalles de error en el estado y los eventos del intervalo.

Solución de problemas

Cuestión Resolution
No aparecen rastros Compruebe que el origen de actividad Microsoft.DurableTask está registrado y que el punto de conexión del exportador es accesible.
Las trazas están incompletas Compruebe que el SDK de OpenTelemetry se inicializa antes del SDK de Durable Task (especialmente en JavaScript/TypeScript).
Intervalos que faltan en Application Insights Deshabilite o ajuste la configuración de muestreo para evitar que se quiten los datos de seguimiento.
Las trazas no se correlacionan Compruebe que usa Durable Task Scheduler como back-end. La propagación del contexto de seguimiento requiere el programador.

Código de ejemplo