Ative o rastreamento distribuído do OpenTelemetry usando o Durable Task Scheduler

O rastreamento distribuído proporciona visibilidade de ponta a ponta sobre a execução da orquestração. Quando ativas o OpenTelemetry com o Durable Task Scheduler, cada orquestração, atividade e suborquestração produz intervalos ligados que mostram o tempo, a ordem e os erros em todo o fluxo de trabalho. Pode exportar estes traços para qualquer backend compatível com OpenTelemetry, como Azure Monitor Application Insights, Jaeger ou Zipkin.

Durable Functions e os SDKs independentes Durable Task SDKs suportam ambos o rastreamento distribuído OpenTelemetry quando se utiliza o Durable Task Scheduler como backend.

Como funciona

Os SDKs de Tarefas Duráveis instrumentam automaticamente orquestrações e atividades com OpenTelemetry spans. O SDK cria um span pai para cada orquestração e spans filhos para cada chamada de atividade, suborquestração e temporizador. O contexto do traço propaga-se automaticamente por todas estas operações, por isso obtém um único traço correlacionado para todo o fluxo de trabalho.

A árvore de traços resultante é a seguinte:

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

Não precisas de adicionar instrumentação personalizada ao teu orquestrador ou código de atividade. Regista a Microsoft.DurableTask fonte da atividade com a tua configuração OpenTelemetry e o SDK trata do resto.

Pré-requisitos

  • Um projeto Funções do Azure com a extensão Durable Functions versão 2.13.0 ou posterior.
  • Durable Task Scheduler configurado como o backend de armazenamento de dados para a sua aplicação de funções.
  • Um backend compatível com OpenTelemetry para visualizar traços (Application Insights, Jaeger ou outro coletor OTLP).
  • .NET 8 SDK ou posterior.
  • Os pacotes NuGet Microsoft.DurableTask.Worker.AzureManaged e Microsoft.DurableTask.Client.AzureManaged.
  • Os pacotes NuGet OpenTelemetry, OpenTelemetry.Extensions.Hosting e OpenTelemetry.Exporter.OpenTelemetryProtocol.
  • Um back-end compatível com OpenTelemetry para visualização de rastros, como Application Insights para produção ou Jaeger para desenvolvimento local.

Habilitar rastreamento distribuído

Para ativar o rastreamento distribuído em Durable Functions, atualize o seu host.json e configure um backend de telemetria compatível com OpenTelemetry.

Atualize o host.json

Adicione a tracing secção sob durableTask no seu ficheiro host.json :

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

Configurar o Application Insights

Define a APPLICATIONINSIGHTS_CONNECTION_STRING variável ambiente na tua aplicação de funções.

Para desenvolvimento local, adicione-o à local.settings.json:

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

Para aplicações Azure alojadas, adicione-as como definição de aplicação em Configuração no portal Azure.

Observação

Se anteriormente utilizaste APPINSIGHTS_INSTRUMENTATIONKEY, altera para APPLICATIONINSIGHTS_CONNECTION_STRING para obter as capacidades mais recentes.

Reduzir o ruído de telemetria

Para impedir que o Application Insights amostre dados de traço, exclua Request das regras de amostragem em host.json:

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

Regista a fonte de atividade Microsoft.DurableTask na tua configuração do OpenTelemetry. O Durable Task SDK cria automaticamente spans para orquestrações e atividades quando regista esta fonte.

Na Program.cs do seu trabalhador, adicione o rastreio OpenTelemetry com a fonte da atividade 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();

A linha-chave é .AddSource("Microsoft.DurableTask"), que diz ao OpenTelemetry para capturar os spans emitidos pelo SDK de Tarefas Duráveis.

Configurar o endpoint OTLP

Os excertos de código acima referem-se à OTEL_EXPORTER_OTLP_ENDPOINT variável de ambiente para definir o destino dos dados de rastreamento. Defina esta variável com base no seu backend:

Backend Valor do endpoint Protocolo
Jaeger (local) http://localhost:4317 gRPC
Jaeger (local, HTTP) http://localhost:4318 HTTP/protobuf
Coletor OpenTelemetry http://<collector-host>:4317 gRPC
Azure Monitor (via OTLP) Use o exportador Azure Monitor em vez disso N/A

