Raccogliere una traccia distribuita

Questo articolo si applica a: ✔️ .NET Core 2.1 e versioni successive ✔️ .NET Framework 4.5 e versioni successive

Il codice instrumentato può creare oggetti Activity come parte di una traccia distribuita, ma le informazioni contenute in questi oggetti devono essere raccolte nell'archiviazione centralizzata, in modo che l'intera traccia possa essere esaminata in un secondo momento. In questa esercitazione, raccoglierai i dati di telemetria della traccia distribuita in modi diversi affinché sia disponibile per diagnosticare i problemi dell'applicazione quando necessario. Vedi l'esercitazione sulla strumentazione se hai la necessità di aggiungere una nuova strumentazione.

Raccogliere tracce con OpenTelemetry

OpenTelemetry è un progetto open source indipendente dal fornitore supportato dalla Cloud Native Computing Foundation che mira a standardizzare la generazione e la raccolta di dati di telemetria per il software nativo del cloud. In questi esempi, raccoglierai e visualizzerai informazioni sulla traccia distribuita nella console. Per scoprire come configurare OpenTelemetry per l'invio di informazioni altrove, vedi la guida introduttiva di OpenTelemetry.

Esempio di ASP.NET

Prerequisiti

Crea un’applicazione di esempio

Crea prima di tutto una nuova app Web ASP.NET da usare come applicazione demo.

dotnet new webapp

Questa app mostra una pagina Web, ma se vi navighiamo vediamo che non vengono raccolte informazioni sulla traccia distribuita.

Configura la raccolta

Per usare OpenTelemetry, è necessario aggiungere riferimenti a diversi pacchetti 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

Nota

Al momento della scrittura, la build 1.4.0 versione finale candidata 1 era la versione più recente di OpenTelemetry disponibile. Una volta disponibile una versione finale, usa invece tale versione.

Modifica quindi il codice sorgente in Program.cs in modo che sia simile al seguente:

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();

Esegui l'app e usa un Web browser per passare alla pagina Web ospitata. Ora che hai abilitato la traccia distribuita OpenTelemetry, dovresti vedere informazioni sulle richieste Web del browser stampate nella 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

Tutte le configurazioni OpenTelemetry si trovano nelle nuove righe di origine che iniziano con builder.Services.AddOpenTelemetry(). Hai usato .WithTracing(...) per abilitare la traccia distribuita. AddAspNetCoreInstrumentation() abilitato OpenTelemetry per raccogliere tutte le attività di traccia distribuite generate dal server Web ASP.NET Core e AddConsoleExporter() indica a OpenTelemetry di inviare tali informazioni alla console. Per un'app meno semplice, puoi aggiungere altre librerie di strumentazione per raccogliere anche la traccia delle query di database o le richieste HTTP in uscita. Puoi anche sostituire l'utilità di esportazione della console con un utilità di esportazione per Jaeger, Zipken o un altro servizio di monitoraggio che hai scelto di usare.

Esempio di app console

Prerequisiti

Crea un’applicazione di esempio

Prima di poter raccogliere i dati di telemetria della traccia distribuita, è necessario produrla. Spesso questa strumentazione si trova nelle librerie, ma per semplicità creerai una piccola app che include una strumentazione di esempio usando StartActivity. A questo punto, non è stata eseguita alcuna raccolta e StartActivity() non ha alcun effetto collaterale e restituisce Null. Per altri dettagli, vedi l'esercitazione sulla strumentazione.

dotnet new console

Le applicazioni destinate a .NET 5 e versioni successive includono già le API di traccia distribuita necessarie. Per le app destinate a versioni precedenti di .NET, aggiungi il pacchetto NuGet System.Diagnostics.DiagnosticSource versione 5 o successiva.

dotnet add package System.Diagnostics.DiagnosticSource

Sostituisci il contenuto del Program.cs generato con l'origine di esempio seguente:

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);
            }
        }
    }
}

L'esecuzione dell'app non raccoglie ancora dati sulla traccia:

> dotnet run
Example work done

Configura la raccolta

Aggiungi il pacchetto NuGet OpenTelemetry.Exporter.Console.

dotnet add package OpenTelemetry.Exporter.Console

Aggiorna Program.cs con direttive using OpenTelemetry aggiuntive:

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

Aggiorna Main() per creare il TracerProvider 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");
        }

L'app ora raccoglie le informazioni sulla traccia distribuita e le mostra nella 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
Origini

Nel codice di esempio hai richiamato AddSource("Sample.DistributedTracing") in modo che OpenTelemetry acquisisca le attività prodotte da ActivitySource già presenti nel codice:

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

