diff --git a/README.md b/README.md index 31188b6..877bc1a 100644 --- a/README.md +++ b/README.md @@ -106,36 +106,6 @@ with `-statsd.add-suffix=false`. An example mapping configuration with `-statsd.add-suffix=false`: - # comments are allowed - test.dispatcher.*.*.* - name="dispatcher_events_total" - processor="$1" - action="$2" - outcome="$3" - job="test_dispatcher" - - *.signup.*.* - name="signup_events_total" - provider="$2" - outcome="$3" - job="${1}_server" - -This would transform these example StatsD metrics into Prometheus metrics as -follows: - - test.dispatcher.FooProcessor.send.success - => dispatcher_events_total{processor="FooProcessor", action="send", outcome="success", job="test_dispatcher"} - - foo_product.signup.facebook.failure - => signup_events_total{provider="facebook", outcome="failure", job="foo_product_server"} - - test.web-server.foo.bar - => test_web__server_foo_bar{} - - -YAML may also be used for the configuration if the passed filename ends in `.yml` or -`.yaml`. The above example mapping, in YAML, would be: - ```yaml mappings: - match: test.dispatcher.*.*.* @@ -153,7 +123,20 @@ mappings: job: "${1}_server" ``` -Using the YAML configuration, one may also set the timer type to "histogram". The +This would transform these example StatsD metrics into Prometheus metrics as +follows: + + test.dispatcher.FooProcessor.send.success + => dispatcher_events_total{processor="FooProcessor", action="send", outcome="success", job="test_dispatcher"} + + foo_product.signup.facebook.failure + => signup_events_total{provider="facebook", outcome="failure", job="foo_product_server"} + + test.web-server.foo.bar + => test_web__server_foo_bar{} + + +In the configuration, one may also set the timer type to "histogram". The default is "summary" as in the plain text configuration format. For example, to set the timer type for a single metric: diff --git a/mapper.go b/mapper.go index 53ce4b2..5fa7b9f 100644 --- a/mapper.go +++ b/mapper.go @@ -16,7 +16,6 @@ package main import ( "fmt" "io/ioutil" - "path/filepath" "regexp" "strings" "sync" @@ -53,78 +52,6 @@ type metricMapping struct { Buckets []float64 `yaml:"buckets"` } -type configLoadStates int - -const ( - SEARCHING configLoadStates = iota - METRIC_DEFINITION -) - -func (m *metricMapper) initFromString(fileContents string) error { - lines := strings.Split(fileContents, "\n") - numLines := len(lines) - state := SEARCHING - - parsedMappings := []metricMapping{} - currentMapping := metricMapping{Labels: prometheus.Labels{}} - for i, line := range lines { - line = strings.TrimSpace(line) - - // skip comments - if strings.HasPrefix(line, "#") { - continue - } - - switch state { - case SEARCHING: - if line == "" { - continue - } - if !metricLineRE.MatchString(line) { - return fmt.Errorf("line %d: expected metric match line, got: %s", i, line) - } - - // Translate the glob-style metric match line into a proper regex that we - // can use to match metrics later on. - metricRe := strings.Replace(line, ".", "\\.", -1) - metricRe = strings.Replace(metricRe, "*", "([^.]+)", -1) - currentMapping.regex = regexp.MustCompile("^" + metricRe + "$") - - state = METRIC_DEFINITION - - case METRIC_DEFINITION: - if (i == numLines-1) && (line != "") { - return fmt.Errorf("Line %d: missing terminating newline", i) - } - if line == "" { - if len(currentMapping.Labels) == 0 { - return fmt.Errorf("Line %d: metric mapping didn't set any labels", i) - } - if _, ok := currentMapping.Labels["name"]; !ok { - return fmt.Errorf("Line %d: metric mapping didn't set a metric name", i) - } - parsedMappings = append(parsedMappings, currentMapping) - state = SEARCHING - currentMapping = metricMapping{Labels: prometheus.Labels{}} - continue - } - if err := m.updateMapping(line, i, ¤tMapping); err != nil { - return err - } - default: - panic("illegal state") - } - } - - m.mutex.Lock() - defer m.mutex.Unlock() - m.Mappings = parsedMappings - - mappingsCount.Set(float64(len(parsedMappings))) - - return nil -} - func (m *metricMapper) initFromYAMLString(fileContents string) error { var n metricMapper @@ -189,12 +116,7 @@ func (m *metricMapper) initFromFile(fileName string) error { if err != nil { return err } - switch strings.ToLower(filepath.Ext(fileName)) { - case ".yaml", ".yml": - return m.initFromYAMLString(string(mappingStr)) - default: - return m.initFromString(string(mappingStr)) - } + return m.initFromYAMLString(string(mappingStr)) } func (m *metricMapper) getMapping(statsdMetric string) (*metricMapping, prometheus.Labels, bool) { @@ -217,16 +139,3 @@ func (m *metricMapper) getMapping(statsdMetric string) (*metricMapping, promethe return nil, nil, false } - -func (m *metricMapper) updateMapping(line string, i int, mapping *metricMapping) error { - matches := labelLineRE.FindStringSubmatch(line) - if len(matches) != 3 { - return fmt.Errorf("Line %d: expected label mapping line, got: %s", i, line) - } - label, value := matches[1], matches[2] - if label == "name" && !metricNameRE.MatchString(value) { - return fmt.Errorf("Line %d: metric name '%s' doesn't match regex '%s'", i, value, metricNameRE) - } - (*mapping).Labels[label] = value - return nil -} diff --git a/mapper_test.go b/mapper_test.go index 149dcb3..e13c14f 100644 --- a/mapper_test.go +++ b/mapper_test.go @@ -17,175 +17,6 @@ import ( "testing" ) -func TestMetricMapper(t *testing.T) { - scenarios := []struct { - config string - configBad bool - mappings map[string]map[string]string - }{ - // Empty config. - {}, - // Config with several mapping definitions. - { - config: ` - # this is a comment - # this is another - test.dispatcher.*.*.* - name="dispatch_events" - processor="$1" - action="$2" - result="$3" - # here is a third - job="test_dispatcher" - - test.my-dispatch-host01.name.dispatcher.*.*.* - name="host_dispatch_events" - processor="$1" - action="$2" - result="$3" - job="test_dispatcher" - - *.* - name="catchall" - first="$1" - second="$2" - third="$3" - job="$1-$2-$3" - `, - mappings: map[string]map[string]string{ - "test.dispatcher.FooProcessor.send.succeeded": map[string]string{ - "name": "dispatch_events", - "processor": "FooProcessor", - "action": "send", - "result": "succeeded", - "job": "test_dispatcher", - }, - "test.my-dispatch-host01.name.dispatcher.FooProcessor.send.succeeded": map[string]string{ - "name": "host_dispatch_events", - "processor": "FooProcessor", - "action": "send", - "result": "succeeded", - "job": "test_dispatcher", - }, - "foo.bar": map[string]string{ - "name": "catchall", - "first": "foo", - "second": "bar", - "third": "", - "job": "foo-bar-", - }, - "foo.bar.baz": map[string]string{}, - }, - }, - // Config with bad regex reference. - { - config: ` - test.* - name="name" - label="$1_foo" - `, - mappings: map[string]map[string]string{ - "test.a": map[string]string{ - "name": "name", - "label": "", - }, - }, - }, - // Config with good regex reference. - { - config: ` - test.* - name="name" - label="${1}_foo" - `, - mappings: map[string]map[string]string{ - "test.a": map[string]string{ - "name": "name", - "label": "a_foo", - }, - }, - }, - // Config with bad metric line. - { - config: ` - bad--metric-line.*.* - name="foo" - `, - configBad: true, - }, - // Config with bad label line. - { - config: ` - test.*.* - name=foo - `, - configBad: true, - }, - // Config with bad label line. - { - config: ` - test.*.* - name="foo-name" - `, - configBad: true, - }, - // Config with bad metric name. - { - config: ` - test.*.* - name="0foo" - `, - configBad: true, - }, - // A single mapping config without a terminating newline. - { - config: ` - test.* - name="name" - label="foo"`, - configBad: true, - }, - // Multiple mapping configs and no terminating newline. - { - config: ` - test.bar - name="name_bar" - label="foo" - - test.foo - name="name_foo" - label="bar"`, - configBad: true, - }, - } - - mapper := metricMapper{} - for i, scenario := range scenarios { - err := mapper.initFromString(scenario.config) - if err != nil && !scenario.configBad { - t.Fatalf("%d. Config load error: %s", i, err) - } - if err == nil && scenario.configBad { - t.Fatalf("%d. Expected bad config, but loaded ok", i) - } - - for metric, mapping := range scenario.mappings { - _, labels, present := mapper.getMapping(metric) - if len(labels) == 0 && present { - t.Fatalf("%d.%q: Expected metric to not be present", i, metric) - } - if len(labels) != len(mapping) { - t.Fatalf("%d.%q: Expected %d labels, got %d", i, metric, len(mapping), len(labels)) - } - for label, value := range labels { - if mapping[label] != value { - t.Fatalf("%d.%q: Expected labels %v, got %v", i, metric, mapping, labels) - } - } - } - } -} - func TestMetricMapperYAML(t *testing.T) { scenarios := []struct { config string