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: Add noise plugin #10057

Merged
merged 19 commits into from
Jan 12, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/LICENSE_OF_DEPENDENCIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ following works:
- go.uber.org/atomic [MIT License](https://pkg.go.dev/go.uber.org/atomic?tab=licenses)
- go.uber.org/multierr [MIT License](https://pkg.go.dev/go.uber.org/multierr?tab=licenses)
- golang.org/x/crypto [BSD 3-Clause Clear License](https://github.com/golang/crypto/blob/master/LICENSE)
- golang.org/x/exp [BSD 3-Clause Clear License](https://github.com/golang/exp/blob/master/LICENSE)
- golang.org/x/net [BSD 3-Clause Clear License](https://github.com/golang/net/blob/master/LICENSE)
- golang.org/x/oauth2 [BSD 3-Clause "New" or "Revised" License](https://github.com/golang/oauth2/blob/master/LICENSE)
- golang.org/x/sync [BSD 3-Clause "New" or "Revised" License](https://github.com/golang/sync/blob/master/LICENSE)
Expand All @@ -256,6 +257,7 @@ following works:
- golang.org/x/xerrors [BSD 3-Clause Clear License](https://github.com/golang/xerrors/blob/master/LICENSE)
- golang.zx2c4.com/wireguard [MIT License](https://github.com/WireGuard/wgctrl-go/blob/master/LICENSE.md)
- golang.zx2c4.com/wireguard/wgctrl [MIT License](https://github.com/WireGuard/wgctrl-go/blob/master/LICENSE.md)
- gonum.org/v1/gonum [BSD 3-Clause "New" or "Revised" License](https://github.com/gonum/gonum/blob/master/LICENSE)
- google.golang.org/api [BSD 3-Clause "New" or "Revised" License](https://github.com/googleapis/google-api-go-client/blob/master/LICENSE)
- google.golang.org/genproto [Apache License 2.0](https://github.com/google/go-genproto/blob/master/LICENSE)
- google.golang.org/grpc [Apache License 2.0](https://github.com/grpc/grpc-go/blob/master/LICENSE)
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,11 @@ require (
go.opentelemetry.io/otel/sdk/export/metric v0.24.0 // indirect
go.opentelemetry.io/otel/trace v1.0.1 // indirect
go.opentelemetry.io/proto/otlp v0.9.0 // indirect
gonum.org/v1/gonum v0.9.3
)

require golang.org/x/exp v0.0.0-20200513190911-00229845015e // indirect

// replaced due to https://github.com/satori/go.uuid/issues/73
replace github.com/satori/go.uuid => github.com/gofrs/uuid v3.2.0+incompatible

Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2709,6 +2709,7 @@ gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
gonum.org/v1/gonum v0.9.3 h1:DnoIG+QAMaF5NvxnGe/oKsgKcAc6PcUyl8q0VetfQ8s=
gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0=
gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY=
Expand Down
1 change: 1 addition & 0 deletions plugins/processors/all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
_ "github.com/influxdata/telegraf/plugins/processors/execd"
_ "github.com/influxdata/telegraf/plugins/processors/filepath"
_ "github.com/influxdata/telegraf/plugins/processors/ifname"
_ "github.com/influxdata/telegraf/plugins/processors/noise"
_ "github.com/influxdata/telegraf/plugins/processors/override"
_ "github.com/influxdata/telegraf/plugins/processors/parser"
_ "github.com/influxdata/telegraf/plugins/processors/pivot"
Expand Down
58 changes: 58 additions & 0 deletions plugins/processors/noise/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Noise Processor

The *Noise* processor is used to add noise to numerical field values. For each field a noise is generated using a defined probability densitiy function and added to the value. The function type can be configured as _Laplace_, _Gaussian_ or _Uniform_.
Depending on the function, various parameters need to be configured:

shypard marked this conversation as resolved.
Show resolved Hide resolved
### Laplacian
* `noise_type = laplacian`
* `scale`: also referred to as _diversity_ parameter, regulates the width & height of the function, a bigger `scale` value means a higher probability of larger noise, default set to 1.0
* `mu`: location of the curve, default set to 0.0

### Gaussian
* `noise_type = gaussian`
* `mu`: mean value, default set to 0.0
* `scale`: standard deviation, default set to 1.0

### Uniform
* `noise_type = uniform`
* `min`: minimal interval value, default set to -1.0
* `max`: maximal interval value, default set to 1.0

### Configuration
Depending on the choice of the distribution function, the respective parameters must be set. Default settings are `noise_type = "laplacian"` with `mu = 0.0` and `scale = 1.0`:

```toml
[[processors.noise]]
scale = 1.0
mu = 0.0
noise_type = "laplacian"
```

Using the `include_fields` and `exclude_fields` options a filter can be configured to apply noise only to numeric fields matching it.

### Example
Add noise to each value the *Inputs.CPU* plugin generates, except for the _usage\_steal_, _usage\_user_, _uptime\_format_, _usage\_idle_ field and all fields of the metrics _swap_, _disk_ and _net_:


```toml
[[inputs.cpu]]
percpu = true
totalcpu = true
collect_cpu_time = false
report_active = false

[[processors.noise]]
scale = 1.0
mu = 0.0
noise_type = "laplacian"
include_fields = []
exclude_fields = ["usage_steal", "usage_user", "uptime_format", "usage_idle" ]
reimda marked this conversation as resolved.
Show resolved Hide resolved
namedrop = ["swap", "disk", "net"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using namepass = ["cpu"] would make more sense in my opinion.

```

Result of noise added to the _cpu_ metric:

```diff
- cpu map[cpu:cpu11 host:98d5b8dbad1c] map[usage_guest:0 usage_guest_nice:0 usage_idle:94.3999999994412 usage_iowait:0 usage_irq:0.1999999999998181 usage_nice:0 usage_softirq:0.20000000000209184 usage_steal:0 usage_system:1.2000000000080036 usage_user:4.000000000014552]
+ cpu map[cpu:cpu11 host:98d5b8dbad1c] map[usage_guest:1.0078071583066057 usage_guest_nice:0.523063861602435 usage_idle:95.53920223476884 usage_iowait:0.5162661526251292 usage_irq:0.7138529816101375 usage_nice:0.6119678488887954 usage_softirq:0.5573585443688622 usage_steal:0.2006120911289802 usage_system:1.2954475820198437 usage_user:6.885664792615023]
```
157 changes: 157 additions & 0 deletions plugins/processors/noise/noise.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package noise

import (
"fmt"
"math"
"reflect"

"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/filter"
"github.com/influxdata/telegraf/plugins/processors"
"gonum.org/v1/gonum/stat/distuv"
)

const (
defaultScale = 1.0
defaultMin = -1.0
defaultMax = 1.0
defaultMu = 0.0
defaultNoiseType = "laplacian"
)

const sampleConfig = `
[[processors.noise]]
shypard marked this conversation as resolved.
Show resolved Hide resolved
## Specified the type of the random distribution.
## Can be "laplacian", "gaussian" or "uniform".
# type = "laplacian

## Center of the distribution.
## Only used for Laplacian and Gaussian distributions.
# mu = 0.0

## Scale parameter for the Laplacian or Gaussian distribution
# scale = 1.0

## Upper and lower bound of the Uniform distribution
# min = -1.0
# max = 1.0

## Apply the noise only to numeric fields matching the filter criteria below.
## Excludes takes precedence over includes.
# include_fields = []
# exclude_fields = []
`

type Noise struct {
Scale float64 `toml:"scale"`
Min float64 `toml:"min"`
Max float64 `toml:"max"`
Mu float64 `toml:"mu"`
IncludeFields []string `toml:"include_fields"`
ExcludeFields []string `toml:"exclude_fields"`
NoiseType string `toml:"type"`
Log telegraf.Logger `toml:"-"`
generator distuv.Rander
fieldFilter filter.Filter
}

func (p *Noise) SampleConfig() string {
return sampleConfig
}

func (p *Noise) Description() string {
return "Adds noise to numerical fields"
}

// generates a random noise value depending on the defined probability density
// function and adds that to the original value. If any integer overflows
// happen during the calculation, the result is set to MaxInt or 0 (for uint)
func (p *Noise) addNoise(value interface{}) interface{} {
n := p.generator.Rand()
switch v := value.(type) {
case int:
case int8:
case int16:
case int32:
case int64:
if v > 0 && (n > math.Nextafter(float64(math.MaxInt64), 0) || int64(n) > math.MaxInt64-v) {
p.Log.Debug("Int64 overflow, setting value to MaxInt64")
return int64(math.MaxInt64)
srebhan marked this conversation as resolved.
Show resolved Hide resolved
}
if v < 0 && (n < math.Nextafter(float64(math.MinInt64), 0) || int64(n) < math.MinInt64-v) {
p.Log.Debug("Int64 (negative) overflow, setting value to MinInt64")
return int64(math.MinInt64)
}
return v + int64(n)
case uint:
case uint8:
case uint16:
case uint32:
case uint64:
if n < 0 {
if uint64(-n) > v {
p.Log.Debug("Uint64 (negative) overflow, setting value to 0")
return uint64(0)
}
return v - uint64(-n)
}
if n > math.Nextafter(float64(math.MaxUint64), 0) || uint64(n) > math.MaxUint64-v {
p.Log.Debug("Uint64 overflow, setting value to MaxUint64")
return uint64(math.MaxUint64)
}
return v + uint64(n)
case float32:
case float64:
return v + n
default:
p.Log.Debugf("Value (%v) type invalid: [%v] is not an int, uint or float", v, reflect.TypeOf(value))
}
return value
}

// Creates a filter for Include and Exclude fields and sets the desired noise
// distribution
func (p *Noise) Init() error {
fieldFilter, err := filter.NewIncludeExcludeFilter(p.IncludeFields, p.ExcludeFields)
if err != nil {
return fmt.Errorf("creating fieldFilter failed: %v", err)
}
p.fieldFilter = fieldFilter

switch p.NoiseType {
case "", "laplacian":
p.generator = &distuv.Laplace{Mu: p.Mu, Scale: p.Scale}
case "uniform":
p.generator = &distuv.Uniform{Min: p.Min, Max: p.Max}
case "gaussian":
p.generator = &distuv.Normal{Mu: p.Mu, Sigma: p.Scale}
default:
return fmt.Errorf("unknown distribution type %q", p.NoiseType)
}
return nil
}

func (p *Noise) Apply(metrics ...telegraf.Metric) []telegraf.Metric {
for _, metric := range metrics {
p.Log.Debugf("Adding noise to [%s]", metric.Name())
shypard marked this conversation as resolved.
Show resolved Hide resolved
for _, field := range metric.FieldList() {
if !p.fieldFilter.Match(field.Key) {
continue
}
field.Value = p.addNoise(field.Value)
}
}
return metrics
}

func init() {
processors.Add("noise", func() telegraf.Processor {
return &Noise{
NoiseType: defaultNoiseType,
Mu: defaultMu,
Scale: defaultScale,
Min: defaultMin,
Max: defaultMax,
}
})
}
Loading