深刻研究 .NET 5 的开放式遥测

OpenTelemetry 介绍

OpenTelemetry是一种开放的源代码规范,工具和SDK,用于检测,生成,收集和导出遥测数据(指标,日志和跟踪),开放遥测技术获得了Cloud Native Computing Foundation(CNCF)的支持,该基金会支持一系列流行的优秀的开源项目,你能够去看一下CNCF景观图,https://landscape.cncf.io/ ,就明白了个人意思,这个SDK支持全部主要的编程语言,包括C#和ASP.NET Core。web

在这篇文章中,我将讨论OpenTelemetry的所有含义,为何要使用它以及如何在.NET中使用,对于典型的应用程序,一般须要记录三组数据:指标,日志和跟踪。redis

Logging 日志

能够监听程序的进程发出的消息日志,在.NET应用程序中,若是您使用NuGet包ILogger中的日志记录功能,就能够轻松的让OpenTelemetry支持 Microsoft.Extensions.Logging, 若是要构建ASP.NET Core应用程序,一般已经使用了此功能。编程

Metrics 指标

提供运行进程的指标信息,包括计数器,仪表盘和直方图,对OpenTelemetry中指标的支持仍在开发中, 可是已经肯定下来了,指标包括如下:服务器

  • CPU 使用百分比
  • 进程内存使用量
  • Http的请求数量

Tracing 追踪

也叫作分布式跟踪,它记录单个操做的开始和结束时间以及与该操做相关的参数,好比在ASP.NET Core中记录HTTP请求的跟踪,您可能会记录请求和响应的开始和结束时间,参数将是 Http的请求方式,请求参数,请求地址等,请求调用会造成链路,您能够深刻了解时间耗费在哪一个服务,或者服务中有异常报错发生。app

Jaeger

收集指标,日志,追踪信息只是一部分,如何进行数据处理,展现是APM系统的功能,由于收集的数据遵循OpenTelemetry标准,因此能够和APM系统完美结合。async

Jaeger和Zipkin是能够收集和显示而且与Open Telemetry兼容APM, Zipkin的话比较久了,而且没有很好的UI,所以我我的推荐Jaeger,看起来像这样:编程语言

上图显示了应用程序的跟踪,您能够看到它如何使用HTTP请求对MySQL,Redis和外部API进行调用, 每行的长度显示了执行所需的时间,您能够轻松地从头至尾查看跟踪中执行的全部主要操做,您还能够深刻研究每一行,并查看与该部分跟踪有关的其余信息。分布式

Spans 跨度

上面Jaeger图中的每一行都称为 Span,在.NET中的每一行均由System.Activities.Activity类型表示,它也具备惟一的标识符,开始和结束时间以及父范围的惟一标识符,因此这些能够造成调用链,而且Span还能够包含其余参数。工具

不幸的是,.NET团队的命名大大偏离了官方的OpenTelemetry规范,我有点疑惑,不过我如今已经明白了大概。this

个人理解是.NET已经包含一个Activity的类型,所以.NET团队决定重用它,而不是从新建立一个 Span的新类型,这意味着不少命名与open-telemetry规范不匹配,在.NET中,你如今能够把 Span 和 Activity身份互换。

注意:在.NET 5中才有ActivitySource,在以前能够用 Activity。

使用Span记录行为很是简单,首先,咱们必须建立一个ActivitySource能够记录Span或活动的对象:

private static ActivitySource activitySource = new ActivitySource(
    "companyname.product.library",
    "semver1.0.0");

而后,咱们能够调用StartActivity开始记录,最后调用Dispose中止记录Span:

