148 lines
3.8 KiB
Go
148 lines
3.8 KiB
Go
|
//go:generate ../../../tools/readme_config_includer/generator
|
||
|
package scale
|
||
|
|
||
|
import (
|
||
|
_ "embed"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/influxdata/telegraf"
|
||
|
"github.com/influxdata/telegraf/filter"
|
||
|
"github.com/influxdata/telegraf/internal"
|
||
|
"github.com/influxdata/telegraf/plugins/processors"
|
||
|
)
|
||
|
|
||
|
//go:embed sample.conf
|
||
|
var sampleConfig string
|
||
|
|
||
|
func (*Scale) SampleConfig() string {
|
||
|
return sampleConfig
|
||
|
}
|
||
|
|
||
|
type Scaling struct {
|
||
|
InMin *float64 `toml:"input_minimum"`
|
||
|
InMax *float64 `toml:"input_maximum"`
|
||
|
OutMin *float64 `toml:"output_minimum"`
|
||
|
OutMax *float64 `toml:"output_maximum"`
|
||
|
Factor *float64 `toml:"factor"`
|
||
|
Offset *float64 `toml:"offset"`
|
||
|
Fields []string `toml:"fields"`
|
||
|
|
||
|
fieldFilter filter.Filter
|
||
|
scale float64
|
||
|
shiftIn float64
|
||
|
shiftOut float64
|
||
|
}
|
||
|
|
||
|
type Scale struct {
|
||
|
Scalings []Scaling `toml:"scaling"`
|
||
|
Log telegraf.Logger `toml:"-"`
|
||
|
}
|
||
|
|
||
|
func (s *Scaling) Init() error {
|
||
|
s.scale, s.shiftOut, s.shiftIn = float64(1.0), float64(0.0), float64(0.0)
|
||
|
allMinMaxSet := s.OutMax != nil && s.OutMin != nil && s.InMax != nil && s.InMin != nil
|
||
|
anyMinMaxSet := s.OutMax != nil || s.OutMin != nil || s.InMax != nil || s.InMin != nil
|
||
|
factorSet := s.Factor != nil || s.Offset != nil
|
||
|
if anyMinMaxSet && factorSet {
|
||
|
return fmt.Errorf("cannot use factor/offset and minimum/maximum at the same time for fields %s",
|
||
|
strings.Join(s.Fields, ","))
|
||
|
} else if anyMinMaxSet && !allMinMaxSet {
|
||
|
return fmt.Errorf("all minimum and maximum values need to be set for fields %s", strings.Join(s.Fields, ","))
|
||
|
} else if !anyMinMaxSet && !factorSet {
|
||
|
return fmt.Errorf("no scaling defined for fields %s", strings.Join(s.Fields, ","))
|
||
|
} else if allMinMaxSet {
|
||
|
if *s.InMax == *s.InMin {
|
||
|
return fmt.Errorf("input minimum and maximum are equal for fields %s", strings.Join(s.Fields, ","))
|
||
|
}
|
||
|
|
||
|
if *s.OutMax == *s.OutMin {
|
||
|
return fmt.Errorf("output minimum and maximum are equal for fields %s", strings.Join(s.Fields, ","))
|
||
|
}
|
||
|
|
||
|
s.scale = (*s.OutMax - *s.OutMin) / (*s.InMax - *s.InMin)
|
||
|
s.shiftOut = *s.OutMin
|
||
|
s.shiftIn = *s.InMin
|
||
|
} else {
|
||
|
if s.Factor != nil {
|
||
|
s.scale = *s.Factor
|
||
|
}
|
||
|
if s.Offset != nil {
|
||
|
s.shiftOut = *s.Offset
|
||
|
}
|
||
|
}
|
||
|
|
||
|
scalingFilter, err := filter.Compile(s.Fields)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("could not compile fields filter: %w", err)
|
||
|
}
|
||
|
s.fieldFilter = scalingFilter
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// scale a float according to the input and output range
|
||
|
func (s *Scaling) process(value float64) float64 {
|
||
|
return s.scale*(value-s.shiftIn) + s.shiftOut
|
||
|
}
|
||
|
|
||
|
func (s *Scale) Init() error {
|
||
|
if s.Scalings == nil {
|
||
|
return errors.New("no valid scaling defined")
|
||
|
}
|
||
|
|
||
|
allFields := make(map[string]bool)
|
||
|
for i := range s.Scalings {
|
||
|
for _, field := range s.Scalings[i].Fields {
|
||
|
// only generate a warning for the first duplicate field filter
|
||
|
if warn, ok := allFields[field]; ok && warn {
|
||
|
s.Log.Warnf("Filter field %q used twice in scalings", field)
|
||
|
allFields[field] = false
|
||
|
} else {
|
||
|
allFields[field] = true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if err := s.Scalings[i].Init(); err != nil {
|
||
|
return fmt.Errorf("scaling %d: %w", i+1, err)
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// handle the scaling process
|
||
|
func (s *Scale) scaleValues(metric telegraf.Metric) {
|
||
|
fields := metric.FieldList()
|
||
|
|
||
|
for _, scaling := range s.Scalings {
|
||
|
for _, field := range fields {
|
||
|
if !scaling.fieldFilter.Match(field.Key) {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
v, err := internal.ToFloat64(field.Value)
|
||
|
if err != nil {
|
||
|
s.Log.Errorf("Error converting %q to float: %v", field.Key, err)
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
// scale the field values using the defined scaler
|
||
|
field.Value = scaling.process(v)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *Scale) Apply(in ...telegraf.Metric) []telegraf.Metric {
|
||
|
for _, metric := range in {
|
||
|
s.scaleValues(metric)
|
||
|
}
|
||
|
return in
|
||
|
}
|
||
|
|
||
|
func init() {
|
||
|
processors.Add("scale", func() telegraf.Processor {
|
||
|
return &Scale{}
|
||
|
})
|
||
|
}
|