github.com/mackerelio/mackerel-agent-plugins@v0.89.3/mackerel-plugin-uwsgi-vassal/lib/uwsgi_vassal.go (about) 1 package mpuwsgivassal 2 3 import ( 4 "encoding/json" 5 "errors" 6 "flag" 7 "net" 8 "net/http" 9 "strings" 10 11 mp "github.com/mackerelio/go-mackerel-plugin" 12 "golang.org/x/text/cases" 13 "golang.org/x/text/language" 14 ) 15 16 // UWSGIVassalPlugin mackerel plugin for uWSGI 17 type UWSGIVassalPlugin struct { 18 Socket string 19 Prefix string 20 LabelPrefix string 21 } 22 23 // { 24 // "workers": [{ 25 // "id": 1, 26 // "pid": 31759, 27 // "requests": 0, 28 // "exceptions": 0, 29 // "status": "idle", 30 // "rss": 0, 31 // "vsz": 0, 32 // "running_time": 0, 33 // "last_spawn": 1317235041, 34 // "respawn_count": 1, 35 // "tx": 0, 36 // "avg_rt": 0, 37 // "apps": [{ 38 // "id": 0, 39 // "modifier1": 0, 40 // "mountpoint": "", 41 // "requests": 0, 42 // "exceptions": 0, 43 // "chdir": "" 44 // }] 45 // }, { 46 // "id": 2, 47 // "pid": 31760, 48 // "requests": 0, 49 // "exceptions": 0, 50 // "status": "idle", 51 // "rss": 0, 52 // "vsz": 0, 53 // "running_time": 0, 54 // "last_spawn": 1317235041, 55 // "respawn_count": 1, 56 // "tx": 0, 57 // "avg_rt": 0, 58 // "apps": [{ 59 // "id": 0, 60 // "modifier1": 0, 61 // "mountpoint": "", 62 // "requests": 0, 63 // "exceptions": 0, 64 // "chdir": "" 65 // }] 66 // }, { 67 // "id": 3, 68 // "pid": 31761, 69 // "requests": 0, 70 // "exceptions": 0, 71 // "status": "idle", 72 // "rss": 0, 73 // "vsz": 0, 74 // "running_time": 0, 75 // "last_spawn": 1317235041, 76 // "respawn_count": 1, 77 // "tx": 0, 78 // "avg_rt": 0, 79 // "apps": [{ 80 // "id": 0, 81 // "modifier1": 0, 82 // "mountpoint": "", 83 // "requests": 0, 84 // "exceptions": 0, 85 // "chdir": "" 86 // }] 87 // }, { 88 // "id": 4, 89 // "pid": 31762, 90 // "requests": 0, 91 // "exceptions": 0, 92 // "status": "idle", 93 // "rss": 0, 94 // "vsz": 0, 95 // "running_time": 0, 96 // "last_spawn": 1317235041, 97 // "respawn_count": 1, 98 // "tx": 0, 99 // "avg_rt": 0, 100 // "apps": [{ 101 // "id": 0, 102 // "modifier1": 0, 103 // "mountpoint": "", 104 // "requests": 0, 105 // "exceptions": 0, 106 // "chdir": "" 107 // }] 108 // } 109 // } 110 111 // field types vary between versions 112 113 // UWSGIWorker struct 114 type UWSGIWorker struct { 115 Requests uint64 `json:"requests"` 116 Status string `json:"status"` 117 } 118 119 // UWSGIWorkers sturct for json struct 120 type UWSGIWorkers struct { 121 Workers []UWSGIWorker `json:"workers"` 122 } 123 124 // FetchMetrics interface for mackerelplugin 125 func (p UWSGIVassalPlugin) FetchMetrics() (map[string]float64, error) { 126 stat := make(map[string]float64) 127 stat["busy"] = 0.0 128 stat["idle"] = 0.0 129 stat["cheap"] = 0.0 130 stat["pause"] = 0.0 131 stat["requests"] = 0.0 132 133 var decoder *json.Decoder 134 if strings.HasPrefix(p.Socket, "unix://") { 135 conn, err := net.Dial("unix", strings.TrimPrefix(p.Socket, "unix://")) 136 if err != nil { 137 return nil, err 138 } 139 defer conn.Close() 140 decoder = json.NewDecoder(conn) 141 } else if strings.HasPrefix(p.Socket, "http://") { 142 req, err := http.NewRequest(http.MethodGet, p.Socket, nil) 143 if err != nil { 144 return nil, err 145 } 146 req.Header.Set("User-Agent", "mackerel-plugin-uwsgi-vessal") 147 148 resp, err := http.DefaultClient.Do(req) 149 if err != nil { 150 return nil, err 151 } 152 defer resp.Body.Close() 153 decoder = json.NewDecoder(resp.Body) 154 } else { 155 err := errors.New("'--socket' is neither http endpoint nor the unix domain socket, try '--help' for more information") 156 return nil, err 157 } 158 159 var workers UWSGIWorkers 160 if err := decoder.Decode(&workers); err != nil { 161 return nil, err 162 } 163 164 for _, worker := range workers.Workers { 165 switch worker.Status { 166 case "idle", "busy", "cheap", "pause": 167 stat[worker.Status]++ 168 } 169 stat["requests"] += float64(worker.Requests) 170 } 171 172 return stat, nil 173 } 174 175 // GraphDefinition interface for mackerelplugin 176 func (p UWSGIVassalPlugin) GraphDefinition() map[string]mp.Graphs { 177 labelPrefix := cases.Title(language.Und, cases.NoLower).String(p.Prefix) 178 179 var graphdef = map[string]mp.Graphs{ 180 (p.Prefix + ".workers"): { 181 Label: labelPrefix + " Workers", 182 Unit: "integer", 183 Metrics: []mp.Metrics{ 184 {Name: "busy", Label: "Busy", Diff: false, Stacked: true}, 185 {Name: "idle", Label: "Idle", Diff: false, Stacked: true}, 186 {Name: "cheap", Label: "Cheap", Diff: false, Stacked: true}, 187 {Name: "pause", Label: "Pause", Diff: false, Stacked: true}, 188 }, 189 }, 190 (p.Prefix + ".req"): { 191 Label: labelPrefix + " Requests", 192 Unit: "float", 193 Metrics: []mp.Metrics{ 194 {Name: "requests", Label: "Requests", Diff: true}, 195 }, 196 }, 197 } 198 199 return graphdef 200 } 201 202 // MetricKeyPrefix interface for PluginWithPrefix 203 func (p UWSGIVassalPlugin) MetricKeyPrefix() string { 204 if p.Prefix == "" { 205 p.Prefix = "uWSGI" 206 } 207 return p.Prefix 208 } 209 210 // Do the plugin 211 func Do() { 212 optSocket := flag.String("socket", "", "Socket (must be with prefix of 'http://' or 'unix://')") 213 optPrefix := flag.String("metric-key-prefix", "uWSGI", "Prefix") 214 optTempfile := flag.String("tempfile", "", "Temp file name") 215 flag.Parse() 216 217 uwsgi := UWSGIVassalPlugin{Socket: *optSocket, Prefix: *optPrefix} 218 uwsgi.LabelPrefix = cases.Title(language.Und, cases.NoLower).String(uwsgi.Prefix) 219 220 helper := mp.NewMackerelPlugin(uwsgi) 221 helper.Tempfile = *optTempfile 222 helper.Run() 223 }