Telegraf Basicstats 聚合器插件:指标基础统计与聚合实践指南
【免费下载链接】telegrafAgent for collecting, processing, aggregating, and writing metrics, logs, and other arbitrary data.项目地址: https://gitcode.com/GitHub_Trending/te/telegraf
本指南围绕 Telegraf 中的basicstats聚合器插件展开,讲解如何对采集到的指标进行计数、差值、最大/最小值、均值、方差、标准差等基础统计聚合,并在每个period周期内输出聚合结果。读者将掌握basicstats的完整配置方法、15 种可选统计项的语义与输出字段命名规则、聚合器生命周期与窗口机制,以及基于源码层面的在线方差算法实现细节。
插件概述
basicstats是 Telegraf 提供的标准聚合器插件之一,用于在一段时间窗口内对一组指标进行计算,输出count(计数)、diff(差值)、min(最小值)、max(最大值)、mean(均值)、non_negative_diff(非负差值)、rate(每秒变化率)等基础统计量。它自 Telegrafv1.5.0起提供,属于statistics类别,支持在所有平台上运行。
与处理器(processor)逐条变换指标不同,聚合器(aggregator)把一个周期内到达的多条指标合并为一条聚合结果,非常适合用于削峰填谷、生成周期级监控汇总(如 CPU 使用率均值、磁盘延迟峰值、连接数差值等)。
工作原理:聚合器生命周期
basicstats遵循 Telegraf 聚合器的标准生命周期接口(见 aggregator.go),由三个核心方法组成:
Add(in Metric):每收到一条输入指标时被调用,将指标值累积进内部缓存;Push(acc Accumulator):每个聚合周期结束时被调用,把聚合结果写入 accumulator,最终发给输出插件;Reset():推送完成后清空内部缓存,为下一个周期做准备。
在 basicstats.go 的Add实现中,插件以指标的HashID()作为缓存键,每个"测量名 + 标签组合"独立聚合;同一个键下每个字段各自维护一份独立的统计状态(basicstats结构体)。也就是说,不同标签序列的指标不会被混算,聚合结果保持了原有标签。
窗口调度由 models/running_aggregator.go 中的RunningAggregator负责:它会根据period、delay、grace计算聚合窗口起止时间,把窗口之外(早于窗口起点减去grace,或晚于窗口终点加上delay)的指标丢弃,并在窗口结束时依次执行Push与Reset。从源码结构看,Add、Push、Reset被RunningAggregator的互斥锁保护,不会并发调用。
配置指南
最小可用配置
在 Telegraf 配置文件中加入如下片段(完整样例见 sample.conf):
# Keep the aggregate basicstats of each metric passing through. [[aggregators.basicstats]] ## The period on which to flush & clear the aggregator. # period = "30s" ## If true, the original metric will be dropped by the ## aggregator and will not get sent to the output plugins. # drop_original = false ## Configures which basic stats to push as fields # stats = ["count","min","max","mean","variance","stdev"]通用参数
basicstats支持聚合器插件通用的全局配置选项,包括指标修改、标签/字段过滤、别名、插件执行顺序等,详见 docs/CONFIGURATION.md。同时它自身包含以下关键参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
period | duration | "30s" | 聚合器冲刷并清空内部缓存的周期,即统计窗口长度 |
drop_original | boolean | false | 为true时,原始指标被聚合器丢弃,不会转发给输出插件;为false时原始指标照常输出 |
stats | array of strings | 见下 | 指定要输出哪些统计量字段 |
stats参数的语义
stats数组控制实际推送的统计字段,basicstats.go 的初始化逻辑规定了三种情况:
- 不指定(
Stats为nil):默认只聚合并输出count、min、max、mean、stdev、s2六个统计项。文档与源码均明确指出,其余统计项默认不聚合,是为了保持向后兼容——避免用户升级后系统毫无预兆地突然开始输出sum等新字段(对应测试 TestBasicStatsWithDefaultStats 专门验证了默认不输出a_sum); - 显式配置:按数组中的名称逐个开启对应的统计项;
- 空数组
[]:不聚合任何统计量,Push阶段不会产生任何输出点(对应测试 TestBasicStatsWithNoStats)。
此外,parseStats对未知的统计名称不会报错终止,而是通过b.Log.Warnf("Unrecognized basic stat %q, ignoring", name)记录一条警告日志后忽略(见 basicstats.go),对应的 TestBasicStatsWithUnknownStat 验证了配置未知统计项时不会产生任何输出。
启用全部统计项
[[aggregators.basicstats]] period = "30s" drop_original = true stats = ["count","min","max","mean","s2","stdev","sum","diff","non_negative_diff","rate","non_negative_rate","percent_change","interval","last","first"]注意:配置示例中的"variance"实际对应的输出字段名为_s2(样本方差),配置stats时同样使用"s2"这个名称。
支持的统计项与输出字段
basicstats对输入指标的每个数值型字段独立计算统计量,输出字段名为原始字段名_统计项。原文档列出的字段族完整如下(以field1为例):
field1_count:周期内该字段的取值个数field1_diff:差值(最后一个值减前一个值)field1_rate:每秒变化率(差值除以采样时间间隔)field1_max:最大值field1_min:最小值field1_mean:均值field1_non_negative_diff:非负差值(差值小于 0 时该字段不输出)field1_non_negative_rate:非负每秒变化率(差值小于 0 时该字段不输出)field1_percent_change:百分比变化(diff / 前值 * 100)field1_sum:求和field1_s2:方差(样本方差,分母为 n-1)field1_stdev:标准差(样本方差的平方根)field1_interval:采样时间间隔(纳秒)field1_last:周期内最后到达的值field1_first:周期内最先到达的值
15 个统计项对应的stats配置名称与源码开关(见 basicstats.go)如下表:
stats配置名 | 输出字段后缀 | 含义 | 默认开启 |
|---|---|---|---|
count | _count | 取值个数 | ✔ |
min | _min | 最小值 | ✔ |
max | _max | 最大值 | ✔ |
mean | _mean | 均值 | ✔ |
s2 | _s2 | 样本方差 | ✔ |
stdev | _stdev | 样本标准差 | ✔ |
sum | _sum | 求和 | ✘ |
diff | _diff | 差值 | ✘ |
non_negative_diff | _non_negative_diff | 非负差值 | ✘ |
rate | _rate | 每秒变化率 | ✘ |
non_negative_rate | _non_negative_rate | 非负每秒变化率 | ✘ |
percent_change | _percent_change | 百分比变化 | ✘ |
interval | _interval | 时间间隔(ns) | ✘ |
last | _last | 最后值 | ✘ |
first | _first | 首值 | ✘ |
关于s2/stdev与count == 1的特殊行为
variance和stdev使用样本方差计算,分母为count - 1(见 basicstats.go)。源码注释明确指出:
if count == 1 StdDev = infinite => so I won't send data
即当count == 1时,分母为 0、方差无穷大,因此_s2、_stdev、_diff、_rate、_non_negative_diff、_non_negative_rate、_percent_change、_interval等依赖"第二笔数据"的统计量都不会输出——只有count、min、max、mean、sum、last、first这类单值即可确定的统计量会被推送。测试 TestBasicStatsDifferentPeriods 中单条指标m1单独推送时只输出 count/min/max/mean/last/first,正是对这一行为的验证。
字段类型与数据转换
basicstats只会聚合数值型字段。Add内部通过convert函数(见 basicstats.go)把字段值转换为float64,仅接受三种 Go 类型:
float64int64uint64
字符串、布尔值等其他类型的字段会被静默跳过(返回false),不会进入聚合计算,也不会导致错误。测试 basicstats_test.go 中的m2指标特意包含了"ignoreme": "string"与"andme": true两个非数值字段,最终结果中确实不包含这两个字段的统计输出。
算法细节:在线方差与均值计算
均值、方差、标准差的计算采用单趟在线算法(Welford 算法,源码注释引用了维基百科相关页面,见 basicstats.go),核心思路如下:
n := count + 1 delta := x - mean mean = mean + delta / n m2 = m2 + delta * (x - mean)其中M2是累积的"平方差和"中间值,最终:
- 方差
s2 = M2 / (count - 1) - 标准差
stdev = sqrt(s2)
这种增量式算法的优点是不需要缓存全部历史数据,内存占用恒定,且数值稳定性优于"先求和再套公式"的朴素实现。差值系列统计(diff、rate、percent_change)依赖内部维护的PREVIOUS(上一个值)与TIME(上一次采样时间)两个中间值,其中:
diff = 当前值 - PREVIOUSinterval = 当前时间 - TIMErate = diff / interval.Seconds()percent_change = diff / PREVIOUS * 100
从测试 TestBasicStatsWithRate 可以印证:m1与m2时间相差 1 毫秒,字段b从 1 变到 3,故b_diff = 2、b_rate = 2 / 0.001 = 2000。
示例输出与逐步验证
原文档给出的示例(两次聚合周期)如下:
system,host=tars load1=1 1475583980000000000 system,host=tars load1=1 1475583990000000000 system,host=tars load1_count=2,load1_diff=0,load1_rate=0,load1_max=1,load1_min=1,load1_mean=1,load1_sum=2,load1_s2=0,load1_stdev=0,load1_interval=10000000000i,load1_last=1 1475584010000000000 system,host=tars load1=1 1475584020000000000 system,host=tars load1=3 1475584030000000000 system,host=tars load1_count=2,load1_diff=2,load1_rate=0.2,load1_max=3,load1_min=1,load1_mean=2,load1_sum=4,load1_s2=2,load1_stdev=1.414162,load1_interval=10000000000i,load1_last=3,load1_first=3 1475584010000000000逐条解读:
第一个聚合窗口(两条原始指标load1=1、load1=1,间隔 10 秒):
load1_count=2:窗口内共 2 个样本;load1_diff=0、load1_rate=0:两值相同,差值为 0;load1_max=1、load1_min=1、load1_mean=1、load1_sum=2:常数序列的统计结果;load1_s2=0、load1_stdev=0:无波动,方差与标准差为 0;load1_interval=10000000000i:相邻样本时间间隔 10 亿纳秒 = 10 秒(整数类型字段以i后缀标记);load1_last=1:最后到达的值。
第二个聚合窗口(两条原始指标load1=1、load1=3):
load1_count=2、load1_diff=2:最后一个值 3 与前一个值 1 的差为 2;load1_rate=0.2:2 / 10秒 = 0.2,即每秒增长 0.2;load1_max=3、load1_min=1、load1_mean=2、load1_sum=4;load1_s2=2、load1_stdev=1.414162:两个样本[1,3]的样本方差为(3-2)²+(1-2)² / (2-1) = 2,标准差约为 √2;load1_last=3、load1_first=3:窗口内最后与最先到达的值。
通过对比可以直观看出:聚合结果完整继承了原始测量名system与标签host=tars,只是把load1字段替换成了load1_*统计字段族。
测试与行为保障
basicstats_test.go 提供了非常全面的行为验证,是理解插件语义的最佳参照:
- 周期内聚合:TestBasicStatsWithPeriod 验证两个指标加入后的 count/min/max/mean/s2/stdev 全部结果;
- 跨周期行为:TestBasicStatsDifferentPeriods 验证
Push/Reset之间的状态隔离,以及last、first在单条样本下的取值; - 单统计项组合:
count、min、max、mean、sum、s2、stdev、diff、rate、non_negative_rate、percent_change、interval、non_negative_diff、last、first各有独立测试,可直接对照预期的字段名与数值; - 浮点精度:TestBasicStatsWithOnlySumFloatingPointErrata 验证
sum直接累加而非由mean * count推算,避免1+1+5+1被算成7.999999...的浮点误差; - 默认与异常配置:默认统计集、空数组、未知统计名均有对应测试;
- 性能基准:BenchmarkApply 提供
Add热路径的基准参考。
插件注册与构建
basicstats通过 basicstats.go 底部的aggregators.Add("basicstats", ...)注册进插件注册表。若使用自定义构建(custom build),可通过 plugins/aggregators/all/basicstats.go 中的构建标签(!custom || aggregators || aggregators.basicstats)精确控制是否打包该插件。
实战建议
- 用
drop_original = true降低写放大:若只需要周期统计而无需原始点,开启该选项可显著减少发往时序数据库的点数,适合低频汇总场景; period与采集间隔匹配:聚合窗口建议取输入插件采集间隔的整数倍,否则窗口内的样本数不稳定,rate、interval的含义会偏移;- 按需开启统计项:默认只输出 6 项是为兼容旧行为;新项目建议显式列出所需统计项,避免输出冗余字段带来额外存储开销;
- 监控差值类指标:
diff/rate/non_negative_diff/percent_change非常适合计数器类指标(如网络流量、连接数)的周期增量观测; - 注意方差为样本方差:窗口内只有 1 个样本时不会输出
s2/stdev等统计量,查询时需按count > 1过滤,避免被空字段干扰。
完整的实战配置示例
[[inputs.cpu]] percpu = true totalcpu = true collect_cpu_time = false report_active = false # 每 60 秒对 CPU 指标做一次基础统计汇总,并丢弃原始点 [[aggregators.basicstats]] period = "60s" drop_original = true stats = ["count", "min", "max", "mean", "sum", "s2", "stdev", "diff", "rate"]以上配置将每个 60 秒窗口内的 CPU 使用率聚合为usage_*_count、usage_*_mean、usage_*_max等字段,便于后续按分钟粒度的告警阈值与趋势分析。
【免费下载链接】telegrafAgent for collecting, processing, aggregating, and writing metrics, logs, and other arbitrary data.项目地址: https://gitcode.com/GitHub_Trending/te/telegraf
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考