github.com/netdata/go.d.plugin@v0.58.1/modules/rabbitmq/collect.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package rabbitmq 4 5 import ( 6 "encoding/json" 7 "fmt" 8 "io" 9 "net/http" 10 "path/filepath" 11 12 "github.com/netdata/go.d.plugin/pkg/stm" 13 "github.com/netdata/go.d.plugin/pkg/web" 14 ) 15 16 const ( 17 urlPathAPIOverview = "/api/overview" 18 urlPathAPINodes = "/api/nodes/" 19 urlPathAPIVhosts = "/api/vhosts" 20 urlPathAPIQueues = "/api/queues" 21 ) 22 23 // TODO: there is built-in prometheus collector since v3.8.0 (https://www.rabbitmq.com/prometheus.html). 24 // Should use it (in addition?), it is the recommended option according to the docs. 25 func (r *RabbitMQ) collect() (map[string]int64, error) { 26 mx := make(map[string]int64) 27 28 if err := r.collectOverviewStats(mx); err != nil { 29 return nil, err 30 } 31 if err := r.collectNodeStats(mx); err != nil { 32 return mx, err 33 } 34 if err := r.collectVhostsStats(mx); err != nil { 35 return mx, err 36 } 37 if r.CollectQueues { 38 if err := r.collectQueuesStats(mx); err != nil { 39 return mx, err 40 } 41 } 42 43 return mx, nil 44 } 45 46 func (r *RabbitMQ) collectOverviewStats(mx map[string]int64) error { 47 var stats overviewStats 48 if err := r.doOKDecode(urlPathAPIOverview, &stats); err != nil { 49 return err 50 } 51 52 if r.nodeName == "" { 53 r.nodeName = stats.Node 54 } 55 56 for k, v := range stm.ToMap(stats) { 57 mx[k] = v 58 } 59 60 return nil 61 } 62 63 func (r *RabbitMQ) collectNodeStats(mx map[string]int64) error { 64 if r.nodeName == "" { 65 return nil 66 } 67 68 var stats nodeStats 69 if err := r.doOKDecode(filepath.Join(urlPathAPINodes, r.nodeName), &stats); err != nil { 70 return err 71 } 72 73 for k, v := range stm.ToMap(stats) { 74 mx[k] = v 75 } 76 mx["proc_available"] = int64(stats.ProcTotal - stats.ProcUsed) 77 78 return nil 79 } 80 81 func (r *RabbitMQ) collectVhostsStats(mx map[string]int64) error { 82 var stats []vhostStats 83 if err := r.doOKDecode(urlPathAPIVhosts, &stats); err != nil { 84 return err 85 } 86 87 seen := make(map[string]bool) 88 89 for _, vhost := range stats { 90 seen[vhost.Name] = true 91 for k, v := range stm.ToMap(vhost) { 92 mx[fmt.Sprintf("vhost_%s_%s", vhost.Name, k)] = v 93 } 94 } 95 96 for name := range seen { 97 if !r.vhosts[name] { 98 r.vhosts[name] = true 99 r.Debugf("new vhost name='%s': creating charts", name) 100 r.addVhostCharts(name) 101 } 102 } 103 for name := range r.vhosts { 104 if !seen[name] { 105 delete(r.vhosts, name) 106 r.Debugf("stale vhost name='%s': removing charts", name) 107 r.removeVhostCharts(name) 108 } 109 } 110 111 return nil 112 } 113 114 func (r *RabbitMQ) collectQueuesStats(mx map[string]int64) error { 115 var stats []queueStats 116 if err := r.doOKDecode(urlPathAPIQueues, &stats); err != nil { 117 return err 118 } 119 120 seen := make(map[string]queueCache) 121 122 for _, queue := range stats { 123 seen[queue.Name+"|"+queue.Vhost] = queueCache{name: queue.Name, vhost: queue.Vhost} 124 for k, v := range stm.ToMap(queue) { 125 mx[fmt.Sprintf("queue_%s_vhost_%s_%s", queue.Name, queue.Vhost, k)] = v 126 } 127 } 128 129 for key, queue := range seen { 130 if _, ok := r.queues[key]; !ok { 131 r.queues[key] = queue 132 r.Debugf("new queue name='%s', vhost='%s': creating charts", queue.name, queue.vhost) 133 r.addQueueCharts(queue.name, queue.vhost) 134 } 135 } 136 for key, queue := range r.queues { 137 if _, ok := seen[key]; !ok { 138 delete(r.queues, key) 139 r.Debugf("stale queue name='%s', vhost='%s': removing charts", queue.name, queue.vhost) 140 r.removeQueueCharts(queue.name, queue.vhost) 141 } 142 } 143 144 return nil 145 } 146 147 func (r *RabbitMQ) doOKDecode(urlPath string, in interface{}) error { 148 req, err := web.NewHTTPRequest(r.Request.Copy()) 149 if err != nil { 150 return fmt.Errorf("error on creating request: %v", err) 151 } 152 153 req.URL.Path = urlPath 154 155 r.Debugf("doing HTTP %s to '%s'", req.Method, req.URL) 156 resp, err := r.httpClient.Do(req) 157 if err != nil { 158 return fmt.Errorf("error on request to %s: %v", req.URL, err) 159 } 160 161 defer closeBody(resp) 162 163 if resp.StatusCode != http.StatusOK { 164 return fmt.Errorf("%s returned HTTP status %d (%s)", req.URL, resp.StatusCode, resp.Status) 165 } 166 167 if err = json.NewDecoder(resp.Body).Decode(&in); err != nil { 168 return fmt.Errorf("error on decoding response from %s: %v", req.URL, err) 169 } 170 171 return nil 172 } 173 174 func closeBody(resp *http.Response) { 175 if resp != nil && resp.Body != nil { 176 _, _ = io.Copy(io.Discard, resp.Body) 177 _ = resp.Body.Close() 178 } 179 }