feat(processors.filter): Convert noop processor to filter processor (#14330)
This commit is contained in:
parent
7b5393c9e5
commit
1c2c03d778
|
|
@ -0,0 +1,5 @@
|
||||||
|
//go:build !custom || processors || processors.filter
|
||||||
|
|
||||||
|
package all
|
||||||
|
|
||||||
|
import _ "github.com/influxdata/telegraf/plugins/processors/filter" // register plugin
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
//go:build !custom || processors || processors.noop
|
|
||||||
|
|
||||||
package all
|
|
||||||
|
|
||||||
import _ "github.com/influxdata/telegraf/plugins/processors/noop" // register plugin
|
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
# Filter Processor Plugin
|
||||||
|
|
||||||
|
The filter processor plugin allows to specify a set of rules for metrics
|
||||||
|
with the ability to _keep_ or _drop_ those metrics. It does _not_ change the
|
||||||
|
metric. As such a user might want to apply this processor to remove metrics
|
||||||
|
from the processing/output stream.
|
||||||
|
__NOTE:__ The filtering is _not_ output specific, but will apply to the metrics
|
||||||
|
processed by this processor.
|
||||||
|
|
||||||
|
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
|
||||||
|
|
||||||
|
In addition to the plugin-specific configuration settings, plugins support
|
||||||
|
additional global and plugin configuration settings. These settings are used to
|
||||||
|
modify metrics, tags, and field or create aliases and configure ordering, etc.
|
||||||
|
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||||
|
|
||||||
|
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```toml @sample.conf
|
||||||
|
# Filter metrics by the given criteria
|
||||||
|
[[processors.filter]]
|
||||||
|
## Default action if no rule applies
|
||||||
|
# default = "pass"
|
||||||
|
|
||||||
|
## Rules to apply on the incoming metrics (multiple rules are possible)
|
||||||
|
## The rules are evaluated in order and the first matching rule is applied.
|
||||||
|
## In case no rule matches the "default" is applied.
|
||||||
|
## All filter criteria in a rule must apply for the rule to match the metric
|
||||||
|
## i.e. the criteria are combined by a logical AND. If a criterion is
|
||||||
|
## omitted it is NOT applied at all and ignored.
|
||||||
|
[[processors.filter.rule]]
|
||||||
|
## List of metric names to match including glob expressions
|
||||||
|
# name = []
|
||||||
|
|
||||||
|
## List of tag key/values pairs to match including glob expressions
|
||||||
|
## ALL given tags keys must exist and at least one value must match
|
||||||
|
## for the metric to match the rule.
|
||||||
|
# tags = {}
|
||||||
|
|
||||||
|
## List of field keys to match including glob expressions
|
||||||
|
## At least one field must exist for the metric to match the rule.
|
||||||
|
# fields = []
|
||||||
|
|
||||||
|
## Action to apply for this rule
|
||||||
|
## "pass" will keep the metric and pass it on, while "drop" will remove
|
||||||
|
## the metric
|
||||||
|
# action = "drop"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Consider a use-case where you collected a bunch of metrics
|
||||||
|
|
||||||
|
```text
|
||||||
|
machine,source="machine1",status="OK" operating_hours=37i,temperature=23.1
|
||||||
|
machine,source="machine2",status="warning" operating_hours=1433i,temperature=48.9,message="too hot"
|
||||||
|
machine,source="machine3",status="OK" operating_hours=811i,temperature=29.5
|
||||||
|
machine,source="machine4",status="failure" operating_hours=1009i,temperature=67.3,message="temperature alert"
|
||||||
|
```
|
||||||
|
|
||||||
|
but only want to keep the ones indicating a `status` of `failure` or `warning`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[processors.filter]]
|
||||||
|
namepass = ["machine"]
|
||||||
|
default = "drop"
|
||||||
|
|
||||||
|
[[processors.filter.rule]]
|
||||||
|
tags = {"status" = ["warning", "failure"]}
|
||||||
|
action = "pass"
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can "black-list" the `OK` value via
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[processors.filter]]
|
||||||
|
namepass = ["machine"]
|
||||||
|
|
||||||
|
[[processors.filter.rule]]
|
||||||
|
tags = {"status" = "OK"}
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
//go:generate ../../../tools/readme_config_includer/generator
|
||||||
|
package filter
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/plugins/processors"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed sample.conf
|
||||||
|
var sampleConfig string
|
||||||
|
|
||||||
|
type Filter struct {
|
||||||
|
Rules []rule `toml:"rule"`
|
||||||
|
DefaultAction string `toml:"default"`
|
||||||
|
Log telegraf.Logger `toml:"-"`
|
||||||
|
defaultPass bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Filter) SampleConfig() string {
|
||||||
|
return sampleConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Filter) Init() error {
|
||||||
|
// Check the default-action setting
|
||||||
|
switch f.DefaultAction {
|
||||||
|
case "", "pass":
|
||||||
|
f.defaultPass = true
|
||||||
|
case "drop":
|
||||||
|
// Do nothing, those options are valid
|
||||||
|
if len(f.Rules) == 0 {
|
||||||
|
f.Log.Warn("dropping all metrics as no rule is provided")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid default action %q", f.DefaultAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check and initialize rules
|
||||||
|
for i := range f.Rules {
|
||||||
|
if err := f.Rules[i].init(); err != nil {
|
||||||
|
return fmt.Errorf("initialization of rule %d failed: %w", i+1, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Filter) Apply(in ...telegraf.Metric) []telegraf.Metric {
|
||||||
|
out := make([]telegraf.Metric, 0, len(in))
|
||||||
|
for _, m := range in {
|
||||||
|
if f.applyRules(m) {
|
||||||
|
out = append(out, m)
|
||||||
|
} else {
|
||||||
|
m.Drop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Filter) applyRules(m telegraf.Metric) bool {
|
||||||
|
for _, r := range f.Rules {
|
||||||
|
if pass, applies := r.apply(m); applies {
|
||||||
|
return pass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return f.defaultPass
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
processors.Add("Filter", func() telegraf.Processor {
|
||||||
|
return &Filter{}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,661 @@
|
||||||
|
package filter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/metric"
|
||||||
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testmetrics = []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"packing",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine A",
|
||||||
|
"location": "main building",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 37,
|
||||||
|
"temperature": 23.1,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
metric.New(
|
||||||
|
"foundry",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine B",
|
||||||
|
"location": "factory X",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 1337,
|
||||||
|
"temperature": 19.9,
|
||||||
|
"pieces": 96878,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
metric.New(
|
||||||
|
"welding",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine C",
|
||||||
|
"location": "factory X",
|
||||||
|
"status": "failure",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 1009,
|
||||||
|
"temperature": 67.3,
|
||||||
|
"message": "temperature alert",
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
metric.New(
|
||||||
|
"welding",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine D",
|
||||||
|
"location": "factory Y",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 825,
|
||||||
|
"temperature": 31.2,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoRules(t *testing.T) {
|
||||||
|
logger := &testutil.CaptureLogger{}
|
||||||
|
plugin := &Filter{
|
||||||
|
DefaultAction: "drop",
|
||||||
|
Log: logger,
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
warnings := logger.Warnings()
|
||||||
|
require.Len(t, warnings, 1)
|
||||||
|
require.Contains(t, warnings[0], "dropping all metrics")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidDefaultAction(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{{Name: []string{"foo"}}},
|
||||||
|
DefaultAction: "foo",
|
||||||
|
}
|
||||||
|
require.ErrorContains(t, plugin.Init(), "invalid default action")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoMetric(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{{Name: []string{"*"}}},
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
input := []telegraf.Metric{}
|
||||||
|
require.Empty(t, plugin.Apply(input...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDropAll(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{{Name: []string{"*"}}},
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
require.Empty(t, plugin.Apply(testmetrics...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDropDefault(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{{Name: []string{"foo"}, Action: "pass"}},
|
||||||
|
DefaultAction: "drop",
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
require.Empty(t, plugin.Apply(testmetrics...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPassAll(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{{Name: []string{"*"}, Action: "pass"}},
|
||||||
|
DefaultAction: "drop",
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
expected := testmetrics
|
||||||
|
actual := plugin.Apply(testmetrics...)
|
||||||
|
testutil.RequireMetricsEqual(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPassDefault(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{{Name: []string{"foo"}, Action: "drop"}},
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
expected := testmetrics
|
||||||
|
actual := plugin.Apply(testmetrics...)
|
||||||
|
testutil.RequireMetricsEqual(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamePass(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{
|
||||||
|
{
|
||||||
|
Name: []string{"welding"},
|
||||||
|
Action: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DefaultAction: "drop",
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"welding",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine C",
|
||||||
|
"location": "factory X",
|
||||||
|
"status": "failure",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 1009,
|
||||||
|
"temperature": 67.3,
|
||||||
|
"message": "temperature alert",
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
metric.New(
|
||||||
|
"welding",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine D",
|
||||||
|
"location": "factory Y",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 825,
|
||||||
|
"temperature": 31.2,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
actual := plugin.Apply(testmetrics...)
|
||||||
|
testutil.RequireMetricsEqual(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNameDrop(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{
|
||||||
|
{
|
||||||
|
Name: []string{"welding"},
|
||||||
|
Action: "drop",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"packing",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine A",
|
||||||
|
"location": "main building",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 37,
|
||||||
|
"temperature": 23.1,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
metric.New(
|
||||||
|
"foundry",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine B",
|
||||||
|
"location": "factory X",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 1337,
|
||||||
|
"temperature": 19.9,
|
||||||
|
"pieces": 96878,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
actual := plugin.Apply(testmetrics...)
|
||||||
|
testutil.RequireMetricsEqual(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNameGlob(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{
|
||||||
|
{
|
||||||
|
Name: []string{"*ing"},
|
||||||
|
Action: "drop",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"foundry",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine B",
|
||||||
|
"location": "factory X",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 1337,
|
||||||
|
"temperature": 19.9,
|
||||||
|
"pieces": 96878,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
actual := plugin.Apply(testmetrics...)
|
||||||
|
testutil.RequireMetricsEqual(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTagPass(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{
|
||||||
|
{
|
||||||
|
Tags: map[string][]string{"status": {"OK"}},
|
||||||
|
Action: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DefaultAction: "drop",
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"packing",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine A",
|
||||||
|
"location": "main building",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 37,
|
||||||
|
"temperature": 23.1,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
metric.New(
|
||||||
|
"foundry",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine B",
|
||||||
|
"location": "factory X",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 1337,
|
||||||
|
"temperature": 19.9,
|
||||||
|
"pieces": 96878,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
metric.New(
|
||||||
|
"welding",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine D",
|
||||||
|
"location": "factory Y",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 825,
|
||||||
|
"temperature": 31.2,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
actual := plugin.Apply(testmetrics...)
|
||||||
|
testutil.RequireMetricsEqual(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTagDrop(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{
|
||||||
|
{
|
||||||
|
Tags: map[string][]string{"status": {"OK"}},
|
||||||
|
Action: "drop",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"welding",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine C",
|
||||||
|
"location": "factory X",
|
||||||
|
"status": "failure",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 1009,
|
||||||
|
"temperature": 67.3,
|
||||||
|
"message": "temperature alert",
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
actual := plugin.Apply(testmetrics...)
|
||||||
|
testutil.RequireMetricsEqual(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTagMultiple(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{
|
||||||
|
{
|
||||||
|
Tags: map[string][]string{
|
||||||
|
"location": {"factory X", "factory Y"},
|
||||||
|
"status": {"OK"},
|
||||||
|
},
|
||||||
|
Action: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DefaultAction: "drop",
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"foundry",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine B",
|
||||||
|
"location": "factory X",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 1337,
|
||||||
|
"temperature": 19.9,
|
||||||
|
"pieces": 96878,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
metric.New(
|
||||||
|
"welding",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine D",
|
||||||
|
"location": "factory Y",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 825,
|
||||||
|
"temperature": 31.2,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
actual := plugin.Apply(testmetrics...)
|
||||||
|
testutil.RequireMetricsEqual(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTagGlob(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{
|
||||||
|
{
|
||||||
|
Tags: map[string][]string{"location": {"factory *"}},
|
||||||
|
Action: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DefaultAction: "drop",
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"foundry",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine B",
|
||||||
|
"location": "factory X",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 1337,
|
||||||
|
"temperature": 19.9,
|
||||||
|
"pieces": 96878,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
metric.New(
|
||||||
|
"welding",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine C",
|
||||||
|
"location": "factory X",
|
||||||
|
"status": "failure",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 1009,
|
||||||
|
"temperature": 67.3,
|
||||||
|
"message": "temperature alert",
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
metric.New(
|
||||||
|
"welding",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine D",
|
||||||
|
"location": "factory Y",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 825,
|
||||||
|
"temperature": 31.2,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
actual := plugin.Apply(testmetrics...)
|
||||||
|
testutil.RequireMetricsEqual(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTagDoesNotExist(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{
|
||||||
|
{
|
||||||
|
Tags: map[string][]string{
|
||||||
|
"operator": {"peter"},
|
||||||
|
"status": {"OK"},
|
||||||
|
},
|
||||||
|
Action: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DefaultAction: "drop",
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
require.Empty(t, plugin.Apply(testmetrics...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFieldPass(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{
|
||||||
|
{
|
||||||
|
Fields: []string{"message", "pieces"},
|
||||||
|
Action: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DefaultAction: "drop",
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"foundry",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine B",
|
||||||
|
"location": "factory X",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 1337,
|
||||||
|
"temperature": 19.9,
|
||||||
|
"pieces": 96878,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
metric.New(
|
||||||
|
"welding",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine C",
|
||||||
|
"location": "factory X",
|
||||||
|
"status": "failure",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 1009,
|
||||||
|
"temperature": 67.3,
|
||||||
|
"message": "temperature alert",
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
actual := plugin.Apply(testmetrics...)
|
||||||
|
testutil.RequireMetricsEqual(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFieldDrop(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{
|
||||||
|
{
|
||||||
|
Fields: []string{"message", "pieces"},
|
||||||
|
Action: "drop",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"packing",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine A",
|
||||||
|
"location": "main building",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 37,
|
||||||
|
"temperature": 23.1,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
metric.New(
|
||||||
|
"welding",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine D",
|
||||||
|
"location": "factory Y",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 825,
|
||||||
|
"temperature": 31.2,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
actual := plugin.Apply(testmetrics...)
|
||||||
|
testutil.RequireMetricsEqual(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFieldGlob(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{
|
||||||
|
{
|
||||||
|
Fields: []string{"{message,piece*}"},
|
||||||
|
Action: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DefaultAction: "drop",
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"foundry",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine B",
|
||||||
|
"location": "factory X",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 1337,
|
||||||
|
"temperature": 19.9,
|
||||||
|
"pieces": 96878,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
metric.New(
|
||||||
|
"welding",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine C",
|
||||||
|
"location": "factory X",
|
||||||
|
"status": "failure",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 1009,
|
||||||
|
"temperature": 67.3,
|
||||||
|
"message": "temperature alert",
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
actual := plugin.Apply(testmetrics...)
|
||||||
|
testutil.RequireMetricsEqual(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRuleOrder(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{
|
||||||
|
{
|
||||||
|
Name: []string{"welding"},
|
||||||
|
Action: "drop",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: []string{"welding"},
|
||||||
|
Action: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DefaultAction: "drop",
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
require.Empty(t, plugin.Apply(testmetrics...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRuleMultiple(t *testing.T) {
|
||||||
|
plugin := &Filter{
|
||||||
|
Rules: []rule{
|
||||||
|
{
|
||||||
|
Name: []string{"welding"},
|
||||||
|
Action: "drop",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: []string{"foundry"},
|
||||||
|
Action: "drop",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DefaultAction: "pass",
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"packing",
|
||||||
|
map[string]string{
|
||||||
|
"source": "machine A",
|
||||||
|
"location": "main building",
|
||||||
|
"status": "OK",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"operating_hours": 37,
|
||||||
|
"temperature": 23.1,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
actual := plugin.Apply(testmetrics...)
|
||||||
|
testutil.RequireMetricsEqual(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
package filter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/filter"
|
||||||
|
)
|
||||||
|
|
||||||
|
type rule struct {
|
||||||
|
Name []string `toml:"name"`
|
||||||
|
Tags map[string][]string `toml:"tags"`
|
||||||
|
Fields []string `toml:"fields"`
|
||||||
|
Action string `toml:"action"`
|
||||||
|
|
||||||
|
nameFilter filter.Filter
|
||||||
|
fieldFilter filter.Filter
|
||||||
|
tagFilters map[string]filter.Filter
|
||||||
|
pass bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rule) init() error {
|
||||||
|
// Check the action setting
|
||||||
|
switch r.Action {
|
||||||
|
case "pass":
|
||||||
|
r.pass = true
|
||||||
|
case "", "drop":
|
||||||
|
// Do nothing, those options are valid
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid action %q", r.Action)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile the filters
|
||||||
|
var err error
|
||||||
|
r.nameFilter, err = filter.Compile(r.Name)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("creating name filter failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.fieldFilter, err = filter.Compile(r.Fields)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("creating fields filter failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.tagFilters = make(map[string]filter.Filter, len(r.Tags))
|
||||||
|
for k, values := range r.Tags {
|
||||||
|
r.tagFilters[k], err = filter.Compile(values)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("creating tag filter for tag %q failed: %w", k, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rule) apply(m telegraf.Metric) (pass, applies bool) {
|
||||||
|
// Check the metric name
|
||||||
|
if r.nameFilter != nil {
|
||||||
|
if !r.nameFilter.Match(m.Name()) {
|
||||||
|
return true, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the tags if given
|
||||||
|
tags := m.Tags()
|
||||||
|
for k, f := range r.tagFilters {
|
||||||
|
if value, found := tags[k]; !found || !f.Match(value) {
|
||||||
|
return true, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the field names
|
||||||
|
if r.fieldFilter != nil {
|
||||||
|
var matches bool
|
||||||
|
for _, field := range m.FieldList() {
|
||||||
|
if r.fieldFilter.Match(field.Key) {
|
||||||
|
matches = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !matches {
|
||||||
|
return true, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.pass, true
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Filter metrics by the given criteria
|
||||||
|
[[processors.filter]]
|
||||||
|
## Default action if no rule applies
|
||||||
|
# default = "pass"
|
||||||
|
|
||||||
|
## Rules to apply on the incoming metrics (multiple rules are possible)
|
||||||
|
## The rules are evaluated in order and the first matching rule is applied.
|
||||||
|
## In case no rule matches the "default" is applied.
|
||||||
|
## All filter criteria in a rule must apply for the rule to match the metric
|
||||||
|
## i.e. the criteria are combined by a logical AND. If a criterion is
|
||||||
|
## omitted it is NOT applied at all and ignored.
|
||||||
|
[[processors.filter.rule]]
|
||||||
|
## List of metric names to match including glob expressions
|
||||||
|
# name = []
|
||||||
|
|
||||||
|
## List of tag key/values pairs to match including glob expressions
|
||||||
|
## ALL given tags keys must exist and at least one value must match
|
||||||
|
## for the metric to match the rule.
|
||||||
|
# tags = {}
|
||||||
|
|
||||||
|
## List of field keys to match including glob expressions
|
||||||
|
## At least one field must exist for the metric to match the rule.
|
||||||
|
# fields = []
|
||||||
|
|
||||||
|
## Action to apply for this rule
|
||||||
|
## "pass" will keep the metric and pass it on, while "drop" will remove
|
||||||
|
## the metric
|
||||||
|
# action = "drop"
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
# Noop Processor Plugin
|
|
||||||
|
|
||||||
The noop processor plugin does nothing to metrics. Instead it can be used to
|
|
||||||
apply the global configuration options after other processing. Global config
|
|
||||||
options like tagpass, fieldpass, and others are applied before a processor,
|
|
||||||
aggregator, or output. As such a user might want to apply these after doing
|
|
||||||
processing, but before an output or another processor.
|
|
||||||
|
|
||||||
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
|
|
||||||
|
|
||||||
In addition to the plugin-specific configuration settings, plugins support
|
|
||||||
additional global and plugin configuration settings. These settings are used to
|
|
||||||
modify metrics, tags, and field or create aliases and configure ordering, etc.
|
|
||||||
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
|
||||||
|
|
||||||
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
```toml @sample.conf
|
|
||||||
# Do nothing processor
|
|
||||||
[[processors.noop]]
|
|
||||||
|
|
||||||
## Metric Filtering
|
|
||||||
## The following options provide mechanisms to include or exclude entire
|
|
||||||
## metrics. For specific details and examples see the metric filtering docs:
|
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/CONFIGURATION.md#metric-filtering
|
|
||||||
|
|
||||||
## Metric Selectors - These will drop entire metrics
|
|
||||||
## Filter on metric name or tag key + value
|
|
||||||
# namepass = []
|
|
||||||
# namedrop = []
|
|
||||||
# tagpass = {}
|
|
||||||
# tagdrop = {}
|
|
||||||
|
|
||||||
## Filter on Common Expression Language (CEL) expression
|
|
||||||
# metricpass = ""
|
|
||||||
|
|
||||||
## Metric Modifiers - These will drop tags and fields from metrics
|
|
||||||
## Filter on tag key or field key
|
|
||||||
# taginclude = []
|
|
||||||
# tagexclude = []
|
|
||||||
# fieldpass = []
|
|
||||||
# fielddrop = []
|
|
||||||
```
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
Consider a use-case where you have processed a metric based on a tag, but no
|
|
||||||
longer need that tag for additional processing:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[[processors.ifname]]
|
|
||||||
order = 1
|
|
||||||
...
|
|
||||||
|
|
||||||
[[processors.noop]]
|
|
||||||
order = 2
|
|
||||||
tagexclude = ["useless_tag"]
|
|
||||||
```
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
//go:generate ../../../tools/readme_config_includer/generator
|
|
||||||
package noop
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/plugins/processors"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed sample.conf
|
|
||||||
var sampleConfig string
|
|
||||||
|
|
||||||
type Noop struct{}
|
|
||||||
|
|
||||||
func (*Noop) SampleConfig() string {
|
|
||||||
return sampleConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Noop) Apply(in ...telegraf.Metric) []telegraf.Metric {
|
|
||||||
return in
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
processors.Add("noop", func() telegraf.Processor {
|
|
||||||
return &Noop{}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
package noop
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/testutil"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNoopNoMetric(t *testing.T) {
|
|
||||||
processor := Noop{}
|
|
||||||
|
|
||||||
m := []telegraf.Metric{}
|
|
||||||
actual := processor.Apply(m...)
|
|
||||||
require.Empty(t, actual)
|
|
||||||
testutil.RequireMetricsEqual(t, m, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNoopSingleMetric(t *testing.T) {
|
|
||||||
processor := Noop{}
|
|
||||||
|
|
||||||
m := []telegraf.Metric{
|
|
||||||
testutil.MustMetric(
|
|
||||||
"test",
|
|
||||||
map[string]string{
|
|
||||||
"tag": "tag_value",
|
|
||||||
},
|
|
||||||
map[string]interface{}{
|
|
||||||
"value": 42,
|
|
||||||
},
|
|
||||||
time.Now(),
|
|
||||||
telegraf.Gauge,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
actual := processor.Apply(m...)
|
|
||||||
require.Len(t, actual, 1)
|
|
||||||
testutil.RequireMetricsEqual(t, m, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNoopMultipleMetrics(t *testing.T) {
|
|
||||||
processor := Noop{}
|
|
||||||
|
|
||||||
m := []telegraf.Metric{
|
|
||||||
testutil.MustMetric(
|
|
||||||
"test",
|
|
||||||
map[string]string{
|
|
||||||
"tag": "tag_value",
|
|
||||||
},
|
|
||||||
map[string]interface{}{
|
|
||||||
"value": 42,
|
|
||||||
},
|
|
||||||
time.Now(),
|
|
||||||
telegraf.Gauge,
|
|
||||||
),
|
|
||||||
testutil.MustMetric(
|
|
||||||
"test",
|
|
||||||
map[string]string{
|
|
||||||
"tag": "tag_value",
|
|
||||||
},
|
|
||||||
map[string]interface{}{
|
|
||||||
"value": 42,
|
|
||||||
},
|
|
||||||
time.Now(),
|
|
||||||
telegraf.Gauge,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
actual := processor.Apply(m...)
|
|
||||||
require.Len(t, actual, 2)
|
|
||||||
testutil.RequireMetricsEqual(t, m, actual)
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
# Do nothing processor
|
|
||||||
[[processors.noop]]
|
|
||||||
|
|
||||||
## Metric Filtering
|
|
||||||
## The following options provide mechanisms to include or exclude entire
|
|
||||||
## metrics. For specific details and examples see the metric filtering docs:
|
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/CONFIGURATION.md#metric-filtering
|
|
||||||
|
|
||||||
## Metric Selectors - These will drop entire metrics
|
|
||||||
## Filter on metric name or tag key + value
|
|
||||||
# namepass = []
|
|
||||||
# namedrop = []
|
|
||||||
# tagpass = {}
|
|
||||||
# tagdrop = {}
|
|
||||||
|
|
||||||
## Filter on Common Expression Language (CEL) expression
|
|
||||||
# metricpass = ""
|
|
||||||
|
|
||||||
## Metric Modifiers - These will drop tags and fields from metrics
|
|
||||||
## Filter on tag key or field key
|
|
||||||
# taginclude = []
|
|
||||||
# tagexclude = []
|
|
||||||
# fieldpass = []
|
|
||||||
# fielddrop = []
|
|
||||||
Loading…
Reference in New Issue