Add diff and non_negative_diff to basicstats aggregator (#4435)

This commit is contained in:
Moritz Maisel 2019-07-31 08:50:49 +02:00 committed by Daniel Nelson
parent df90230341
commit 282c8ce096
3 changed files with 118 additions and 13 deletions

View File

@ -1,6 +1,6 @@
# BasicStats Aggregator Plugin # BasicStats Aggregator Plugin
The BasicStats aggregator plugin give us count,max,min,mean,sum,s2(variance), stdev for a set of values, The BasicStats aggregator plugin give us count,diff,max,min,mean,non_negative_diff,sum,s2(variance), stdev for a set of values,
emitting the aggregate every `period` seconds. emitting the aggregate every `period` seconds.
### Configuration: ### Configuration:
@ -15,20 +15,22 @@ emitting the aggregate every `period` seconds.
drop_original = false drop_original = false
## Configures which basic stats to push as fields ## Configures which basic stats to push as fields
# stats = ["count", "min", "max", "mean", "stdev", "s2", "sum"] # stats = ["count","diff","min","max","mean","non_negative_diff","stdev","s2","sum"]
``` ```
- stats - stats
- If not specified, then `count`, `min`, `max`, `mean`, `stdev`, and `s2` are aggregated and pushed as fields. `sum` is not aggregated by default to maintain backwards compatibility. - If not specified, then `count`, `min`, `max`, `mean`, `stdev`, and `s2` are aggregated and pushed as fields. `sum`, `diff` and `non_negative_diff` are not aggregated by default to maintain backwards compatibility.
- If empty array, no stats are aggregated - If empty array, no stats are aggregated
### Measurements & Fields: ### Measurements & Fields:
- measurement1 - measurement1
- field1_count - field1_count
- field1_diff (difference)
- field1_max - field1_max
- field1_min - field1_min
- field1_mean - field1_mean
- field1_non_negative_diff (non-negative difference)
- field1_sum - field1_sum
- field1_s2 (variance) - field1_s2 (variance)
- field1_stdev (standard deviation) - field1_stdev (standard deviation)
@ -43,8 +45,8 @@ No tags are applied by this aggregator.
$ telegraf --config telegraf.conf --quiet $ telegraf --config telegraf.conf --quiet
system,host=tars load1=1 1475583980000000000 system,host=tars load1=1 1475583980000000000
system,host=tars load1=1 1475583990000000000 system,host=tars load1=1 1475583990000000000
system,host=tars load1_count=2,load1_max=1,load1_min=1,load1_mean=1,load1_sum=2,load1_s2=0,load1_stdev=0 1475584010000000000 system,host=tars load1_count=2,load1_diff=0,load1_max=1,load1_min=1,load1_mean=1,load1_sum=2,load1_s2=0,load1_stdev=0 1475584010000000000
system,host=tars load1=1 1475584020000000000 system,host=tars load1=1 1475584020000000000
system,host=tars load1=3 1475584030000000000 system,host=tars load1=3 1475584030000000000
system,host=tars load1_count=2,load1_max=3,load1_min=1,load1_mean=2,load1_sum=4,load1_s2=2,load1_stdev=1.414162 1475584010000000000 system,host=tars load1_count=2,load1_diff=2,load1_max=3,load1_min=1,load1_mean=2,load1_sum=4,load1_s2=2,load1_stdev=1.414162 1475584010000000000
``` ```

View File

@ -16,13 +16,15 @@ type BasicStats struct {
} }
type configuredStats struct { type configuredStats struct {
count bool count bool
min bool min bool
max bool max bool
mean bool mean bool
variance bool variance bool
stdev bool stdev bool
sum bool sum bool
diff bool
non_negative_diff bool
} }
func NewBasicStats() *BasicStats { func NewBasicStats() *BasicStats {
@ -43,7 +45,9 @@ type basicstats struct {
max float64 max float64
sum float64 sum float64
mean float64 mean float64
M2 float64 //intermedia value for variance/stdev diff float64
M2 float64 //intermediate value for variance/stdev
LAST float64 //intermediate value for diff
} }
var sampleConfig = ` var sampleConfig = `
@ -82,7 +86,9 @@ func (m *BasicStats) Add(in telegraf.Metric) {
max: fv, max: fv,
mean: fv, mean: fv,
sum: fv, sum: fv,
diff: 0.0,
M2: 0.0, M2: 0.0,
LAST: fv,
} }
} }
} }
@ -98,7 +104,9 @@ func (m *BasicStats) Add(in telegraf.Metric) {
max: fv, max: fv,
mean: fv, mean: fv,
sum: fv, sum: fv,
diff: 0.0,
M2: 0.0, M2: 0.0,
LAST: fv,
} }
continue continue
} }
@ -127,6 +135,8 @@ func (m *BasicStats) Add(in telegraf.Metric) {
} }
//sum compute //sum compute
tmp.sum += fv tmp.sum += fv
//diff compute
tmp.diff = fv - tmp.LAST
//store final data //store final data
m.cache[id].fields[field.Key] = tmp m.cache[id].fields[field.Key] = tmp
} }
@ -167,6 +177,13 @@ func (m *BasicStats) Push(acc telegraf.Accumulator) {
if config.stdev { if config.stdev {
fields[k+"_stdev"] = math.Sqrt(variance) fields[k+"_stdev"] = math.Sqrt(variance)
} }
if config.diff {
fields[k+"_diff"] = v.diff
}
if config.non_negative_diff && v.diff >= 0 {
fields[k+"_non_negative_diff"] = v.diff
}
} }
//if count == 1 StdDev = infinite => so I won't send data //if count == 1 StdDev = infinite => so I won't send data
} }
@ -199,6 +216,10 @@ func parseStats(names []string) *configuredStats {
parsed.stdev = true parsed.stdev = true
case "sum": case "sum":
parsed.sum = true parsed.sum = true
case "diff":
parsed.diff = true
case "non_negative_diff":
parsed.non_negative_diff = true
default: default:
log.Printf("W! Unrecognized basic stat '%s', ignoring", name) log.Printf("W! Unrecognized basic stat '%s', ignoring", name)
@ -219,6 +240,7 @@ func defaultStats() *configuredStats {
defaults.variance = true defaults.variance = true
defaults.stdev = true defaults.stdev = true
defaults.sum = false defaults.sum = false
defaults.non_negative_diff = false
return defaults return defaults
} }

