forked from mirrors/statsd_exporter
* Added `regex` match_type as valid config callout in both individual mappers and mapper defaults * Added test coverage for changes * Updated documentation to reflect usage of `match_type` parameter and provide example of a raw regex match rule
This commit is contained in:
parent
bd0f2139af
commit
ec3cc120e2
4 changed files with 110 additions and 11 deletions
21
README.md
21
README.md
|
@ -161,6 +161,24 @@ mappings:
|
||||||
job: "${1}_server"
|
job: "${1}_server"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Another capability when using YAML configuration is the ability to define matches
|
||||||
|
using raw regular expressions as opposed to the default globbing style of match.
|
||||||
|
This may allow for pulling structured data from otherwise poorly named statsd
|
||||||
|
metrics AND allow for more precise targetting of match rules. When no `match_type`
|
||||||
|
paramter is specified the default value of `glob` will be assumed:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
mappings:
|
||||||
|
- match: (.*)\.(.*)--(.*)\.status\.(.*)\.count
|
||||||
|
match_type: regex
|
||||||
|
labels:
|
||||||
|
name: "request_total"
|
||||||
|
hostname: "$1"
|
||||||
|
exec: "$2"
|
||||||
|
protocol: "$3"
|
||||||
|
code: "$4"
|
||||||
|
```
|
||||||
|
|
||||||
Note, that one may also set the histogram buckets. If not set, then the default
|
Note, that one may also set the histogram buckets. If not set, then the default
|
||||||
[Prometheus client values](https://godoc.org/github.com/prometheus/client_golang/prometheus#pkg-variables) are used: `[.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10]`. `+Inf` is added
|
[Prometheus client values](https://godoc.org/github.com/prometheus/client_golang/prometheus#pkg-variables) are used: `[.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10]`. `+Inf` is added
|
||||||
automatically.
|
automatically.
|
||||||
|
@ -169,13 +187,14 @@ automatically.
|
||||||
only used when the statsd metric type is a timerand the `timer_type` is set to
|
only used when the statsd metric type is a timerand the `timer_type` is set to
|
||||||
"histogram."
|
"histogram."
|
||||||
|
|
||||||
One may also set defaults for the timer type and the buckets. These will be used
|
One may also set defaults for the timer type, buckets and match_type. These will be used
|
||||||
by all mappings that do not define these.
|
by all mappings that do not define these.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
defaults:
|
defaults:
|
||||||
timer_type: histogram
|
timer_type: histogram
|
||||||
buckets: [.005, .01, .025, .05, .1, .25, .5, 1, 2.5 ]
|
buckets: [.005, .01, .025, .05, .1, .25, .5, 1, 2.5 ]
|
||||||
|
match_type: glob
|
||||||
mappings:
|
mappings:
|
||||||
# This will be a histogram using the buckets set in `defaults`.
|
# This will be a histogram using the buckets set in `defaults`.
|
||||||
- match: test.timing.*.*.*
|
- match: test.timing.*.*.*
|
||||||
|
|
22
mapper.go
22
mapper.go
|
@ -37,6 +37,7 @@ var (
|
||||||
type mapperConfigDefaults struct {
|
type mapperConfigDefaults struct {
|
||||||
TimerType timerType `yaml:"timer_type"`
|
TimerType timerType `yaml:"timer_type"`
|
||||||
Buckets []float64 `yaml:"buckets"`
|
Buckets []float64 `yaml:"buckets"`
|
||||||
|
MatchType matchType `yaml:"match_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type metricMapper struct {
|
type metricMapper struct {
|
||||||
|
@ -51,6 +52,7 @@ type metricMapping struct {
|
||||||
Labels prometheus.Labels `yaml:"labels"`
|
Labels prometheus.Labels `yaml:"labels"`
|
||||||
TimerType timerType `yaml:"timer_type"`
|
TimerType timerType `yaml:"timer_type"`
|
||||||
Buckets []float64 `yaml:"buckets"`
|
Buckets []float64 `yaml:"buckets"`
|
||||||
|
MatchType matchType `yaml:"match_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type configLoadStates int
|
type configLoadStates int
|
||||||
|
@ -136,14 +138,14 @@ func (m *metricMapper) initFromYAMLString(fileContents string) error {
|
||||||
n.Defaults.Buckets = prometheus.DefBuckets
|
n.Defaults.Buckets = prometheus.DefBuckets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if n.Defaults.MatchType == matchTypeDefault {
|
||||||
|
n.Defaults.MatchType = matchTypeGlob
|
||||||
|
}
|
||||||
|
|
||||||
for i := range n.Mappings {
|
for i := range n.Mappings {
|
||||||
currentMapping := &n.Mappings[i]
|
currentMapping := &n.Mappings[i]
|
||||||
|
|
||||||
if !metricLineRE.MatchString(currentMapping.Match) {
|
// check that label is correct
|
||||||
return fmt.Errorf("invalid match: %s", currentMapping.Match)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that label is correct.
|
|
||||||
for k, v := range currentMapping.Labels {
|
for k, v := range currentMapping.Labels {
|
||||||
if !metricNameRE.MatchString(k) {
|
if !metricNameRE.MatchString(k) {
|
||||||
return fmt.Errorf("invalid label key: %s", k)
|
return fmt.Errorf("invalid label key: %s", k)
|
||||||
|
@ -156,12 +158,22 @@ func (m *metricMapper) initFromYAMLString(fileContents string) error {
|
||||||
if _, ok := currentMapping.Labels["name"]; !ok {
|
if _, ok := currentMapping.Labels["name"]; !ok {
|
||||||
return fmt.Errorf("line %d: metric mapping didn't set a metric name", i)
|
return fmt.Errorf("line %d: metric mapping didn't set a metric name", i)
|
||||||
}
|
}
|
||||||
|
if currentMapping.MatchType == "" {
|
||||||
|
currentMapping.MatchType = n.Defaults.MatchType
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentMapping.MatchType == matchTypeGlob {
|
||||||
|
if !metricLineRE.MatchString(currentMapping.Match) {
|
||||||
|
return fmt.Errorf("invalid match: %s", currentMapping.Match)
|
||||||
|
}
|
||||||
// Translate the glob-style metric match line into a proper regex that we
|
// Translate the glob-style metric match line into a proper regex that we
|
||||||
// can use to match metrics later on.
|
// can use to match metrics later on.
|
||||||
metricRe := strings.Replace(currentMapping.Match, ".", "\\.", -1)
|
metricRe := strings.Replace(currentMapping.Match, ".", "\\.", -1)
|
||||||
metricRe = strings.Replace(metricRe, "*", "([^.]+)", -1)
|
metricRe = strings.Replace(metricRe, "*", "([^.]+)", -1)
|
||||||
currentMapping.regex = regexp.MustCompile("^" + metricRe + "$")
|
currentMapping.regex = regexp.MustCompile("^" + metricRe + "$")
|
||||||
|
} else {
|
||||||
|
currentMapping.regex = regexp.MustCompile(currentMapping.Match)
|
||||||
|
}
|
||||||
|
|
||||||
if currentMapping.TimerType == "" {
|
if currentMapping.TimerType == "" {
|
||||||
currentMapping.TimerType = n.Defaults.TimerType
|
currentMapping.TimerType = n.Defaults.TimerType
|
||||||
|
|
|
@ -219,6 +219,15 @@ mappings:
|
||||||
second: "$2"
|
second: "$2"
|
||||||
third: "$3"
|
third: "$3"
|
||||||
job: "$1-$2-$3"
|
job: "$1-$2-$3"
|
||||||
|
- match: (.*)\.(.*)-(.*)\.(.*)
|
||||||
|
match_type: regex
|
||||||
|
labels:
|
||||||
|
name: "proxy_requests_total"
|
||||||
|
job: "$1"
|
||||||
|
protocol: "$2"
|
||||||
|
endpoint: "$3"
|
||||||
|
result: "$4"
|
||||||
|
|
||||||
`,
|
`,
|
||||||
mappings: map[string]map[string]string{
|
mappings: map[string]map[string]string{
|
||||||
"test.dispatcher.FooProcessor.send.succeeded": map[string]string{
|
"test.dispatcher.FooProcessor.send.succeeded": map[string]string{
|
||||||
|
@ -243,6 +252,13 @@ mappings:
|
||||||
"job": "foo-bar-",
|
"job": "foo-bar-",
|
||||||
},
|
},
|
||||||
"foo.bar.baz": map[string]string{},
|
"foo.bar.baz": map[string]string{},
|
||||||
|
"proxy-1.http-goober.success": map[string]string{
|
||||||
|
"name": "proxy_requests_total",
|
||||||
|
"job": "proxy-1",
|
||||||
|
"protocol": "http",
|
||||||
|
"endpoint": "goober",
|
||||||
|
"result": "success",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Config with bad regex reference.
|
// Config with bad regex reference.
|
||||||
|
@ -333,6 +349,17 @@ mappings:
|
||||||
mappings:
|
mappings:
|
||||||
- match: test.*.*
|
- match: test.*.*
|
||||||
timer_type: wrong
|
timer_type: wrong
|
||||||
|
labels:
|
||||||
|
name: "foo"
|
||||||
|
`,
|
||||||
|
configBad: true,
|
||||||
|
},
|
||||||
|
//Config with uncompilable regex.
|
||||||
|
{
|
||||||
|
config: `---
|
||||||
|
mappings:
|
||||||
|
- match: *\.foo
|
||||||
|
match_type: regex
|
||||||
labels:
|
labels:
|
||||||
name: "foo"
|
name: "foo"
|
||||||
`,
|
`,
|
||||||
|
|
41
match.go
Normal file
41
match.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright 2013 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 matchType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
matchTypeGlob matchType = "glob"
|
||||||
|
matchTypeRegex matchType = "regex"
|
||||||
|
matchTypeDefault matchType = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t *matchType) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
|
var v string
|
||||||
|
if err := unmarshal(&v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch matchType(v) {
|
||||||
|
case matchTypeRegex:
|
||||||
|
*t = matchTypeRegex
|
||||||
|
case matchTypeGlob, matchTypeDefault:
|
||||||
|
*t = matchTypeGlob
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid match type %q", v)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in a new issue