Para desenvolvimento local com o Jaeger, o padrão http://localhost:4317 funciona quando o Jaeger está a correr com o OTLP gRPC ativado (porta 4317). Por padrão, o SDK JavaScript usa HTTP/protobuf, por isso direciona para a porta 4318 com o caminho /v1/traces.

Visualizar traços localmente com a interface Jaeger

Para desenvolvimento local, usar o emulador Durable Task Scheduler com Jaeger para visualizar os rastreamentos. Use a docker-compose.yml para iniciar ambos os serviços:

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 a infraestrutura:

docker compose up -d

Depois de executares a tua aplicação, abre a interface do Jaeger em http://localhost:16686 e procura o nome do teu serviço (por exemplo, durable-worker) para visualizar os traços.

Para desenvolvimento local com Durable Functions, os dados de rastreamento distribuído são enviados para o Application Insights por defeito. Para visualizar traços localmente sem implementar, pode adicionar um exportador OTLP juntamente com o Application Insights na sua aplicação Program.csfuncional:

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

Depois executa o Jaeger localmente com docker run -d -p 16686:16686 -p 4317:4317 jaegertracing/jaeger:latest e abre a interface do Jaeger em http://localhost:16686.

Ver trilhos no Application Insights

Para cargas de trabalho em produção, o Application Insights é o backend de telemetria recomendado.

Assim que DistributedTracingEnabled está definido como true com Version definido como V2 em host.json, a sua aplicação de Durable Functions emite intervalos correlacionados para o Application Insights. Para ver o traçado completo de orquestração no portal Azure:

  1. Vá ao seu recurso Application Insights no portal do Azure.
  2. Abre a ferramenta de pesquisa de transações e procura por a tua orquestração através do nome ou ID de instância.
  3. Selecione um traço para visualizar a transação de ponta a ponta com todos os intervalos correlacionados.

O rastreio mostra a orquestração como um span pai, com spans filhos para cada chamada de atividade, suborquestração e espera temporizador. Os seguintes padrões produzem formas traçadas distintas:

Padrão Forma traçada
Encadeamento de funções A atividade sequencial aninha-se sob o intervalo do orquestrador.
Fan-out/fan-in A atividade paralela abrange sobreposições temporais.
Interação humana Um intervalo de orquestrador com uma longa espera para um evento externo.
Monitor Atividades repetidas têm intervalos com esperas controladas por temporizador entre iterações.

Configure o exportador OTLP para enviar traços para o Application Insights usando o exportador OpenTelemetry do Azure Monitor, ou exporte através do OTLP para um OpenTelemetry Collector que encaminhe para o Application Insights.

Instale o pacote NuGet Azure.Monitor.OpenTelemetry.Exporter e substitua o exportador 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");
            });
    });

O que os dados de traço mostram

Os dados de rastreamento produzidos pelos SDKs de Tarefas Duradouras incluem:

Tipo de vão estrutural Descrição
create_orchestration O intervalo do lado do cliente emitido ao agendar uma nova orquestração
orchestration O processo de orquestração do lado do servidor abrange todo o ciclo de vida da execução
activity:<name> Um intervalo para cada invocação de atividade, mostrando o tempo e o resultado
sub_orchestration:<name> Um intervalo para cada chamada de suborquestração
timer Um período de espera para temporizadores duráveis

Cada intervalo inclui atributos como durabletask.type, durabletask.task.name, durabletask.task.instance_id, e durabletask.task.task_id. Atividades e orquestrações falhadas incluem detalhes de erro no estado e nos eventos do intervalo.

Troubleshooting

Issue Resolução
Não aparecem vestígios Verifique se a Microsoft.DurableTask fonte da atividade está registada e que o endpoint exportador está acessível.
Os vestígios estão incompletos Verifique se o SDK do OpenTelemetry está inicializado antes do Durable Task SDK (especialmente em JavaScript/TypeScript).
Intervalos ausentes no Application Insights Desative ou ajuste as definições de amostragem para evitar que os dados de rastreio sejam perdidos.
Os vestígios não correlacionam Verifica se estás a usar o Durable Task Scheduler como função de back-end. A propagação do contexto de traço requer o escalonador.

Código de exemplo