Compartilhar via


Coletar um rastreamento distribuído

Este artigo se aplica a: ✔️ .NET Core 2.1 e versões posteriores ✔️ .NET Framework 4.5 e versões posteriores

O código instrumentado pode criar objetos Activity como parte de um rastreamento distribuído, mas as informações nesses objetos precisam ser coletadas no armazenamento centralizado para que todo o rastreamento possa ser examinado posteriormente. Neste tutorial, você coletará a telemetria de rastreamento distribuído de maneiras diferentes disponibilizando-a para diagnosticar problemas do aplicativo quando necessário. Confira o tutorial de instrumentação se precisar adicionar uma nova instrumentação.

Coletar rastreamentos usando OpenTelemetry

O OpenTelemetry é um projeto de código aberto sem fornecedor específico compatível com o Cloud Native Computing Foundation que visa padronizar a geração e a coleta de telemetria de software nativo na nuvem. Nestes exemplos, você coletará e exibirá informações de rastreamento distribuído no console. Para saber como configurar o OpenTelemetry para enviar informações para outro lugar, consulte o Guia de introdução do OpenTelemetry.

Exemplo do ASP.NET

Pré-requisitos

Criar um aplicativo de exemplo

Primeiro, crie um aplicativo Web ASP.NET a ser usado como o aplicativo de demonstração.

dotnet new webapp

Este aplicativo exibe uma página da Web, mas nenhuma informação de rastreamento distribuído será coletada se navegarmos na página da Web.

Configurar a coleta

Para usar o OpenTelemetry, você precisa adicionar referências a vários pacotes NuGet.

dotnet add package OpenTelemetry --version 1.4.0-rc1
dotnet add package OpenTelemetry.Exporter.Console --version 1.4.0-rc1
dotnet add package OpenTelemetry.Extensions.Hosting --version 1.4.0-rc1
dotnet add package OpenTelemetry.Instrumentation.AspNetCore --version 1.0.0-rc9.10

Observação

No momento da redação deste artigo, o build 1 da versão Release Candidate 1.4.0 era a versão mais recente do OpenTelemetry disponível. Quando uma versão final estiver disponível, use-a.

Em seguida, modifique o código-fonte em Program.cs para que tenha esta aparência:

using OpenTelemetry;
using OpenTelemetry.Trace;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddOpenTelemetry()
    .WithTracing(builder =>
    {
        builder.AddAspNetCoreInstrumentation();
        builder.AddConsoleExporter();
    }).StartWithHost();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

Execute o aplicativo e use um navegador da Web para navegar até a página da Web que está sendo hospedada. Agora que habilitou o rastreamento distribuído do OpenTelemetry, você verá informações sobre as solicitações da Web do navegador impressas no console:

Activity.TraceId:            9c4519ce65a667280daedb3808d376f0
Activity.SpanId:             727c6a8a6cff664f
Activity.TraceFlags:         Recorded
Activity.ActivitySourceName: Microsoft.AspNetCore
Activity.DisplayName:        /
Activity.Kind:               Server
Activity.StartTime:          2023-01-08T01:56:05.4529879Z
Activity.Duration:           00:00:00.1048255
Activity.Tags:
    net.host.name: localhost
    net.host.port: 5163
    http.method: GET
    http.scheme: http
    http.target: /
    http.url: http://localhost:5163/
    http.flavor: 1.1
    http.user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.76
    http.status_code: 200
Resource associated with Activity:
    service.name: unknown_service:demo

Toda a configuração do OpenTelemetry ocorre nas novas linhas de código-fonte que começam com builder.Services.AddOpenTelemetry(). Você usou .WithTracing(...) para habilitar o rastreamento distribuído. O AddAspNetCoreInstrumentation() habilitou o OpenTelemetry a coletar todas as Atividades de rastreamento distribuído produzidas pelo servidor Web ASP.NET Core, e o AddConsoleExporter() instrui o OpenTelemetry a enviar essas informações para o console. Para um aplicativo menos trivial, você pode adicionar mais bibliotecas de instrumentação para também coletar rastreamento para consultas de banco de dados ou solicitações HTTP de saída. Você também substituiria o exportador do console por um exportador para Jaeger, Zipken ou outro serviço de monitoramento que escolheu usar.

