Merge pull request #101 from bakins/bakins/metric-name

Allow using name in mapping rather than 'name' label
This commit is contained in:
Matthias Rampke 2017-11-13 14:24:54 +01:00 committed by GitHub
commit 520e51e6ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 184 additions and 118 deletions

View file

@ -101,15 +101,15 @@ An example mapping configuration:
```yaml ```yaml
mappings: mappings:
- match: test.dispatcher.*.*.* - match: test.dispatcher.*.*.*
labels:
name: "dispatcher_events_total" name: "dispatcher_events_total"
labels:
processor: "$1" processor: "$1"
action: "$2" action: "$2"
outcome: "$3" outcome: "$3"
job: "test_dispatcher" job: "test_dispatcher"
- match: "*.signup.*.*" - match: *.signup.*.*
labels:
name: "signup_events_total" name: "signup_events_total"
labels:
provider: "$2" provider: "$2"
outcome: "$3" outcome: "$3"
job: "${1}_server" job: "${1}_server"
@ -127,6 +127,7 @@ follows:
test.web-server.foo.bar test.web-server.foo.bar
=> test_web_server_foo_bar{} => test_web_server_foo_bar{}
Each mapping in the configuration file must define a `name` for the metric.
If the default metric help text is insufficient for your needs you may use the YAML If the default metric help text is insufficient for your needs you may use the YAML
configuration to specify a custom help text for each mapping: configuration to specify a custom help text for each mapping:
@ -134,8 +135,8 @@ configuration to specify a custom help text for each mapping:
mappings: mappings:
- match: http.request.* - match: http.request.*
help: "Total number of http requests" help: "Total number of http requests"
labels:
name: "http_requests_total" name: "http_requests_total"
labels:
code: "$1" code: "$1"
``` ```
@ -148,8 +149,8 @@ mappings:
- match: test.timing.*.*.* - match: test.timing.*.*.*
timer_type: histogram timer_type: histogram
buckets: [ 0.01, 0.025, 0.05, 0.1 ] buckets: [ 0.01, 0.025, 0.05, 0.1 ]
labels:
name: "my_timer" name: "my_timer"
labels:
provider: "$2" provider: "$2"
outcome: "$3" outcome: "$3"
job: "${1}_server" job: "${1}_server"
@ -165,8 +166,8 @@ paramter is specified the default value of `glob` will be assumed:
mappings: mappings:
- match: (.*)\.(.*)--(.*)\.status\.(.*)\.count - match: (.*)\.(.*)--(.*)\.status\.(.*)\.count
match_type: regex match_type: regex
labels:
name: "request_total" name: "request_total"
labels:
hostname: "$1" hostname: "$1"
exec: "$2" exec: "$2"
protocol: "$3" protocol: "$3"
@ -192,16 +193,16 @@ defaults:
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.*.*.*
labels:
name: "my_timer" name: "my_timer"
labels:
provider: "$2" provider: "$2"
outcome: "$3" outcome: "$3"
job: "${1}_server" job: "${1}_server"
# This will be a summary timer. # This will be a summary timer.
- match: other.timing.*.*.* - match: other.timing.*.*.*
timer_type: summary timer_type: summary
labels:
name: "other_timer" name: "other_timer"
labels:
provider: "$2" provider: "$2"
outcome: "$3" outcome: "$3"
job: "${1}_server_other" job: "${1}_server_other"

View file

