Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(aggregators.basicstats): Add "last" field #15030

Merged
merged 1 commit into from Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 7 additions & 7 deletions plugins/aggregators/basicstats/README.md
@@ -1,6 +1,6 @@
# BasicStats Aggregator Plugin

The BasicStats aggregator plugin give us count, diff, max, min, mean,
The BasicStats aggregator plugin gives count, diff, max, min, mean,
non_negative_diff, sum, s2(variance), stdev for a set of values, emitting the
aggregate every `period` seconds.

Expand All @@ -26,14 +26,13 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
drop_original = false

## Configures which basic stats to push as fields
# stats = ["count","diff","rate","min","max","mean","non_negative_diff","non_negative_rate","percent_change","stdev","s2","sum","interval"]
# stats = ["count","diff","rate","min","max","mean","non_negative_diff","non_negative_rate","percent_change","stdev","s2","sum","interval","last"]
```

- stats
- If not specified, then `count`, `min`, `max`, `mean`, `stdev`, and `s2` are
aggregated and pushed as fields. `sum`, `diff`, `non_negative_diff`,
`percent_change` are not aggregated by default to maintain backwards
compatibility.
aggregated and pushed as fields. Other fields are not aggregated by default
to maintain backwards compatibility.
- If empty array, no stats are aggregated

## Measurements & Fields
Expand All @@ -52,6 +51,7 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
- field1_s2 (variance)
- field1_stdev (standard deviation)
- field1_interval (interval in nanoseconds)
- field1_last (last aggregated value)

## Tags

Expand All @@ -62,8 +62,8 @@ No tags are applied by this aggregator.
```text
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 1475584010000000000
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 1475584010000000000
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 1475584010000000000
```
42 changes: 28 additions & 14 deletions plugins/aggregators/basicstats/basicstats.go
Expand Up @@ -35,6 +35,7 @@ type configuredStats struct {
nonNegativeRate bool
percentChange bool
interval bool
last bool
}

func NewBasicStats() *BasicStats {
Expand All @@ -58,8 +59,9 @@ type basicstats struct {
diff float64
rate float64
interval time.Duration
last float64
M2 float64 //intermediate value for variance/stdev
LAST float64 //intermediate value for diff
PREVIOUS float64 //intermediate value for diff
TIME time.Time //intermediate value for rate
}

Expand All @@ -79,16 +81,17 @@ func (b *BasicStats) Add(in telegraf.Metric) {
for _, field := range in.FieldList() {
if fv, ok := convert(field.Value); ok {
a.fields[field.Key] = basicstats{
count: 1,
min: fv,
max: fv,
mean: fv,
sum: fv,
diff: 0.0,
rate: 0.0,
M2: 0.0,
LAST: fv,
TIME: in.Time(),
count: 1,
min: fv,
max: fv,
mean: fv,
sum: fv,
diff: 0.0,
rate: 0.0,
last: fv,
M2: 0.0,
PREVIOUS: fv,
TIME: in.Time(),
}
}
}
Expand All @@ -107,8 +110,9 @@ func (b *BasicStats) Add(in telegraf.Metric) {
diff: 0.0,
rate: 0.0,
interval: 0,
last: fv,
M2: 0.0,
LAST: fv,
PREVIOUS: fv,
TIME: in.Time(),
}
continue
Expand Down Expand Up @@ -139,13 +143,15 @@ func (b *BasicStats) Add(in telegraf.Metric) {
//sum compute
tmp.sum += fv
//diff compute
tmp.diff = fv - tmp.LAST
tmp.diff = fv - tmp.PREVIOUS
//interval compute
tmp.interval = in.Time().Sub(tmp.TIME)
//rate compute
if !in.Time().Equal(tmp.TIME) {
tmp.rate = tmp.diff / tmp.interval.Seconds()
}
//last compute
tmp.last = fv
srebhan marked this conversation as resolved.
Show resolved Hide resolved
//store final data
b.cache[id].fields[field.Key] = tmp
}
Expand All @@ -172,6 +178,9 @@ func (b *BasicStats) Push(acc telegraf.Accumulator) {
if b.statsConfig.sum {
fields[k+"_sum"] = v.sum
}
if b.statsConfig.last {
fields[k+"_last"] = v.last
}

//v.count always >=1
if v.count > 1 {
Expand All @@ -193,7 +202,7 @@ func (b *BasicStats) Push(acc telegraf.Accumulator) {
fields[k+"_rate"] = v.rate
}
if b.statsConfig.percentChange {
fields[k+"_percent_change"] = v.diff / v.LAST * 100
fields[k+"_percent_change"] = v.diff / v.PREVIOUS * 100
}
if b.statsConfig.nonNegativeRate && v.diff >= 0 {
fields[k+"_non_negative_rate"] = v.rate
Expand Down Expand Up @@ -243,6 +252,8 @@ func (b *BasicStats) parseStats() *configuredStats {
parsed.percentChange = true
case "interval":
parsed.interval = true
case "last":
parsed.last = true
default:
b.Log.Warnf("Unrecognized basic stat %q, ignoring", name)
}
Expand All @@ -261,10 +272,13 @@ func (b *BasicStats) getConfiguredStats() {
variance: true,
stdev: true,
sum: false,
diff: false,
nonNegativeDiff: false,
rate: false,
nonNegativeRate: false,
percentChange: false,
interval: false,
last: false,
}
} else {
b.statsConfig = b.parseStats()
Expand Down
49 changes: 48 additions & 1 deletion plugins/aggregators/basicstats/basicstats_test.go
Expand Up @@ -111,6 +111,7 @@ func TestBasicStatsWithPeriod(t *testing.T) {
func TestBasicStatsDifferentPeriods(t *testing.T) {
acc := testutil.Accumulator{}
minmax := NewBasicStats()
minmax.Stats = []string{"count", "max", "min", "mean", "last"}
minmax.Log = testutil.Logger{}
minmax.getConfiguredStats()

Expand All @@ -121,22 +122,27 @@ func TestBasicStatsDifferentPeriods(t *testing.T) {
"a_max": float64(1),
"a_min": float64(1),
"a_mean": float64(1),
"a_last": float64(1),
"b_count": float64(1), //b
"b_max": float64(1),
"b_min": float64(1),
"b_mean": float64(1),
"b_last": float64(1),
"c_count": float64(1), //c
"c_max": float64(2),
"c_min": float64(2),
"c_mean": float64(2),
"c_last": float64(2),
"d_count": float64(1), //d
"d_max": float64(2),
"d_min": float64(2),
"d_mean": float64(2),
"d_last": float64(2),
"g_count": float64(1), //g
"g_max": float64(3),
"g_min": float64(3),
"g_mean": float64(3),
"g_last": float64(3),
}
expectedTags := map[string]string{
"foo": "bar",
Expand All @@ -152,30 +158,37 @@ func TestBasicStatsDifferentPeriods(t *testing.T) {
"a_max": float64(1),
"a_min": float64(1),
"a_mean": float64(1),
"a_last": float64(1),
"b_count": float64(1), //b
"b_max": float64(3),
"b_min": float64(3),
"b_mean": float64(3),
"b_last": float64(3),
"c_count": float64(1), //c
"c_max": float64(4),
"c_min": float64(4),
"c_mean": float64(4),
"c_last": float64(4),
"d_count": float64(1), //d
"d_max": float64(6),
"d_min": float64(6),
"d_mean": float64(6),
"d_last": float64(6),
"e_count": float64(1), //e
"e_max": float64(200),
"e_min": float64(200),
"e_mean": float64(200),
"e_last": float64(200),
"f_count": float64(1), //f
"f_max": float64(200),
"f_min": float64(200),
"f_mean": float64(200),
"f_last": float64(200),
"g_count": float64(1), //g
"g_max": float64(1),
"g_min": float64(1),
"g_mean": float64(1),
"g_last": float64(1),
}
expectedTags = map[string]string{
"foo": "bar",
Expand Down Expand Up @@ -616,7 +629,7 @@ func TestBasicStatsWithAllStats(t *testing.T) {
acc := testutil.Accumulator{}
minmax := NewBasicStats()
minmax.Log = testutil.Logger{}
minmax.Stats = []string{"count", "min", "max", "mean", "stdev", "s2", "sum"}
minmax.Stats = []string{"count", "min", "max", "mean", "stdev", "s2", "sum", "last"}
minmax.getConfiguredStats()

minmax.Add(m1)
Expand All @@ -631,12 +644,14 @@ func TestBasicStatsWithAllStats(t *testing.T) {
"a_stdev": float64(0),
"a_s2": float64(0),
"a_sum": float64(2),
"a_last": float64(1),
"b_count": float64(2), //b
"b_max": float64(3),
"b_min": float64(1),
"b_mean": float64(2),
"b_s2": float64(2),
"b_sum": float64(4),
"b_last": float64(3),
"b_stdev": math.Sqrt(2),
"c_count": float64(2), //c
"c_max": float64(4),
Expand All @@ -645,30 +660,35 @@ func TestBasicStatsWithAllStats(t *testing.T) {
"c_s2": float64(2),
"c_stdev": math.Sqrt(2),
"c_sum": float64(6),
"c_last": float64(4),
"d_count": float64(2), //d
"d_max": float64(6),
"d_min": float64(2),
"d_mean": float64(4),
"d_s2": float64(8),
"d_stdev": math.Sqrt(8),
"d_sum": float64(8),
"d_last": float64(6),
"e_count": float64(1), //e
"e_max": float64(200),
"e_min": float64(200),
"e_mean": float64(200),
"e_sum": float64(200),
"e_last": float64(200),
"f_count": float64(1), //f
"f_max": float64(200),
"f_min": float64(200),
"f_mean": float64(200),
"f_sum": float64(200),
"f_last": float64(200),
"g_count": float64(2), //g
"g_max": float64(3),
"g_min": float64(1),
"g_mean": float64(2),
"g_s2": float64(2),
"g_stdev": math.Sqrt(2),
"g_sum": float64(4),
"g_last": float64(1),
}
expectedTags := map[string]string{
"foo": "bar",
Expand Down Expand Up @@ -731,3 +751,30 @@ func TestBasicStatsWithDefaultStats(t *testing.T) {
require.True(t, acc.HasField("m1", "a_s2"))
require.False(t, acc.HasField("m1", "a_sum"))
}

func TestBasicStatsWithOnlyLast(t *testing.T) {
aggregator := NewBasicStats()
aggregator.Stats = []string{"last"}
aggregator.Log = testutil.Logger{}
aggregator.getConfiguredStats()

aggregator.Add(m1)
aggregator.Add(m2)

acc := testutil.Accumulator{}
aggregator.Push(&acc)

expectedFields := map[string]interface{}{
"a_last": float64(1),
"b_last": float64(3),
"c_last": float64(4),
"d_last": float64(6),
"e_last": float64(200),
"f_last": float64(200),
"g_last": float64(1),
}
expectedTags := map[string]string{
"foo": "bar",
}
acc.AssertContainsTaggedFields(t, "m1", expectedFields, expectedTags)
}
2 changes: 1 addition & 1 deletion plugins/aggregators/basicstats/sample.conf
Expand Up @@ -8,4 +8,4 @@
drop_original = false

## Configures which basic stats to push as fields
# stats = ["count","diff","rate","min","max","mean","non_negative_diff","non_negative_rate","percent_change","stdev","s2","sum","interval"]
# stats = ["count","diff","rate","min","max","mean","non_negative_diff","non_negative_rate","percent_change","stdev","s2","sum","interval","last"]