From 27ee4050c4f66b700cd0140ab8eee78369c07cbb Mon Sep 17 00:00:00 2001 From: Erick Pintor Date: Tue, 16 Jan 2018 11:16:15 -0200 Subject: [PATCH] allow for dynamic metric name --- README.md | 12 +++++++++++- exporter.go | 2 +- mapper.go | 18 +++++++++++++----- mapper_test.go | 27 +++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0e73284..b184876 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,17 @@ follows: test.web-server.foo.bar => test_web_server_foo_bar{} -Each mapping in the configuration file must define a `name` for the metric. +Each mapping in the configuration file must define a `name` for the metric. The +metric's name can contain `$n`-style references to be replaced by the n-th +wildcard match in the matching line. That allows for dynamic rewrites, such as: + +```yaml +mappings: +- match: test.*.*.counter + name: "${2}_counter" + labels: + provider: "$1" +``` 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: diff --git a/exporter.go b/exporter.go index bbb3868..1d56614 100644 --- a/exporter.go +++ b/exporter.go @@ -263,7 +263,7 @@ func (b *Exporter) Listen(e <-chan Events) { help = mapping.HelpText } if present { - metricName = mapping.Name + metricName = escapeMetricName(mapping.Name) for label, value := range labels { prometheusLabels[label] = value } diff --git a/mapper.go b/mapper.go index 752dc3f..980daa3 100644 --- a/mapper.go +++ b/mapper.go @@ -25,12 +25,12 @@ import ( ) var ( - identifierRE = `[a-zA-Z_][a-zA-Z0-9_]+` - statsdMetricRE = `[a-zA-Z_](-?[a-zA-Z0-9_])+` + statsdMetricRE = `[a-zA-Z_](-?[a-zA-Z0-9_])+` + templateReplaceRE = `(\$\{?\d+\}?)` metricLineRE = regexp.MustCompile(`^(\*\.|` + statsdMetricRE + `\.)+(\*|` + statsdMetricRE + `)$`) - labelLineRE = regexp.MustCompile(`^(` + identifierRE + `)\s*=\s*"(.*)"$`) - metricNameRE = regexp.MustCompile(`^` + identifierRE + `$`) + metricNameRE = regexp.MustCompile(`^([a-zA-Z_]|` + templateReplaceRE + `)([a-zA-Z0-9_]|` + templateReplaceRE + `)*$`) + labelNameRE = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]+$`) ) type mapperConfigDefaults struct { @@ -77,7 +77,7 @@ func (m *metricMapper) initFromYAMLString(fileContents string) error { // check that label is correct for k := range currentMapping.Labels { - if !metricNameRE.MatchString(k) { + if !labelNameRE.MatchString(k) { return fmt.Errorf("invalid label key: %s", k) } } @@ -150,11 +150,19 @@ func (m *metricMapper) getMapping(statsdMetric string) (*metricMapping, promethe continue } + mapping.Name = string(mapping.regex.ExpandString( + []byte{}, + mapping.Name, + statsdMetric, + matches, + )) + labels := prometheus.Labels{} for label, valueExpr := range mapping.Labels { value := mapping.regex.ExpandString([]byte{}, valueExpr, statsdMetric, matches) labels[label] = string(value) } + return &mapping, labels, true } diff --git a/mapper_test.go b/mapper_test.go index 2955c73..5803fb7 100644 --- a/mapper_test.go +++ b/mapper_test.go @@ -184,6 +184,33 @@ mappings: `, configBad: true, }, + // Config with dynamic metric name. + { + config: `--- +mappings: +- match: test1.*.* + name: "$1" + labels: {} +- match: test2.*.* + name: "${1}_$2" + labels: {} +- match: test3\.(\w+)\.(\w+) + match_type: regex + name: "${2}_$1" + labels: {} + `, + mappings: mappings{ + "test1.total_requests.count": { + name: "total_requests", + }, + "test2.total_requests.count": { + name: "total_requests_count", + }, + "test3.total_requests.count": { + name: "count_total_requests", + }, + }, + }, // Config with bad metric name. { config: `---