| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | package uwsgi |
| 4 | |
| 5 | import ( |
| 6 | "encoding/json" |
| 7 | "fmt" |
| 8 | |
| 9 | "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/oldmetrix" |
| 10 | ) |
| 11 | |
| 12 | type statsResponse struct { |
| 13 | Workers []workerStats `json:"workers"` |
| 14 | } |
| 15 | |
| 16 | type workerStats struct { |
| 17 | ID int `json:"id"` |
| 18 | Accepting int64 `json:"accepting"` |
| 19 | Requests int64 `json:"requests"` |
| 20 | DeltaRequests int64 `json:"delta_requests"` |
| 21 | Exceptions int64 `json:"exceptions"` |
| 22 | HarakiriCount int64 `json:"harakiri_count"` |
| 23 | Status string `json:"status"` |
| 24 | RSS int64 `json:"rss"` |
| 25 | VSZ int64 `json:"vsz"` |
| 26 | RespawnCount int64 `json:"respawn_count"` |
| 27 | TX int64 `json:"tx"` |
| 28 | AvgRT int64 `json:"avg_rt"` |
| 29 | } |
| 30 | |
| 31 | func (c *Collector) collect() (map[string]int64, error) { |
| 32 | stats, err := c.conn.queryStats() |
| 33 | if err != nil { |
| 34 | return nil, fmt.Errorf("failed to query stats: %v", err) |
| 35 | } |
| 36 | |
| 37 | mx := make(map[string]int64) |
| 38 | |
| 39 | if err := c.collectStats(mx, stats); err != nil { |
| 40 | return nil, err |
| 41 | } |
| 42 | |
| 43 | return mx, nil |
| 44 | } |
| 45 | |
| 46 | func (c *Collector) collectStats(mx map[string]int64, stats []byte) error { |
| 47 | var resp statsResponse |
| 48 | if err := json.Unmarshal(stats, &resp); err != nil { |
| 49 | return fmt.Errorf("failed to json decode stats response: %v", err) |
| 50 | } |
| 51 | |
| 52 | // stats server returns an empty array if there are no workers |
| 53 | if resp.Workers == nil { |
| 54 | return fmt.Errorf("unexpected stats response: no workers found") |
| 55 | } |
| 56 | |
| 57 | seen := make(map[int]bool) |
| 58 | |
| 59 | mx["workers_tx"] = 0 |
| 60 | mx["workers_requests"] = 0 |
| 61 | mx["workers_harakiris"] = 0 |
| 62 | mx["workers_exceptions"] = 0 |
| 63 | mx["workers_respawns"] = 0 |
| 64 | |
| 65 | for _, w := range resp.Workers { |
| 66 | mx["workers_tx"] += w.TX |
| 67 | mx["workers_requests"] += w.Requests |
| 68 | mx["workers_harakiris"] += w.HarakiriCount |
| 69 | mx["workers_exceptions"] += w.Exceptions |
| 70 | mx["workers_respawns"] += w.RespawnCount |
| 71 | |
| 72 | seen[w.ID] = true |
| 73 | |
| 74 | if !c.seenWorkers[w.ID] { |
| 75 | c.seenWorkers[w.ID] = true |
| 76 | c.addWorkerCharts(w.ID) |
| 77 | } |
| 78 | |
| 79 | px := fmt.Sprintf("worker_%d_", w.ID) |
| 80 | |
| 81 | mx[px+"tx"] = w.TX |
| 82 | mx[px+"requests"] = w.Requests |
| 83 | mx[px+"delta_requests"] = w.DeltaRequests |
| 84 | mx[px+"average_request_time"] = w.AvgRT |
| 85 | mx[px+"harakiris"] = w.HarakiriCount |
| 86 | mx[px+"exceptions"] = w.Exceptions |
| 87 | mx[px+"respawns"] = w.RespawnCount |
| 88 | mx[px+"memory_rss"] = w.RSS |
| 89 | mx[px+"memory_vsz"] = w.VSZ |
| 90 | |
| 91 | for _, v := range []string{"idle", "busy", "cheap", "pause", "sig"} { |
| 92 | mx[px+"status_"+v] = oldmetrix.Bool(w.Status == v) |
| 93 | } |
| 94 | mx[px+"request_handling_status_accepting"] = oldmetrix.Bool(w.Accepting == 1) |
| 95 | mx[px+"request_handling_status_not_accepting"] = oldmetrix.Bool(w.Accepting == 0) |
| 96 | } |
| 97 | |
| 98 | for id := range c.seenWorkers { |
| 99 | if !seen[id] { |
| 100 | delete(c.seenWorkers, id) |
| 101 | c.removeWorkerCharts(id) |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | return nil |
| 106 | } |