Files
2018-08-24 12:30:02 -07:00

107 lines
2.4 KiB
Go

package stats
import (
"fmt"
"log"
"math"
"strconv"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/processors"
)
type Stats struct {
StatsField string `toml:"stats_field"`
WindowSize int `toml:"window_size"`
Window Window
Buffer Window
}
type Window struct {
Count int
ValueSum float64
DiffSum float64
Mean float64
Variance float64
Std float64
}
func (s *Stats) SampleConfig() string {
return `
[[processors.stats]]
## field to compile stats for
stats_field = "trace_id"
## number of metrics considered for stats at a time
window_size = 8`
}
func (s *Stats) Description() string {
return "will append a field to each metric indicating the running average of the specified field"
}
func (s *Stats) Apply(in ...telegraf.Metric) []telegraf.Metric {
for _, metric := range in {
value := metric.Fields()[s.StatsField]
sVal := fmt.Sprintf("%v", value)
fVal, err := strconv.ParseFloat(sVal, 64)
if err != nil {
log.Printf("E! cannot convert field: %v to float, %v", s.StatsField, err)
}
// for warmup
if s.Window.Count < s.WindowSize/2 {
s.Window.Count++
s.Window.ValueSum += fVal
s.Window.Mean = s.Window.ValueSum / float64(s.Window.Count)
s.Window.Variance = varianceCalculator(fVal, &s.Window)
s.Window.Std = math.Sqrt(s.Window.Variance)
continue
}
if s.Window.Count >= s.WindowSize {
s.Window = s.Buffer
s.Buffer = Window{}
}
// when window count is more than half, fill the buffer and window
s.Window.Count++
s.Buffer.Count++
s.Window.ValueSum += fVal
s.Buffer.ValueSum += fVal
s.Window.Mean = s.Window.ValueSum / float64(s.Window.Count)
s.Buffer.Mean = s.Buffer.ValueSum / float64(s.Buffer.Count)
s.Window.Variance = varianceCalculator(fVal, &s.Window)
s.Buffer.Variance = varianceCalculator(fVal, &s.Buffer)
s.Window.Std = math.Sqrt(s.Window.Variance)
s.Buffer.Std = math.Sqrt(s.Buffer.Variance)
metric.AddField(s.StatsField+"_mean", s.Window.Mean)
metric.AddField(s.StatsField+"_deviation", s.Window.Std)
metric.AddField(s.StatsField+"_variance", s.Window.Variance)
}
return in
}
func varianceCalculator(currentVal float64, w *Window) float64 {
diff := currentVal - w.Mean
sqrDiff := math.Pow(diff, 2)
w.DiffSum = w.DiffSum + sqrDiff
if w.Count-1 == 0 {
return 0
}
variance := w.DiffSum / float64(w.Count-1)
return variance
}
func init() {
processors.Add("stats", func() telegraf.Processor {
return &Stats{}
})
}