平台相关指标采集#那么APM探针都是如何采集 .NET 平台相关指标呢其实采集这些指标在 .NET 上是非常简单的因为.NET提供了相关的API接口我们可以直接获得这些指标这里指的平台指标是如 CPU 占用率、线程数量、GC 次数等指标。比如在 SkyAPM-dotne t项目中我们可以查看 SkyApm.Core 项目中的 Common 文件夹文件夹中就有诸如里面有 CPU 指标、GC 指标等平台相关指标采集实现帮助类。同样在 OpenTelemetry-dotnet-contrib 项目中我们可以在 Process 和 Runtime 文件夹中查看进程和运行时等平台相关指标采集的实现。这些都是简单的 API 调用有兴趣的同学可以自行查看代码本文就不再赘述这些内容。组件相关指标采集#除了平台相关指标采集还有组件相关的指标这里所指的组件相关指标拿 ASP.NET Core 应用程序举例我们接口秒并发是多少、一个请求执行了多久在这个请求执行的时候访问了哪些中间件( Redis 、MySql 、Http 调用、RPC 等等)访问中间件时传递的参数(Redis 命令、Sql 语句、请求响应体等等)是什么访问中间件花费了多少时间。在 SkyAPM-dotnet 项目中我们可以直接在src目录找到这些组件相关指标采集的实现代码。同样在 OpenTelemetry-dotnet-contrib 项目中我们也可以在src目录找到这些组件相关指标采集代码。如果看过这两个APM探针实现的朋友应该都知道组件指标采集是非常依赖DiagnosticSource技术。.NET官方社区一直推荐的的方式是组件开发者自己在组件的关键路径进行埋点使用DiagnosticSource的方式将事件传播出去然后其它监测软件工具可以订阅DiagnosticListener来获取组件运行状态。就拿 ASP.NET Core 来举例组件源码中有[HostingApplicationDiagnostics.cs](https://github.com/dotnet/aspnetcore/blob/main/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs)这样一个类这个类中定义了 Hosting 在请求处理过程中的几个事件。internal const string ActivityName Microsoft.AspNetCore.Hosting.HttpRequestIn; private const string ActivityStartKey ActivityName .Start; private const string ActivityStopKey ActivityName .Stop;当 Hosting 开始处理请求时会检测当前是否有监听者监听这些事件如果有的话就会写入事件事件也会携带当前的一些上下文信息代码如下所示以 SkyAPM-dotnet 举例有对应的HostingTracingDiagnosticProcessor.cs监听事件然后获取上下文信息记录 APM 埋点信息代码如下所示这种方式的优点有高效和高性能DiagnosticSource是 .NET 平台自带的框架使用它硬编码可以享受到编译器和 JIT 相关优化可以避免一些性能开销。组件开发者可以控制事件传递的频率和内容以达到最佳的性能和资源利用率。灵活通过使用DiagnosticSource组件开发者可以灵活地定义自己的事件模型并按需发布事件。这意味着可以轻松地定制自己的监测需求而不必担心过多的日志数据产生过大的开销。可扩展性使用DiagnosticSource可以让组件的监测需求随着时间的推移而演变而不必担心日志系统的限制。开发者可以根据自己的需要添加新的事件类型以适应不断变化的监测需求。易用性DiagnosticSource的 API 简单易用订阅事件数据也很容易。这使得使用它进行组件监测变得非常容易并且可以快速地集成到现有的监测系统中。可移植性DiagnosticSource可以在多个平台上运行包括 Windows、Linux 和 macOS 等。这意味着可以使用相同的事件模型来监测不同的应用程序和服务从而简化了监测系统的设计和管理。不过这种方式的缺点也很明显就是必须由组件开发者显式的添加事件代码探针的开发者也因此束手束脚这就导致一些没有进行手动埋点的三方组件都无法添加事件监听所以现阶段 SkyAPM-dotnet 支持的第三方组件还不是很丰富。那么其实只要解决如何为没有进行手动埋点的组件库加入埋点就能解决 SkyAPM-dotnet 支持第三方组件多样性的问题。.NET方法注入#从上一节我们可以知道目前制约APM支持组件不够丰富的原因之一就是很多组件库都没有进行可观测性的适配没有在关键路径进行埋点。那么要解决这个问题其实很简单我们只需要修改组件库关键路径代码给加上一些埋点就可以了那应该如何给这些第三方库的代码加点料呢聊到这个问题我们需要知道一个 .NET 程序是怎么从源代码变得可以运行的。通常情况下一个 .NET 程序从源码到运行会经过两次编译忽略 ReadyToRun 、NativeAOT 、分层编译等情况。如下图所示