using (var activity = activitySource.StartActivity("ActivityName")
{
    // Pretend to do some work.
    await LongRunningAsync().ConfigureAwait(false);
} // Activity gets stopped automatically at end of this block during dispose.

Events 事件

当Span开启记录后,咱们也能够在这期间记录事件,这些事件包含了时间戳信息:

using (var activity = activitySource.StartActivity("ActivityName")
{
    await LongRunningOperation().ConfigureAwait(false);
}

public async Task LongRunningOperationAsync()
{
    await Task.Delay(1000).ConfigureAwait(false);

    // Log timestamped events that can take place during an activity. 
    Activity.Current?.AddEvent(new ActivityEvent("Something happened."));
}

在LongRunningOperationAsync方法中,有一个问题是,如何把activity传入这个方法,若是咱们定义了一个参数,这体验也太差了,可是,将两个操做分离的一个好的方法是使用Activity.Current。

一个常见的错误,我能够预见的是,Activity.Current多是null,因此这里我加了null判断。

Attributes 属性

属性是数据的键值对,您能够将其记录为单个Span的一部分,好比Http的请求方式,请求状态码等。

注意,在Open Telemetry规范中叫 Attributes,在 咱们.NET 中叫Tag

using (var activity = activitySource.StartActivity("ActivityName")
{
    await LongRunningOperation().ConfigureAwait(false);
}

public async Task LongRunningOperationAsync()
{
    await Task.Delay(1000).ConfigureAwait(false);

    // Log an attribute containing arbitrary data.
    Activity.Current?.SetTag("http.method", "GET");
}

IsRecording 记录

IsRecording是Span上的一个标志,若是返回false,代表该Span已结束,另外,若是你的数据量比较大的话,你须要抽样采集,好比10%,那么你也能够手动把这些span设置为false,它不会采集。

注意:在open-telemetryg规范中叫IsRecording,在.NET Core 3.1中是 Recorded,在.NET 5 中是 IsAllDataRequested。

using (var activity = activitySource.StartActivity("ActivityName")
{
    await LongRunningOperation().ConfigureAwait(false);
}

public async Task LongRunningOperationAsync()
{
    await Task.Delay(1000).ConfigureAwait(false);

    // It's possible to optionally request more data from a particular span.
    var activity = Activity.Current;
    if (activity != null && activity.IsAllDataRequested)
    {
        activity.SetTag("http.url", "http://www.mywebsite.com");
    }
}

Trace的语义约定

注意属性名称http.method,http.url,我在以上示例中使用了该属性,由于在open-telemetry规范中已经标准化了某些经常使用的属性名称,标准化经常使用属性名称能够在Jaeger等APM中很好的展现它们,属性名称已分类为几个不一样的类别,你能够花点时间看一下:

  • General: 可用于描述不一样种类的操做的常规语义属性
  • HTTP: 客户端和服务器的Http调用
  • Database: SQL 和 NoSql的调用
  • RPC/RMI: 远程调用,好比gRPC等
  • Messaging: 用于与消息传递系统(队列,发布/订阅等)
  • Exceptions: 用于记录与Span关联的异常的属性

Exporting 导出

有不少用于导出使用OpenTelemetry收集的数据的插件,我将在个人下一篇博客文章中讨论有关在ASP.NET Core中使用Open Telemetry的信息, 能够很方便的处理这些数据,您能够轻松地订阅而后消费OpenTelemetry数据,以下所示:

using var subscriber = DiagnosticListener.AllListeners.Subscribe(
    listener =>
    {
        Console.WriteLine($"Listener name {listener.Name}");

        listener.Subscribe(kvp => Console.WriteLine($"Received event {kvp.Key}:{kvp.Value}"));
    });

跨进程的追踪

为何这些程序会造成调用链,它们是不一样的进程,这个怎么实现的呢?

这就是W3C跟踪上下文标准,它定义了一系列HTTP Header,这些Header将有关当前正在记录的任何跟踪的信息从一个进程传递到另外一个进程,它经过Http的Header来传递信息,规范中定义了两个HTTP Header:

  • traceparent-包含version,trace-id,parent-id和trace-flags

    • version - 在open-telemetry规范中,它始终是00
    • trace-id - 跟踪的惟一标识符。
    • parent-id -做为当前 patent span 的惟一标识符。
    • trace-flags -当前跟踪的一组标志,用于肯定是否正在采样当前跟踪以及跟踪级别。
  • tracestate -由一组名称/值对表示的特定于供应商的数据。

traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
tracestate: asp=00f067aa0ba902b7,redis=t61rcWkgMzE

Baggage

与Attributes相似,Baggage是咱们能够将数据做为键值对添加到跟踪的另外一种方式,不一样之处在于,Baggage使用W3C规范中baggage定义的HTTP Header跨进程边界传递,可是Attributes的值数据只在当前Span中可用

baggage: userId=alice,serverNode=DF:28,isProduction=false

使用的话,也有些相同之处:

using (var activity = activitySource.StartActivity("ActivityName")
{
    await LongRunningOperation().ConfigureAwait(false);
}

public async Task LongRunningOperationAsync()
{
    await Task.Delay(1000).ConfigureAwait(false);

    // Log an attribute containing arbitrary data.
    Activity.Current?.AddBaggage("http.method", "GET");
}

它的用途在于,好比说我须要传递一个订单ID,我就能够放到 Baggage 数据中,它在整个请求链路中均可以访问。

总结

.NET团队对OpenTelemetry很是重视,你能够看到Activity类型在.NET 5 中的加强,而且默认 HttpClient 调用时,它会自动传输W3C跟踪上下文HTTP Header, 基于ILogger的统一日志,也能够很好的收集和OpenTelemetry兼容的日志。

原文做者: Rehan Saeed
原文连接: https://rehansaeed.com/deep-dive-into-open-telemetry-for-net/

最后

欢迎扫码关注咱们的公众号 【全球技术精选】,专一国外优秀博客的翻译和开源项目分享,也能够添加QQ群 897216102

相关文章
相关标签/搜索