@ -258,12 +258,10 @@ func (b *Exporter) Listen(e <-chan Events) {
help = mapping.HelpText help = mapping.HelpText
} }
if present { if present {
metricName = labels["name"] metricName = mapping.Name
for label, value := range labels { for label, value := range labels {
if label != "name" {
prometheusLabels[label] = value prometheusLabels[label] = value
} }
}
} else { } else {
eventsUnmapped.Inc() eventsUnmapped.Inc()
metricName = escapeMetricName(event.MetricName()) metricName = escapeMetricName(event.MetricName())

View file

@ -47,6 +47,7 @@ type metricMapper struct {
type metricMapping struct { type metricMapping struct {
Match string `yaml:"match"` Match string `yaml:"match"`
Name string `yaml:"name"`
regex *regexp.Regexp regex *regexp.Regexp
Labels prometheus.Labels `yaml:"labels"` Labels prometheus.Labels `yaml:"labels"`
TimerType timerType `yaml:"timer_type"` TimerType timerType `yaml:"timer_type"`
@ -74,18 +75,20 @@ func (m *metricMapper) initFromYAMLString(fileContents string) error {
currentMapping := &n.Mappings[i] currentMapping := &n.Mappings[i]
// check that label is correct // check that label is correct
for k, v := range currentMapping.Labels { for k := 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)
} }
if k == "name" && !metricNameRE.MatchString(v) {
return fmt.Errorf("metric name '%s' doesn't match regex '%s'", v, metricNameRE)
}
} }
if _, ok := currentMapping.Labels["name"]; !ok { if currentMapping.Name == "" {
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 !metricNameRE.MatchString(currentMapping.Name) {
return fmt.Errorf("metric name '%s' doesn't match regex '%s'", currentMapping.Name, metricNameRE)
}
if currentMapping.MatchType == "" { if currentMapping.MatchType == "" {
currentMapping.MatchType = n.Defaults.MatchType currentMapping.MatchType = n.Defaults.MatchType
} }

View file

@ -17,11 +17,17 @@ import (
"testing" "testing"
) )
type mappings map[string]struct {
name string
labels map[string]string
notPresent bool
}
func TestMetricMapperYAML(t *testing.T) { func TestMetricMapperYAML(t *testing.T) {
scenarios := []struct { scenarios := []struct {
config string config string
configBad bool configBad bool
mappings map[string]map[string]string mappings mappings
}{ }{
// Empty config. // Empty config.
{}, {},
@ -30,22 +36,22 @@ func TestMetricMapperYAML(t *testing.T) {
config: `--- config: `---
mappings: mappings:
- match: test.dispatcher.*.*.* - match: test.dispatcher.*.*.*
labels:
name: "dispatch_events" name: "dispatch_events"
labels:
processor: "$1" processor: "$1"
action: "$2" action: "$2"
result: "$3" result: "$3"
job: "test_dispatcher" job: "test_dispatcher"
- match: test.my-dispatch-host01.name.dispatcher.*.*.* - match: test.my-dispatch-host01.name.dispatcher.*.*.*
labels:
name: "host_dispatch_events" name: "host_dispatch_events"
labels:
processor: "$1" processor: "$1"
action: "$2" action: "$2"
result: "$3" result: "$3"
job: "test_dispatcher" job: "test_dispatcher"
- match: request_time.*.*.*.*.*.*.*.*.*.*.*.* - match: request_time.*.*.*.*.*.*.*.*.*.*.*.*
labels:
name: "tyk_http_request" name: "tyk_http_request"
labels:
method_and_path: "${1}" method_and_path: "${1}"
response_code: "${2}" response_code: "${2}"
apikey: "${3}" apikey: "${3}"
@ -59,39 +65,44 @@ mappings:
orgid: "${11}" orgid: "${11}"
oauthid: "${12}" oauthid: "${12}"
- match: "*.*" - match: "*.*"
labels:
name: "catchall" name: "catchall"
labels:
first: "$1" first: "$1"
second: "$2" second: "$2"
third: "$3" third: "$3"
job: "$1-$2-$3" job: "$1-$2-$3"
- match: (.*)\.(.*)-(.*)\.(.*) - match: (.*)\.(.*)-(.*)\.(.*)
match_type: regex match_type: regex
labels:
name: "proxy_requests_total" name: "proxy_requests_total"
labels:
job: "$1" job: "$1"
protocol: "$2" protocol: "$2"
endpoint: "$3" endpoint: "$3"
result: "$4" result: "$4"
`, `,
mappings: map[string]map[string]string{ mappings: mappings{
"test.dispatcher.FooProcessor.send.succeeded": map[string]string{ "test.dispatcher.FooProcessor.send.succeeded": {
"name": "dispatch_events", name: "dispatch_events",
labels: map[string]string{
"processor": "FooProcessor", "processor": "FooProcessor",
"action": "send", "action": "send",
"result": "succeeded", "result": "succeeded",
"job": "test_dispatcher", "job": "test_dispatcher",
}, },
"test.my-dispatch-host01.name.dispatcher.FooProcessor.send.succeeded": map[string]string{ },
"name": "host_dispatch_events", "test.my-dispatch-host01.name.dispatcher.FooProcessor.send.succeeded": {
name: "host_dispatch_events",
labels: map[string]string{
"processor": "FooProcessor", "processor": "FooProcessor",
"action": "send", "action": "send",
"result": "succeeded", "result": "succeeded",
"job": "test_dispatcher", "job": "test_dispatcher",
}, },
"request_time.get/threads/1/posts.200.00000000.nonversioned.discussions.a11bbcdf0ac64ec243658dc64b7100fb.172.20.0.1.12ba97b7eaa1a50001000001.": map[string]string{ },
"name": "tyk_http_request", "request_time.get/threads/1/posts.200.00000000.nonversioned.discussions.a11bbcdf0ac64ec243658dc64b7100fb.172.20.0.1.12ba97b7eaa1a50001000001.": {
name: "tyk_http_request",
labels: map[string]string{
"method_and_path": "get/threads/1/posts", "method_and_path": "get/threads/1/posts",
"response_code": "200", "response_code": "200",
"apikey": "00000000", "apikey": "00000000",
@ -105,16 +116,20 @@ mappings:
"orgid": "12ba97b7eaa1a50001000001", "orgid": "12ba97b7eaa1a50001000001",
"oauthid": "", "oauthid": "",
}, },
"foo.bar": map[string]string{ },
"name": "catchall", "foo.bar": {
name: "catchall",
labels: map[string]string{
"first": "foo", "first": "foo",
"second": "bar", "second": "bar",
"third": "", "third": "",
"job": "foo-bar-", "job": "foo-bar-",
}, },
"foo.bar.baz": map[string]string{}, },
"proxy-1.http-goober.success": map[string]string{ "foo.bar.baz": {},
"name": "proxy_requests_total", "proxy-1.http-goober.success": {
name: "proxy_requests_total",
labels: map[string]string{
"job": "proxy-1", "job": "proxy-1",
"protocol": "http", "protocol": "http",
"endpoint": "goober", "endpoint": "goober",
@ -122,45 +137,50 @@ mappings:
}, },
}, },
}, },
},
// Config with bad regex reference. // Config with bad regex reference.
{ {
config: `--- config: `---
mappings: mappings:
- match: test.* - match: test.*
labels:
name: "name" name: "name"
labels:
label: "$1_foo" label: "$1_foo"
`, `,
mappings: map[string]map[string]string{ mappings: mappings{
"test.a": map[string]string{ "test.a": {
"name": "name", name: "name",
labels: map[string]string{
"label": "", "label": "",
}, },
}, },
}, },
},
// Config with good regex reference. // Config with good regex reference.
{ {
config: ` config: `
mappings: mappings:
- match: test.* - match: test.*
labels:
name: "name" name: "name"
labels:
label: "${1}_foo" label: "${1}_foo"
`, `,
mappings: map[string]map[string]string{ mappings: mappings{
"test.a": map[string]string{ "test.a": {
"name": "name", name: "name",
labels: map[string]string{
"label": "a_foo", "label": "a_foo",
}, },
}, },
}, },
},
// Config with bad metric line. // Config with bad metric line.
{ {
config: `--- config: `---
mappings: mappings:
- match: bad--metric-line.*.* - match: bad--metric-line.*.*
labels:
name: "foo" name: "foo"
labels: {}
`, `,
configBad: true, configBad: true,
}, },
@ -169,8 +189,8 @@ mappings:
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
labels:
name: "0foo" name: "0foo"
labels: {}
`, `,
configBad: true, configBad: true,
}, },
@ -187,29 +207,31 @@ mappings:
// Config with no mappings. // Config with no mappings.
{ {
config: ``, config: ``,
mappings: map[string]map[string]string{}, mappings: mappings{},
}, },
// Config without a trailing newline. // Config without a trailing newline.
{ {
config: `mappings: config: `mappings:
- match: test.* - match: test.*
labels:
name: "name" name: "name"
labels:
label: "${1}_foo"`, label: "${1}_foo"`,
mappings: map[string]map[string]string{ mappings: mappings{
"test.a": map[string]string{ "test.a": {
"name": "name", name: "name",
labels: map[string]string{
"label": "a_foo", "label": "a_foo",
}, },
}, },
}, },
},
// Config with an improperly escaped *. // Config with an improperly escaped *.
{ {
config: ` config: `
mappings: mappings:
- match: *.test.* - match: *.test.*
labels:
name: "name" name: "name"
labels:
label: "${1}_foo"`, label: "${1}_foo"`,
configBad: true, configBad: true,
}, },
@ -218,28 +240,31 @@ mappings:
config: ` config: `
mappings: mappings:
- match: "*.test.*" - match: "*.test.*"
labels:
name: "name" name: "name"
labels:
label: "${2}_foo"`, label: "${2}_foo"`,
mappings: map[string]map[string]string{ mappings: mappings{
"foo.test.a": map[string]string{ "foo.test.a": {
"name": "name", name: "name",
labels: map[string]string{
"label": "a_foo", "label": "a_foo",
}, },
}, },
}, },
},
// Config with good timer type. // Config with good timer type.
{ {
config: `--- config: `---
mappings: mappings:
- match: test.*.* - match: test.*.*
timer_type: summary timer_type: summary
labels:
name: "foo" name: "foo"
labels: {}
`, `,
mappings: map[string]map[string]string{ mappings: mappings{
"test.*.*": map[string]string{ "test.*.*": {
"name": "foo", name: "foo",
labels: map[string]string{},
}, },
}, },
}, },
@ -249,8 +274,8 @@ mappings:
mappings: mappings:
- match: test.*.* - match: test.*.*
timer_type: wrong timer_type: wrong
labels:
name: "foo" name: "foo"
labels: {}
`, `,
configBad: true, configBad: true,
}, },
@ -260,8 +285,36 @@ mappings:
mappings: mappings:
- match: "*\.foo" - match: "*\.foo"
match_type: regex match_type: regex
labels:
name: "foo" name: "foo"
labels: {}
`,
configBad: true,
},
//Config with non-matched metric.
{
config: `---
mappings:
- match: foo.*.*
timer_type: summary
name: "foo"
labels: {}
`,
mappings: mappings{
"test.1.2": {
name: "test_1_2",
labels: map[string]string{},
notPresent: true,
},
},
},
//Config with no name.
{
config: `---
mappings:
- match: *\.foo
match_type: regex
labels:
bar: "foo"
`, `,
configBad: true, configBad: true,
}, },
@ -270,34 +323,41 @@ mappings:
config: ` config: `
mappings: mappings:
- match: test.dispatcher.*.*.* - match: test.dispatcher.*.*.*
labels:
name: "dispatcher_events_total" name: "dispatcher_events_total"
labels:
processor: "$1" processor: "$1"
action: "$2" action: "$2"
outcome: "$3" outcome: "$3"
job: "test_dispatcher" job: "test_dispatcher"
- match: "*.signup.*.*" - match: "*.signup.*.*"
labels:
name: "signup_events_total" name: "signup_events_total"
labels:
provider: "$2" provider: "$2"
outcome: "$3" outcome: "$3"
job: "${1}_server" job: "${1}_server"
`, `,
mappings: map[string]map[string]string{ mappings: mappings{
"test.dispatcher.FooProcessor.send.success": map[string]string{ "test.dispatcher.FooProcessor.send.success": {
"name": "dispatcher_events_total", name: "dispatcher_events_total",
labels: map[string]string{
"processor": "FooProcessor", "processor": "FooProcessor",
"action": "send", "action": "send",
"outcome": "success", "outcome": "success",
"job": "test_dispatcher", "job": "test_dispatcher",
}, },
"foo_product.signup.facebook.failure": map[string]string{ },
"name": "signup_events_total", "foo_product.signup.facebook.failure": {
name: "signup_events_total",
labels: map[string]string{
"provider": "facebook", "provider": "facebook",
"outcome": "failure", "outcome": "failure",
"job": "foo_product_server", "job": "foo_product_server",
}, },
"test.web-server.foo.bar": map[string]string{}, },
"test.web-server.foo.bar": {
name: "test_web_server_foo_bar",
labels: map[string]string{},
},
}, },
}, },
} }
@ -313,18 +373,22 @@ mappings:
} }
for metric, mapping := range scenario.mappings { for metric, mapping := range scenario.mappings {
_, labels, present := mapper.getMapping(metric) m, labels, present := mapper.getMapping(metric)
if len(labels) == 0 && present { if present && m.Name != mapping.name {
t.Fatalf("%d.%q: Expected name %v, got %v", i, metric, m.Name, mapping.name)
}
if mapping.notPresent && present {
t.Fatalf("%d.%q: Expected metric to not be present", i, metric) t.Fatalf("%d.%q: Expected metric to not be present", i, metric)
} }
if len(labels) != len(mapping) { if len(labels) != len(mapping.labels) {
t.Fatalf("%d.%q: Expected %d labels, got %d", i, metric, len(mapping), len(labels)) t.Fatalf("%d.%q: Expected %d labels, got %d", i, metric, len(mapping.labels), len(labels))
} }
for label, value := range labels { for label, value := range labels {
if mapping[label] != value { if mapping.labels[label] != value {
t.Fatalf("%d.%q: Expected labels %v, got %v", i, metric, mapping, labels) t.Fatalf("%d.%q: Expected labels %v, got %v", i, metric, mapping, labels)
} }
} }
} }
} }
} }