.NET 7中System.Diagnostics.Metrics.Histogram桶值不符合预期问题
我正在学习遥测指标并将其发送至Prometheus,编写了一个.NET 7的C#控制台应用,希望将ping时间计入带有自定义边界的Histogram中。
配置代码如下:
using System.Diagnostics.Metrics; var meter = new Meter("MeterUsing"); var pingHistogram = meter.CreateHistogram<int>("ping", "ms", "The ping time"); var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddView(pingHistogram.Name, new ExplicitBucketHistogramConfiguration { Boundaries = new double[] { 100, 200, 500, 1000 } }) .Build();
记录值的代码为:
pingHistogram.Record(rand.Next(30, 2000));
我将数据推送至OpenTelemetry Collector后,由Prometheus抓取数据。查看Collector的指标端点时,得到如下内容:
# HELP ping The ping time # TYPE ping histogram ping_bucket{instance="280616b2-1c17-426a-9cf0-f33183ef7dfc",job="MeterUsing",le="100"} 2 ping_bucket{instance="280616b2-1c17-426a-9cf0-f33183ef7dfc",job="MeterUsing",le="200"} 5 ping_bucket{instance="280616b2-1c17-426a-9cf0-f33183ef7dfc",job="MeterUsing",le="500"} 13 ping_bucket{instance="280616b2-1c17-426a-9cf0-f33183ef7dfc",job="MeterUsing",le="1000"} 23 ping_bucket{instance="280616b2-1c17-426a-9cf0-f33183ef7dfc",job="MeterUsing",le="+Inf"} 64 ping_sum{instance="280616b2-1c17-426a-9cf0-f33183ef7dfc",job="MeterUsing"} 69801 ping_count{instance="280616b2-1c17-426a-9cf0-f33183ef7dfc",job="MeterUsing"} 64
这与我的预期不符,我期望每个桶统计对应区间内的数值数量,但当前桶内计数是按le递增的累积值,且ping_count等于ping_bucket{le="+Inf"}。请问是否可以配置视图来统计特定桶内的计量值?
首先明确:Prometheus的Histogram指标规范本身就是使用累积桶(cumulative buckets)来表示的,这是Prometheus定义的标准格式,并非OpenTelemetry或代码配置的问题。le="X"代表所有小于等于X的样本总数,这种设计是为了方便计算分位数、区间占比等统计指标,比如要获取100<值≤200区间的样本数,只需执行ping_bucket{le="200"} - ping_bucket{le="100"}即可。
关于是否能通过OpenTelemetry视图配置改为非累积桶:
- 目前OpenTelemetry的.NET SDK没有直接提供将Histogram转换为非累积桶(即每个桶单独统计区间内数量)的视图配置选项。因为OpenTelemetry的Metric API遵循官方规范,而规范中Histogram的默认导出格式为累积型,以兼容Prometheus等主流监控系统的要求。
如果确实需要单独的区间计数指标,有两种替代方案:
Prometheus侧处理:通过PromQL计算区间值并创建记录规则(Recording Rule),示例:
ping_bucket_range_0_100 = ping_bucket{le="100"} ping_bucket_range_101_200 = ping_bucket{le="200"} - ping_bucket{le="100"} ping_bucket_range_201_500 = ping_bucket{le="500"} - ping_bucket{le="200"} ping_bucket_range_501_1000 = ping_bucket{le="1000"} - ping_bucket{le="500"} ping_bucket_range_1001_inf = ping_bucket{le="+Inf"} - ping_bucket{le="1000"}这种方式可以在Prometheus中生成单独的区间计数指标,方便直接查询。
应用侧自定义指标:放弃使用Histogram,改用多个Counter分别统计每个区间的样本数,示例:
var meter = new Meter("MeterUsing"); var pingCounter0_100 = meter.CreateCounter<long>("ping_0_100", "ms", "Ping time 0-100ms count"); var pingCounter101_200 = meter.CreateCounter<long>("ping_101_200", "ms", "Ping time 101-200ms count"); var pingCounter201_500 = meter.CreateCounter<long>("ping_201_500", "ms", "Ping time 201-500ms count"); var pingCounter501_1000 = meter.CreateCounter<long>("ping_501_1000", "ms", "Ping time 501-1000ms count"); var pingCounter1001_Inf = meter.CreateCounter<long>("ping_1001_inf", "ms", "Ping time >1000ms count"); // 记录时判断区间 var pingValue = rand.Next(30, 2000); if (pingValue <= 100) pingCounter0_100.Add(1); else if (pingValue <= 200) pingCounter101_200.Add(1); else if (pingValue <= 500) pingCounter201_500.Add(1); else if (pingValue <= 1000) pingCounter501_1000.Add(1); else pingCounter1001_Inf.Add(1);这种方式可以直接导出每个区间的独立计数,但缺点是失去了Histogram自带的sum、count以及分位数计算能力,需要自行维护这些统计值。
内容的提问来源于stack exchange,提问作者MirrorBoy

