Merge pull request #136 from westphahl/match-metric-type

Allow mapping match on metric type
This commit is contained in:
Matthias Rampke 2018-07-02 15:22:54 +00:00 committed by GitHub
commit 09e16e7aa9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 103 additions and 12 deletions

View file

@ -251,6 +251,21 @@ mappings:
You can drop any metric using the normal match syntax.
The default action is "map" which does the normal metrics mapping.
StatsD allows emitting of different metric types under the same metric name,
but the Prometheus client library can't merge those. For this use-case the
mapping definition allows you to specify which metric type to match:
```
mappings:
- match: test.foo.*
name: "test_foo"
match_metric_type: counter
labels:
provider: "$1"
```
Possible values for `match_metric_type` are `gauge`, `counter` and `timer`.
## Using Docker
You can deploy this exporter using the [prom/statsd-exporter](https://registry.hub.docker.com/u/prom/statsd-exporter/) Docker image.

View file

@ -182,6 +182,7 @@ type Event interface {
MetricName() string
Value() float64
Labels() map[string]string
MetricType() metricType
}
type CounterEvent struct {
@ -193,6 +194,7 @@ type CounterEvent struct {
func (c *CounterEvent) MetricName() string { return c.metricName }
func (c *CounterEvent) Value() float64 { return c.value }
func (c *CounterEvent) Labels() map[string]string { return c.labels }
func (c *CounterEvent) MetricType() metricType { return metricTypeCounter }
type GaugeEvent struct {
metricName string
@ -204,6 +206,7 @@ type GaugeEvent struct {
func (g *GaugeEvent) MetricName() string { return g.metricName }
func (g *GaugeEvent) Value() float64 { return g.value }
func (c *GaugeEvent) Labels() map[string]string { return c.labels }
func (c *GaugeEvent) MetricType() metricType { return metricTypeGauge }
type TimerEvent struct {
metricName string
@ -214,6 +217,7 @@ type TimerEvent struct {
func (t *TimerEvent) MetricName() string { return t.metricName }
func (t *TimerEvent) Value() float64 { return t.value }
func (c *TimerEvent) Labels() map[string]string { return c.labels }
func (c *TimerEvent) MetricType() metricType { return metricTypeTimer }
type Events []Event
@ -248,7 +252,7 @@ func (b *Exporter) Listen(e <-chan Events) {
metricName := ""
prometheusLabels := event.Labels()
mapping, labels, present := b.mapper.getMapping(event.MetricName())
mapping, labels, present := b.mapper.getMapping(event.MetricName(), event.MetricType())
if mapping == nil {
mapping = &metricMapping{}
}

View file

@ -45,16 +45,19 @@ type metricMapper struct {
mutex sync.Mutex
}
type matchMetricType string
type metricMapping struct {
Match string `yaml:"match"`
Name string `yaml:"name"`
regex *regexp.Regexp
Labels prometheus.Labels `yaml:"labels"`
TimerType timerType `yaml:"timer_type"`
Buckets []float64 `yaml:"buckets"`
MatchType matchType `yaml:"match_type"`
HelpText string `yaml:"help"`
Action actionType `yaml:"action"`
Match string `yaml:"match"`
Name string `yaml:"name"`
regex *regexp.Regexp
Labels prometheus.Labels `yaml:"labels"`
TimerType timerType `yaml:"timer_type"`
Buckets []float64 `yaml:"buckets"`
MatchType matchType `yaml:"match_type"`
HelpText string `yaml:"help"`
Action actionType `yaml:"action"`
MatchMetricType metricType `yaml:"match_metric_type"`
}
func (m *metricMapper) initFromYAMLString(fileContents string) error {
@ -148,7 +151,7 @@ func (m *metricMapper) initFromFile(fileName string) error {
return m.initFromYAMLString(string(mappingStr))
}
func (m *metricMapper) getMapping(statsdMetric string) (*metricMapping, prometheus.Labels, bool) {
func (m *metricMapper) getMapping(statsdMetric string, statsdMetricType metricType) (*metricMapping, prometheus.Labels, bool) {
m.mutex.Lock()
defer m.mutex.Unlock()
@ -165,6 +168,10 @@ func (m *metricMapper) getMapping(statsdMetric string) (*metricMapping, promethe
matches,
))
if mt := mapping.MatchMetricType; mt != "" && mt != statsdMetricType {
continue
}
labels := prometheus.Labels{}
for label, valueExpr := range mapping.Labels {
value := mapping.regex.ExpandString([]byte{}, valueExpr, statsdMetric, matches)

View file

@ -302,6 +302,27 @@ mappings:
- match: test.*.*
timer_type: wrong
name: "foo"
labels: {}
`,
configBad: true,
},
// Config with good metric type.
{
config: `---
mappings:
- match: test.*.*
match_metric_type: counter
name: "foo"
labels: {}
`,
},
// Config with bad metric type matcher.
{
config: `---
mappings:
- match: test.*.*
match_metric_type: wrong
name: "foo"
labels: {}
`,
configBad: true,
@ -432,8 +453,9 @@ mappings:
t.Fatalf("%d. Expected bad config, but loaded ok: %s", i, scenario.config)
}
var dummyMetricType metricType = ""
for metric, mapping := range scenario.mappings {
m, labels, present := mapper.getMapping(metric)
m, labels, present := mapper.getMapping(metric, dummyMetricType)
if present && mapping.name != "" && m.Name != mapping.name {
t.Fatalf("%d.%q: Expected name %v, got %v", i, metric, m.Name, mapping.name)
}

43
metric_type.go Normal file
View file

@ -0,0 +1,43 @@
// 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 metricType string
const (
metricTypeCounter metricType = "counter"
metricTypeGauge metricType = "gauge"
metricTypeTimer metricType = "timer"
)
func (m *metricType) UnmarshalYAML(unmarshal func(interface{}) error) error {
var v string
if err := unmarshal(&v); err != nil {
return err
}
switch metricType(v) {
case metricTypeCounter:
*m = metricTypeCounter
case metricTypeGauge:
*m = metricTypeGauge
case metricTypeTimer:
*m = metricTypeTimer
default:
return fmt.Errorf("invalid metric type '%s'", v)
}
return nil
}