传统的.NET Framework提供的System.Diagnostics.PerformanceCounter类型可以帮助我们收集Windows操作系统下物理机或者进程的性能指标,基于PerformanceCounter类型的性能计数API在.NET Core下被彻底放弃。但是.NET Core程序的很多核心性能指标都会采用事件的方式发出来,具体使用的就是如下所示的这个名为RuntimeEventSource的内部类型。源代码可以从这里查看。
[EventSource(Guid="49592C0F-5A05-516D-AA4B-A64E02026C89", Name="System.Runtime")] internal sealed class RuntimeEventSource : EventSource { ... }
我们可以利用EventListener对象监听由RuntimeEventSource发送的事件,进而得到当前的性能指标。如下所示的代码片段就是用来获取性能计数的PerformanceCounterListener类型的定义。在重写的OnEventSourceCreated方法中,可以根据名称订阅针对RuntimeEventSource的事件。在具体调用EnableEvents方法时,我们提供了一个字典作为参数,参数利用一个名为EventCounterIntervalSec的元素将取样的时间间隔设置为5秒。
public class PerformanceCounterListener: EventListener { private static HashSet<string> _keys = new HashSet<string> { "Count", "Min", "Max", "Mean", "Increment" }; private static DateTimeOffset? _lastSampleTime; protected override void OnEventSourceCreated(EventSource eventSource) { base.OnEventSourceCreated(eventSource); if (eventSource.Name == "System.Runtime") { EnableEvents(eventSource, EventLevel.Critical, (EventKeywords)(-1), new Dictionary<string, string> { ["EventCounterIntervalSec"] = "5" }); } } protected override void OnEventWritten(EventWrittenEventArgs eventData) { if (_lastSampleTime != null && DateTimeOffset.UtcNow - _lastSampleTime.Value > TimeSpan.FromSeconds(1)) { Console.WriteLine(); } _lastSampleTime = DateTimeOffset.UtcNow; var metrics = (IDictionary<string, object>)eventData.Payload[0]; var name = metrics ["Name"]; var values = metrics .Where(it=>_keys.Contains(it.Key)) .Select(it => $"{it.Key} = {it.Value}"); var timestamp = DateTimeOffset.UtcNow.ToString("yyyy-MM-dd hh:mm::ss"); Console.WriteLine($"[{timestamp}]{name, -32}: {string.Join("; ", values.ToArray())}"); } } class Program { static void Main() { _ = new PerformanceCounterListener(); Console.Read(); } }
在重写的OnEventWritten方法中,可以得到性能计数时间的内容载荷(体现为一个字典对象),并从中提取出性能指标的名称(Name)和相关的采样值(Max、Min、Count、Mean和Increment)。提取出的性能指标数据连同当前时间戳经过格式化后直接输出到控制台上。在作为入口的Main方法中,我们直接创建了PerformanceCounterListener对象,它会以5秒的间隔收集当前的性能指标,并以下图所示的形式输出到控制台上。
如上图所示,利用PerformanceCounterListener对象几乎可以收集到.NET Core程序所在进程以及物理机的绝大部分核心指标,其中包括CPU、内存、GC、线程池相关的指标。如果需要开发APM(Application Performance Management)框架,或者直接集成第三方APM(如我个人比较钟爱的Elastic APM),就可以直接利用这种方式采集所需的性能指标。