Поделиться через


Сбор распределенной трассировки

Эта статья применяется к следующему. ✔️ .NET Core 2.1 и более поздних версий ✔️.NET Framework 4.5 и более поздних версий

Инструментированный код может создавать объекты Activity в рамках распределенной трассировки, но сведения в этих объектах необходимо собирать в централизованное хранилище, чтобы позже можно было изучить всю трассировку. В этом учебнике данные телеметрии распределенной трассировки будут собраны различными способами, чтобы они были доступными для диагностики проблем приложения в случае необходимости. Если необходимо добавить новое инструментирование, см. учебник по инструментированию.

Сбор трассировок с помощью OpenTelemetry

OpenTelemetry — это независимый от поставщика проект с открытым исходным кодом, поддерживаемый фондом Cloud Native Computing Foundation, который стремится стандартизировать создание и сбор данных телеметрии для ориентированного на облако программного обеспечения. В этих примерах вы будете собирать и отображать сведения распределенной трассировки в консоли. Сведения о том, как настроить OpenTelemetry для отправки информации в другое место, см. в руководстве по началу работы с OpenTelemetry.

пример ASP.NET

Предварительные требования

Создание примера приложения

Сначала создайте веб-приложение ASP.NET для использования в качестве демонстрационного приложения.

dotnet new webapp

Это приложение отображает веб-страницу, но при просмотре веб-страницы данные распределенной трассировки еще не собираются.

Настройка коллекции

Чтобы использовать OpenTelemetry, необходимо добавить ссылки на несколько пакетов 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

Примечание

На момент написания статьи сборка 1.4.0 Release Candidate 1 была последней доступной версией OpenTelemetry. После того как окончательная версия будет доступна, используйте ее.

Затем измените исходный код в Program.cs , чтобы он выглядел следующим образом:

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

Запустите приложение и используйте веб-браузер для перехода на размещенную веб-страницу. Теперь, когда вы включили распределенную трассировку OpenTelemetry, вы увидите сведения о веб-запросах браузера, выводимые на консоль:

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

Вся конфигурация OpenTelemetry выполняется в новых строках исходного кода, которые начинаются с builder.Services.AddOpenTelemetry(). Вы использовали для .WithTracing(...) включения распределенной трассировки. AddAspNetCoreInstrumentation()включил OpenTelemetry для сбора всех распределенных действий трассировки, создаваемых веб-сервером ASP.NET Core, и AddConsoleExporter() указывает OpenTelemetry отправить эти сведения в консоль. Для менее тривиального приложения можно добавить дополнительные библиотеки инструментирования, чтобы также собирать трассировку запросов к базе данных или исходящих HTTP-запросов. Вы также замените средство экспорта консоли экспортом для Jaeger, Zipken или другой выбранной службы мониторинга.

Пример консольного приложения

Предварительные требования

Создание примера приложения

Перед сбором данных телеметрии распределенной трассировки их необходимо создать. Часто это инструментирование выполняется в библиотеках, но для простоты вы создадите небольшое приложение с примером инструментирования с помощью StartActivity. На этом этапе сбор еще не происходит, и StartActivity() не имеет побочных эффектов и возвращает значение NULL. Дополнительные сведения см. в учебнике по инструментированию.

dotnet new console

В приложения, предназначенные для .NET 5 и более поздних версий, уже включены необходимые API распределенной трассировки. Для приложений, предназначенных для более старых версий .NET, добавьте пакет NuGet System.Diagnostics.DiagnosticSource версии 5 или более поздней.

dotnet add package System.Diagnostics.DiagnosticSource

Замените содержимое созданного файла Program.cs исходным кодом примера.

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

При запуске приложения данные трассировки еще не собираются:

> dotnet run
Example work done

Настройка коллекции

Добавьте пакет NuGet OpenTelemetry.Exporter.Console.

dotnet add package OpenTelemetry.Exporter.Console

Измените файл Program.cs с использованием дополнительных директив using OpenTelemetry.

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

Измените Main(), чтобы создать 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");
        }

Теперь приложение собирает сведения распределенной трассировки и отображает их в консоли.

> 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
Источники

В примере кода вы вызвали AddSource("Sample.DistributedTracing"), поэтому OpenTelemetry будет захватывать действия, созданные ActivitySource, который уже присутствовал в коде.

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

