Remove metric recreation when filtering (#4767)
This commit is contained in:
parent
cc64b14ab4
commit
7553c8fd13
|
@ -5,6 +5,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/metric"
|
||||||
"github.com/influxdata/telegraf/selfstat"
|
"github.com/influxdata/telegraf/selfstat"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,13 +15,13 @@ var (
|
||||||
|
|
||||||
type MetricMaker interface {
|
type MetricMaker interface {
|
||||||
Name() string
|
Name() string
|
||||||
MakeMetric(
|
MakeMetric(metric telegraf.Metric) telegraf.Metric
|
||||||
measurement string,
|
}
|
||||||
fields map[string]interface{},
|
|
||||||
tags map[string]string,
|
type accumulator struct {
|
||||||
mType telegraf.ValueType,
|
maker MetricMaker
|
||||||
t time.Time,
|
metrics chan telegraf.Metric
|
||||||
) telegraf.Metric
|
precision time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAccumulator(
|
func NewAccumulator(
|
||||||
|
@ -35,23 +36,13 @@ func NewAccumulator(
|
||||||
return &acc
|
return &acc
|
||||||
}
|
}
|
||||||
|
|
||||||
type accumulator struct {
|
|
||||||
metrics chan telegraf.Metric
|
|
||||||
|
|
||||||
maker MetricMaker
|
|
||||||
|
|
||||||
precision time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ac *accumulator) AddFields(
|
func (ac *accumulator) AddFields(
|
||||||
measurement string,
|
measurement string,
|
||||||
fields map[string]interface{},
|
fields map[string]interface{},
|
||||||
tags map[string]string,
|
tags map[string]string,
|
||||||
t ...time.Time,
|
t ...time.Time,
|
||||||
) {
|
) {
|
||||||
if m := ac.maker.MakeMetric(measurement, fields, tags, telegraf.Untyped, ac.getTime(t)); m != nil {
|
ac.addMetric(measurement, tags, fields, telegraf.Untyped, t...)
|
||||||
ac.metrics <- m
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ac *accumulator) AddGauge(
|
func (ac *accumulator) AddGauge(
|
||||||
|
@ -60,9 +51,7 @@ func (ac *accumulator) AddGauge(
|
||||||
tags map[string]string,
|
tags map[string]string,
|
||||||
t ...time.Time,
|
t ...time.Time,
|
||||||
) {
|
) {
|
||||||
if m := ac.maker.MakeMetric(measurement, fields, tags, telegraf.Gauge, ac.getTime(t)); m != nil {
|
ac.addMetric(measurement, tags, fields, telegraf.Gauge, t...)
|
||||||
ac.metrics <- m
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ac *accumulator) AddCounter(
|
func (ac *accumulator) AddCounter(
|
||||||
|
@ -71,9 +60,7 @@ func (ac *accumulator) AddCounter(
|
||||||
tags map[string]string,
|
tags map[string]string,
|
||||||
t ...time.Time,
|
t ...time.Time,
|
||||||
) {
|
) {
|
||||||
if m := ac.maker.MakeMetric(measurement, fields, tags, telegraf.Counter, ac.getTime(t)); m != nil {
|
ac.addMetric(measurement, tags, fields, telegraf.Counter, t...)
|
||||||
ac.metrics <- m
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ac *accumulator) AddSummary(
|
func (ac *accumulator) AddSummary(
|
||||||
|
@ -82,9 +69,7 @@ func (ac *accumulator) AddSummary(
|
||||||
tags map[string]string,
|
tags map[string]string,
|
||||||
t ...time.Time,
|
t ...time.Time,
|
||||||
) {
|
) {
|
||||||
if m := ac.maker.MakeMetric(measurement, fields, tags, telegraf.Summary, ac.getTime(t)); m != nil {
|
ac.addMetric(measurement, tags, fields, telegraf.Summary, t...)
|
||||||
ac.metrics <- m
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ac *accumulator) AddHistogram(
|
func (ac *accumulator) AddHistogram(
|
||||||
|
@ -93,7 +78,21 @@ func (ac *accumulator) AddHistogram(
|
||||||
tags map[string]string,
|
tags map[string]string,
|
||||||
t ...time.Time,
|
t ...time.Time,
|
||||||
) {
|
) {
|
||||||
if m := ac.maker.MakeMetric(measurement, fields, tags, telegraf.Histogram, ac.getTime(t)); m != nil {
|
ac.addMetric(measurement, tags, fields, telegraf.Histogram, t...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ac *accumulator) addMetric(
|
||||||
|
measurement string,
|
||||||
|
tags map[string]string,
|
||||||
|
fields map[string]interface{},
|
||||||
|
tp telegraf.ValueType,
|
||||||
|
t ...time.Time,
|
||||||
|
) {
|
||||||
|
m, err := metric.New(measurement, tags, fields, ac.getTime(t), tp)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if m := ac.maker.MakeMetric(m); m != nil {
|
||||||
ac.metrics <- m
|
ac.metrics <- m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,7 +104,6 @@ func (ac *accumulator) AddError(err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
NErrors.Incr(1)
|
NErrors.Incr(1)
|
||||||
//TODO suppress/throttle consecutive duplicate errors?
|
|
||||||
log.Printf("E! Error in plugin [%s]: %s", ac.maker.Name(), err)
|
log.Printf("E! Error in plugin [%s]: %s", ac.maker.Name(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/metric"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -134,26 +133,7 @@ type TestMetricMaker struct {
|
||||||
func (tm *TestMetricMaker) Name() string {
|
func (tm *TestMetricMaker) Name() string {
|
||||||
return "TestPlugin"
|
return "TestPlugin"
|
||||||
}
|
}
|
||||||
func (tm *TestMetricMaker) MakeMetric(
|
|
||||||
measurement string,
|
func (tm *TestMetricMaker) MakeMetric(metric telegraf.Metric) telegraf.Metric {
|
||||||
fields map[string]interface{},
|
return metric
|
||||||
tags map[string]string,
|
|
||||||
mType telegraf.ValueType,
|
|
||||||
t time.Time,
|
|
||||||
) telegraf.Metric {
|
|
||||||
switch mType {
|
|
||||||
case telegraf.Untyped:
|
|
||||||
if m, err := metric.New(measurement, tags, fields, t); err == nil {
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
case telegraf.Counter:
|
|
||||||
if m, err := metric.New(measurement, tags, fields, t, telegraf.Counter); err == nil {
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
case telegraf.Gauge:
|
|
||||||
if m, err := metric.New(measurement, tags, fields, t, telegraf.Gauge); err == nil {
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
# Telegraf Configuration
|
# Configuration
|
||||||
|
|
||||||
You can see the latest config file with all available plugins here:
|
Telegraf's configuration file is written using
|
||||||
[telegraf.conf](https://github.com/influxdata/telegraf/blob/master/etc/telegraf.conf)
|
[TOML](https://github.com/toml-lang/toml#toml).
|
||||||
|
|
||||||
|
[View the telegraf.conf config file with all available
|
||||||
|
plugins](/etc/telegraf.conf).
|
||||||
|
|
||||||
## Generating a Configuration File
|
## Generating a Configuration File
|
||||||
|
|
||||||
A default Telegraf config file can be auto-generated by telegraf:
|
A default config file can be generated by telegraf:
|
||||||
|
|
||||||
```
|
```
|
||||||
telegraf config > telegraf.conf
|
telegraf config > telegraf.conf
|
||||||
|
@ -18,7 +21,7 @@ To generate a file with specific inputs and outputs, you can use the
|
||||||
telegraf --input-filter cpu:mem:net:swap --output-filter influxdb:kafka config
|
telegraf --input-filter cpu:mem:net:swap --output-filter influxdb:kafka config
|
||||||
```
|
```
|
||||||
|
|
||||||
## Environment Variables
|
### Environment Variables
|
||||||
|
|
||||||
Environment variables can be used anywhere in the config file, simply prepend
|
Environment variables can be used anywhere in the config file, simply prepend
|
||||||
them with $. For strings the variable must be within quotes (ie, "$STR_VAR"),
|
them with $. For strings the variable must be within quotes (ie, "$STR_VAR"),
|
||||||
|
@ -27,7 +30,7 @@ for numbers and booleans they should be plain (ie, $INT_VAR, $BOOL_VAR)
|
||||||
When using the `.deb` or `.rpm` packages, you can define environment variables
|
When using the `.deb` or `.rpm` packages, you can define environment variables
|
||||||
in the `/etc/default/telegraf` file.
|
in the `/etc/default/telegraf` file.
|
||||||
|
|
||||||
## Configuration file locations
|
### Configuration file locations
|
||||||
|
|
||||||
The location of the configuration file can be set via the `--config` command
|
The location of the configuration file can be set via the `--config` command
|
||||||
line flag.
|
line flag.
|
||||||
|
@ -40,13 +43,13 @@ On most systems, the default locations are `/etc/telegraf/telegraf.conf` for
|
||||||
the main configuration file and `/etc/telegraf/telegraf.d` for the directory of
|
the main configuration file and `/etc/telegraf/telegraf.d` for the directory of
|
||||||
configuration files.
|
configuration files.
|
||||||
|
|
||||||
# Global Tags
|
### Global Tags
|
||||||
|
|
||||||
Global tags can be specified in the `[global_tags]` section of the config file
|
Global tags can be specified in the `[global_tags]` section of the config file
|
||||||
in key="value" format. All metrics being gathered on this host will be tagged
|
in key="value" format. All metrics being gathered on this host will be tagged
|
||||||
with the tags specified here.
|
with the tags specified here.
|
||||||
|
|
||||||
## Agent Configuration
|
### Agent Configuration
|
||||||
|
|
||||||
Telegraf has a few options you can configure under the `[agent]` section of the
|
Telegraf has a few options you can configure under the `[agent]` section of the
|
||||||
config.
|
config.
|
||||||
|
@ -85,7 +88,7 @@ ie, a jitter of 5s and flush_interval 10s means flushes will happen every 10-15s
|
||||||
* **hostname**: Override default hostname, if empty use os.Hostname().
|
* **hostname**: Override default hostname, if empty use os.Hostname().
|
||||||
* **omit_hostname**: If true, do no set the "host" tag in the telegraf agent.
|
* **omit_hostname**: If true, do no set the "host" tag in the telegraf agent.
|
||||||
|
|
||||||
## Input Configuration
|
### Input Configuration
|
||||||
|
|
||||||
The following config parameters are available for all inputs:
|
The following config parameters are available for all inputs:
|
||||||
|
|
||||||
|
@ -98,15 +101,15 @@ you can configure that here.
|
||||||
* **name_suffix**: Specifies a suffix to attach to the measurement name.
|
* **name_suffix**: Specifies a suffix to attach to the measurement name.
|
||||||
* **tags**: A map of tags to apply to a specific input's measurements.
|
* **tags**: A map of tags to apply to a specific input's measurements.
|
||||||
|
|
||||||
The [measurement filtering](#measurement-filtering) parameters can be used to
|
The [metric filtering](#metric-filtering) parameters can be used to limit what metrics are
|
||||||
limit what metrics are emitted from the input plugin.
|
emitted from the input plugin.
|
||||||
|
|
||||||
## Output Configuration
|
### Output Configuration
|
||||||
|
|
||||||
The [measurement filtering](#measurement-filtering) parameters can be used to
|
The [metric filtering](#metric-filtering) parameters can be used to limit what metrics are
|
||||||
limit what metrics are emitted from the output plugin.
|
emitted from the output plugin.
|
||||||
|
|
||||||
## Aggregator Configuration
|
### Aggregator Configuration
|
||||||
|
|
||||||
The following config parameters are available for all aggregators:
|
The following config parameters are available for all aggregators:
|
||||||
|
|
||||||
|
@ -125,63 +128,77 @@ aggregator and will not get sent to the output plugins.
|
||||||
* **name_suffix**: Specifies a suffix to attach to the measurement name.
|
* **name_suffix**: Specifies a suffix to attach to the measurement name.
|
||||||
* **tags**: A map of tags to apply to a specific input's measurements.
|
* **tags**: A map of tags to apply to a specific input's measurements.
|
||||||
|
|
||||||
The [measurement filtering](#measurement-filtering) parameters can be used to
|
The [metric filtering](#metric-filtering) parameters can be used to limit what metrics are
|
||||||
limit what metrics are handled by the aggregator. Excluded metrics are passed
|
handled by the aggregator. Excluded metrics are passed downstream to the next
|
||||||
downstream to the next aggregator.
|
aggregator.
|
||||||
|
|
||||||
## Processor Configuration
|
### Processor Configuration
|
||||||
|
|
||||||
The following config parameters are available for all processors:
|
The following config parameters are available for all processors:
|
||||||
|
|
||||||
* **order**: This is the order in which the processor(s) get executed. If this
|
* **order**: This is the order in which the processor(s) get executed. If this
|
||||||
is not specified then processor execution order will be random.
|
is not specified then processor execution order will be random.
|
||||||
|
|
||||||
The [measurement filtering](#measurement-filtering) parameters can be used
|
The [metric filtering](#metric-filtering) parameters can be used to limit what metrics are
|
||||||
to limit what metrics are handled by the processor. Excluded metrics are
|
handled by the processor. Excluded metrics are passed downstream to the next
|
||||||
passed downstream to the next processor.
|
processor.
|
||||||
|
|
||||||
#### Measurement Filtering
|
<a id="measurement-filtering"></a>
|
||||||
|
### Metric Filtering
|
||||||
|
|
||||||
Filters can be configured per input, output, processor, or aggregator,
|
Metric filtering can be configured per plugin on any input, output, processor,
|
||||||
see below for examples.
|
and aggregator plugin. Filters fall under two categories: Selectors and
|
||||||
|
Modifiers.
|
||||||
|
|
||||||
* **namepass**:
|
#### Selectors
|
||||||
An array of glob pattern strings. Only points whose measurement name matches
|
|
||||||
|
Selector filters include or exclude entire metrics. When a metric is excluded
|
||||||
|
from a Input or an Output plugin, the metric is dropped. If a metric is
|
||||||
|
excluded from a Processor or Aggregator plugin, it is skips the plugin and is
|
||||||
|
sent onwards to the next stage of processing.
|
||||||
|
|
||||||
|
- **namepass**:
|
||||||
|
An array of glob pattern strings. Only metrics whose measurement name matches
|
||||||
a pattern in this list are emitted.
|
a pattern in this list are emitted.
|
||||||
* **namedrop**:
|
|
||||||
The inverse of `namepass`. If a match is found the point is discarded. This
|
- **namedrop**:
|
||||||
is tested on points after they have passed the `namepass` test.
|
The inverse of `namepass`. If a match is found the metric is discarded. This
|
||||||
* **fieldpass**:
|
is tested on metrics after they have passed the `namepass` test.
|
||||||
An array of glob pattern strings. Only fields whose field key matches a
|
|
||||||
pattern in this list are emitted.
|
- **tagpass**:
|
||||||
* **fielddrop**:
|
A table mapping tag keys to arrays of glob pattern strings. Only metrics
|
||||||
The inverse of `fieldpass`. Fields with a field key matching one of the
|
|
||||||
patterns will be discarded from the point. This is tested on points after
|
|
||||||
they have passed the `fieldpass` test.
|
|
||||||
* **tagpass**:
|
|
||||||
A table mapping tag keys to arrays of glob pattern strings. Only points
|
|
||||||
that contain a tag key in the table and a tag value matching one of its
|
that contain a tag key in the table and a tag value matching one of its
|
||||||
patterns is emitted.
|
patterns is emitted.
|
||||||
* **tagdrop**:
|
|
||||||
The inverse of `tagpass`. If a match is found the point is discarded. This
|
- **tagdrop**:
|
||||||
is tested on points after they have passed the `tagpass` test.
|
The inverse of `tagpass`. If a match is found the metric is discarded. This
|
||||||
* **taginclude**:
|
is tested on metrics after they have passed the `tagpass` test.
|
||||||
|
|
||||||
|
#### Modifiers
|
||||||
|
|
||||||
|
Modifier filters remove tags and fields from a metric. If all fields are
|
||||||
|
removed the metric is removed.
|
||||||
|
|
||||||
|
- **fieldpass**:
|
||||||
|
An array of glob pattern strings. Only fields whose field key matches a
|
||||||
|
pattern in this list are emitted.
|
||||||
|
|
||||||
|
- **fielddrop**:
|
||||||
|
The inverse of `fieldpass`. Fields with a field key matching one of the
|
||||||
|
patterns will be discarded from the metric. This is tested on metrics after
|
||||||
|
they have passed the `fieldpass` test.
|
||||||
|
|
||||||
|
- **taginclude**:
|
||||||
An array of glob pattern strings. Only tags with a tag key matching one of
|
An array of glob pattern strings. Only tags with a tag key matching one of
|
||||||
the patterns are emitted. In contrast to `tagpass`, which will pass an entire
|
the patterns are emitted. In contrast to `tagpass`, which will pass an entire
|
||||||
point based on its tag, `taginclude` removes all non matching tags from the
|
metric based on its tag, `taginclude` removes all non matching tags from the
|
||||||
point. This filter can be used on both inputs & outputs, but it is
|
metric.
|
||||||
_recommended_ to be used on inputs, as it is more efficient to filter out tags
|
|
||||||
at the ingestion point.
|
- **tagexclude**:
|
||||||
* **tagexclude**:
|
|
||||||
The inverse of `taginclude`. Tags with a tag key matching one of the patterns
|
The inverse of `taginclude`. Tags with a tag key matching one of the patterns
|
||||||
will be discarded from the point.
|
will be discarded from the metric.
|
||||||
|
|
||||||
**NOTE** Due to the way TOML is parsed, `tagpass` and `tagdrop` parameters
|
### Input Configuration Examples
|
||||||
must be defined at the _end_ of the plugin definition, otherwise subsequent
|
|
||||||
plugin config options will be interpreted as part of the tagpass/tagdrop
|
|
||||||
tables.
|
|
||||||
|
|
||||||
#### Input Configuration Examples
|
|
||||||
|
|
||||||
This is a full working config that will output CPU data to an InfluxDB instance
|
This is a full working config that will output CPU data to an InfluxDB instance
|
||||||
at 192.168.59.103:8086, tagging measurements with dc="denver-1". It will output
|
at 192.168.59.103:8086, tagging measurements with dc="denver-1". It will output
|
||||||
|
|
|
@ -884,14 +884,6 @@ func (c *Config) addInput(name string, table *ast.Table) error {
|
||||||
// builds the filter and returns a
|
// builds the filter and returns a
|
||||||
// models.AggregatorConfig to be inserted into models.RunningAggregator
|
// models.AggregatorConfig to be inserted into models.RunningAggregator
|
||||||
func buildAggregator(name string, tbl *ast.Table) (*models.AggregatorConfig, error) {
|
func buildAggregator(name string, tbl *ast.Table) (*models.AggregatorConfig, error) {
|
||||||
unsupportedFields := []string{"tagexclude", "taginclude"}
|
|
||||||
for _, field := range unsupportedFields {
|
|
||||||
if _, ok := tbl.Fields[field]; ok {
|
|
||||||
return nil, fmt.Errorf("%s is not supported for aggregator plugins (%s).",
|
|
||||||
field, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
conf := &models.AggregatorConfig{
|
conf := &models.AggregatorConfig{
|
||||||
Name: name,
|
Name: name,
|
||||||
Delay: time.Millisecond * 100,
|
Delay: time.Millisecond * 100,
|
||||||
|
@ -989,13 +981,6 @@ func buildAggregator(name string, tbl *ast.Table) (*models.AggregatorConfig, err
|
||||||
// models.ProcessorConfig to be inserted into models.RunningProcessor
|
// models.ProcessorConfig to be inserted into models.RunningProcessor
|
||||||
func buildProcessor(name string, tbl *ast.Table) (*models.ProcessorConfig, error) {
|
func buildProcessor(name string, tbl *ast.Table) (*models.ProcessorConfig, error) {
|
||||||
conf := &models.ProcessorConfig{Name: name}
|
conf := &models.ProcessorConfig{Name: name}
|
||||||
unsupportedFields := []string{"tagexclude", "taginclude", "fielddrop", "fieldpass"}
|
|
||||||
for _, field := range unsupportedFields {
|
|
||||||
if _, ok := tbl.Fields[field]; ok {
|
|
||||||
return nil, fmt.Errorf("%s is not supported for processor plugins (%s).",
|
|
||||||
field, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if node, ok := tbl.Fields["order"]; ok {
|
if node, ok := tbl.Fields["order"]; ok {
|
||||||
if kv, ok := node.(*ast.KeyValue); ok {
|
if kv, ok := node.(*ast.KeyValue); ok {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package models
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/filter"
|
"github.com/influxdata/telegraf/filter"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -93,45 +94,35 @@ func (f *Filter) Compile() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply applies the filter to the given measurement name, fields map, and
|
// Select returns true if the metric matches according to the
|
||||||
// tags map. It will return false if the metric should be "filtered out", and
|
// namepass/namedrop and tagpass/tagdrop filters. The metric is not modified.
|
||||||
// true if the metric should "pass".
|
func (f *Filter) Select(metric telegraf.Metric) bool {
|
||||||
// It will modify tags & fields in-place if they need to be deleted.
|
|
||||||
func (f *Filter) Apply(
|
|
||||||
measurement string,
|
|
||||||
fields map[string]interface{},
|
|
||||||
tags map[string]string,
|
|
||||||
) bool {
|
|
||||||
if !f.isActive {
|
if !f.isActive {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the measurement name should pass
|
if !f.shouldNamePass(metric.Name()) {
|
||||||
if !f.shouldNamePass(measurement) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the tags should pass
|
if !f.shouldTagsPass(metric.TagList()) {
|
||||||
if !f.shouldTagsPass(tags) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter fields
|
|
||||||
for fieldkey, _ := range fields {
|
|
||||||
if !f.shouldFieldPass(fieldkey) {
|
|
||||||
delete(fields, fieldkey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(fields) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter tags
|
|
||||||
f.filterTags(tags)
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Modify removes any tags and fields from the metric according to the
|
||||||
|
// fieldpass/fielddrop and taginclude/tagexclude filters.
|
||||||
|
func (f *Filter) Modify(metric telegraf.Metric) {
|
||||||
|
if !f.isActive {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.filterFields(metric)
|
||||||
|
f.filterTags(metric)
|
||||||
|
}
|
||||||
|
|
||||||
// IsActive checking if filter is active
|
// IsActive checking if filter is active
|
||||||
func (f *Filter) IsActive() bool {
|
func (f *Filter) IsActive() bool {
|
||||||
return f.isActive
|
return f.isActive
|
||||||
|
@ -140,7 +131,6 @@ func (f *Filter) IsActive() bool {
|
||||||
// shouldNamePass returns true if the metric should pass, false if should drop
|
// shouldNamePass returns true if the metric should pass, false if should drop
|
||||||
// based on the drop/pass filter parameters
|
// based on the drop/pass filter parameters
|
||||||
func (f *Filter) shouldNamePass(key string) bool {
|
func (f *Filter) shouldNamePass(key string) bool {
|
||||||
|
|
||||||
pass := func(f *Filter) bool {
|
pass := func(f *Filter) bool {
|
||||||
if f.namePass.Match(key) {
|
if f.namePass.Match(key) {
|
||||||
return true
|
return true
|
||||||
|
@ -169,47 +159,32 @@ func (f *Filter) shouldNamePass(key string) bool {
|
||||||
// shouldFieldPass returns true if the metric should pass, false if should drop
|
// shouldFieldPass returns true if the metric should pass, false if should drop
|
||||||
// based on the drop/pass filter parameters
|
// based on the drop/pass filter parameters
|
||||||
func (f *Filter) shouldFieldPass(key string) bool {
|
func (f *Filter) shouldFieldPass(key string) bool {
|
||||||
|
|
||||||
pass := func(f *Filter) bool {
|
|
||||||
if f.fieldPass.Match(key) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
drop := func(f *Filter) bool {
|
|
||||||
if f.fieldDrop.Match(key) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.fieldPass != nil && f.fieldDrop != nil {
|
if f.fieldPass != nil && f.fieldDrop != nil {
|
||||||
return pass(f) && drop(f)
|
return f.fieldPass.Match(key) && !f.fieldDrop.Match(key)
|
||||||
} else if f.fieldPass != nil {
|
} else if f.fieldPass != nil {
|
||||||
return pass(f)
|
return f.fieldPass.Match(key)
|
||||||
} else if f.fieldDrop != nil {
|
} else if f.fieldDrop != nil {
|
||||||
return drop(f)
|
return !f.fieldDrop.Match(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// shouldTagsPass returns true if the metric should pass, false if should drop
|
// shouldTagsPass returns true if the metric should pass, false if should drop
|
||||||
// based on the tagdrop/tagpass filter parameters
|
// based on the tagdrop/tagpass filter parameters
|
||||||
func (f *Filter) shouldTagsPass(tags map[string]string) bool {
|
func (f *Filter) shouldTagsPass(tags []*telegraf.Tag) bool {
|
||||||
|
|
||||||
pass := func(f *Filter) bool {
|
pass := func(f *Filter) bool {
|
||||||
for _, pat := range f.TagPass {
|
for _, pat := range f.TagPass {
|
||||||
if pat.filter == nil {
|
if pat.filter == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if tagval, ok := tags[pat.Name]; ok {
|
for _, tag := range tags {
|
||||||
if pat.filter.Match(tagval) {
|
if tag.Key == pat.Name {
|
||||||
|
if pat.filter.Match(tag.Value) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,12 +193,14 @@ func (f *Filter) shouldTagsPass(tags map[string]string) bool {
|
||||||
if pat.filter == nil {
|
if pat.filter == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if tagval, ok := tags[pat.Name]; ok {
|
for _, tag := range tags {
|
||||||
if pat.filter.Match(tagval) {
|
if tag.Key == pat.Name {
|
||||||
|
if pat.filter.Match(tag.Value) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,22 +219,42 @@ func (f *Filter) shouldTagsPass(tags map[string]string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply TagInclude and TagExclude filters.
|
// filterFields removes fields according to fieldpass/fielddrop.
|
||||||
// modifies the tags map in-place.
|
func (f *Filter) filterFields(metric telegraf.Metric) {
|
||||||
func (f *Filter) filterTags(tags map[string]string) {
|
filterKeys := []string{}
|
||||||
if f.tagInclude != nil {
|
for _, field := range metric.FieldList() {
|
||||||
for k, _ := range tags {
|
if !f.shouldFieldPass(field.Key) {
|
||||||
if !f.tagInclude.Match(k) {
|
filterKeys = append(filterKeys, field.Key)
|
||||||
delete(tags, k)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, key := range filterKeys {
|
||||||
|
metric.RemoveField(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterTags removes tags according to taginclude/tagexclude.
|
||||||
|
func (f *Filter) filterTags(metric telegraf.Metric) {
|
||||||
|
filterKeys := []string{}
|
||||||
|
if f.tagInclude != nil {
|
||||||
|
for _, tag := range metric.TagList() {
|
||||||
|
if !f.tagInclude.Match(tag.Key) {
|
||||||
|
filterKeys = append(filterKeys, tag.Key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, key := range filterKeys {
|
||||||
|
metric.RemoveTag(key)
|
||||||
|
}
|
||||||
|
|
||||||
if f.tagExclude != nil {
|
if f.tagExclude != nil {
|
||||||
for k, _ := range tags {
|
for _, tag := range metric.TagList() {
|
||||||
if f.tagExclude.Match(k) {
|
if f.tagExclude.Match(tag.Key) {
|
||||||
delete(tags, k)
|
filterKeys = append(filterKeys, tag.Key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, key := range filterKeys {
|
||||||
|
metric.RemoveTag(key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,17 +2,24 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/metric"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFilter_ApplyEmpty(t *testing.T) {
|
func TestFilter_ApplyEmpty(t *testing.T) {
|
||||||
f := Filter{}
|
f := Filter{}
|
||||||
require.NoError(t, f.Compile())
|
require.NoError(t, f.Compile())
|
||||||
assert.False(t, f.IsActive())
|
require.False(t, f.IsActive())
|
||||||
|
|
||||||
assert.True(t, f.Apply("m", map[string]interface{}{"value": int64(1)}, map[string]string{}))
|
m, err := metric.New("m",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{"value": int64(1)},
|
||||||
|
time.Now())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, f.Select(m))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFilter_ApplyTagsDontPass(t *testing.T) {
|
func TestFilter_ApplyTagsDontPass(t *testing.T) {
|
||||||
|
@ -27,11 +34,14 @@ func TestFilter_ApplyTagsDontPass(t *testing.T) {
|
||||||
}
|
}
|
||||||
require.NoError(t, f.Compile())
|
require.NoError(t, f.Compile())
|
||||||
require.NoError(t, f.Compile())
|
require.NoError(t, f.Compile())
|
||||||
assert.True(t, f.IsActive())
|
require.True(t, f.IsActive())
|
||||||
|
|
||||||
assert.False(t, f.Apply("m",
|
m, err := metric.New("m",
|
||||||
|
map[string]string{"cpu": "cpu-total"},
|
||||||
map[string]interface{}{"value": int64(1)},
|
map[string]interface{}{"value": int64(1)},
|
||||||
map[string]string{"cpu": "cpu-total"}))
|
time.Now())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, f.Select(m))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFilter_ApplyDeleteFields(t *testing.T) {
|
func TestFilter_ApplyDeleteFields(t *testing.T) {
|
||||||
|
@ -40,11 +50,19 @@ func TestFilter_ApplyDeleteFields(t *testing.T) {
|
||||||
}
|
}
|
||||||
require.NoError(t, f.Compile())
|
require.NoError(t, f.Compile())
|
||||||
require.NoError(t, f.Compile())
|
require.NoError(t, f.Compile())
|
||||||
assert.True(t, f.IsActive())
|
require.True(t, f.IsActive())
|
||||||
|
|
||||||
fields := map[string]interface{}{"value": int64(1), "value2": int64(2)}
|
m, err := metric.New("m",
|
||||||
assert.True(t, f.Apply("m", fields, nil))
|
map[string]string{},
|
||||||
assert.Equal(t, map[string]interface{}{"value2": int64(2)}, fields)
|
map[string]interface{}{
|
||||||
|
"value": int64(1),
|
||||||
|
"value2": int64(2),
|
||||||
|
},
|
||||||
|
time.Now())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, f.Select(m))
|
||||||
|
f.Modify(m)
|
||||||
|
require.Equal(t, map[string]interface{}{"value2": int64(2)}, m.Fields())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFilter_ApplyDeleteAllFields(t *testing.T) {
|
func TestFilter_ApplyDeleteAllFields(t *testing.T) {
|
||||||
|
@ -53,10 +71,19 @@ func TestFilter_ApplyDeleteAllFields(t *testing.T) {
|
||||||
}
|
}
|
||||||
require.NoError(t, f.Compile())
|
require.NoError(t, f.Compile())
|
||||||
require.NoError(t, f.Compile())
|
require.NoError(t, f.Compile())
|
||||||
assert.True(t, f.IsActive())
|
require.True(t, f.IsActive())
|
||||||
|
|
||||||
fields := map[string]interface{}{"value": int64(1), "value2": int64(2)}
|
m, err := metric.New("m",
|
||||||
assert.False(t, f.Apply("m", fields, nil))
|
map[string]string{},
|
||||||
|
map[string]interface{}{
|
||||||
|
"value": int64(1),
|
||||||
|
"value2": int64(2),
|
||||||
|
},
|
||||||
|
time.Now())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, f.Select(m))
|
||||||
|
f.Modify(m)
|
||||||
|
require.Len(t, m.FieldList(), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFilter_Empty(t *testing.T) {
|
func TestFilter_Empty(t *testing.T) {
|
||||||
|
@ -230,20 +257,20 @@ func TestFilter_TagPass(t *testing.T) {
|
||||||
}
|
}
|
||||||
require.NoError(t, f.Compile())
|
require.NoError(t, f.Compile())
|
||||||
|
|
||||||
passes := []map[string]string{
|
passes := [][]*telegraf.Tag{
|
||||||
{"cpu": "cpu-total"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "cpu", Value: "cpu-total"}},
|
||||||
{"cpu": "cpu-0"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "cpu", Value: "cpu-0"}},
|
||||||
{"cpu": "cpu-1"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "cpu", Value: "cpu-1"}},
|
||||||
{"cpu": "cpu-2"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "cpu", Value: "cpu-2"}},
|
||||||
{"mem": "mem_free"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "mem", Value: "mem_free"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
drops := []map[string]string{
|
drops := [][]*telegraf.Tag{
|
||||||
{"cpu": "cputotal"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "cpu", Value: "cputotal"}},
|
||||||
{"cpu": "cpu0"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "cpu", Value: "cpu0"}},
|
||||||
{"cpu": "cpu1"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "cpu", Value: "cpu1"}},
|
||||||
{"cpu": "cpu2"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "cpu", Value: "cpu2"}},
|
||||||
{"mem": "mem_used"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "mem", Value: "mem_used"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tags := range passes {
|
for _, tags := range passes {
|
||||||
|
@ -274,20 +301,20 @@ func TestFilter_TagDrop(t *testing.T) {
|
||||||
}
|
}
|
||||||
require.NoError(t, f.Compile())
|
require.NoError(t, f.Compile())
|
||||||
|
|
||||||
drops := []map[string]string{
|
drops := [][]*telegraf.Tag{
|
||||||
{"cpu": "cpu-total"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "cpu", Value: "cpu-total"}},
|
||||||
{"cpu": "cpu-0"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "cpu", Value: "cpu-0"}},
|
||||||
{"cpu": "cpu-1"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "cpu", Value: "cpu-1"}},
|
||||||
{"cpu": "cpu-2"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "cpu", Value: "cpu-2"}},
|
||||||
{"mem": "mem_free"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "mem", Value: "mem_free"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
passes := []map[string]string{
|
passes := [][]*telegraf.Tag{
|
||||||
{"cpu": "cputotal"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "cpu", Value: "cputotal"}},
|
||||||
{"cpu": "cpu0"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "cpu", Value: "cpu0"}},
|
||||||
{"cpu": "cpu1"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "cpu", Value: "cpu1"}},
|
||||||
{"cpu": "cpu2"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "cpu", Value: "cpu2"}},
|
||||||
{"mem": "mem_used"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "mem", Value: "mem_used"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tags := range passes {
|
for _, tags := range passes {
|
||||||
|
@ -304,58 +331,70 @@ func TestFilter_TagDrop(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFilter_FilterTagsNoMatches(t *testing.T) {
|
func TestFilter_FilterTagsNoMatches(t *testing.T) {
|
||||||
pretags := map[string]string{
|
m, err := metric.New("m",
|
||||||
|
map[string]string{
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"mytag": "foobar",
|
"mytag": "foobar",
|
||||||
}
|
},
|
||||||
|
map[string]interface{}{"value": int64(1)},
|
||||||
|
time.Now())
|
||||||
|
require.NoError(t, err)
|
||||||
f := Filter{
|
f := Filter{
|
||||||
TagExclude: []string{"nomatch"},
|
TagExclude: []string{"nomatch"},
|
||||||
}
|
}
|
||||||
require.NoError(t, f.Compile())
|
require.NoError(t, f.Compile())
|
||||||
|
|
||||||
f.filterTags(pretags)
|
f.filterTags(m)
|
||||||
assert.Equal(t, map[string]string{
|
require.Equal(t, map[string]string{
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"mytag": "foobar",
|
"mytag": "foobar",
|
||||||
}, pretags)
|
}, m.Tags())
|
||||||
|
|
||||||
f = Filter{
|
f = Filter{
|
||||||
TagInclude: []string{"nomatch"},
|
TagInclude: []string{"nomatch"},
|
||||||
}
|
}
|
||||||
require.NoError(t, f.Compile())
|
require.NoError(t, f.Compile())
|
||||||
|
|
||||||
f.filterTags(pretags)
|
f.filterTags(m)
|
||||||
assert.Equal(t, map[string]string{}, pretags)
|
require.Equal(t, map[string]string{}, m.Tags())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFilter_FilterTagsMatches(t *testing.T) {
|
func TestFilter_FilterTagsMatches(t *testing.T) {
|
||||||
pretags := map[string]string{
|
m, err := metric.New("m",
|
||||||
|
map[string]string{
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"mytag": "foobar",
|
"mytag": "foobar",
|
||||||
}
|
},
|
||||||
|
map[string]interface{}{"value": int64(1)},
|
||||||
|
time.Now())
|
||||||
|
require.NoError(t, err)
|
||||||
f := Filter{
|
f := Filter{
|
||||||
TagExclude: []string{"ho*"},
|
TagExclude: []string{"ho*"},
|
||||||
}
|
}
|
||||||
require.NoError(t, f.Compile())
|
require.NoError(t, f.Compile())
|
||||||
|
|
||||||
f.filterTags(pretags)
|
f.filterTags(m)
|
||||||
assert.Equal(t, map[string]string{
|
require.Equal(t, map[string]string{
|
||||||
"mytag": "foobar",
|
"mytag": "foobar",
|
||||||
}, pretags)
|
}, m.Tags())
|
||||||
|
|
||||||
pretags = map[string]string{
|
m, err = metric.New("m",
|
||||||
|
map[string]string{
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"mytag": "foobar",
|
"mytag": "foobar",
|
||||||
}
|
},
|
||||||
|
map[string]interface{}{"value": int64(1)},
|
||||||
|
time.Now())
|
||||||
|
require.NoError(t, err)
|
||||||
f = Filter{
|
f = Filter{
|
||||||
TagInclude: []string{"my*"},
|
TagInclude: []string{"my*"},
|
||||||
}
|
}
|
||||||
require.NoError(t, f.Compile())
|
require.NoError(t, f.Compile())
|
||||||
|
|
||||||
f.filterTags(pretags)
|
f.filterTags(m)
|
||||||
assert.Equal(t, map[string]string{
|
require.Equal(t, map[string]string{
|
||||||
"mytag": "foobar",
|
"mytag": "foobar",
|
||||||
}, pretags)
|
}, m.Tags())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestFilter_FilterNamePassAndDrop used for check case when
|
// TestFilter_FilterNamePassAndDrop used for check case when
|
||||||
|
@ -374,7 +413,7 @@ func TestFilter_FilterNamePassAndDrop(t *testing.T) {
|
||||||
require.NoError(t, f.Compile())
|
require.NoError(t, f.Compile())
|
||||||
|
|
||||||
for i, name := range inputData {
|
for i, name := range inputData {
|
||||||
assert.Equal(t, f.shouldNamePass(name), expectedResult[i])
|
require.Equal(t, f.shouldNamePass(name), expectedResult[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,7 +433,7 @@ func TestFilter_FilterFieldPassAndDrop(t *testing.T) {
|
||||||
require.NoError(t, f.Compile())
|
require.NoError(t, f.Compile())
|
||||||
|
|
||||||
for i, field := range inputData {
|
for i, field := range inputData {
|
||||||
assert.Equal(t, f.shouldFieldPass(field), expectedResult[i])
|
require.Equal(t, f.shouldFieldPass(field), expectedResult[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,12 +441,11 @@ func TestFilter_FilterFieldPassAndDrop(t *testing.T) {
|
||||||
// both parameters were defined
|
// both parameters were defined
|
||||||
// see: https://github.com/influxdata/telegraf/issues/2860
|
// see: https://github.com/influxdata/telegraf/issues/2860
|
||||||
func TestFilter_FilterTagsPassAndDrop(t *testing.T) {
|
func TestFilter_FilterTagsPassAndDrop(t *testing.T) {
|
||||||
|
inputData := [][]*telegraf.Tag{
|
||||||
inputData := []map[string]string{
|
[]*telegraf.Tag{&telegraf.Tag{Key: "tag1", Value: "1"}, &telegraf.Tag{Key: "tag2", Value: "3"}},
|
||||||
{"tag1": "1", "tag2": "3"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "tag1", Value: "1"}, &telegraf.Tag{Key: "tag2", Value: "2"}},
|
||||||
{"tag1": "1", "tag2": "2"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "tag1", Value: "2"}, &telegraf.Tag{Key: "tag2", Value: "1"}},
|
||||||
{"tag1": "2", "tag2": "1"},
|
[]*telegraf.Tag{&telegraf.Tag{Key: "tag1", Value: "4"}, &telegraf.Tag{Key: "tag2", Value: "1"}},
|
||||||
{"tag1": "4", "tag2": "1"},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedResult := []bool{false, true, false, false}
|
expectedResult := []bool{false, true, false, false}
|
||||||
|
@ -438,7 +476,7 @@ func TestFilter_FilterTagsPassAndDrop(t *testing.T) {
|
||||||
require.NoError(t, f.Compile())
|
require.NoError(t, f.Compile())
|
||||||
|
|
||||||
for i, tag := range inputData {
|
for i, tag := range inputData {
|
||||||
assert.Equal(t, f.shouldTagsPass(tag), expectedResult[i])
|
require.Equal(t, f.shouldTagsPass(tag), expectedResult[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,86 +1,42 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/metric"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// makemetric is used by both RunningAggregator & RunningInput
|
// Makemetric applies new metric plugin and agent measurement and tag
|
||||||
// to make metrics.
|
// settings.
|
||||||
// nameOverride: override the name of the measurement being made.
|
|
||||||
// namePrefix: add this prefix to each measurement name.
|
|
||||||
// nameSuffix: add this suffix to each measurement name.
|
|
||||||
// pluginTags: these are tags that are specific to this plugin.
|
|
||||||
// daemonTags: these are daemon-wide global tags, and get applied after pluginTags.
|
|
||||||
// filter: this is a filter to apply to each metric being made.
|
|
||||||
// applyFilter: if false, the above filter is not applied to each metric.
|
|
||||||
// This is used by Aggregators, because aggregators use filters
|
|
||||||
// on incoming metrics instead of on created metrics.
|
|
||||||
// TODO refactor this to not have such a huge func signature.
|
|
||||||
func makemetric(
|
func makemetric(
|
||||||
measurement string,
|
metric telegraf.Metric,
|
||||||
fields map[string]interface{},
|
|
||||||
tags map[string]string,
|
|
||||||
nameOverride string,
|
nameOverride string,
|
||||||
namePrefix string,
|
namePrefix string,
|
||||||
nameSuffix string,
|
nameSuffix string,
|
||||||
pluginTags map[string]string,
|
tags map[string]string,
|
||||||
daemonTags map[string]string,
|
globalTags map[string]string,
|
||||||
filter Filter,
|
|
||||||
applyFilter bool,
|
|
||||||
mType telegraf.ValueType,
|
|
||||||
t time.Time,
|
|
||||||
) telegraf.Metric {
|
) telegraf.Metric {
|
||||||
if len(fields) == 0 || len(measurement) == 0 {
|
if len(nameOverride) != 0 {
|
||||||
return nil
|
metric.SetName(nameOverride)
|
||||||
}
|
|
||||||
if tags == nil {
|
|
||||||
tags = make(map[string]string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override measurement name if set
|
|
||||||
if len(nameOverride) != 0 {
|
|
||||||
measurement = nameOverride
|
|
||||||
}
|
|
||||||
// Apply measurement prefix and suffix if set
|
|
||||||
if len(namePrefix) != 0 {
|
if len(namePrefix) != 0 {
|
||||||
measurement = namePrefix + measurement
|
metric.AddPrefix(namePrefix)
|
||||||
}
|
}
|
||||||
if len(nameSuffix) != 0 {
|
if len(nameSuffix) != 0 {
|
||||||
measurement = measurement + nameSuffix
|
metric.AddSuffix(nameSuffix)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply plugin-wide tags if set
|
// Apply plugin-wide tags
|
||||||
for k, v := range pluginTags {
|
for k, v := range tags {
|
||||||
if _, ok := tags[k]; !ok {
|
if _, ok := metric.GetTag(k); !ok {
|
||||||
tags[k] = v
|
metric.AddTag(k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Apply daemon-wide tags if set
|
// Apply global tags
|
||||||
for k, v := range daemonTags {
|
for k, v := range globalTags {
|
||||||
if _, ok := tags[k]; !ok {
|
if _, ok := metric.GetTag(k); !ok {
|
||||||
tags[k] = v
|
metric.AddTag(k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the metric filter(s)
|
return metric
|
||||||
// for aggregators, the filter does not get applied when the metric is made.
|
|
||||||
// instead, the filter is applied to metric incoming into the plugin.
|
|
||||||
// ie, it gets applied in the RunningAggregator.Apply function.
|
|
||||||
if applyFilter {
|
|
||||||
if ok := filter.Apply(measurement, fields, tags); !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err := metric.New(measurement, tags, fields, t, mType)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error adding point [%s]: %s\n", measurement, err.Error())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return m
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/metric"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type RunningAggregator struct {
|
type RunningAggregator struct {
|
||||||
|
@ -29,47 +28,32 @@ func NewRunningAggregator(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AggregatorConfig containing configuration parameters for the running
|
// AggregatorConfig is the common config for all aggregators.
|
||||||
// aggregator plugin.
|
|
||||||
type AggregatorConfig struct {
|
type AggregatorConfig struct {
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
DropOriginal bool
|
DropOriginal bool
|
||||||
|
Period time.Duration
|
||||||
|
Delay time.Duration
|
||||||
|
|
||||||
NameOverride string
|
NameOverride string
|
||||||
MeasurementPrefix string
|
MeasurementPrefix string
|
||||||
MeasurementSuffix string
|
MeasurementSuffix string
|
||||||
Tags map[string]string
|
Tags map[string]string
|
||||||
Filter Filter
|
Filter Filter
|
||||||
|
|
||||||
Period time.Duration
|
|
||||||
Delay time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RunningAggregator) Name() string {
|
func (r *RunningAggregator) Name() string {
|
||||||
return "aggregators." + r.Config.Name
|
return "aggregators." + r.Config.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RunningAggregator) MakeMetric(
|
func (r *RunningAggregator) MakeMetric(metric telegraf.Metric) telegraf.Metric {
|
||||||
measurement string,
|
|
||||||
fields map[string]interface{},
|
|
||||||
tags map[string]string,
|
|
||||||
mType telegraf.ValueType,
|
|
||||||
t time.Time,
|
|
||||||
) telegraf.Metric {
|
|
||||||
m := makemetric(
|
m := makemetric(
|
||||||
measurement,
|
metric,
|
||||||
fields,
|
|
||||||
tags,
|
|
||||||
r.Config.NameOverride,
|
r.Config.NameOverride,
|
||||||
r.Config.MeasurementPrefix,
|
r.Config.MeasurementPrefix,
|
||||||
r.Config.MeasurementSuffix,
|
r.Config.MeasurementSuffix,
|
||||||
r.Config.Tags,
|
r.Config.Tags,
|
||||||
nil,
|
nil)
|
||||||
r.Config.Filter,
|
|
||||||
false,
|
|
||||||
mType,
|
|
||||||
t,
|
|
||||||
)
|
|
||||||
|
|
||||||
if m != nil {
|
if m != nil {
|
||||||
m.SetAggregate(true)
|
m.SetAggregate(true)
|
||||||
|
@ -78,27 +62,23 @@ func (r *RunningAggregator) MakeMetric(
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add applies the given metric to the aggregator.
|
// Add a metric to the aggregator and return true if the original metric
|
||||||
// Before applying to the plugin, it will run any defined filters on the metric.
|
// should be dropped.
|
||||||
// Apply returns true if the original metric should be dropped.
|
func (r *RunningAggregator) Add(metric telegraf.Metric) bool {
|
||||||
func (r *RunningAggregator) Add(in telegraf.Metric) bool {
|
if ok := r.Config.Filter.Select(metric); !ok {
|
||||||
if r.Config.Filter.IsActive() {
|
|
||||||
// check if the aggregator should apply this metric
|
|
||||||
name := in.Name()
|
|
||||||
fields := in.Fields()
|
|
||||||
tags := in.Tags()
|
|
||||||
t := in.Time()
|
|
||||||
if ok := r.Config.Filter.Apply(name, fields, tags); !ok {
|
|
||||||
// aggregator should not apply this metric
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
in, _ = metric.New(name, tags, fields, t)
|
r.Config.Filter.Modify(metric)
|
||||||
}
|
if len(metric.FieldList()) == 0 {
|
||||||
|
|
||||||
r.metrics <- in
|
|
||||||
return r.Config.DropOriginal
|
return r.Config.DropOriginal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r.metrics <- metric
|
||||||
|
|
||||||
|
return r.Config.DropOriginal
|
||||||
|
}
|
||||||
|
|
||||||
func (r *RunningAggregator) add(in telegraf.Metric) {
|
func (r *RunningAggregator) add(in telegraf.Metric) {
|
||||||
r.a.Add(in)
|
r.a.Add(in)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,11 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/metric"
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAdd(t *testing.T) {
|
func TestAdd(t *testing.T) {
|
||||||
|
@ -25,13 +27,15 @@ func TestAdd(t *testing.T) {
|
||||||
acc := testutil.Accumulator{}
|
acc := testutil.Accumulator{}
|
||||||
go ra.Run(&acc, make(chan struct{}))
|
go ra.Run(&acc, make(chan struct{}))
|
||||||
|
|
||||||
m := ra.MakeMetric(
|
m, err := metric.New("RITest",
|
||||||
"RITest",
|
|
||||||
map[string]interface{}{"value": int(101)},
|
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
map[string]interface{}{
|
||||||
|
"value": int64(101),
|
||||||
|
},
|
||||||
time.Now().Add(time.Millisecond*150),
|
time.Now().Add(time.Millisecond*150),
|
||||||
)
|
telegraf.Untyped)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.False(t, ra.Add(m))
|
assert.False(t, ra.Add(m))
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -56,34 +60,37 @@ func TestAddMetricsOutsideCurrentPeriod(t *testing.T) {
|
||||||
acc := testutil.Accumulator{}
|
acc := testutil.Accumulator{}
|
||||||
go ra.Run(&acc, make(chan struct{}))
|
go ra.Run(&acc, make(chan struct{}))
|
||||||
|
|
||||||
// metric before current period
|
m, err := metric.New("RITest",
|
||||||
m := ra.MakeMetric(
|
|
||||||
"RITest",
|
|
||||||
map[string]interface{}{"value": int(101)},
|
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
map[string]interface{}{
|
||||||
|
"value": int64(101),
|
||||||
|
},
|
||||||
time.Now().Add(-time.Hour),
|
time.Now().Add(-time.Hour),
|
||||||
)
|
telegraf.Untyped)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.False(t, ra.Add(m))
|
assert.False(t, ra.Add(m))
|
||||||
|
|
||||||
// metric after current period
|
// metric after current period
|
||||||
m = ra.MakeMetric(
|
m, err = metric.New("RITest",
|
||||||
"RITest",
|
|
||||||
map[string]interface{}{"value": int(101)},
|
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
map[string]interface{}{
|
||||||
|
"value": int64(101),
|
||||||
|
},
|
||||||
time.Now().Add(time.Hour),
|
time.Now().Add(time.Hour),
|
||||||
)
|
telegraf.Untyped)
|
||||||
|
require.NoError(t, err)
|
||||||
assert.False(t, ra.Add(m))
|
assert.False(t, ra.Add(m))
|
||||||
|
|
||||||
// "now" metric
|
// "now" metric
|
||||||
m = ra.MakeMetric(
|
m, err = metric.New("RITest",
|
||||||
"RITest",
|
|
||||||
map[string]interface{}{"value": int(101)},
|
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
map[string]interface{}{
|
||||||
|
"value": int64(101),
|
||||||
|
},
|
||||||
time.Now().Add(time.Millisecond*50),
|
time.Now().Add(time.Millisecond*50),
|
||||||
)
|
telegraf.Untyped)
|
||||||
|
require.NoError(t, err)
|
||||||
assert.False(t, ra.Add(m))
|
assert.False(t, ra.Add(m))
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -115,13 +122,14 @@ func TestAddAndPushOnePeriod(t *testing.T) {
|
||||||
ra.Run(&acc, shutdown)
|
ra.Run(&acc, shutdown)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
m := ra.MakeMetric(
|
m, err := metric.New("RITest",
|
||||||
"RITest",
|
|
||||||
map[string]interface{}{"value": int(101)},
|
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
map[string]interface{}{
|
||||||
|
"value": int64(101),
|
||||||
|
},
|
||||||
time.Now().Add(time.Millisecond*100),
|
time.Now().Add(time.Millisecond*100),
|
||||||
)
|
telegraf.Untyped)
|
||||||
|
require.NoError(t, err)
|
||||||
assert.False(t, ra.Add(m))
|
assert.False(t, ra.Add(m))
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -146,23 +154,25 @@ func TestAddDropOriginal(t *testing.T) {
|
||||||
})
|
})
|
||||||
assert.NoError(t, ra.Config.Filter.Compile())
|
assert.NoError(t, ra.Config.Filter.Compile())
|
||||||
|
|
||||||
m := ra.MakeMetric(
|
m, err := metric.New("RITest",
|
||||||
"RITest",
|
|
||||||
map[string]interface{}{"value": int(101)},
|
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
map[string]interface{}{
|
||||||
|
"value": int64(101),
|
||||||
|
},
|
||||||
time.Now(),
|
time.Now(),
|
||||||
)
|
telegraf.Untyped)
|
||||||
|
require.NoError(t, err)
|
||||||
assert.True(t, ra.Add(m))
|
assert.True(t, ra.Add(m))
|
||||||
|
|
||||||
// this metric name doesn't match the filter, so Add will return false
|
// this metric name doesn't match the filter, so Add will return false
|
||||||
m2 := ra.MakeMetric(
|
m2, err := metric.New("foobar",
|
||||||
"foobar",
|
|
||||||
map[string]interface{}{"value": int(101)},
|
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
map[string]interface{}{
|
||||||
|
"value": int64(101),
|
||||||
|
},
|
||||||
time.Now(),
|
time.Now(),
|
||||||
)
|
telegraf.Untyped)
|
||||||
|
require.NoError(t, err)
|
||||||
assert.False(t, ra.Add(m2))
|
assert.False(t, ra.Add(m2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,44 +36,39 @@ func NewRunningInput(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// InputConfig containing a name, interval, and filter
|
// InputConfig is the common config for all inputs.
|
||||||
type InputConfig struct {
|
type InputConfig struct {
|
||||||
Name string
|
Name string
|
||||||
|
Interval time.Duration
|
||||||
|
|
||||||
NameOverride string
|
NameOverride string
|
||||||
MeasurementPrefix string
|
MeasurementPrefix string
|
||||||
MeasurementSuffix string
|
MeasurementSuffix string
|
||||||
Tags map[string]string
|
Tags map[string]string
|
||||||
Filter Filter
|
Filter Filter
|
||||||
Interval time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RunningInput) Name() string {
|
func (r *RunningInput) Name() string {
|
||||||
return "inputs." + r.Config.Name
|
return "inputs." + r.Config.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeMetric either returns a metric, or returns nil if the metric doesn't
|
func (r *RunningInput) MakeMetric(metric telegraf.Metric) telegraf.Metric {
|
||||||
// need to be created (because of filtering, an error, etc.)
|
if ok := r.Config.Filter.Select(metric); !ok {
|
||||||
func (r *RunningInput) MakeMetric(
|
return nil
|
||||||
measurement string,
|
}
|
||||||
fields map[string]interface{},
|
|
||||||
tags map[string]string,
|
r.Config.Filter.Modify(metric)
|
||||||
mType telegraf.ValueType,
|
if len(metric.FieldList()) == 0 {
|
||||||
t time.Time,
|
return nil
|
||||||
) telegraf.Metric {
|
}
|
||||||
|
|
||||||
m := makemetric(
|
m := makemetric(
|
||||||
measurement,
|
metric,
|
||||||
fields,
|
|
||||||
tags,
|
|
||||||
r.Config.NameOverride,
|
r.Config.NameOverride,
|
||||||
r.Config.MeasurementPrefix,
|
r.Config.MeasurementPrefix,
|
||||||
r.Config.MeasurementSuffix,
|
r.Config.MeasurementSuffix,
|
||||||
r.Config.Tags,
|
r.Config.Tags,
|
||||||
r.defaultTags,
|
r.defaultTags)
|
||||||
r.Config.Filter,
|
|
||||||
true,
|
|
||||||
mType,
|
|
||||||
t,
|
|
||||||
)
|
|
||||||
|
|
||||||
if r.trace && m != nil {
|
if r.trace && m != nil {
|
||||||
s := influx.NewSerializer()
|
s := influx.NewSerializer()
|
||||||
|
|
|
@ -17,13 +17,13 @@ func TestMakeMetricNoFields(t *testing.T) {
|
||||||
Name: "TestRunningInput",
|
Name: "TestRunningInput",
|
||||||
})
|
})
|
||||||
|
|
||||||
m := ri.MakeMetric(
|
m, err := metric.New("RITest",
|
||||||
"RITest",
|
|
||||||
map[string]interface{}{},
|
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
map[string]interface{}{},
|
||||||
now,
|
now,
|
||||||
)
|
telegraf.Untyped)
|
||||||
|
m = ri.MakeMetric(m)
|
||||||
|
require.NoError(t, err)
|
||||||
assert.Nil(t, m)
|
assert.Nil(t, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,16 +34,16 @@ func TestMakeMetricNilFields(t *testing.T) {
|
||||||
Name: "TestRunningInput",
|
Name: "TestRunningInput",
|
||||||
})
|
})
|
||||||
|
|
||||||
m := ri.MakeMetric(
|
m, err := metric.New("RITest",
|
||||||
"RITest",
|
map[string]string{},
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"value": int(101),
|
"value": int64(101),
|
||||||
"nil": nil,
|
"nil": nil,
|
||||||
},
|
},
|
||||||
map[string]string{},
|
|
||||||
telegraf.Untyped,
|
|
||||||
now,
|
now,
|
||||||
)
|
telegraf.Untyped)
|
||||||
|
require.NoError(t, err)
|
||||||
|
m = ri.MakeMetric(m)
|
||||||
|
|
||||||
expected, err := metric.New("RITest",
|
expected, err := metric.New("RITest",
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
|
@ -69,13 +69,15 @@ func TestMakeMetricWithPluginTags(t *testing.T) {
|
||||||
ri.SetTrace(true)
|
ri.SetTrace(true)
|
||||||
assert.Equal(t, true, ri.Trace())
|
assert.Equal(t, true, ri.Trace())
|
||||||
|
|
||||||
m := ri.MakeMetric(
|
m, err := metric.New("RITest",
|
||||||
"RITest",
|
map[string]string{},
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{
|
||||||
nil,
|
"value": int64(101),
|
||||||
telegraf.Untyped,
|
},
|
||||||
now,
|
now,
|
||||||
)
|
telegraf.Untyped)
|
||||||
|
require.NoError(t, err)
|
||||||
|
m = ri.MakeMetric(m)
|
||||||
|
|
||||||
expected, err := metric.New("RITest",
|
expected, err := metric.New("RITest",
|
||||||
map[string]string{
|
map[string]string{
|
||||||
|
@ -104,13 +106,15 @@ func TestMakeMetricFilteredOut(t *testing.T) {
|
||||||
assert.Equal(t, true, ri.Trace())
|
assert.Equal(t, true, ri.Trace())
|
||||||
assert.NoError(t, ri.Config.Filter.Compile())
|
assert.NoError(t, ri.Config.Filter.Compile())
|
||||||
|
|
||||||
m := ri.MakeMetric(
|
m, err := metric.New("RITest",
|
||||||
"RITest",
|
map[string]string{},
|
||||||
map[string]interface{}{"value": int(101)},
|
map[string]interface{}{
|
||||||
nil,
|
"value": int64(101),
|
||||||
telegraf.Untyped,
|
},
|
||||||
now,
|
now,
|
||||||
)
|
telegraf.Untyped)
|
||||||
|
m = ri.MakeMetric(m)
|
||||||
|
require.NoError(t, err)
|
||||||
assert.Nil(t, m)
|
assert.Nil(t, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,13 +130,15 @@ func TestMakeMetricWithDaemonTags(t *testing.T) {
|
||||||
ri.SetTrace(true)
|
ri.SetTrace(true)
|
||||||
assert.Equal(t, true, ri.Trace())
|
assert.Equal(t, true, ri.Trace())
|
||||||
|
|
||||||
m := ri.MakeMetric(
|
m, err := metric.New("RITest",
|
||||||
"RITest",
|
|
||||||
map[string]interface{}{"value": int(101)},
|
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
map[string]interface{}{
|
||||||
|
"value": int64(101),
|
||||||
|
},
|
||||||
now,
|
now,
|
||||||
)
|
telegraf.Untyped)
|
||||||
|
require.NoError(t, err)
|
||||||
|
m = ri.MakeMetric(m)
|
||||||
expected, err := metric.New("RITest",
|
expected, err := metric.New("RITest",
|
||||||
map[string]string{
|
map[string]string{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
|
@ -153,13 +159,15 @@ func TestMakeMetricNameOverride(t *testing.T) {
|
||||||
NameOverride: "foobar",
|
NameOverride: "foobar",
|
||||||
})
|
})
|
||||||
|
|
||||||
m := ri.MakeMetric(
|
m, err := metric.New("RITest",
|
||||||
"RITest",
|
|
||||||
map[string]interface{}{"value": int(101)},
|
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
map[string]interface{}{
|
||||||
|
"value": int64(101),
|
||||||
|
},
|
||||||
now,
|
now,
|
||||||
)
|
telegraf.Untyped)
|
||||||
|
require.NoError(t, err)
|
||||||
|
m = ri.MakeMetric(m)
|
||||||
expected, err := metric.New("foobar",
|
expected, err := metric.New("foobar",
|
||||||
nil,
|
nil,
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
|
@ -178,13 +186,15 @@ func TestMakeMetricNamePrefix(t *testing.T) {
|
||||||
MeasurementPrefix: "foobar_",
|
MeasurementPrefix: "foobar_",
|
||||||
})
|
})
|
||||||
|
|
||||||
m := ri.MakeMetric(
|
m, err := metric.New("RITest",
|
||||||
"RITest",
|
|
||||||
map[string]interface{}{"value": int(101)},
|
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
map[string]interface{}{
|
||||||
|
"value": int64(101),
|
||||||
|
},
|
||||||
now,
|
now,
|
||||||
)
|
telegraf.Untyped)
|
||||||
|
require.NoError(t, err)
|
||||||
|
m = ri.MakeMetric(m)
|
||||||
expected, err := metric.New("foobar_RITest",
|
expected, err := metric.New("foobar_RITest",
|
||||||
nil,
|
nil,
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
|
@ -203,13 +213,15 @@ func TestMakeMetricNameSuffix(t *testing.T) {
|
||||||
MeasurementSuffix: "_foobar",
|
MeasurementSuffix: "_foobar",
|
||||||
})
|
})
|
||||||
|
|
||||||
m := ri.MakeMetric(
|
m, err := metric.New("RITest",
|
||||||
"RITest",
|
|
||||||
map[string]interface{}{"value": int(101)},
|
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
telegraf.Untyped,
|
map[string]interface{}{
|
||||||
|
"value": int64(101),
|
||||||
|
},
|
||||||
now,
|
now,
|
||||||
)
|
telegraf.Untyped)
|
||||||
|
require.NoError(t, err)
|
||||||
|
m = ri.MakeMetric(m)
|
||||||
expected, err := metric.New("RITest_foobar",
|
expected, err := metric.New("RITest_foobar",
|
||||||
nil,
|
nil,
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/internal/buffer"
|
"github.com/influxdata/telegraf/internal/buffer"
|
||||||
"github.com/influxdata/telegraf/metric"
|
|
||||||
"github.com/influxdata/telegraf/selfstat"
|
"github.com/influxdata/telegraf/selfstat"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -42,6 +41,12 @@ type RunningOutput struct {
|
||||||
writeMutex sync.Mutex
|
writeMutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OutputConfig containing name and filter
|
||||||
|
type OutputConfig struct {
|
||||||
|
Name string
|
||||||
|
Filter Filter
|
||||||
|
}
|
||||||
|
|
||||||
func NewRunningOutput(
|
func NewRunningOutput(
|
||||||
name string,
|
name string,
|
||||||
output telegraf.Output,
|
output telegraf.Output,
|
||||||
|
@ -95,36 +100,25 @@ func NewRunningOutput(
|
||||||
|
|
||||||
// AddMetric adds a metric to the output. This function can also write cached
|
// AddMetric adds a metric to the output. This function can also write cached
|
||||||
// points if FlushBufferWhenFull is true.
|
// points if FlushBufferWhenFull is true.
|
||||||
func (ro *RunningOutput) AddMetric(m telegraf.Metric) {
|
func (ro *RunningOutput) AddMetric(metric telegraf.Metric) {
|
||||||
|
if ok := ro.Config.Filter.Select(metric); !ok {
|
||||||
if m == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Filter any tagexclude/taginclude parameters before adding metric
|
|
||||||
if ro.Config.Filter.IsActive() {
|
|
||||||
// In order to filter out tags, we need to create a new metric, since
|
|
||||||
// metrics are immutable once created.
|
|
||||||
name := m.Name()
|
|
||||||
tags := m.Tags()
|
|
||||||
fields := m.Fields()
|
|
||||||
t := m.Time()
|
|
||||||
tp := m.Type()
|
|
||||||
if ok := ro.Config.Filter.Apply(name, fields, tags); !ok {
|
|
||||||
ro.MetricsFiltered.Incr(1)
|
ro.MetricsFiltered.Incr(1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// error is not possible if creating from another metric, so ignore.
|
|
||||||
m, _ = metric.New(name, tags, fields, t, tp)
|
ro.Config.Filter.Modify(metric)
|
||||||
|
if len(metric.FieldList()) == 0 {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if output, ok := ro.Output.(telegraf.AggregatingOutput); ok {
|
if output, ok := ro.Output.(telegraf.AggregatingOutput); ok {
|
||||||
ro.aggMutex.Lock()
|
ro.aggMutex.Lock()
|
||||||
output.Add(m)
|
output.Add(metric)
|
||||||
ro.aggMutex.Unlock()
|
ro.aggMutex.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ro.metrics.Add(m)
|
ro.metrics.Add(metric)
|
||||||
if ro.metrics.Len() == ro.MetricBatchSize {
|
if ro.metrics.Len() == ro.MetricBatchSize {
|
||||||
batch := ro.metrics.Batch(ro.MetricBatchSize)
|
batch := ro.metrics.Batch(ro.MetricBatchSize)
|
||||||
err := ro.write(batch)
|
err := ro.write(batch)
|
||||||
|
@ -206,9 +200,3 @@ func (ro *RunningOutput) write(metrics []telegraf.Metric) error {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// OutputConfig containing name and filter
|
|
||||||
type OutputConfig struct {
|
|
||||||
Name string
|
|
||||||
Filter Filter
|
|
||||||
}
|
|
||||||
|
|
|
@ -75,23 +75,6 @@ func BenchmarkRunningOutputAddFailWrites(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddingNilMetric(t *testing.T) {
|
|
||||||
conf := &OutputConfig{
|
|
||||||
Filter: Filter{},
|
|
||||||
}
|
|
||||||
|
|
||||||
m := &mockOutput{}
|
|
||||||
ro := NewRunningOutput("test", m, conf, 1000, 10000)
|
|
||||||
|
|
||||||
ro.AddMetric(nil)
|
|
||||||
ro.AddMetric(nil)
|
|
||||||
ro.AddMetric(nil)
|
|
||||||
|
|
||||||
err := ro.Write()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Len(t, m.Metrics(), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that NameDrop filters ger properly applied.
|
// Test that NameDrop filters ger properly applied.
|
||||||
func TestRunningOutput_DropFilter(t *testing.T) {
|
func TestRunningOutput_DropFilter(t *testing.T) {
|
||||||
conf := &OutputConfig{
|
conf := &OutputConfig{
|
||||||
|
|
|
@ -34,14 +34,18 @@ func (rp *RunningProcessor) Apply(in ...telegraf.Metric) []telegraf.Metric {
|
||||||
ret := []telegraf.Metric{}
|
ret := []telegraf.Metric{}
|
||||||
|
|
||||||
for _, metric := range in {
|
for _, metric := range in {
|
||||||
if rp.Config.Filter.IsActive() {
|
// In processors when a filter selects a metric it is sent through the
|
||||||
// check if the filter should be applied to this metric
|
// processor. Otherwise the metric continues downstream unmodified.
|
||||||
if ok := rp.Config.Filter.Apply(metric.Name(), metric.Fields(), metric.Tags()); !ok {
|
if ok := rp.Config.Filter.Select(metric); !ok {
|
||||||
// this means filter should not be applied
|
|
||||||
ret = append(ret, metric)
|
ret = append(ret, metric)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rp.Config.Filter.Modify(metric)
|
||||||
|
if len(metric.FieldList()) == 0 {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// This metric should pass through the filter, so call the filter Apply
|
// This metric should pass through the filter, so call the filter Apply
|
||||||
// function and append results to the output slice.
|
// function and append results to the output slice.
|
||||||
ret = append(ret, rp.Processor.Apply(metric)...)
|
ret = append(ret, rp.Processor.Apply(metric)...)
|
||||||
|
|
|
@ -1,117 +1,203 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/metric"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestProcessor struct {
|
// MockProcessor is a Processor with an overrideable Apply implementation.
|
||||||
|
type MockProcessor struct {
|
||||||
|
ApplyF func(in ...telegraf.Metric) []telegraf.Metric
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *TestProcessor) SampleConfig() string { return "" }
|
func (p *MockProcessor) SampleConfig() string {
|
||||||
func (f *TestProcessor) Description() string { return "" }
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// Apply renames:
|
func (p *MockProcessor) Description() string {
|
||||||
// "foo" to "fuz"
|
return ""
|
||||||
// "bar" to "baz"
|
}
|
||||||
// And it also drops measurements named "dropme"
|
|
||||||
func (f *TestProcessor) Apply(in ...telegraf.Metric) []telegraf.Metric {
|
func (p *MockProcessor) Apply(in ...telegraf.Metric) []telegraf.Metric {
|
||||||
out := make([]telegraf.Metric, 0)
|
return p.ApplyF(in...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagProcessor returns a Processor whose Apply function adds the tag and
|
||||||
|
// value.
|
||||||
|
func TagProcessor(key, value string) *MockProcessor {
|
||||||
|
return &MockProcessor{
|
||||||
|
ApplyF: func(in ...telegraf.Metric) []telegraf.Metric {
|
||||||
for _, m := range in {
|
for _, m := range in {
|
||||||
switch m.Name() {
|
m.AddTag(key, value)
|
||||||
case "foo":
|
|
||||||
out = append(out, testutil.TestMetric(1, "fuz"))
|
|
||||||
case "bar":
|
|
||||||
out = append(out, testutil.TestMetric(1, "baz"))
|
|
||||||
case "dropme":
|
|
||||||
// drop the metric!
|
|
||||||
default:
|
|
||||||
out = append(out, m)
|
|
||||||
}
|
}
|
||||||
|
return in
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return out
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTestRunningProcessor() *RunningProcessor {
|
func Metric(
|
||||||
out := &RunningProcessor{
|
name string,
|
||||||
Name: "test",
|
tags map[string]string,
|
||||||
Processor: &TestProcessor{},
|
fields map[string]interface{},
|
||||||
Config: &ProcessorConfig{Filter: Filter{}},
|
tm time.Time,
|
||||||
|
tp ...telegraf.ValueType,
|
||||||
|
) telegraf.Metric {
|
||||||
|
m, err := metric.New(name, tags, fields, tm, tp...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
return out
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunningProcessor(t *testing.T) {
|
func TestRunningProcessor_Apply(t *testing.T) {
|
||||||
inmetrics := []telegraf.Metric{
|
type args struct {
|
||||||
testutil.TestMetric(1, "foo"),
|
Processor telegraf.Processor
|
||||||
testutil.TestMetric(1, "bar"),
|
Config *ProcessorConfig
|
||||||
testutil.TestMetric(1, "baz"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedNames := []string{
|
tests := []struct {
|
||||||
"fuz",
|
name string
|
||||||
"baz",
|
args args
|
||||||
"baz",
|
input []telegraf.Metric
|
||||||
}
|
expected []telegraf.Metric
|
||||||
rfp := NewTestRunningProcessor()
|
}{
|
||||||
filteredMetrics := rfp.Apply(inmetrics...)
|
{
|
||||||
|
name: "inactive filter applies metrics",
|
||||||
actualNames := []string{
|
args: args{
|
||||||
filteredMetrics[0].Name(),
|
Processor: TagProcessor("apply", "true"),
|
||||||
filteredMetrics[1].Name(),
|
Config: &ProcessorConfig{
|
||||||
filteredMetrics[2].Name(),
|
Filter: Filter{},
|
||||||
}
|
},
|
||||||
assert.Equal(t, expectedNames, actualNames)
|
},
|
||||||
|
input: []telegraf.Metric{
|
||||||
|
Metric(
|
||||||
|
"cpu",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{
|
||||||
|
"value": 42.0,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
expected: []telegraf.Metric{
|
||||||
|
Metric(
|
||||||
|
"cpu",
|
||||||
|
map[string]string{
|
||||||
|
"apply": "true",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"value": 42.0,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "filter applies",
|
||||||
|
args: args{
|
||||||
|
Processor: TagProcessor("apply", "true"),
|
||||||
|
Config: &ProcessorConfig{
|
||||||
|
Filter: Filter{
|
||||||
|
NamePass: []string{"cpu"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input: []telegraf.Metric{
|
||||||
|
Metric(
|
||||||
|
"cpu",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{
|
||||||
|
"value": 42.0,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
expected: []telegraf.Metric{
|
||||||
|
Metric(
|
||||||
|
"cpu",
|
||||||
|
map[string]string{
|
||||||
|
"apply": "true",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"value": 42.0,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "filter doesn't apply",
|
||||||
|
args: args{
|
||||||
|
Processor: TagProcessor("apply", "true"),
|
||||||
|
Config: &ProcessorConfig{
|
||||||
|
Filter: Filter{
|
||||||
|
NameDrop: []string{"cpu"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input: []telegraf.Metric{
|
||||||
|
Metric(
|
||||||
|
"cpu",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{
|
||||||
|
"value": 42.0,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
expected: []telegraf.Metric{
|
||||||
|
Metric(
|
||||||
|
"cpu",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{
|
||||||
|
"value": 42.0,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunningProcessor_WithNameDrop(t *testing.T) {
|
for _, tt := range tests {
|
||||||
inmetrics := []telegraf.Metric{
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
testutil.TestMetric(1, "foo"),
|
rp := &RunningProcessor{
|
||||||
testutil.TestMetric(1, "bar"),
|
Processor: tt.args.Processor,
|
||||||
testutil.TestMetric(1, "baz"),
|
Config: tt.args.Config,
|
||||||
|
}
|
||||||
|
rp.Config.Filter.Compile()
|
||||||
|
|
||||||
|
actual := rp.Apply(tt.input...)
|
||||||
|
require.Equal(t, tt.expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedNames := []string{
|
func TestRunningProcessor_Order(t *testing.T) {
|
||||||
"foo",
|
rp1 := &RunningProcessor{
|
||||||
"baz",
|
Config: &ProcessorConfig{
|
||||||
"baz",
|
Order: 1,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
rfp := NewTestRunningProcessor()
|
rp2 := &RunningProcessor{
|
||||||
|
Config: &ProcessorConfig{
|
||||||
rfp.Config.Filter.NameDrop = []string{"foo"}
|
Order: 2,
|
||||||
assert.NoError(t, rfp.Config.Filter.Compile())
|
},
|
||||||
|
|
||||||
filteredMetrics := rfp.Apply(inmetrics...)
|
|
||||||
|
|
||||||
actualNames := []string{
|
|
||||||
filteredMetrics[0].Name(),
|
|
||||||
filteredMetrics[1].Name(),
|
|
||||||
filteredMetrics[2].Name(),
|
|
||||||
}
|
}
|
||||||
assert.Equal(t, expectedNames, actualNames)
|
rp3 := &RunningProcessor{
|
||||||
|
Config: &ProcessorConfig{
|
||||||
|
Order: 3,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunningProcessor_DroppedMetric(t *testing.T) {
|
procs := RunningProcessors{rp2, rp3, rp1}
|
||||||
inmetrics := []telegraf.Metric{
|
sort.Sort(procs)
|
||||||
testutil.TestMetric(1, "dropme"),
|
require.Equal(t,
|
||||||
testutil.TestMetric(1, "foo"),
|
RunningProcessors{rp1, rp2, rp3},
|
||||||
testutil.TestMetric(1, "bar"),
|
procs)
|
||||||
}
|
|
||||||
|
|
||||||
expectedNames := []string{
|
|
||||||
"fuz",
|
|
||||||
"baz",
|
|
||||||
}
|
|
||||||
rfp := NewTestRunningProcessor()
|
|
||||||
filteredMetrics := rfp.Apply(inmetrics...)
|
|
||||||
|
|
||||||
actualNames := []string{
|
|
||||||
filteredMetrics[0].Name(),
|
|
||||||
filteredMetrics[1].Name(),
|
|
||||||
}
|
|
||||||
assert.Equal(t, expectedNames, actualNames)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue