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= diff --git a/unbound_exporter.go b/unbound_exporter.go index 17fbb61..7ba9ebb 100644 --- a/unbound_exporter.go +++ b/unbound_exporter.go @@ -273,15 +273,22 @@ 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+)$") + histogramPattern := regexp.MustCompile(`^histogram\.\d+\.\d+\.to\.(\d+\.\d+)$`) histogramCount := uint64(0) 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,14 +478,14 @@ 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, 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, @@ -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) }