From 5b22c5bca332b89d9800e5937a2e4430f4955595 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Wed, 9 Sep 2020 21:29:16 +0100 Subject: [PATCH 1/4] Use formatted log to print error message --- unbound_exporter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unbound_exporter.go b/unbound_exporter.go index 17fbb61..eefd2dd 100644 --- a/unbound_exporter.go +++ b/unbound_exporter.go @@ -456,7 +456,7 @@ func (e *UnboundExporter) Collect(ch chan<- prometheus.Metric) { prometheus.GaugeValue, 1.0) } else { - log.Error("Failed to scrape socket: %s", err) + log.Errorf("Failed to scrape socket: %s", err) ch <- prometheus.MustNewConstMetric( unboundUpDesc, prometheus.GaugeValue, From ee30dd2fdd89330856ff1df7235713be45078074 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Wed, 9 Sep 2020 21:45:11 +0100 Subject: [PATCH 2/4] Use backticks to avoid double backslashes Fixes following staticcheck warning. $ staticcheck ./unbound_exporter.go unbound_exporter.go:279:22: should use raw string (`...`) with regexp.MustCompile to avoid having to escape twice (S1007) --- unbound_exporter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unbound_exporter.go b/unbound_exporter.go index eefd2dd..d1d2203 100644 --- a/unbound_exporter.go +++ b/unbound_exporter.go @@ -276,7 +276,7 @@ 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+)$") + histogramPattern := regexp.MustCompile(`^histogram\.\d+\.\d+\.to\.(\d+\.\d+)$`) histogramCount := uint64(0) histogramAvg := float64(0) From 66e91d04dc8542d4daaaf9d95da1aa1e4e00af14 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Wed, 9 Sep 2020 21:53:02 +0100 Subject: [PATCH 3/4] Update go module files --- go.mod | 1 + go.sum | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/go.mod b/go.mod index b1235b2..d58d0c1 100644 --- a/go.mod +++ b/go.mod @@ -7,5 +7,6 @@ require ( github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 // indirect github.com/prometheus/client_golang v1.0.0 github.com/prometheus/common v0.5.0 + golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 // indirect golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect ) diff --git a/go.sum b/go.sum index 83fc0a9..cecfd84 100644 --- a/go.sum +++ b/go.sum @@ -55,7 +55,10 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -63,6 +66,7 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 7ca9d34340cf1454c4e5741abc0fe76439dbf989 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Wed, 9 Sep 2020 23:00:06 +0100 Subject: [PATCH 4/4] Allow user to request merging thread specific counters Per thread metrics can be unnecessarily detailed in environments where there are lots of unbound instances, and prometheus server memory is short in supply. --- unbound_exporter.go | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/unbound_exporter.go b/unbound_exporter.go index d1d2203..7ba9ebb 100644 --- a/unbound_exporter.go +++ b/unbound_exporter.go @@ -273,7 +273,12 @@ func newUnboundMetric(name string, description string, valueType prometheus.Valu } } -func CollectFromReader(file io.Reader, ch chan<- prometheus.Metric) error { +type threadHelper struct { + match []string + value float64 +} + +func CollectFromReader(file io.Reader, threads bool, ch chan<- prometheus.Metric) error { scanner := bufio.NewScanner(file) scanner.Split(bufio.ScanLines) histogramPattern := regexp.MustCompile(`^histogram\.\d+\.\d+\.to\.(\d+\.\d+)$`) @@ -282,6 +287,8 @@ func CollectFromReader(file io.Reader, ch chan<- prometheus.Metric) error { histogramAvg := float64(0) histogramBuckets := make(map[float64]uint64) + threadSummary := make(map[unboundMetric]threadHelper) + for scanner.Scan() { fields := strings.Split(scanner.Text(), "=") if len(fields) != 2 { @@ -297,6 +304,15 @@ func CollectFromReader(file io.Reader, ch chan<- prometheus.Metric) error { if err != nil { return err } + + if !threads && strings.HasPrefix(matches[0], "thread") { + update := threadSummary[*metric] + matches[1] = "sum" + update.match = matches[1:] + update.value += value + threadSummary[*metric] = update + break + } ch <- prometheus.MustNewConstMetric( metric.desc, metric.valueType, @@ -328,6 +344,16 @@ func CollectFromReader(file io.Reader, ch chan<- prometheus.Metric) error { } } + if !threads { + for metric, helper := range threadSummary { + ch <- prometheus.MustNewConstMetric( + metric.desc, + metric.valueType, + helper.value, + helper.match...) + } + } + // Convert the metrics to a cumulative Prometheus histogram. // Reconstruct the sum of all samples from the average value // provided by Unbound. Hopefully this does not break @@ -356,10 +382,11 @@ func CollectFromFile(path string, ch chan<- prometheus.Metric) error { if err != nil { return err } - return CollectFromReader(conn, ch) + return CollectFromReader(conn, true, ch) } -func CollectFromSocket(socketFamily string, host string, tlsConfig *tls.Config, ch chan<- prometheus.Metric) error { +func CollectFromSocket(socketFamily string, host string, tlsConfig *tls.Config, + threads bool, ch chan<- prometheus.Metric) error { var ( conn net.Conn err error @@ -377,16 +404,17 @@ func CollectFromSocket(socketFamily string, host string, tlsConfig *tls.Config, if err != nil { return err } - return CollectFromReader(conn, ch) + return CollectFromReader(conn, threads, ch) } type UnboundExporter struct { socketFamily string host string tlsConfig *tls.Config + threads bool } -func NewUnboundExporter(host string, ca string, cert string, key string) (*UnboundExporter, error) { +func NewUnboundExporter(host string, ca string, cert string, key string, threads bool) (*UnboundExporter, error) { u, err := url.Parse(host) if err != nil { return &UnboundExporter{}, err @@ -438,6 +466,7 @@ func NewUnboundExporter(host string, ca string, cert string, key string) (*Unbou RootCAs: roots, ServerName: "unbound", }, + threads: threads, }, nil } @@ -449,7 +478,7 @@ func (e *UnboundExporter) Describe(ch chan<- *prometheus.Desc) { } func (e *UnboundExporter) Collect(ch chan<- prometheus.Metric) { - err := CollectFromSocket(e.socketFamily, e.host, e.tlsConfig, ch) + err := CollectFromSocket(e.socketFamily, e.host, e.tlsConfig, e.threads, ch) if err == nil { ch <- prometheus.MustNewConstMetric( unboundUpDesc, @@ -472,11 +501,12 @@ func main() { unboundCa = flag.String("unbound.ca", "/etc/unbound/unbound_server.pem", "Unbound server certificate.") unboundCert = flag.String("unbound.cert", "/etc/unbound/unbound_control.pem", "Unbound client certificate.") unboundKey = flag.String("unbound.key", "/etc/unbound/unbound_control.key", "Unbound client key.") + threads = flag.Bool("threads", true, "Export per thread metrics.") ) flag.Parse() log.Info("Starting unbound_exporter") - exporter, err := NewUnboundExporter(*unboundHost, *unboundCa, *unboundCert, *unboundKey) + exporter, err := NewUnboundExporter(*unboundHost, *unboundCa, *unboundCert, *unboundKey, *threads) if err != nil { panic(err) }