From 11f53c861236c6cebb3a5b4c9c198c05e0b97b4d Mon Sep 17 00:00:00 2001 From: Bart Vercoulen Date: Thu, 19 Oct 2017 16:08:41 +0200 Subject: [PATCH] Refactored the unbound histogram metrics from gauge metrics into Prometheus cumulative histogram metrics. Fixes: #4 --- .gitignore | 1 + unbound_exporter.go | 48 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index b0ac14d..32f447f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ unbound_exporter +.idea/ diff --git a/unbound_exporter.go b/unbound_exporter.go index 8c64999..bc7e3ca 100644 --- a/unbound_exporter.go +++ b/unbound_exporter.go @@ -29,6 +29,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" + "sort" ) var ( @@ -37,6 +38,11 @@ var ( "Whether scraping Unbound's metrics was successful.", nil, nil) + unboundHistogram = prometheus.NewDesc( + prometheus.BuildFQName("unbound", "", "response_time_seconds"), + "Query response time in seconds.", + nil, nil) + unboundMetrics = []*unboundMetric{ newUnboundMetric( "answer_rcodes_total", @@ -176,12 +182,6 @@ var ( prometheus.CounterValue, []string{"thread"}, "^thread(\\d+)\\.num\\.recursivereplies$"), - newUnboundMetric( - "response_time_seconds_bucket", - "Histogram buckets for query response time in seconds.", - prometheus.CounterValue, - []string{"le"}, - "^histogram\\.\\d+\\.\\d+\\.to\\.0*(\\d+\\.\\d+?)0*$"), newUnboundMetric( "rrset_bogus_total", "Total number of rrsets marked bogus by the validator.", @@ -242,6 +242,11 @@ func newUnboundMetric(name string, description string, valueType prometheus.Valu func CollectFromReader(file io.Reader, ch chan<- prometheus.Metric) error { scanner := bufio.NewScanner(file) scanner.Split(bufio.ScanLines) + histogramPattern := regexp.MustCompile("^histogram\\.(\\d+\\.\\d+)\\.to\\.(\\d+\\.\\d+)$") + + histogramCount := uint64(0) + histogramSum := float64(0) + histogramBuckets := make(map[float64]uint64) for scanner.Scan() { fields := strings.Split(scanner.Text(), "=") @@ -252,8 +257,7 @@ func CollectFromReader(file io.Reader, ch chan<- prometheus.Metric) error { } for _, metric := range unboundMetrics { - matches := metric.pattern.FindStringSubmatch(fields[0]) - if matches != nil { + if matches := metric.pattern.FindStringSubmatch(fields[0]); matches != nil { value, err := strconv.ParseFloat(fields[1], 64) if err != nil { return err @@ -267,7 +271,35 @@ func CollectFromReader(file io.Reader, ch chan<- prometheus.Metric) error { break } } + + if matches := histogramPattern.FindStringSubmatch(fields[0]); matches != nil { + begin, _ := strconv.ParseFloat(matches[1], 64) + end, _ := strconv.ParseFloat(matches[2], 64) + value, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + return err + } + histogramBuckets[end] = value + histogramCount += value + // There are no real data points to calculate the sum in the unbound stats + // Therefore the mean latency times the amount of samples is calculated and summed + histogramSum += (end - begin) * float64(value) + } } + + // Convert the metrics to a cumulative prometheus histogram + keys := []float64{} + for k := range histogramBuckets { + keys = append(keys, k) + } + sort.Float64s(keys) + prev := uint64(0) + for _, i := range keys { + histogramBuckets[i] += prev + prev = histogramBuckets[i] + } + ch <- prometheus.MustNewConstHistogram(unboundHistogram, histogramCount, histogramSum, histogramBuckets) + return scanner.Err() }