diff --git a/metrics.go b/metrics.go index 5c7fb8f..f4c91d1 100644 --- a/metrics.go +++ b/metrics.go @@ -15,8 +15,8 @@ type gaugeDefinition struct { key string } -// Used to programmatically create prometheus.CounterVec metrics -type counterVecDefinition struct { +// Used to programmatically create prometheus.Counter metrics +type counterDefinition struct { id int name string desc string @@ -64,22 +64,22 @@ var ( gaugeDefinition{3, "cache_size", "Number of entries in the cache.", "cache-entries"}, } - recursorCounterVecDefs = []counterVecDefinition{ - counterVecDefinition{ + recursorCounterDefs = []counterDefinition{ + counterDefinition{ 1, "incoming_queries_total", "Total number of incoming queries by network.", "net", map[string]string{"questions": "udp", "tcp-questions": "tcp"}, }, - counterVecDefinition{ + counterDefinition{ 2, "outgoing_queries_total", "Total number of outgoing queries by network.", "net", map[string]string{"all-outqueries": "udp", "tcp-outqueries": "tcp"}, }, - counterVecDefinition{ + counterDefinition{ 3, "cache_lookups_total", "Total number of cache lookups by result.", "result", map[string]string{"cache-hits": "hit", "cache-misses": "miss"}, }, - counterVecDefinition{4, "answers_rcodes_total", "Total number of answers by response code.", "rcode", rCodeLabelMap}, - counterVecDefinition{5, "answers_rtime_total", "Total number of answers grouped by response time slots.", "timeslot", rTimeLabelMap}, - counterVecDefinition{6, "exceptions_total", "Total number of exceptions by error.", "error", exceptionsLabelMap}, + counterDefinition{4, "answers_rcodes_total", "Total number of answers by response code.", "rcode", rCodeLabelMap}, + counterDefinition{5, "answers_rtime_total", "Total number of answers grouped by response time slots.", "timeslot", rTimeLabelMap}, + counterDefinition{6, "exceptions_total", "Total number of exceptions by error.", "error", exceptionsLabelMap}, } ) @@ -93,32 +93,32 @@ var ( gaugeDefinition{5, "metadata_cache_size", "Number of entries in the metadata cache.", "meta-cache-size"}, gaugeDefinition{6, "qsize", "Number of packets waiting for database attention.", "qsize-q"}, } - authoritativeCounterVecDefs = []counterVecDefinition{ - counterVecDefinition{ + authoritativeCounterDefs = []counterDefinition{ + counterDefinition{ 1, "queries_total", "Total number of queries by network.", "net", map[string]string{"tcp-queries": "tcp", "udp-queries": "udp"}, }, - counterVecDefinition{ + counterDefinition{ 2, "answers_total", "Total number of answers by network.", "net", map[string]string{"tcp-answers": "tcp", "udp-answers": "udp"}, }, - counterVecDefinition{ + counterDefinition{ 3, "recursive_queries_total", "Total number of recursive queries by status.", "status", map[string]string{"rd-queries": "requested", "recursing-questions": "processed", "recursing-answers": "answered", "recursion-unanswered": "unanswered"}, }, - counterVecDefinition{ + counterDefinition{ 4, "update_queries_total", "Total number of DNS update queries by status.", "status", map[string]string{"dnsupdate-answers": "answered", "dnsupdate-changes": "applied", "dnsupdate-queries": "requested", "dnsupdate-refused": "refused"}, }, - counterVecDefinition{ + counterDefinition{ 5, "packet_cache_lookups_total", "Total number of packet-cache lookups by result.", "result", map[string]string{"packetcache-hit": "hit", "packetcache-miss": "miss"}, }, - counterVecDefinition{ + counterDefinition{ 6, "query_cache_lookups_total", "Total number of query-cache lookups by result.", "result", map[string]string{"query-cache-hit": "hit", "query-cache-miss": "miss"}, }, - counterVecDefinition{ + counterDefinition{ 7, "exceptions_total", "Total number of exceptions by error.", "error", map[string]string{"servfail-packets": "servfail", "timedout-questions": "timeout", "udp-recvbuf-errors": "recvbuf-error", "udp-sndbuf-errors": "sndbuf-error"}, }, @@ -128,7 +128,7 @@ var ( // PowerDNS Dnsdist metrics definitions var ( dnsdistGaugeDefs = []gaugeDefinition{} - dnsdistCounterVecDefs = []counterVecDefinition{} + dnsdistCounterDefs = []counterDefinition{} ) // Creates a fixed-value response time histogram from the following stats counters: @@ -168,4 +168,4 @@ func makeRecursorRTimeHistogram(statsMap map[string]float64) (prometheus.Metric, h := prometheus.MustNewConstHistogram(desc, count, 0, buckets) return h, nil -} \ No newline at end of file +} diff --git a/powerdns_exporter.go b/powerdns_exporter.go index 2627e70..69371d6 100644 --- a/powerdns_exporter.go +++ b/powerdns_exporter.go @@ -14,6 +14,7 @@ import ( "time" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/common/log" ) @@ -70,24 +71,12 @@ type Exporter struct { totalScrapes prometheus.Counter jsonParseFailures prometheus.Counter gaugeMetrics map[int]prometheus.Gauge - counterVecMetrics map[int]*prometheus.CounterVec + counterMetrics map[int]*prometheus.Desc gaugeDefs []gaugeDefinition - counterVecDefs []counterVecDefinition + counterDefs []counterDefinition client *http.Client } -func newCounterVecMetric(serverType, metricName, docString string, labelNames []string) *prometheus.CounterVec { - return prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: namespace, - Subsystem: serverType, - Name: metricName, - Help: docString, - }, - labelNames, - ) -} - func newGaugeMetric(serverType, metricName, docString string) prometheus.Gauge { return prometheus.NewGauge( prometheus.GaugeOpts{ @@ -102,29 +91,42 @@ func newGaugeMetric(serverType, metricName, docString string) prometheus.Gauge { // NewExporter returns an initialized Exporter. func NewExporter(apiKey, serverType string, hostURL *url.URL) *Exporter { var gaugeDefs []gaugeDefinition - var counterVecDefs []counterVecDefinition + var counterDefs []counterDefinition gaugeMetrics := make(map[int]prometheus.Gauge) - counterVecMetrics := make(map[int]*prometheus.CounterVec) + counterMetrics := make(map[int]*prometheus.Desc) switch serverType { case "recursor": gaugeDefs = recursorGaugeDefs - counterVecDefs = recursorCounterVecDefs + counterDefs = recursorCounterDefs case "authoritative": gaugeDefs = authoritativeGaugeDefs - counterVecDefs = authoritativeCounterVecDefs + counterDefs = authoritativeCounterDefs case "dnsdist": gaugeDefs = dnsdistGaugeDefs - counterVecDefs = dnsdistCounterVecDefs + counterDefs = dnsdistCounterDefs } for _, def := range gaugeDefs { gaugeMetrics[def.id] = newGaugeMetric(serverType, def.name, def.desc) } - for _, def := range counterVecDefs { - counterVecMetrics[def.id] = newCounterVecMetric(serverType, def.name, def.desc, []string{def.label}) + for _, def := range counterDefs { + labels := make([]string, 0, len(def.labelMap)) + for _, l := range def.labelMap { + labels = append(labels, l) + } + counterMetrics[def.id] = prometheus.NewDesc( + prometheus.BuildFQName( + def.label, + serverType, + def.name, + ), + def.desc, + labels, + nil, + ) } return &Exporter{ @@ -150,17 +152,17 @@ func NewExporter(apiKey, serverType string, hostURL *url.URL) *Exporter { Help: "Number of errors while parsing PowerDNS JSON stats.", }), gaugeMetrics: gaugeMetrics, - counterVecMetrics: counterVecMetrics, + counterMetrics: counterMetrics, gaugeDefs: gaugeDefs, - counterVecDefs: counterVecDefs, + counterDefs: counterDefs, } } // Describe describes all the metrics ever exported by the PowerDNS exporter. It // implements prometheus.Collector. func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { - for _, m := range e.counterVecMetrics { - m.Describe(ch) + for _, m := range e.counterMetrics { + ch <- m } for _, m := range e.gaugeMetrics { ch <- m.Desc() @@ -179,12 +181,10 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) { e.mutex.Lock() defer e.mutex.Unlock() - e.resetMetrics() - statsMap := e.setMetrics(jsonStats) ch <- e.up ch <- e.totalScrapes ch <- e.jsonParseFailures - e.collectMetrics(ch, statsMap) + e.collectMetrics(ch, jsonStats) } func (e *Exporter) scrape(jsonStats chan<- []StatsEntry) { @@ -207,32 +207,8 @@ func (e *Exporter) scrape(jsonStats chan<- []StatsEntry) { jsonStats <- data } -func (e *Exporter) resetMetrics() { - for _, m := range e.counterVecMetrics { - m.Reset() - } -} - -func (e *Exporter) collectMetrics(ch chan<- prometheus.Metric, statsMap map[string]float64) { - for _, m := range e.counterVecMetrics { - m.Collect(ch) - } - for _, m := range e.gaugeMetrics { - ch <- m - } - - if e.ServerType == "recursor" { - h, err := makeRecursorRTimeHistogram(statsMap) - if err != nil { - log.Errorf("Could not create response time histogram: %v", err) - return - } - ch <- h - } -} - -func (e *Exporter) setMetrics(jsonStats <-chan []StatsEntry) (statsMap map[string]float64) { - statsMap = make(map[string]float64) +func (e *Exporter) collectMetrics(ch chan<- prometheus.Metric, jsonStats <-chan []StatsEntry) { + statsMap := make(map[string]float64) stats := <-jsonStats for _, s := range stats { statsMap[s.Name] = s.Value @@ -254,17 +230,25 @@ func (e *Exporter) setMetrics(jsonStats <-chan []StatsEntry) (statsMap map[strin } } - for _, def := range e.counterVecDefs { + for _, def := range e.counterDefs { for key, label := range def.labelMap { if value, ok := statsMap[key]; ok { - e.counterVecMetrics[def.id].WithLabelValues(label).Set(value) + ch <- prometheus.MustNewConstMetric(e.counterMetrics[def.id], prometheus.CounterValue, float64(value), label) } else { log.Errorf("Expected PowerDNS stats key not found: %s", key) e.jsonParseFailures.Inc() } } } - return + + if e.ServerType == "recursor" { + h, err := makeRecursorRTimeHistogram(statsMap) + if err != nil { + log.Errorf("Could not create response time histogram: %v", err) + return + } + ch <- h + } } func getServerInfo(hostURL *url.URL, apiKey string) (*ServerInfo, error) { @@ -336,7 +320,7 @@ func main() { prometheus.MustRegister(exporter) log.Infof("Starting Server: %s", *listenAddress) - http.Handle(*metricsPath, prometheus.Handler()) + http.Handle(*metricsPath, promhttp.Handler()) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(` PowerDNS Exporter