System.Net 库中的分布式跟踪
分布式跟踪 是一种诊断技术,可帮助工程师本地化应用程序中的故障和性能问题,尤其是分布在多台计算机或进程中的故障和性能问题。 此方法通过应用程序跟踪请求,方法是将不同组件完成的工作关联在一起,并将其与应用程序可能针对并发请求执行的其他工作分开。 例如,对典型 Web 服务的请求可能首先由负载均衡器接收,然后转发到 Web 服务器进程,然后向数据库发出多个查询。 分布式跟踪允许工程师区分这些步骤是否失败,以及每个步骤花费的时间。 它还可以在运行时记录每个步骤生成的消息。
.NET 中的跟踪系统设计为使用 OpenTelemetry (OTel),并使用 OTel 将数据导出到监视系统。 .NET 中的跟踪是使用 System.Diagnostics API 实现的,其中工作单元由 System.Diagnostics.Activity 类表示,该类对应于 OTel 范围。 OpenTelemetry 为范围(活动)及其属性(标记)定义了一个全行业的标准命名方案,称为语义约定。 .NET 遥测尽可能使用现有的语义约定。
注意
本文中的术语范围和活动为同义词。 在 .NET 代码的上下文中,它们引用 System.Diagnostics.Activity 实例。 不要将 OTel 范围与 System.Span<T>混淆。
提示
有关所有内置活动及其标记/属性的综合列表,请参阅 .NET 中的内置活动。
仪表
为了发出跟踪信息,System.Net 库会通过内置 ActivitySource 源进行检测,而这些源会创建 Activity 对象来跟踪已执行的工作。 只有订阅了 ActivitySource 的侦听器才会创建活动。
内置检测随 .NET 版本的演变。
- 在 .NET 8 及更早版本中,检测仅限于创建一个空的 HTTP 客户端请求活动。 这意味着用户必须依靠
OpenTelemetry.Instrumentation.Http
库为活动填充所需的信息(例如标记),以发出有用的跟踪。 - .NET 9 根据 OTel HTTP 客户端语义约定,在 HTTP 客户端请求活动中发出名称、状态、异常信息和最重要的标记,从而扩展了仪器功能。 这意味着,在 .NET 9+ 上,可以省略
OpenTelemetry.Instrumentation.Http
依赖项,除非需要更高级的功能,如 扩充。 - .NET 9 还引入了 试验性连接跟踪,在
System.Net
库中添加新活动以支持诊断连接问题。
收集 System.Net 跟踪
在最低级别,AddActivityListener 方法可支持跟踪收集,该方法可注册包含用户定义逻辑的 ActivityListener 对象。
但是,作为应用程序开发人员,你可能更愿意依靠由 OpenTelemetry .NET SDK 提供的功能所构建的丰富生态系统来收集、导出和监控跟踪。
- 要从根本上了解如何使用 OTel 收集跟踪信息,请参阅我们的使用 OpenTelemetry 收集跟踪信息指南。
- 对于生产时间跟踪收集和监控,可以将 OpenTelemetry 与 Prometheus、Grafana 和 Jaeger 或 Azure Monitor 和 Application Insights 配合使用。 但是,这些工具相当复杂,在开发时可能不方便使用。
- 对于 开发时 跟踪收集和监视,我们建议使用 .NET Aspire,它提供了一种简单但可扩展的方法来启动应用程序中的分布式跟踪,并在本地诊断问题。
- 也可以重复使用 Aspire 服务默认设置项目,而不使用 Aspire 业务流程。 这是在 ASP.NET 项目中引入和配置 OpenTelemetry 跟踪和指标的便捷方法。
使用 .NET Aspire 收集日志
在 ASP.NET 应用程序中收集跟踪和指标的一种简单方法是使用 .NET Aspire。 .NET Aspire 是 .NET 的一组扩展,便于创建和使用分布式应用程序。 使用 .NET Aspire 的好处之一是,遥测是内置的,使用适用于 .NET 的 OpenTelemetry 库。
.NET Aspire 的默认项目模板包含 ServiceDefaults
项目。 .NET Aspire 解决方案中的每个服务都有对服务默认值项目的引用。 服务使用它来设置和配置 OTel。
服务默认值项目模板包括 OTel SDK、ASP.NET、HttpClient 和运行时检测包。 这些检测组件在 Extensions.cs 文件中配置。 为了支持 Aspire 仪表板中的遥测可视化,服务默认值项目还默认包含 OTLP 导出程序。
Aspire 仪表板旨在将遥测观察引入本地调试周期,使开发人员能够确保应用程序生成遥测数据。 遥测可视化还有助于在本地诊断这些应用程序。 能够查看服务之间的调用在调试时与在生产中一样有用。 F5 Visual Studio 中的 AppHost
项目或从命令行 dotnet run
AppHost
项目时,将自动启动 .NET Aspire 仪表板。
有关 .NET Aspire 的详细信息,请参阅:
在不使用 .NET Aspire 业务流程的情况下重复使用服务默认设置项目
Aspire Service Defaults 项目提供了一种简单的方式来为 ASP.NET 项目配置 OTel,即使不使用 .NET Aspire 的其余部分(例如用于业务流程的 AppHost)。 服务默认值项目通过 Visual Studio 或 dotnet new
作为项目模板提供。 它配置 OTel 并设置 OTLP 导出程序。 然后,可以使用 OTel 环境变量 将 OTLP 终结点配置为向其发送遥测数据,并为应用程序提供资源属性。
在 .NET Aspire 之外使用 ServiceDefaults 的步骤如下:
使用 Visual Studio 中的“添加新项目”将 ServiceDefaults 项目添加到解决方案,或使用
dotnet new
:dotnet new aspire-servicedefaults --output ServiceDefaults
从 ASP.NET 应用程序中引用 ServiceDefaults 项目。 在 Visual Studio 中,选择 添加>项目引用 并选择 ServiceDefaults 项目”
调用 OpenTelemetry 安装程序函数
ConfigureOpenTelemetry()
作为应用程序生成器初始化的一部分。var builder = WebApplication.CreateBuilder(args) builder.ConfigureOpenTelemetry(); // Extension method from ServiceDefaults. var app = builder.Build(); app.MapGet("/", () => "Hello World!"); app.Run();
要获取完整演示,请参阅 示例:使用 OpenTelemetry 结合 OTLP 和独立的 Aspire 仪表板。
实验性连接跟踪
排查 HttpClient
问题或瓶颈时,查看发送 HTTP 请求时花费的时间可能至关重要。 通常,此问题发生在 HTTP 连接建立期间,通常分解为 DNS 查找、TCP 连接和 TLS 握手。
.NET 9 引入了实验性连接跟踪,添加了一个 HTTP connection setup
跨度,其中包含三个子范围,表示连接建立的 DNS、TCP 和 TLS 阶段。 连接跟踪的 HTTP 部分在 SocketsHttpHandler内实现,这意味着活动模型必须遵循基础连接池行为。
注意
在 SocketsHttpHandler中,连接和请求具有独立的生命周期。 共用连接 可以长时间存在,并处理许多请求。 发出请求时,如果连接池中没有立即可用的连接,则会将请求添加到请求队列中,等待可用连接。 等待请求与连接之间没有直接关系。 当另一个连接可供使用时,连接过程可能已启动,在这种情况下使用释放的连接。 因此,HTTP connection setup
范围并未作为 HTTP client request
范围的子代建模,而是使用了范围链接。
.NET 9 引入了以下范围,用于收集详细的连接信息:
名字 | ActivitySource | 描述 |
---|---|---|
HTTP wait_for_connection |
Experimental.System.Net.Http.Connections |
HTTP client request 范围的子范围,表示请求在请求队列中等待可用连接的时间间隔。 |
HTTP connection_setup |
Experimental.System.Net.Http.Connections |
表示建立 HTTP 连接。 单独的跟踪根范围,有自己的 TraceId 。 HTTP client request 范围可能包含指向 HTTP connection_setup 的链接。 |
DNS lookup |
Experimental.System.Net.NameResolution |
DNS 查找由 Dns 类执行。 |
socket connect |
Experimental.System.Net.Sockets |
建立 Socket 连接。 |
TLS handshake |
Experimental.System.Net.Security |
由 SslStream 执行的 TLS 客户端或服务器握手。 |
注意
相应的 ActivitySource
名称以前缀 Experimental
开头,因为随着我们对这些范围在生产中的运行情况有了更多了解,它们可能会在未来版本中进行修改。
这些范围过于详细,不适合在工作负荷较高的生产场景中全天候使用 - 它们干扰较大,而且通常不需要这种级别的检测。 但是,如果尝试诊断连接问题或更深入地了解网络和连接延迟如何影响服务,则它们会提供难以通过其他方式收集的见解。
启用 Experimental.System.Net.Http.Connections
ActivitySource 后,HTTP client request
跨度包含一个链接,该链接指向与处理请求的连接相对应的 HTTP connection_setup
跨度。 由于 HTTP 连接可能会持续较长时间,这可能会导致每个请求活动都有许多链接指向连接范围。 某些 APM 监控工具会积极地遍历跨度之间的链接来构建其视图,因此,在工具未设计为处理大量链接的情况下,包含此跨度可能会导致问题。
下图说明了跨度的行为及其关系:
演练:在 .NET 9 中使用试验性连接跟踪
本演练使用 .NET 9 Aspire Starter App 来演示连接跟踪,但使用其他监控工具进行设置也很简单。 关键步骤是启用 ActivitySources。
使用
dotnet new
创建 .NET Aspire 9 入门应用:dotnet new aspire-starter-9 --output ConnectionTracingDemo
或在 Visual Studio 中:
中创建 .NET Aspire 9 初学者应用
在
ServiceDefaults
项目中打开Extensions.cs
,然后编辑ConfigureOpenTelemetry
方法,在跟踪配置回调中添加用于连接的 ActivitySources:.WithTracing(tracing => { tracing.AddAspNetCoreInstrumentation() // Instead of using .AddHttpClientInstrumentation() // .NET 9 allows to add the ActivitySources directly. .AddSource("System.Net.Http") // Add the experimental connection tracking ActivitySources using a wildcard. .AddSource("Experimental.System.Net.*"); });
启动解决方案。 这样将打开 .NET Aspire 仪表板。
导航到
webfrontend
应用的“天气”页,以生成一个指向apiservice
的HttpClient
请求。返回仪表板并导航到跟踪页。 打开
webfrontend: GET /weather
跟踪。在 Aspire 仪表板HttpClient 跨度
启用连接工具后发出 HTTP 请求时,您应会看到对客户端请求跨度的以下更改:
- 如果需要建立连接,或者应用正在等待连接池的连接,则会显示一个额外的
HTTP wait_for_connection
时间段,表示等待建立连接时的延迟。 这有助于了解代码中发出的HttpClient
请求之间的延迟,以及请求的处理实际开始的时间。 在上图中:- 所选范围为 HttpClient 请求。
- 下面的范围表示请求等待建立连接所花费的时间。
- 最后一个黄色范围来自处理请求的目标。
- HttpClient 范围将具有指向
HTTP connection_setup
范围的链接,该范围表示创建请求使用的 HTTP 连接的活动。
如前所述,HTTP connection_setup
区间是一个单独的区间,拥有自己独立的TraceId
,因为它的生命周期不依赖于每个单独的客户端请求。 此范围通常具有子范围 DNS lookup
、(TCP)socket connect
和 TLS client handshake
。
扩充
在某些情况下,必须增强现有的 System.Net
跟踪功能。 这通常意味着向内置活动注入其他标记/属性。 这被称为扩充。
OpenTelemetry 检测工具库中的增强 API
若要向 HTTP 客户端请求活动添加其他标记/属性,最简单的方法是使用 OpenTelemetry HttpClient 和 HttpWebRequest 检测库 HttpClient
扩充 API。 这需要依赖 OpenTelemetry.Instrumentation.Http
包。
手动扩充
可以手动实现 HTTP client request
活动的扩充。 为此,需要在活动完成之前访问在请求活动范围内运行的代码中的 Activity.Current。 这可以通过实现 IObserver<DiagnosticListener>
并将其订阅到 AllListeners 来实现,以便在网络活动发生时获得回调。 事实上,OpenTelemetry HttpClient 和 HttpWebRequest 检测库 就是这样实现的。 有关代码示例,请参阅 DiagnosticSourceSubscriber.cs
中的订阅代码以及 HttpHandlerDiagnosticListener.cs 中的基础实现,其中通知是委托给该基础实现的。
需要更多的跟踪吗?
如果对可以通过跟踪公开的其他有用信息有建议,请创建 dotnet/运行时问题。