Exemplo de aplicativo de console

Pré-requisitos

Criar um aplicativo de exemplo

Para que qualquer telemetria de rastreamento distribuído possa ser coletada, você precisa produzi-la. Geralmente, essa instrumentação está em bibliotecas, mas para simplificar, você criará um aplicativo pequeno com instrumentação de exemplo usando StartActivity. Neste ponto, nenhuma coleção aconteceu e StartActivity() não tem efeito colateral e retorna nulo. Confira o tutorial de instrumentação para obter mais detalhes.

dotnet new console

Aplicativos direcionados ao .NET 5 e posteriores já têm as APIs de rastreamento distribuído necessárias incluídas. Para aplicativos direcionados a versões mais antigas do .NET, adicione o pacote NuGet System.Diagnostics.DiagnosticSource versão 5 ou superior.

dotnet add package System.Diagnostics.DiagnosticSource

Substitua o conteúdo do arquivo Program.cs gerado pelo código de exemplo:

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace Sample.DistributedTracing
{
    class Program
    {
        static ActivitySource s_source = new ActivitySource("Sample.DistributedTracing");

        static async Task Main(string[] args)
        {
            await DoSomeWork();
            Console.WriteLine("Example work done");
        }

        static async Task DoSomeWork()
        {
            using (Activity a = s_source.StartActivity("SomeWork"))
            {
                await StepOne();
                await StepTwo();
            }
        }

        static async Task StepOne()
        {
            using (Activity a = s_source.StartActivity("StepOne"))
            {
                await Task.Delay(500);
            }
        }

        static async Task StepTwo()
        {
            using (Activity a = s_source.StartActivity("StepTwo"))
            {
                await Task.Delay(1000);
            }
        }
    }
}

A execução do aplicativo ainda não coleta nenhum dado de rastreamento:

> dotnet run
Example work done

Configurar a coleta

Adicione o pacote NuGet OpenTelemetry.Export.Console.

dotnet add package OpenTelemetry.Exporter.Console

Atualize o Program.cs com as diretivas using adicionais do OpenTelemetry:

using OpenTelemetry;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using System;
using System.Diagnostics;
using System.Threading.Tasks;

Atualize Main() para criar o TracerProvider do OpenTelemetry:

        public static async Task Main()
        {
            using var tracerProvider = Sdk.CreateTracerProviderBuilder()
                .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MySample"))
                .AddSource("Sample.DistributedTracing")
                .AddConsoleExporter()
                .Build();

            await DoSomeWork();
            Console.WriteLine("Example work done");
        }

Agora, o aplicativo coleta informações de rastreamento distribuídas e exibe-as no console:

> dotnet run
Activity.Id:          00-7759221f2c5599489d455b84fa0f90f4-6081a9b8041cd840-01
Activity.ParentId:    00-7759221f2c5599489d455b84fa0f90f4-9a52f72c08a9d447-01
Activity.DisplayName: StepOne
Activity.Kind:        Internal
Activity.StartTime:   2021-03-18T10:46:46.8649754Z
Activity.Duration:    00:00:00.5069226
Resource associated with Activity:
    service.name: MySample
    service.instance.id: 909a4624-3b2e-40e4-a86b-4a2c8003219e

Activity.Id:          00-7759221f2c5599489d455b84fa0f90f4-d2b283db91cf774c-01
Activity.ParentId:    00-7759221f2c5599489d455b84fa0f90f4-9a52f72c08a9d447-01
Activity.DisplayName: StepTwo
Activity.Kind:        Internal
Activity.StartTime:   2021-03-18T10:46:47.3838737Z
Activity.Duration:    00:00:01.0142278
Resource associated with Activity:
    service.name: MySample
    service.instance.id: 909a4624-3b2e-40e4-a86b-4a2c8003219e