Данные телеметрии из любого ActivitySource могут быть захвачены путем вызова AddSource() с именем источника.

Средства экспорта

Средство экспорта консоли удобно для простых примеров или локальной разработки, но в рабочей среде вы, вероятно, захотите отправить трассировки в централизованное хранилище. OpenTelemetry поддерживает различные назначения, используя разные средства экспорта. Дополнительные сведения о настройке OpenTelemetry см. в руководстве по началу работы с OpenTelemetry.

Сбор трассировок с помощью Application Insights

Данные телеметрии распределенной трассировки автоматически собираются после настройки пакета SDK для Application Insights для приложений ASP.NET или ASP.NET Core либо при включении инструментирования без кода.

Дополнительные сведения см. в документации по распределенной трассировке Application Insights.

Примечание

Сейчас Application Insights поддерживает только сбор инструментирования определенных и известных действий и игнорирует новые действия, добавленные пользователем. Application Insights предоставляет TrackDependency в качестве API поставщика для добавления настраиваемых данных распределенной трассировки.

Сбор трассировок с помощью пользовательской логики

Разработчики могут создавать собственную пользовательскую логику сбора для данных трассировки действий. Этот пример собирает данные телеметрии с помощью API System.Diagnostics.ActivityListener, предоставляемого .NET, и выводит их в консоль.

Предварительные требования

Создание примера приложения

Сначала вы создадите пример приложения, в котором есть инструментирование распределенной трассировки, но данные трассировки собираться не будут.

dotnet new console

В приложения, предназначенные для .NET 5 и более поздних версий, уже включены необходимые API распределенной трассировки. Для приложений, предназначенных для более старых версий .NET, добавьте пакет NuGet System.Diagnostics.DiagnosticSource версии 5 или более поздней.

dotnet add package System.Diagnostics.DiagnosticSource

Замените содержимое созданного файла Program.cs исходным кодом примера.

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

При запуске приложения данные трассировки еще не собираются:

> dotnet run
Example work done

Добавление кода для сбора трассировок

Измените Main() с использованием следующего кода.

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

Теперь выходные данные включают в себя ведение журнала.

> 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

Параметры DefaultIdFormat и ForceDefaultIdFormat являются необязательными, но помогают убедиться, что пример создает аналогичные выходные данные в разных версиях среды выполнения .NET. .NET 5 по умолчанию использует формат идентификатора W3C TraceContext, но более ранние версии .NET по умолчанию используют формат идентификатора Hierarchical. Дополнительные сведения см. в разделе ИД действий.

System.Diagnostics.ActivityListener используется для получения обратных вызовов в течение времени существования действия.

  • ShouldListenTo — каждое действие связано с ActivitySource, который выступает в качестве его пространства имен и производителя. Этот обратный вызов выполняется один раз для каждого ActivitySource в процессе. Вы можете возвратить значение true, если заинтересованы в выполнении выборки или получении уведомлений о событиях запуска и остановки для действий, создаваемых этим источником.
  • Sample — по умолчанию StartActivity не создает объект действия, если только какой-либо ActivityListener не указывает на необходимость выборки. Возвращается значение , указывающее AllDataAndRecorded , что действие должно быть создано, IsAllDataRequested для него должно быть задано значение true и ActivityTraceFlags будет Recorded установлен флаг . IsAllDataRequested может рассматриваться инструментированным кодом как указание, что прослушиватель должен гарантировать заполнение дополнительных сведений о действии, таких как теги и события. Записанный флаг кодируется в идентификаторе W3C TraceContext и является указанием для других процессов, участвующих в распределенной трассировке, о необходимости выборки этой трассировки.
  • ActivityStarted и ActivityStopped вызываются при запуске и остановке действия соответственно. Эти обратные вызовы позволяют записать релевантные сведения о действии или, возможно, изменить их. Если действие было запущено только что, большая часть данных может оставаться неполной и будет заполнена до остановки действия.

После создания ActivityListener и заполнения ActivitySource.AddActivityListener(ActivityListener) обратных вызовов инициирует вызов обратных вызовов. Вызовите ActivityListener.Dispose(), чтобы прерывать последовательность обратных вызовов. Учтите, что в ходе обратного вызова многопоточного кода уведомления о выполнении могут быть получены при выполнении Dispose() или даже вскоре после возврата им данных.