View File

@ -17,6 +17,7 @@ var m1, _ = metric.New("m1",
"b": int64(1), "b": int64(1),
"c": float64(2), "c": float64(2),
"d": float64(2), "d": float64(2),
"g": int64(3),
}, },
time.Now(), time.Now(),
) )
@ -31,6 +32,7 @@ var m2, _ = metric.New("m1",
"f": uint64(200), "f": uint64(200),
"ignoreme": "string", "ignoreme": "string",
"andme": true, "andme": true,
"g": int64(1),
}, },
time.Now(), time.Now(),
) )
@ -86,6 +88,12 @@ func TestBasicStatsWithPeriod(t *testing.T) {
"f_max": float64(200), "f_max": float64(200),
"f_min": float64(200), "f_min": float64(200),
"f_mean": float64(200), "f_mean": 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),
} }
expectedTags := map[string]string{ expectedTags := map[string]string{
"foo": "bar", "foo": "bar",
@ -118,6 +126,10 @@ func TestBasicStatsDifferentPeriods(t *testing.T) {
"d_max": float64(2), "d_max": float64(2),
"d_min": float64(2), "d_min": float64(2),
"d_mean": float64(2), "d_mean": float64(2),
"g_count": float64(1), //g
"g_max": float64(3),
"g_min": float64(3),
"g_mean": float64(3),
} }
expectedTags := map[string]string{ expectedTags := map[string]string{
"foo": "bar", "foo": "bar",
@ -153,6 +165,10 @@ func TestBasicStatsDifferentPeriods(t *testing.T) {
"f_max": float64(200), "f_max": float64(200),
"f_min": float64(200), "f_min": float64(200),
"f_mean": float64(200), "f_mean": float64(200),
"g_count": float64(1), //g
"g_max": float64(1),
"g_min": float64(1),
"g_mean": float64(1),
} }
expectedTags = map[string]string{ expectedTags = map[string]string{
"foo": "bar", "foo": "bar",
@ -179,6 +195,7 @@ func TestBasicStatsWithOnlyCount(t *testing.T) {
"d_count": float64(2), "d_count": float64(2),
"e_count": float64(1), "e_count": float64(1),
"f_count": float64(1), "f_count": float64(1),
"g_count": float64(2),
} }
expectedTags := map[string]string{ expectedTags := map[string]string{
"foo": "bar", "foo": "bar",
@ -205,6 +222,7 @@ func TestBasicStatsWithOnlyMin(t *testing.T) {
"d_min": float64(2), "d_min": float64(2),
"e_min": float64(200), "e_min": float64(200),
"f_min": float64(200), "f_min": float64(200),
"g_min": float64(1),
} }
expectedTags := map[string]string{ expectedTags := map[string]string{
"foo": "bar", "foo": "bar",
@ -231,6 +249,7 @@ func TestBasicStatsWithOnlyMax(t *testing.T) {
"d_max": float64(6), "d_max": float64(6),
"e_max": float64(200), "e_max": float64(200),
"f_max": float64(200), "f_max": float64(200),
"g_max": float64(3),
} }
expectedTags := map[string]string{ expectedTags := map[string]string{
"foo": "bar", "foo": "bar",
@ -257,6 +276,7 @@ func TestBasicStatsWithOnlyMean(t *testing.T) {
"d_mean": float64(4), "d_mean": float64(4),
"e_mean": float64(200), "e_mean": float64(200),
"f_mean": float64(200), "f_mean": float64(200),
"g_mean": float64(2),
} }
expectedTags := map[string]string{ expectedTags := map[string]string{
"foo": "bar", "foo": "bar",
@ -283,6 +303,7 @@ func TestBasicStatsWithOnlySum(t *testing.T) {
"d_sum": float64(8), "d_sum": float64(8),
"e_sum": float64(200), "e_sum": float64(200),
"f_sum": float64(200), "f_sum": float64(200),
"g_sum": float64(4),
} }
expectedTags := map[string]string{ expectedTags := map[string]string{
"foo": "bar", "foo": "bar",
@ -359,6 +380,7 @@ func TestBasicStatsWithOnlyVariance(t *testing.T) {
"b_s2": float64(2), "b_s2": float64(2),
"c_s2": float64(2), "c_s2": float64(2),
"d_s2": float64(8), "d_s2": float64(8),
"g_s2": float64(2),
} }
expectedTags := map[string]string{ expectedTags := map[string]string{
"foo": "bar", "foo": "bar",
@ -383,6 +405,7 @@ func TestBasicStatsWithOnlyStandardDeviation(t *testing.T) {
"b_stdev": math.Sqrt(2), "b_stdev": math.Sqrt(2),
"c_stdev": math.Sqrt(2), "c_stdev": math.Sqrt(2),
"d_stdev": math.Sqrt(8), "d_stdev": math.Sqrt(8),
"g_stdev": math.Sqrt(2),
} }
expectedTags := map[string]string{ expectedTags := map[string]string{
"foo": "bar", "foo": "bar",
@ -415,6 +438,57 @@ func TestBasicStatsWithMinAndMax(t *testing.T) {
"e_min": float64(200), "e_min": float64(200),
"f_max": float64(200), //f "f_max": float64(200), //f
"f_min": float64(200), "f_min": float64(200),
"g_max": float64(3), //g
"g_min": float64(1),
}
expectedTags := map[string]string{
"foo": "bar",
}
acc.AssertContainsTaggedFields(t, "m1", expectedFields, expectedTags)
}
// Test only aggregating diff
func TestBasicStatsWithDiff(t *testing.T) {
aggregator := NewBasicStats()
aggregator.Stats = []string{"diff"}
aggregator.Add(m1)
aggregator.Add(m2)
acc := testutil.Accumulator{}
aggregator.Push(&acc)
expectedFields := map[string]interface{}{
"a_diff": float64(0),
"b_diff": float64(2),
"c_diff": float64(2),
"d_diff": float64(4),
"g_diff": float64(-2),
}
expectedTags := map[string]string{
"foo": "bar",
}
acc.AssertContainsTaggedFields(t, "m1", expectedFields, expectedTags)
}
// Test only aggregating non_negative_diff
func TestBasicStatsWithNonNegativeDiff(t *testing.T) {
aggregator := NewBasicStats()
aggregator.Stats = []string{"non_negative_diff"}
aggregator.Add(m1)
aggregator.Add(m2)
acc := testutil.Accumulator{}
aggregator.Push(&acc)
expectedFields := map[string]interface{}{
"a_non_negative_diff": float64(0),
"b_non_negative_diff": float64(2),
"c_non_negative_diff": float64(2),
"d_non_negative_diff": float64(4),
} }
expectedTags := map[string]string{ expectedTags := map[string]string{
"foo": "bar", "foo": "bar",
@ -471,6 +545,13 @@ func TestBasicStatsWithAllStats(t *testing.T) {
"f_min": float64(200), "f_min": float64(200),
"f_mean": float64(200), "f_mean": float64(200),
"f_sum": float64(200), "f_sum": 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),
} }
expectedTags := map[string]string{ expectedTags := map[string]string{
"foo": "bar", "foo": "bar",