Activity.Id:          00-7759221f2c5599489d455b84fa0f90f4-9a52f72c08a9d447-01
Activity.DisplayName: SomeWork
Activity.Kind:        Internal
Activity.StartTime:   2021-03-18T10:46:46.8634510Z
Activity.Duration:    00:00:01.5402045
Resource associated with Activity:
    service.name: MySample
    service.instance.id: 909a4624-3b2e-40e4-a86b-4a2c8003219e

Example work done
Origens

No código de exemplo, você invocou AddSource("Sample.DistributedTracing") para que o OpenTelemetry capturasse as atividades produzidas pelo ActivitySource que já estava presente no código:

static ActivitySource s_source = new ActivitySource("Sample.DistributedTracing");

A telemetria de qualquer ActivitySource pode ser capturada chamando AddSource() com o nome da origem.

Exportadores

O exportador de console é útil para exemplos rápidos ou desenvolvimento local, mas em uma implantação de produção, é melhor rastreamentos a um repositório centralizado. O OpenTelemetry dá suporte a vários destinos usando diferentes exportadores. Para obter mais informações de como configurar o OpenTelemetry, confira o guia de introdução ao OpenTelemetry.

Coletar rastreamentos usando o Application Insights

A telemetria de rastreamento distribuído é capturada automaticamente após a configuração do SDK do Application Insights para aplicativos ASP.NET ou ASP.NET Core ou a habilitação da instrumentação sem código.

Para obter mais informações, confira a documentação de rastreamento distribuído do Application Insights.

Observação

No momento, o Application Insights dá suporte apenas à coleta de instrumentação de atividade conhecida específica e ignora novas atividades adicionadas pelo usuário. O Application Insights oferece o TrackDependency como uma API específica do fornecedor para adicionar informações de rastreamento distribuído personalizadas.

Coletar rastreamentos usando a lógica personalizada

Os desenvolvedores são livres para criar a própria lógica de coleta personalizada para dados de rastreamento de atividade. Este exemplo coleta a telemetria usando a API System.Diagnostics.ActivityListener fornecida pelo .NET e imprime-a no console.

Pré-requisitos

Criar um aplicativo de exemplo

Primeiro, você criará um aplicativo de exemplo que com uma instrumentação de rastreamento distribuído, mas sem dados de rastreamento sendo coletados.

dotnet new console

Aplicativos direcionados ao .NET 5 e posteriores já têm as APIs de rastreamento distribuído necessárias incluídas. Para aplicativos direcionados a versões mais antigas do .NET, adicione o pacote NuGet System.Diagnostics.DiagnosticSource versão 5 ou superior.

dotnet add package System.Diagnostics.DiagnosticSource

Substitua o conteúdo do arquivo Program.cs gerado pelo código de exemplo:

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace Sample.DistributedTracing
{
    class Program
    {
        static ActivitySource s_source = new ActivitySource("Sample.DistributedTracing");

        static async Task Main(string[] args)
        {
            await DoSomeWork();
            Console.WriteLine("Example work done");
        }

        static async Task DoSomeWork()
        {
            using (Activity a = s_source.StartActivity("SomeWork"))
            {
                await StepOne();
                await StepTwo();
            }
        }

        static async Task StepOne()
        {
            using (Activity a = s_source.StartActivity("StepOne"))
            {
                await Task.Delay(500);
            }
        }

        static async Task StepTwo()
        {
            using (Activity a = s_source.StartActivity("StepTwo"))
            {
                await Task.Delay(1000);
            }
        }
    }
}

A execução do aplicativo ainda não coleta nenhum dado de rastreamento:

> dotnet run
Example work done

Adicionar código para coletar os rastreamentos

