From ab844a3f6308b0e4015a582318f8203bad341745 Mon Sep 17 00:00:00 2001 From: Tobias Schmidt Date: Fri, 2 Nov 2018 18:28:18 +0100 Subject: [PATCH] Break out event handling into its own function It's idiomatic in go to keep code indentation due to nested loops to a minimum. Decoupling the events handling function into its own function makes it easier to read the code and test the behavior in isolation of the channel handling. It's now also easily possible to process events in parallel without having to touch actual business code. Signed-off-by: Tobias Schmidt --- exporter.go | 245 +++++++++++++++++++++++++++------------------------- 1 file changed, 125 insertions(+), 120 deletions(-) diff --git a/exporter.go b/exporter.go index 9924b0d..b227516 100644 --- a/exporter.go +++ b/exporter.go @@ -253,6 +253,8 @@ func escapeMetricName(metricName string) string { return metricName } +// Listen handles all events sent to the given channel sequentially. It +// terminates when the channel is closed. func (b *Exporter) Listen(e <-chan Events) { for { events, ok := <-e @@ -261,130 +263,133 @@ func (b *Exporter) Listen(e <-chan Events) { return } for _, event := range events { - var help string - metricName := "" - prometheusLabels := event.Labels() - - mapping, labels, present := b.mapper.GetMapping(event.MetricName(), event.MetricType()) - if mapping == nil { - mapping = &mapper.MetricMapping{} - } - - if mapping.Action == mapper.ActionTypeDrop { - continue - } - - if mapping.HelpText == "" { - help = defaultHelp - } else { - help = mapping.HelpText - } - if present { - metricName = escapeMetricName(mapping.Name) - for label, value := range labels { - prometheusLabels[label] = value - } - } else { - eventsUnmapped.Inc() - metricName = escapeMetricName(event.MetricName()) - } - - switch ev := event.(type) { - case *CounterEvent: - // We don't accept negative values for counters. Incrementing the counter with a negative number - // will cause the exporter to panic. Instead we will warn and continue to the next event. - if event.Value() < 0.0 { - log.Debugf("Counter %q is: '%f' (counter must be non-negative value)", metricName, event.Value()) - eventStats.WithLabelValues("illegal_negative_counter").Inc() - continue - } - - counter, err := b.Counters.Get( - metricName, - prometheusLabels, - help, - ) - if err == nil { - counter.Add(event.Value()) - - eventStats.WithLabelValues("counter").Inc() - } else { - log.Debugf(regErrF, metricName, err) - conflictingEventStats.WithLabelValues("counter").Inc() - } - - case *GaugeEvent: - gauge, err := b.Gauges.Get( - metricName, - prometheusLabels, - help, - ) - - if err == nil { - if ev.relative { - gauge.Add(event.Value()) - } else { - gauge.Set(event.Value()) - } - - eventStats.WithLabelValues("gauge").Inc() - } else { - log.Debugf(regErrF, metricName, err) - conflictingEventStats.WithLabelValues("gauge").Inc() - } - - case *TimerEvent: - t := mapper.TimerTypeDefault - if mapping != nil { - t = mapping.TimerType - } - if t == mapper.TimerTypeDefault { - t = b.mapper.Defaults.TimerType - } - - switch t { - case mapper.TimerTypeHistogram: - histogram, err := b.Histograms.Get( - metricName, - prometheusLabels, - help, - mapping, - ) - if err == nil { - histogram.Observe(event.Value() / 1000) // prometheus presumes seconds, statsd millisecond - eventStats.WithLabelValues("timer").Inc() - } else { - log.Debugf(regErrF, metricName, err) - conflictingEventStats.WithLabelValues("timer").Inc() - } - - case mapper.TimerTypeDefault, mapper.TimerTypeSummary: - summary, err := b.Summaries.Get( - metricName, - prometheusLabels, - help, - mapping, - ) - if err == nil { - summary.Observe(event.Value()) - eventStats.WithLabelValues("timer").Inc() - } else { - log.Debugf(regErrF, metricName, err) - conflictingEventStats.WithLabelValues("timer").Inc() - } - - default: - panic(fmt.Sprintf("unknown timer type '%s'", t)) - } - - default: - log.Debugln("Unsupported event type") - eventStats.WithLabelValues("illegal").Inc() - } + b.handleEvent(event) } } } +// handleEvent processes a single Event according to the configured mapping. +func (b *Exporter) handleEvent(event Event) { + mapping, labels, present := b.mapper.GetMapping(event.MetricName(), event.MetricType()) + if mapping == nil { + mapping = &mapper.MetricMapping{} + } + + if mapping.Action == mapper.ActionTypeDrop { + return + } + + help := defaultHelp + if mapping.HelpText != "" { + help = mapping.HelpText + } + + metricName := "" + prometheusLabels := event.Labels() + if present { + metricName = escapeMetricName(mapping.Name) + for label, value := range labels { + prometheusLabels[label] = value + } + } else { + eventsUnmapped.Inc() + metricName = escapeMetricName(event.MetricName()) + } + + switch ev := event.(type) { + case *CounterEvent: + // We don't accept negative values for counters. Incrementing the counter with a negative number + // will cause the exporter to panic. Instead we will warn and continue to the next event. + if event.Value() < 0.0 { + log.Debugf("Counter %q is: '%f' (counter must be non-negative value)", metricName, event.Value()) + eventStats.WithLabelValues("illegal_negative_counter").Inc() + return + } + + counter, err := b.Counters.Get( + metricName, + prometheusLabels, + help, + ) + if err == nil { + counter.Add(event.Value()) + + eventStats.WithLabelValues("counter").Inc() + } else { + log.Debugf(regErrF, metricName, err) + conflictingEventStats.WithLabelValues("counter").Inc() + } + + case *GaugeEvent: + gauge, err := b.Gauges.Get( + metricName, + prometheusLabels, + help, + ) + + if err == nil { + if ev.relative { + gauge.Add(event.Value()) + } else { + gauge.Set(event.Value()) + } + + eventStats.WithLabelValues("gauge").Inc() + } else { + log.Debugf(regErrF, metricName, err) + conflictingEventStats.WithLabelValues("gauge").Inc() + } + + case *TimerEvent: + t := mapper.TimerTypeDefault + if mapping != nil { + t = mapping.TimerType + } + if t == mapper.TimerTypeDefault { + t = b.mapper.Defaults.TimerType + } + + switch t { + case mapper.TimerTypeHistogram: + histogram, err := b.Histograms.Get( + metricName, + prometheusLabels, + help, + mapping, + ) + if err == nil { + histogram.Observe(event.Value() / 1000) // prometheus presumes seconds, statsd millisecond + eventStats.WithLabelValues("timer").Inc() + } else { + log.Debugf(regErrF, metricName, err) + conflictingEventStats.WithLabelValues("timer").Inc() + } + + case mapper.TimerTypeDefault, mapper.TimerTypeSummary: + summary, err := b.Summaries.Get( + metricName, + prometheusLabels, + help, + mapping, + ) + if err == nil { + summary.Observe(event.Value()) + eventStats.WithLabelValues("timer").Inc() + } else { + log.Debugf(regErrF, metricName, err) + conflictingEventStats.WithLabelValues("timer").Inc() + } + + default: + panic(fmt.Sprintf("unknown timer type '%s'", t)) + } + + default: + log.Debugln("Unsupported event type") + eventStats.WithLabelValues("illegal").Inc() + } +} + func NewExporter(mapper *mapper.MetricMapper) *Exporter { return &Exporter{ Counters: NewCounterContainer(),