I dati di telemetria di qualsiasi ActivitySource possono essere acquisiti chiamando AddSource() con il nome dell'origine.

Esportatori

L'utilità di esportazione della console è utile per esempi rapidi o sviluppo locale, ma in una distribuzione di produzione è probabile che si voglia inviare tracce a un archivio centralizzato. OpenTelemetry supporta varie destinazioni usando diversi strumenti di esportazione. Per altre informazioni sulla configurazione di OpenTelemetry, vedi la guida introduttiva di OpenTelemetry.

Raccogliere tracce con Application Insights

I dati di telemetria della traccia distribuita vengono acquisiti automaticamente dopo aver configurato l’SDK Application Insights per le app ASP.NET o ASP.NET Core oppure abilitando la strumentazione senza codice.

Per altre informazioni, vedi la documentazione sulla traccia distribuita di Application Insights.

Nota

Attualmente, Application Insights supporta solo la raccolta di una strumentazione attività nota specifica e ignora le nuove attività aggiunte dall'utente. Application Insights offre TrackDependency come API specifica del fornitore per l'aggiunta di informazioni sulla traccia distribuita personalizzate.

Raccogliere tracce usando una logica personalizzata

Gli sviluppatori sono liberi di creare la propria logica di raccolta personalizzata per i dati sulle tracce delle attività. Questo esempio raccoglie i dati di telemetria usando l'API System.Diagnostics.ActivityListener fornita da .NET e li stampa nella console.

Prerequisiti

Crea un’applicazione di esempio

Prima di tutto creerai un'applicazione di esempio che dispone di una strumentazione di traccia distribuita, ma non vengono raccolti dati sulla traccia.

dotnet new console

Le applicazioni destinate a .NET 5 e versioni successive includono già le API di traccia distribuita necessarie. Per le app destinate a versioni precedenti di .NET, aggiungi il pacchetto NuGet System.Diagnostics.DiagnosticSource versione 5 o successiva.

dotnet add package System.Diagnostics.DiagnosticSource

Sostituisci il contenuto del Program.cs generato con l'origine di esempio seguente:

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);
            }
        }
    }
}

L'esecuzione dell'app non raccoglie ancora dati sulla traccia:

> dotnet run
Example work done

Aggiungi codice per raccogliere le tracce

Aggiorna Main() con questo codice:

        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");
        }

L'output ora include la registrazione:

> 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

L'impostazione DefaultIdFormat e è ForceDefaultIdFormat facoltativa, ma garantisce che l'esempio produca un output simile in versioni di runtime .NET diverse. .NET 5 usa il formato ID W3C TraceContext per impostazione predefinita, ma per impostazione predefinita le versioni precedenti di .NET usano il formato ID Hierarchical. Per altre informazioni, vedi ID attività.

System.Diagnostics.ActivityListener viene usato per ricevere callback per la durata di un'attività.

  • ShouldListenTo: Ogni attività è associata a un ActivitySource, che funge da spazio dei nomi e producer. Questo callback viene richiamato una volta per ogni ActivitySource nel processo. Restituire true se si è interessati a eseguire il campionamento o a ricevere una notifica sugli eventi di avvio/arresto per le attività generate da questa origine.
  • Sample: Per impostazione predefinita StartActivity non crea un oggetto Attività a meno che non sia necessario campionare alcuni ActivityListener. Restituire AllDataAndRecorded indica che l'attività deve essere creata, IsAllDataRequested deve essere impostata su true e ActivityTraceFlags avrà il flag Recorded impostato. Osservare IsAllDataRequested dal codice instrumentato può significare che un listener vuole garantire che le informazioni sulle attività ausiliarie, ad esempio Tag ed Eventi, vengano popolate. Il flag Registrato è codificato nell'ID TraceContext W3C ed è un suggerimento per altri processi coinvolti nella traccia distribuita che deve essere campionata.
  • ActivityStarted e ActivityStopped vengono chiamati rispettivamente all'avvio e all'arresto di un'attività. Questi callback offrono l'opportunità di registrare informazioni rilevanti sull'attività o potenzialmente modificarle. Quando un'attività è appena stata avviata, la maggior parte dei dati potrebbe essere ancora incompleta e verrà popolata prima dell'arresto dell'attività.

Dopo aver creato un ActivityListener e aver popolato i callback, la chiamata a ActivitySource.AddActivityListener(ActivityListener) avvia l’invocazione dei callback. Chiama ActivityListener.Dispose() per arrestare il flusso dei callback. Tieni presente che nel codice multithread le notifiche di callback in corso potrebbero essere ricevute durante l'esecuzione di Dispose() o anche poco dopo la restituzione.