Atualize Main() com este código:

        static async Task Main(string[] args)
        {
            Activity.DefaultIdFormat = ActivityIdFormat.W3C;
            Activity.ForceDefaultIdFormat = true;

            Console.WriteLine("         {0,-15} {1,-60} {2,-15}", "OperationName", "Id", "Duration");
            ActivitySource.AddActivityListener(new ActivityListener()
            {
                ShouldListenTo = (source) => true,
                Sample = (ref ActivityCreationOptions<ActivityContext> options) => ActivitySamplingResult.AllDataAndRecorded,
                ActivityStarted = activity => Console.WriteLine("Started: {0,-15} {1,-60}", activity.OperationName, activity.Id),
                ActivityStopped = activity => Console.WriteLine("Stopped: {0,-15} {1,-60} {2,-15}", activity.OperationName, activity.Id, activity.Duration)
            });

            await DoSomeWork();
            Console.WriteLine("Example work done");
        }

A saída agora inclui o registro em log:

> dotnet run
         OperationName   Id                                                           Duration
Started: SomeWork        00-bdb5faffc2fc1548b6ba49a31c4a0ae0-c447fb302059784f-01
Started: StepOne         00-bdb5faffc2fc1548b6ba49a31c4a0ae0-a7c77a4e9a02dc4a-01
Stopped: StepOne         00-bdb5faffc2fc1548b6ba49a31c4a0ae0-a7c77a4e9a02dc4a-01      00:00:00.5093849
Started: StepTwo         00-bdb5faffc2fc1548b6ba49a31c4a0ae0-9210ad536cae9e4e-01
Stopped: StepTwo         00-bdb5faffc2fc1548b6ba49a31c4a0ae0-9210ad536cae9e4e-01      00:00:01.0111847
Stopped: SomeWork        00-bdb5faffc2fc1548b6ba49a31c4a0ae0-c447fb302059784f-01      00:00:01.5236391
Example work done

A configuração de DefaultIdFormat e ForceDefaultIdFormat é opcional, mas ajuda a garantir que o exemplo produza uma saída semelhante em diferentes versões de runtime do .NET. O .NET 5 usa o formato de ID de TraceContext do W3C por padrão, mas nas versões anteriores do .NET, o padrão é usar o formato de ID Hierarchical. Para obter mais informações, confira IDs de atividade.

System.Diagnostics.ActivityListener é usado para receber retornos de chamada durante o tempo de vida de uma atividade.

  • ShouldListenTo – Cada atividade está associada a um ActivitySource, que atua como o namespace e o produtor. Esse retorno de chamada é invocado uma vez para cada ActivitySource no processo. Retorne true se quiser executar o exemplo ou receber uma notificação sobre eventos de início/parada de atividades produzidas por essa origem.
  • Sample – Por padrão, StartActivity não cria um objeto Activity, a menos que algum ActivityListener indique que ele deve ser amostrado. O retorno de AllDataAndRecorded indica que a atividade deve ser criada, IsAllDataRequested deve ser definido como true e ActivityTraceFlags terá o sinalizador Recorded definido. IsAllDataRequested pode ser observado pelo código instrumentado como uma dica de que um ouvinte quer garantir que as informações auxiliares da atividade, como Marcas e Eventos, sejam preenchidas. O sinalizador Recorded é codificado na ID de TraceContext do W3C e é uma dica para outros processos envolvidos no rastreamento distribuído de que esse rastreamento deve ser amostrado.
  • ActivityStarted e ActivityStopped são chamados quando uma atividade é iniciada e interrompida, respectivamente. Esses retornos de chamada oferecem uma oportunidade de registrar informações relevantes sobre a atividade ou até modificá-la. Quando uma atividade acaba de ser iniciada, grande parte dos dados ainda pode estar incompleta e ser populada antes que a atividade pare.

Depois que um ActivityListener é criado e os retornos de chamada são populados, a chamada a ActivitySource.AddActivityListener(ActivityListener) inicia a invocação dos retornos de chamada. Chame ActivityListener.Dispose() para interromper o fluxo de retornos de chamada. Lembre-se de que, no código com vários threads, as notificações de retorno de chamada em andamento podem ser recebidas durante a execução de Dispose() ou até mesmo logo após o retorno.