diff --git a/README.md b/README.md index 00142aa..0e73284 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,27 @@ mappings: job: "${1}_server_other" ``` +You may also drop metrics by specifying a "drop" action on a match. For example: + +```yaml +mappings: +# This metric would match as normal. +- match: test.timing.*.*.* + name: "my_timer" + labels: + provider: "$2" + outcome: "$3" + job: "${1}_server" +# Any metric not matched will be dropped because "." matches all metrics. +- match: . + match_type: regex + action: drop + name: "dropped" +``` + +You can drop any metric using the normal match syntax. +The default action is "map" which does the normal metrics mapping. + ## Using Docker You can deploy this exporter using the [prom/statsd-exporter](https://registry.hub.docker.com/u/prom/statsd-exporter/) Docker image. diff --git a/action.go b/action.go new file mode 100644 index 0000000..c1baab3 --- /dev/null +++ b/action.go @@ -0,0 +1,42 @@ +// Copyright 2018 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import "fmt" + +type actionType string + +const ( + actionTypeMap actionType = "map" + actionTypeDrop actionType = "drop" + actionTypeDefault actionType = "" +) + +func (t *actionType) UnmarshalYAML(unmarshal func(interface{}) error) error { + var v string + + if err := unmarshal(&v); err != nil { + return err + } + + switch actionType(v) { + case actionTypeDrop: + *t = actionTypeDrop + case actionTypeMap, actionTypeDefault: + *t = actionTypeMap + default: + return fmt.Errorf("invalid action type %q", v) + } + return nil +} diff --git a/exporter.go b/exporter.go index d83c483..bbb3868 100644 --- a/exporter.go +++ b/exporter.go @@ -252,6 +252,11 @@ func (b *Exporter) Listen(e <-chan Events) { if mapping == nil { mapping = &metricMapping{} } + + if mapping.Action == actionTypeDrop { + continue + } + if mapping.HelpText == "" { help = defaultHelp } else { diff --git a/mapper.go b/mapper.go index 9c80cfc..752dc3f 100644 --- a/mapper.go +++ b/mapper.go @@ -54,6 +54,7 @@ type metricMapping struct { Buckets []float64 `yaml:"buckets"` MatchType matchType `yaml:"match_type"` HelpText string `yaml:"help"` + Action actionType `yaml:"action"` } func (m *metricMapper) initFromYAMLString(fileContents string) error { @@ -93,6 +94,10 @@ func (m *metricMapper) initFromYAMLString(fileContents string) error { currentMapping.MatchType = n.Defaults.MatchType } + if currentMapping.Action == "" { + currentMapping.Action = actionTypeMap + } + if currentMapping.MatchType == matchTypeGlob { if !metricLineRE.MatchString(currentMapping.Match) { return fmt.Errorf("invalid match: %s", currentMapping.Match) diff --git a/mapper_test.go b/mapper_test.go index d01bd80..2955c73 100644 --- a/mapper_test.go +++ b/mapper_test.go @@ -360,6 +360,39 @@ mappings: }, }, }, + // Config that drops all. + { + config: `mappings: +- match: . + match_type: regex + name: "drop" + action: drop`, + mappings: mappings{ + "test.a": {}, + "abc": {}, + }, + }, + // Config that has a catch-all to drop all. + { + config: `mappings: +- match: web.* + name: "web" + labels: + site: "$1" +- match: . + match_type: regex + name: "drop" + action: drop`, + mappings: mappings{ + "test.a": {}, + "web.localhost": { + name: "web", + labels: map[string]string{ + "site": "localhost", + }, + }, + }, + }, } mapper := metricMapper{} @@ -374,7 +407,7 @@ mappings: for metric, mapping := range scenario.mappings { m, labels, present := mapper.getMapping(metric) - if present && m.Name != mapping.name { + if present && mapping.name != "" && m.Name != mapping.name { t.Fatalf("%d.%q: Expected name %v, got %v", i, metric, m.Name, mapping.name) } if mapping.notPresent && present { @@ -392,3 +425,73 @@ mappings: } } } + +func TestAction(t *testing.T) { + scenarios := []struct { + config string + configBad bool + expectedAction actionType + }{ + { + // no action set + config: `--- +mappings: +- match: test.*.* + name: "foo" +`, + configBad: false, + expectedAction: actionTypeMap, + }, + { + // map action set + config: `--- +mappings: +- match: test.*.* + name: "foo" + action: map +`, + configBad: false, + expectedAction: actionTypeMap, + }, + { + // drop action set + config: `--- +mappings: +- match: test.*.* + name: "foo" + action: drop +`, + configBad: false, + expectedAction: actionTypeDrop, + }, + { + // invalid action set + config: `--- +mappings: +- match: test.*.* + name: "foo" + action: xyz +`, + configBad: true, + expectedAction: actionTypeDrop, + }, + } + + for i, scenario := range scenarios { + mapper := metricMapper{} + err := mapper.initFromYAMLString(scenario.config) + if err != nil && !scenario.configBad { + t.Fatalf("%d. Config load error: %s %s", i, scenario.config, err) + } + if err == nil && scenario.configBad { + t.Fatalf("%d. Expected bad config, but loaded ok: %s", i, scenario.config) + } + + if !scenario.configBad { + a := mapper.Mappings[0].Action + if scenario.expectedAction != a { + t.Fatalf("%d: Expected action %v, got %v", i, scenario.expectedAction, a) + } + } + } +}