diff --git a/promunifi/clients.go b/promunifi/clients.go index 96effa1f..7bab559b 100644 --- a/promunifi/clients.go +++ b/promunifi/clients.go @@ -82,6 +82,13 @@ func descClient(ns string) *uclient { } } +func (u *unifiCollector) exportClients(clients []*unifi.Client) (e []*metricExports) { + for _, c := range clients { + e = append(e, u.exportClient(c)...) + } + return +} + // CollectClient exports Clients' Data func (u *unifiCollector) exportClient(c *unifi.Client) []*metricExports { labels := []string{c.ID, c.Mac, c.UserID, c.SiteID, c.SiteName, diff --git a/promunifi/collector.go b/promunifi/collector.go index 8be7b539..9cfa90d3 100644 --- a/promunifi/collector.go +++ b/promunifi/collector.go @@ -3,6 +3,7 @@ package promunifi import ( "fmt" "reflect" + "sync" "time" "github.com/davidnewhall/unifi-poller/metrics" @@ -51,6 +52,9 @@ type Report struct { Descs int Metrics *metrics.Metrics Elapsed time.Duration + Start time.Time + ch chan []*metricExports + wait sync.WaitGroup } // NewUnifiCollector returns a prometheus collector that will export any available @@ -97,83 +101,65 @@ func (u *unifiCollector) Describe(ch chan<- *prometheus.Desc) { // Collect satisifes the prometheus Collector. This runs the input method to get // the current metrics (from another package) then exports them for prometheus. func (u *unifiCollector) Collect(ch chan<- prometheus.Metric) { - start := time.Now() - unifiMetrics, err := u.Config.CollectFn() - if err != nil { + var err error + r := &Report{Start: time.Now(), ch: make(chan []*metricExports)} + defer func() { + r.wait.Wait() + close(r.ch) + }() + + if r.Metrics, err = u.Config.CollectFn(); err != nil { ch <- prometheus.NewInvalidMetric( prometheus.NewInvalidDesc(fmt.Errorf("metric fetch failed")), err) return } - descs := make(map[*prometheus.Desc]bool) // used as a counter - r := &Report{Metrics: unifiMetrics} - if u.Config.LoggingFn != nil { - defer func() { - r.Elapsed = time.Since(start) - r.Descs = len(descs) - u.Config.LoggingFn(r) - }() - } + go u.exportMetrics(ch, r) - export := func(metrics []*metricExports) { - count, errors := u.export(ch, metrics) - r.Total += count - r.Errors += errors - for _, d := range metrics { - descs[d.Desc] = true - } - } - - for _, asset := range r.Metrics.Clients { - export(u.exportClient(asset)) - } - for _, asset := range r.Metrics.Sites { - export(u.exportSite(asset)) - } + r.wait.Add(2) + go func() { r.ch <- u.exportClients(r.Metrics.Clients) }() + go func() { r.ch <- u.exportSites(r.Metrics.Sites) }() if r.Metrics.Devices == nil { return } - for _, asset := range r.Metrics.Devices.UAPs { - export(u.exportUAP(asset)) - } - for _, asset := range r.Metrics.Devices.USGs { - export(u.exportUSG(asset)) - } - for _, asset := range r.Metrics.Devices.USWs { - export(u.exportUSW(asset)) - } - for _, asset := range r.Metrics.Devices.UDMs { - export(u.exportUDM(asset)) - } + r.wait.Add(4) + go func() { r.ch <- u.exportUAPs(r.Metrics.Devices.UAPs) }() + go func() { r.ch <- u.exportUSGs(r.Metrics.Devices.USGs) }() + go func() { r.ch <- u.exportUSWs(r.Metrics.Devices.USWs) }() + go func() { r.ch <- u.exportUDMs(r.Metrics.Devices.UDMs) }() } -func (u *unifiCollector) export(ch chan<- prometheus.Metric, exports []*metricExports) (count, errors int) { - for _, e := range exports { - var val float64 +// Call this once (at least as-is). It sets all the counters and runs the logging function. +func (u *unifiCollector) exportMetrics(ch chan<- prometheus.Metric, r *Report) { + descs := make(map[*prometheus.Desc]bool) // used as a counter + for newMetrics := range r.ch { + for _, m := range newMetrics { + r.Total++ + descs[m.Desc] = true - switch v := e.Value.(type) { - case float64: - val = v + switch v := m.Value.(type) { + case float64: + ch <- prometheus.MustNewConstMetric(m.Desc, m.ValueType, v, m.Labels...) + case int64: + ch <- prometheus.MustNewConstMetric(m.Desc, m.ValueType, float64(v), m.Labels...) + case unifi.FlexInt: + ch <- prometheus.MustNewConstMetric(m.Desc, m.ValueType, v.Val, m.Labels...) - case int64: - val = float64(v) - - case unifi.FlexInt: - val = v.Val - - default: - errors++ - if u.Config.ReportErrors { - ch <- prometheus.NewInvalidMetric(e.Desc, fmt.Errorf("not a number: %v", e.Value)) + default: + r.Errors++ + if u.Config.ReportErrors { + ch <- prometheus.NewInvalidMetric(m.Desc, fmt.Errorf("not a number: %v", m.Value)) + } } - continue } - - count++ - ch <- prometheus.MustNewConstMetric(e.Desc, e.ValueType, val, e.Labels...) + r.wait.Done() } - return + if u.Config.LoggingFn == nil { + return + } + r.Descs, r.Elapsed = len(descs), time.Since(r.Start) + u.Config.LoggingFn(r) } diff --git a/promunifi/site.go b/promunifi/site.go index 5e8d0eaf..140372f8 100644 --- a/promunifi/site.go +++ b/promunifi/site.go @@ -67,6 +67,13 @@ func descSite(ns string) *site { } } +func (u *unifiCollector) exportSites(sites unifi.Sites) (e []*metricExports) { + for _, s := range sites { + e = append(e, u.exportSite(s)...) + } + return +} + // exportSite exports Network Site Data func (u *unifiCollector) exportSite(s *unifi.Site) []*metricExports { labels := []string{s.Name, s.Desc, s.SiteName} diff --git a/promunifi/uap.go b/promunifi/uap.go index 8e9869a3..d6c791d3 100644 --- a/promunifi/uap.go +++ b/promunifi/uap.go @@ -102,6 +102,13 @@ func descUAP(ns string) *uap { } } +func (u *unifiCollector) exportUAPs(uaps []*unifi.UAP) (e []*metricExports) { + for _, a := range uaps { + e = append(e, u.exportUAP(a)...) + } + return +} + // exportUAP exports Access Point Data func (u *unifiCollector) exportUAP(a *unifi.UAP) []*metricExports { labels := []string{a.SiteName, a.Mac, a.Model, a.Name, a.Serial, a.SiteID, diff --git a/promunifi/udm.go b/promunifi/udm.go index 917478ca..b09bbaa0 100644 --- a/promunifi/udm.go +++ b/promunifi/udm.go @@ -11,6 +11,13 @@ func descUDM(ns string) *udm { return &udm{} } +func (u *unifiCollector) exportUDMs(udms []*unifi.UDM) (e []*metricExports) { + for _, d := range udms { + e = append(e, u.exportUDM(d)...) + } + return +} + // exportUDM exports UniFi Dream Machine (and Pro) Data func (u *unifiCollector) exportUDM(d *unifi.UDM) []*metricExports { return nil diff --git a/promunifi/usg.go b/promunifi/usg.go index cfc6d321..e4cc58b5 100644 --- a/promunifi/usg.go +++ b/promunifi/usg.go @@ -109,6 +109,13 @@ func descUSG(ns string) *usg { } } +func (u *unifiCollector) exportUSGs(usgs []*unifi.USG) (e []*metricExports) { + for _, sg := range usgs { + e = append(e, u.exportUSG(sg)...) + } + return +} + // exportUSG Exports Security Gateway Data // uplink and port tables structs are ignored. that data should be in other exported fields. func (u *unifiCollector) exportUSG(s *unifi.USG) []*metricExports { diff --git a/promunifi/usw.go b/promunifi/usw.go index dc1b947f..6c496428 100644 --- a/promunifi/usw.go +++ b/promunifi/usw.go @@ -136,6 +136,13 @@ func descUSW(ns string) *usw { } } +func (u *unifiCollector) exportUSWs(usws []*unifi.USW) (e []*metricExports) { + for _, sw := range usws { + e = append(e, u.exportUSW(sw)...) + } + return +} + // exportUSW exports Network Switch Data func (u *unifiCollector) exportUSW(s *unifi.USW) []*metricExports { labels := []string{s.Type, s.Version, s.DeviceID, s.IP,