Skip to content

Commit

Permalink
Add metric pass/drop filter
Browse files Browse the repository at this point in the history
  • Loading branch information
titilambert authored and sparrc committed Feb 22, 2016
1 parent 9ce8d78 commit d00550c
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 34 deletions.
11 changes: 10 additions & 1 deletion agent/accumulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ func (ac *accumulator) Add(
) {
fields := make(map[string]interface{})
fields["value"] = value

if !ac.inputConfig.Filter.ShouldNamePass(measurement) {
return
}

ac.AddFields(measurement, fields, tags, t...)
}

Expand All @@ -56,6 +61,10 @@ func (ac *accumulator) AddFields(
return
}

if !ac.inputConfig.Filter.ShouldNamePass(measurement) {
return
}

if !ac.inputConfig.Filter.ShouldTagsPass(tags) {
return
}
Expand Down Expand Up @@ -92,7 +101,7 @@ func (ac *accumulator) AddFields(
for k, v := range fields {
// Filter out any filtered fields
if ac.inputConfig != nil {
if !ac.inputConfig.Filter.ShouldPass(k) {
if !ac.inputConfig.Filter.ShouldFieldsPass(k) {
continue
}
}
Expand Down
28 changes: 23 additions & 5 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,14 @@ you can configure that here.

There are also filters that can be configured per input:

* **pass**: An array of strings that is used to filter metrics generated by the
* **namepass**: An array of strings that is used to filter metrics generated by the
current input. Each string in the array is tested as a glob match against
measurement names and if it matches, the field is emitted.
* **namedrop**: The inverse of pass, if a measurement name matches, it is not emitted.
* **fieldpass**: An array of strings that is used to filter metrics generated by the
current input. Each string in the array is tested as a glob match against field names
and if it matches, the field is emitted.
* **drop**: The inverse of pass, if a field name matches, it is not emitted.
* **fielddrop**: The inverse of pass, if a field name matches, it is not emitted.
* **tagpass**: tag names and arrays of strings that are used to filter
measurements by the current input. Each string in the array is tested as a glob
match against the tag name, and if it matches the measurement is emitted.
Expand Down Expand Up @@ -117,18 +121,32 @@ fields which begin with `time_`.
path = [ "/opt", "/home*" ]
```

#### Input Config: pass and drop
#### Input Config: fieldpass and fielddrop

```toml
# Drop all metrics for guest & steal CPU usage
[[inputs.cpu]]
percpu = false
totalcpu = true
drop = ["usage_guest", "usage_steal"]
fielddrop = ["usage_guest", "usage_steal"]

# Only store inode related metrics for disks
[[inputs.disk]]
pass = ["inodes*"]
fieldpass = ["inodes*"]
```

#### Input Config: namepass and namedrop

```toml
# Drop all metrics about containers for kubelet
[[inputs.prometheus]]
urls = ["http://kube-node-1:4194/metrics"]
namedrop = ["container_"]

# Only store rest client related metrics for kubelet
[[inputs.prometheus]]
urls = ["http://kube-node-1:4194/metrics"]
namepass = ["rest_client_"]
```

#### Input config: prefix, suffix, and override
Expand Down
44 changes: 40 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,32 +483,64 @@ func (c *Config) addInput(name string, table *ast.Table) error {
func buildFilter(tbl *ast.Table) internal_models.Filter {
f := internal_models.Filter{}

if node, ok := tbl.Fields["pass"]; ok {
if node, ok := tbl.Fields["namepass"]; ok {
if kv, ok := node.(*ast.KeyValue); ok {
if ary, ok := kv.Value.(*ast.Array); ok {
for _, elem := range ary.Value {
if str, ok := elem.(*ast.String); ok {
f.Pass = append(f.Pass, str.Value)
f.NamePass = append(f.NamePass, str.Value)
f.IsActive = true
}
}
}
}
}

if node, ok := tbl.Fields["drop"]; ok {
if node, ok := tbl.Fields["namedrop"]; ok {
if kv, ok := node.(*ast.KeyValue); ok {
if ary, ok := kv.Value.(*ast.Array); ok {
for _, elem := range ary.Value {
if str, ok := elem.(*ast.String); ok {
f.Drop = append(f.Drop, str.Value)
f.NameDrop = append(f.NameDrop, str.Value)
f.IsActive = true
}
}
}
}
}

fields := []string{"pass", "fieldpass"}
for _, field := range fields {
if node, ok := tbl.Fields[field]; ok {
if kv, ok := node.(*ast.KeyValue); ok {
if ary, ok := kv.Value.(*ast.Array); ok {
for _, elem := range ary.Value {
if str, ok := elem.(*ast.String); ok {
f.FieldPass = append(f.FieldPass, str.Value)
f.IsActive = true
}
}
}
}
}
}

fields = []string{"drop", "fielddrop"}
for _, field := range fields {
if node, ok := tbl.Fields[field]; ok {
if kv, ok := node.(*ast.KeyValue); ok {
if ary, ok := kv.Value.(*ast.Array); ok {
for _, elem := range ary.Value {
if str, ok := elem.(*ast.String); ok {
f.FieldDrop = append(f.FieldDrop, str.Value)
f.IsActive = true
}
}
}
}
}
}

if node, ok := tbl.Fields["tagpass"]; ok {
if subtbl, ok := node.(*ast.Table); ok {
for name, val := range subtbl.Fields {
Expand Down Expand Up @@ -547,6 +579,10 @@ func buildFilter(tbl *ast.Table) internal_models.Filter {
}
}

delete(tbl.Fields, "namedrop")
delete(tbl.Fields, "namepass")
delete(tbl.Fields, "fielddrop")
delete(tbl.Fields, "fieldpass")
delete(tbl.Fields, "drop")
delete(tbl.Fields, "pass")
delete(tbl.Fields, "tagdrop")
Expand Down
12 changes: 8 additions & 4 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ func TestConfig_LoadSingleInput(t *testing.T) {
mConfig := &internal_models.InputConfig{
Name: "memcached",
Filter: internal_models.Filter{
Drop: []string{"other", "stuff"},
Pass: []string{"some", "strings"},
NameDrop: []string{"metricname2"},
NamePass: []string{"metricname1"},
FieldDrop: []string{"other", "stuff"},
FieldPass: []string{"some", "strings"},
TagDrop: []internal_models.TagFilter{
internal_models.TagFilter{
Name: "badtag",
Expand Down Expand Up @@ -66,8 +68,10 @@ func TestConfig_LoadDirectory(t *testing.T) {
mConfig := &internal_models.InputConfig{
Name: "memcached",
Filter: internal_models.Filter{
Drop: []string{"other", "stuff"},
Pass: []string{"some", "strings"},
NameDrop: []string{"metricname2"},
NamePass: []string{"metricname1"},
FieldDrop: []string{"other", "stuff"},
FieldPass: []string{"some", "strings"},
TagDrop: []internal_models.TagFilter{
internal_models.TagFilter{
Name: "badtag",
Expand Down
6 changes: 4 additions & 2 deletions internal/config/testdata/single_plugin.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
[[inputs.memcached]]
servers = ["localhost"]
pass = ["some", "strings"]
drop = ["other", "stuff"]
namepass = ["metricname1"]
namedrop = ["metricname2"]
fieldpass = ["some", "strings"]
fielddrop = ["other", "stuff"]
interval = "5s"
[inputs.memcached.tagpass]
goodtag = ["mytag"]
Expand Down
2 changes: 2 additions & 0 deletions internal/config/testdata/subconfig/memcached.conf
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[[inputs.memcached]]
servers = ["192.168.1.1"]
namepass = ["metricname1"]
namedrop = ["metricname2"]
pass = ["some", "strings"]
drop = ["other", "stuff"]
interval = "5s"
Expand Down
49 changes: 40 additions & 9 deletions internal/models/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ type TagFilter struct {

// Filter containing drop/pass and tagdrop/tagpass rules
type Filter struct {
Drop []string
Pass []string
NameDrop []string
NamePass []string

FieldDrop []string
FieldPass []string

TagDrop []TagFilter
TagPass []TagFilter
Expand All @@ -25,17 +28,45 @@ type Filter struct {
}

func (f Filter) ShouldMetricPass(metric telegraf.Metric) bool {
if f.ShouldPass(metric.Name()) && f.ShouldTagsPass(metric.Tags()) {
if f.ShouldFieldsPass(metric.Name()) && f.ShouldTagsPass(metric.Tags()) {
return true
}
return false
}

// ShouldPass returns true if the metric should pass, false if should drop
// ShouldFieldsPass returns true if the metric should pass, false if should drop
// based on the drop/pass filter parameters
func (f Filter) ShouldNamePass(key string) bool {
if f.NamePass != nil {
for _, pat := range f.NamePass {
// TODO remove HasPrefix check, leaving it for now for legacy support.
// Cam, 2015-12-07
if strings.HasPrefix(key, pat) || internal.Glob(pat, key) {
return true
}
}
return false
}

if f.NameDrop != nil {
for _, pat := range f.NameDrop {
// TODO remove HasPrefix check, leaving it for now for legacy support.
// Cam, 2015-12-07
if strings.HasPrefix(key, pat) || internal.Glob(pat, key) {
return false
}
}

return true
}
return true
}

// ShouldFieldsPass returns true if the metric should pass, false if should drop
// based on the drop/pass filter parameters
func (f Filter) ShouldPass(key string) bool {
if f.Pass != nil {
for _, pat := range f.Pass {
func (f Filter) ShouldFieldsPass(key string) bool {
if f.FieldPass != nil {
for _, pat := range f.FieldPass {
// TODO remove HasPrefix check, leaving it for now for legacy support.
// Cam, 2015-12-07
if strings.HasPrefix(key, pat) || internal.Glob(pat, key) {
Expand All @@ -45,8 +76,8 @@ func (f Filter) ShouldPass(key string) bool {
return false
}

if f.Drop != nil {
for _, pat := range f.Drop {
if f.FieldDrop != nil {
for _, pat := range f.FieldDrop {
// TODO remove HasPrefix check, leaving it for now for legacy support.
// Cam, 2015-12-07
if strings.HasPrefix(key, pat) || internal.Glob(pat, key) {
Expand Down
Loading

0 comments on commit d00550c

Please sign in to comment.