Recopilación de un seguimiento distribuido

Este artículo se aplica a: ✔️.NET Core 2.1 y versiones posteriores ✔️ .NET Framework 4.5 y versiones posteriores

El código instrumentado puede crear objetos Activity como parte de un seguimiento distribuido, pero es necesario recopilar la información de estos objetos en un almacenamiento centralizado para poder revisar después todo el seguimiento. En este tutorial, recopilará la telemetría de seguimiento distribuido de maneras diferentes, a fin de que esté disponible para diagnosticar problemas de la aplicación cuando sea necesario. Vea el tutorial sobre instrumentación si necesita agregar una nueva instrumentación.

Recopilación de seguimientos mediante OpenTelemetry

OpenTelemetry es un proyecto de código abierto independiente del proveedor admitido por Cloud Native Computing Foundation que pretende estandarizar la generación y la recopilación de telemetría para software nativo de nube. En estos ejemplos, recopilará y mostrará información sobre el seguimiento distribuido en la consola. Si desea aprender a configurar OpenTelemetry para enviar información a otro lugar, consulte la guía de introducción a OpenTelemetry.

Ejemplo de ASP.NET

Prerrequisitos

Creación de una aplicación de ejemplo

En primer lugar, cree una aplicación web de ASP.NET para usarla como aplicación de demostración.

dotnet new webapp

Esta aplicación muestra una página web, pero aún no se recopila información de seguimiento distribuido si examinamos la página web.

Configuración de la colección

Para usar OpenTelemetry, debe agregar referencias a varios paquetes 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

En el momento de redactar este artículo, la compilación 1.4.0 RC 1 era la versión disponible de OpenTelemetry más reciente. Use en su lugar una versión final una vez que esté disponible.

A continuación, modifique el código fuente en Program.cs para que tenga este aspecto:

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

Ejecute la aplicación y use un explorador web para ir a la página web que se hospeda. Ahora que habilitó el seguimiento distribuido de OpenTelemetry, debería ver información sobre las solicitudes web del explorador impresas en la consola:

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 la configuración de OpenTelemetry se produce en las líneas de origen nuevas que empiezan con builder.Services.AddOpenTelemetry(). Usó .WithTracing(...) para habilitar el seguimiento distribuido. AddAspNetCoreInstrumentation() habilitó OpenTelemetry para recopilar todas las actividades de seguimiento distribuido generadas por el servidor web de ASP.NET Core, y AddConsoleExporter() indica a OpenTelemetry que envíe esa información a la consola. Para una aplicación menos trivial, puede agregar más bibliotecas de instrumentación a fin de recopilar también el seguimiento de las consultas de base de datos o las solicitudes HTTP salientes. También podría reemplazar el exportador de la consola por un exportador de Jaeger, Zipken u otro servicio de supervisión que haya elegido usar.

Ejemplo de aplicación de consola

Prerrequisitos

Creación de una aplicación de ejemplo

Para que se pueda recopilar la telemetría de seguimiento distribuido, es necesario generarla. A menudo, esta instrumentación está en bibliotecas, pero, por simplicidad, creará una aplicación pequeña que tiene alguna instrumentación de ejemplo con StartActivity. En este momento, todavía no se ha producido ninguna recopilación, StartActivity() no tiene ningún efecto secundario y devuelve NULL. Vea el tutorial sobre instrumentación para obtener más información.

dotnet new console

Las aplicaciones destinadas a .NET 5 y versiones posteriores ya tienen las API de seguimiento distribuido necesarias. En el caso de las aplicaciones destinadas a versiones anteriores de .NET, agregue el paquete NuGet System.Diagnostics.DiagnosticSource, versión 5 o superior.

dotnet add package System.Diagnostics.DiagnosticSource

Reemplace el contenido del archivo Program.cs generado con este código de ejemplo:

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

La ejecución de la aplicación todavía no recopila ningún dato de seguimiento:

> dotnet run
Example work done

Configuración de la colección

Agregue el paquete NuGet OpenTelemetry.Exporter.Console.

dotnet add package OpenTelemetry.Exporter.Console

Actualice Program.cs con directivas usingadicionales de OpenTelemetry:

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

Actualice Main() para crear el objeto TracerProvider de 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");
        }

Ahora la aplicación recopila información de seguimiento distribuido y la muestra en la consola:

> 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
Orígenes

En el código de ejemplo, ha invocado AddSource("Sample.DistributedTracing") para que OpenTelemetry capture las actividades generadas por el objeto ActivitySource que ya estaba presente en el código:

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

La telemetría de cualquier objeto ActivitySource se puede capturar si se llama a AddSource() con el nombre del origen.

