收集分布式跟踪

2022-01-07 08:39:05 浏览数 (1)

本文适用范围:✔️ .NET Core 2.1 及更高版本 ✔️ .NET Framework 4.5 及更高版本

检测代码可以创建 Activity 对象作为分布式跟踪的一部分,但需要将这些对象中的信息收集到集中存储中,以便稍后可以查看整个跟踪。 本教程将以不同的方式收集分布式跟踪遥测,以便在需要时可用于诊断应用程序问题。 如果需要添加新的检测,请参阅检测教程。

使用 OpenTelemetry 收集跟踪

先决条件

.NET Core 2.1 SDK 或更高版本

创建一个示例应用程序

我们需要先生成分布式跟踪遥测,然后才能进行收集。 通常,此检测可能位于库中,但为简单起见,将使用 StartActivity 创建一个小型应用并在其中包含一些示例检测。 此时,尚未进行任何收集,StartActivity() 没有副作用,并且返回 null。 有关详细信息,请参阅检测教程。

dotnet new console

面向 .NET 5 及更高版本的应用程序已包含必要的分布式跟踪 API。 对于面向早期 .NET 版本的应用,请添加 System.Diagnostics.DiagnosticSource NuGet 包版本 5 或更高版本。

dotnet add package System.Diagnostics.DiagnosticSource

将生成的 Program.cs 内容替换为此示例源:

代码语言:javascript复制
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);
            }
        }
    }
}

运行应用时暂时不会收集任何跟踪数据:

代码语言:javascript复制
> dotnet run
Example work done

使用 OpenTelemetry 收集

OpenTelemetry 是由云本机计算基础支持的与供应商无关的开源项目,旨在标准化为云原生软件生成和收集遥测的过程。 此示例将收集和显示控制台上的分布式跟踪信息,但可以重新配置 OpenTelemetry 以将其发送到其他位置。 有关详细信息,请参阅 OpenTelemetry 入门指南。

添加 OpenTelemetry.Exporter.Console NuGet 包。

dotnet add package OpenTelemetry.Exporter.Console

使用其他 OpenTelemetry using 指令更新 Program.cs:

代码语言:javascript复制
using OpenTelemetry;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using System;
using System.Diagnostics;
using System.Threading.Tasks;

更新 Main() 以创建 OpenTelemetry TracerProvider:

代码语言:javascript复制
        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");
        }

现在,应用将收集分布式跟踪信息,并将其显示到控制台:

代码语言:javascript复制
> 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 所生成的 Activity:

代码语言:javascript复制
static ActivitySource s_source = new ActivitySource("Sample.DistributedTracing");

可以通过使用源名称调用 AddSource() 来捕获任何 ActivitySource 中的遥测。

导出工具

控制台导出工具对简单的示例或本地开发非常有用,但在生产部署中,可能需要将跟踪发送到集中存储。 OpenTelemetry 支持使用不同导出工具的各种目标。

有关如何配置 OpenTelemetry 的详细信息,请参阅 OpenTelemetry 入门指南。

使用 Application Insights 收集跟踪

为 ASP.NET 或 ASP.NET Core 应用配置 Application Insights SDK 或者启用无代码检测后,系统会自动捕获分布式跟踪遥测。

有关详细信息,请参阅 Application Insights 分布式跟踪文档。

备注

目前,Application Insights 仅支持收集特定的已知 Activity 检测,并忽略新用户添加的 Activity。 Application Insights 提供 TrackDependency 作为供应商特定的 API,用于添加自定义分布式跟踪信息。

使用自定义逻辑收集跟踪

开发人员可随意为活动跟踪数据创建自定义收集逻辑。 此示例使用 .NET 提供的 System.Diagnostics.ActivityListener API 收集遥测数据,并将其输出到控制台。

先决条件

.NET Core 2.1 SDK 或更高版本

创建一个示例应用程序

首先将创建一个示例应用程序,并在其中包含一些分布式跟踪检测,但未收集任何跟踪数据。

代码语言:javascript复制
dotnet new console

面向 .NET 5 及更高版本的应用程序已包含必要的分布式跟踪 API。 对于面向早期 .NET 版本的应用,请添加 System.Diagnostics.DiagnosticSource NuGet 包版本 5 或更高版本。

dotnet add package System.Diagnostics.DiagnosticSource

将生成的 Program.cs 内容替换为此示例源:

代码语言:javascript复制
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);
            }
        }
    }
}

运行应用时暂时不会收集任何跟踪数据:

代码语言:javascript复制
> 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");
        }

现在,输出包括日志记录:

代码语言:javascript复制
> 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 ID 格式,但早期的 .NET 版本默认使用 Hierarchical ID 格式。 有关详细信息,请参阅 Activity ID。

System.Diagnostics.ActivityListener 用于在活动的生存期内接收回调。

ShouldListenTo - 每个 Activity 都与 ActivitySource 关联,后者可充当 Activity 的命名空间和生成方。

对于进程中的每个 ActivitySource,都会调用一次此回调。 如果你有兴趣执行采样或收到有关此源产生的活动的启动/停止事件的通知,则返回 true。

Sample - 默认情况下,StartActivity 不会创建 Activity 对象,除非某个 ActivityListener 指示应对其进行采用。 返回 AllDataAndRecorded

表示应创建活动,IsAllDataRequested 应设置为 true,并且 ActivityTraceFlags 将设置 Recorded 标志。 IsAllDataRequested 可被检测代码观测到,这提示侦听器需要确保填充辅助 Activity 信息(如标记和事件)。

记录的标志在 W3C TraceContext ID 中进行编码,暗示分布式跟踪中涉及的其他进程应对此跟踪进行采样。

ActivityStarted 和 ActivityStopped 分别在启动和停止活动时调用。 可以通过这些回调记录 Activity 的相关信息或对其进行修改。

当 Activity 刚启动时,许多数据可能仍然不完整,在 Activity 停止之前,系统会对这些数据进行填充。

创建 ActivityListener 并填充回调之后,即可通过调用 ActivitySource.AddActivityListener(ActivityListener)

启动调用回调操作。 调用 ActivityListener.Dispose() 可停止回调流。 请注意,在多线程代码中,当 Dispose() 运行时,甚至在它返回后不久,都可能会收到正在进行的回调通知。

0 人点赞