From f031e3072197cd67b87e4ecb131cbe95788084e5 Mon Sep 17 00:00:00 2001 From: Johannes 'fish' Ziemke Date: Mon, 2 May 2016 20:53:00 +0200 Subject: [PATCH 1/2] Add flag to disable metric type suffixes --- README.md | 37 +++++++++++++++++++++---------------- exporter.go | 18 ++++++++++++++---- main.go | 6 +++++- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 90bea40..ad6e449 100644 --- a/README.md +++ b/README.md @@ -57,28 +57,33 @@ Metrics that don't match any mapping in the configuration file are translated into Prometheus metrics without any labels and with certain characters escaped (`_` -> `__`; `-` -> `__`; `.` -> `_`). -In general, the different metric types are translated as follows, with certain -suffixes appended to the Prometheus metric names: +In general, the different metric types are translated as follows: - StatsD gauge -> Prometheus gauge (suffix `_gauge`) + StatsD gauge -> Prometheus gauge - StatsD counter -> Prometheus counter (suffix `_counter`) + StatsD counter -> Prometheus counter - StatsD timer -> Prometheus summary (suffix `_timer`) <-- indicates timer quantiles - -> Prometheus counter (suffix `_timer_total`) <-- indicates total time spent - -> Prometheus counter (suffix `_timer_count`) <-- indicates total number of timer events + StatsD timer -> Prometheus summary <-- indicates timer quantiles + -> Prometheus counter (suffix `_total`) <-- indicates total time spent + -> Prometheus counter (suffix `_count`) <-- indicates total number of timer events -An example mapping configuration: +If `-statsd.add-suffix` is set, the exporter appends the metric type (`_gauge`, +`_counter`, `_timer`) to the resulting metrics. This is enabled by default for +backward compatibility but discouraged to use. Instead, it is better to +explicitly define the full metric name in your mapping and run the exporter +with `-statsd.add-suffix=false`. + +An example mapping configuration with `-statsd.add-suffix=false`: test.dispatcher.*.*.* - name="dispatcher_events" + name="dispatcher_events_total" processor="$1" action="$2" outcome="$3" job="test_dispatcher" *.signup.*.* - name="signup_events" + name="signup_events_total" provider="$2" outcome="$3" job="${1}_server" @@ -86,14 +91,14 @@ An example mapping configuration: This would transform these example StatsD metrics into Prometheus metrics as follows: - test.dispatcher.FooProcessor.send.success (counter) - => dispatcher_events_counter{processor="FooProcessor", action="send", outcome="success", job="test_dispatcher"} + test.dispatcher.FooProcessor.send.success + => dispatcher_events_total{processor="FooProcessor", action="send", outcome="success", job="test_dispatcher"} - foo_product.signup.facebook.failure (counter) - => signup_events_counter{provider="facebook", outcome="failure", job="foo_product_server"} + foo_product.signup.facebook.failure + => signup_events_total{provider="facebook", outcome="failure", job="foo_product_server"} - test.web-server.foo.bar (gauge) - => test_web__server_foo_bar_gauge{} + test.web-server.foo.bar + => test_web__server_foo_bar{} ## Using Docker diff --git a/exporter.go b/exporter.go index c09fe07..0eea62b 100644 --- a/exporter.go +++ b/exporter.go @@ -176,6 +176,7 @@ type Exporter struct { Gauges *GaugeContainer Summaries *SummaryContainer mapper *metricMapper + addSuffix bool } func escapeMetricName(metricName string) string { @@ -189,6 +190,14 @@ func escapeMetricName(metricName string) string { return metricName } +func (b *Exporter) suffix(metricName, suffix string) string { + str := metricName + if b.addSuffix { + str += "_" + suffix + } + return str +} + func (b *Exporter) Listen(e <-chan Events) { for { events, ok := <-e @@ -215,7 +224,7 @@ func (b *Exporter) Listen(e <-chan Events) { switch event.(type) { case *CounterEvent: counter := b.Counters.Get( - metricName+"_counter", + b.suffix(metricName, "counter"), prometheusLabels, ) // We don't accept negative values for counters. Incrementing the counter with a negative number @@ -231,7 +240,7 @@ func (b *Exporter) Listen(e <-chan Events) { case *GaugeEvent: gauge := b.Gauges.Get( - metricName+"_gauge", + b.suffix(metricName, "gauge"), prometheusLabels, ) gauge.Set(event.Value()) @@ -240,7 +249,7 @@ func (b *Exporter) Listen(e <-chan Events) { case *TimerEvent: summary := b.Summaries.Get( - metricName+"_timer", + b.suffix(metricName, "timer"), prometheusLabels, ) summary.Observe(event.Value()) @@ -255,8 +264,9 @@ func (b *Exporter) Listen(e <-chan Events) { } } -func NewExporter(mapper *metricMapper) *Exporter { +func NewExporter(mapper *metricMapper, addSuffix bool) *Exporter { return &Exporter{ + addSuffix: addSuffix, Counters: NewCounterContainer(), Gauges: NewGaugeContainer(), Summaries: NewSummaryContainer(), diff --git a/main.go b/main.go index fe2a134..da8ff36 100644 --- a/main.go +++ b/main.go @@ -30,6 +30,7 @@ var ( statsdListenAddress = flag.String("statsd.listen-address", ":9125", "The UDP address on which to receive statsd metric lines.") mappingConfig = flag.String("statsd.mapping-config", "", "Metric mapping configuration file name.") readBuffer = flag.Int("statsd.read-buffer", 0, "Size (in bytes) of the operating system's transmit read buffer associated with the UDP connection. Please make sure the kernel parameters net.core.rmem_max is set to a value greater than the value specified.") + addSuffix = flag.Bool("statsd.add-suffix", true, "Add the metric type (counter/gauge/timer) as suffix to the generated Prometheus metric (NOT recommended, but set by default for backward compatibility).") ) func serveHTTP() { @@ -108,6 +109,9 @@ func watchConfig(fileName string, mapper *metricMapper) { func main() { flag.Parse() + if *addSuffix { + log.Println("Warning: Using -statsd.add-suffix is discouraged. We recommend explicitly naming metrics appropriately in the mapping configuration.") + } log.Println("Starting StatsD -> Prometheus Exporter...") log.Println("Accepting StatsD Traffic on", *statsdListenAddress) log.Println("Accepting Prometheus Requests on", *listenAddress) @@ -141,6 +145,6 @@ func main() { } go watchConfig(*mappingConfig, mapper) } - exporter := NewExporter(mapper) + exporter := NewExporter(mapper, *addSuffix) exporter.Listen(events) } From 3d0ccb9ed58f2d18f5dabf693d3376aa5823878e Mon Sep 17 00:00:00 2001 From: Johannes 'fish' Ziemke Date: Mon, 2 May 2016 21:19:46 +0200 Subject: [PATCH 2/2] Count events for which no mapping was found --- exporter.go | 1 + telemetry.go | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/exporter.go b/exporter.go index 0eea62b..616d2a6 100644 --- a/exporter.go +++ b/exporter.go @@ -218,6 +218,7 @@ func (b *Exporter) Listen(e <-chan Events) { } } } else { + eventsUnmapped.Inc() metricName = escapeMetricName(event.MetricName()) } diff --git a/telemetry.go b/telemetry.go index 1224f66..159dc93 100644 --- a/telemetry.go +++ b/telemetry.go @@ -25,6 +25,10 @@ var ( }, []string{"type"}, ) + eventsUnmapped = prometheus.NewCounter(prometheus.CounterOpts{ + Name: "statsd_exporter_events_unmapped_total", + Help: "The total number of StatsD events no mapping was found for.", + }) networkStats = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "statsd_exporter_packets_total", @@ -41,7 +45,7 @@ var ( ) mappingsCount = prometheus.NewGauge(prometheus.GaugeOpts{ Name: "statsd_exporter_loaded_mappings_count", - Help: "The number of configured metric mappings.", + Help: "The current number of configured metric mappings.", }) )