Exportadores

El exportador de la consola es útil para obtener ejemplos rápidos o para el desarrollo local, pero en una implementación de producción probablemente querrá enviar los seguimientos a un almacén centralizado. OpenTelemetry admite varios destinos mediante distintos exportadores. Para obtener más información sobre la configuración de OpenTelemetry, vea la Guía de introducción a OpenTelemetry.

Recopilación de seguimientos con Application Insights

La telemetría de seguimiento distribuido se captura de forma automática después de configurar el SDK de Application Insights para aplicaciones ASP.NET o ASP.NET Core, o bien si se habilita la instrumentación sin código.

Para obtener más información, vea la documentación de seguimiento distribuido de Application Insights.

Nota

En la actualidad, Application Insights solo admite la recopilación de instrumentación de actividades conocidas específicas y omite las nuevas actividades agregadas por el usuario. Application Insights ofrece TrackDependency como una API específica del proveedor para agregar información de seguimiento distribuido personalizada.

Recopilación de seguimientos mediante lógica personalizada

Los desarrolladores pueden crear lógica de recopilación personalizada propia para los datos de seguimiento de las actividades. En este ejemplo se recopila la telemetría mediante la API System.Diagnostics.ActivityListener proporcionada por .NET y se imprime en la consola.

Prerrequisitos

Creación de una aplicación de ejemplo

Primero creará una aplicación de ejemplo que tiene cierta instrumentación de seguimiento distribuido, pero no se recopilan datos de seguimiento.

dotnet new console

Las aplicaciones destinadas a .NET 5 y versiones posteriores ya tienen las API de seguimiento distribuido necesarias. En el caso de las aplicaciones destinadas a versiones anteriores de .NET, agregue el paquete NuGet System.Diagnostics.DiagnosticSource, versión 5 o superior.

dotnet add package System.Diagnostics.DiagnosticSource

Reemplace el contenido del archivo Program.cs generado con este código de ejemplo:

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

La ejecución de la aplicación todavía no recopila ningún dato de seguimiento:

> dotnet run
Example work done

Adición de código para recopilar los seguimientos

Actualice Main() con 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");
        }

La salida ahora incluye el registro:

> 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

El establecimiento de DefaultIdFormat y ForceDefaultIdFormat es opcional, pero ayuda a garantizar que el ejemplo genere una salida similar en diferentes versiones del entorno de ejecución de .NET. En .NET 5 se usa el formato de identificador TraceContext de W3C de forma predeterminada, pero en versiones anteriores de .NET se utiliza Hierarchical de forma predeterminada. Para obtener más información, vea Identificadores de actividad.

System.Diagnostics.ActivityListener se usa para recibir devoluciones de llamada durante la vigencia de una actividad.

  • ShouldListenTo: cada actividad tiene un objeto ActivitySource asociado que actúa como su espacio de nombres y productor. Esta devolución de llamada se invoca una vez para cada objeto ActivitySource en el proceso. Devuelva true si está interesado en realizar el muestreo o recibir notificaciones sobre los eventos de inicio y detención de las actividades generadas por este origen.
  • Sample: de forma predeterminada, StartActivity no crea un objeto Activity a menos que alguna instancia de ActivityListener indique que se debe muestrear. La devolución de AllDataAndRecorded indica que se debe crear la actividad, que IsAllDataRequested se debe establecer en true y que ActivityTraceFlags tendrá establecida la marca Recorded. El código instrumentado puede observar IsAllDataRequested como una sugerencia de que un cliente de escucha quiere asegurarse de que se rellena información de actividad auxiliar, como etiquetas y eventos. La marca Recorded se codifica en el identificador TraceContext de W3C y es una sugerencia para otros procesos implicados en el seguimiento distribuido de que se debe realizar el muestreo de este seguimiento.
  • Se llama a ActivityStarted y ActivityStopped cuando se inicia y se detiene una actividad, respectivamente. Estas devoluciones de llamada proporcionan una oportunidad para registrar información pertinente sobre la actividad o, potencialmente, para modificarla. Cuando una actividad se acaba de iniciar, es posible que gran parte de los datos todavía estén incompletos y se rellenarán antes de que se detenga la actividad.

Una vez que se ha creado un objeto ActivityListener y se han rellenado las devoluciones de llamada, la llamada a ActivitySource.AddActivityListener(ActivityListener) inicia la invocación de las devoluciones de llamada. Llame ActivityListener.Dispose() a para detener el flujo de devoluciones de llamada. Tenga en cuenta que, en el código multiproceso, se pueden recibir notificaciones de devolución de llamada en curso mientras se ejecuta Dispose(), o incluso poco después de que se haya devuelto.