github.com/mackerelio/mackerel-agent-plugins@v0.89.3/mackerel-plugin-nginx/lib/nginx.go (about) 1 package mpnginx 2 3 import ( 4 "bufio" 5 "flag" 6 "fmt" 7 "io" 8 9 "errors" 10 "net/http" 11 "regexp" 12 "strconv" 13 "strings" 14 15 mp "github.com/mackerelio/go-mackerel-plugin-helper" 16 ) 17 18 var graphdef = map[string]mp.Graphs{ 19 "nginx.connections": { 20 Label: "Nginx Connections", 21 Unit: "integer", 22 Metrics: []mp.Metrics{ 23 {Name: "connections", Label: "Active connections", Diff: false}, 24 }, 25 }, 26 "nginx.requests": { 27 Label: "Nginx requests", 28 Unit: "float", 29 Metrics: []mp.Metrics{ 30 {Name: "accepts", Label: "Accepted connections", Diff: true, Type: "uint64"}, 31 {Name: "handled", Label: "Handled connections", Diff: true, Type: "uint64"}, 32 {Name: "requests", Label: "Handled requests", Diff: true, Type: "uint64"}, 33 }, 34 }, 35 "nginx.queue": { 36 Label: "Nginx connection status", 37 Unit: "integer", 38 Metrics: []mp.Metrics{ 39 {Name: "reading", Label: "Reading", Diff: false}, 40 {Name: "writing", Label: "Writing", Diff: false}, 41 {Name: "waiting", Label: "Waiting", Diff: false}, 42 }, 43 }, 44 } 45 46 type stringSlice []string 47 48 func (s *stringSlice) Set(v string) error { 49 *s = append(*s, v) 50 return nil 51 } 52 53 func (s *stringSlice) String() string { 54 return fmt.Sprintf("%v", *s) 55 } 56 57 // NginxPlugin mackerel plugin for Nginx 58 type NginxPlugin struct { 59 URI string 60 Header stringSlice 61 } 62 63 // % wget -qO- http://localhost:8080/nginx_status 64 // Active connections: 123 65 // server accepts handled requests 66 // 1693613501 1693613501 7996986318 67 // Reading: 66 Writing: 16 Waiting: 41 68 69 // FetchMetrics interface for mackerelplugin 70 func (n NginxPlugin) FetchMetrics() (map[string]interface{}, error) { 71 req, err := http.NewRequest("GET", n.URI, nil) 72 if err != nil { 73 return nil, err 74 } 75 for _, h := range n.Header { 76 kv := strings.SplitN(h, ":", 2) 77 var k, v string 78 k = strings.TrimSpace(kv[0]) 79 if len(kv) == 2 { 80 v = strings.TrimSpace(kv[1]) 81 } 82 if http.CanonicalHeaderKey(k) == "Host" { 83 req.Host = v 84 } else { 85 req.Header.Set(k, v) 86 } 87 } 88 89 // set default User-Agent unless specified by n.Header 90 if _, ok := req.Header["User-Agent"]; !ok { 91 req.Header.Set("User-Agent", "mackerel-plugin-nginx") 92 } 93 94 resp, err := http.DefaultClient.Do(req) 95 if err != nil { 96 return nil, err 97 } 98 defer resp.Body.Close() 99 100 return n.parseStats(resp.Body) 101 } 102 103 func (n NginxPlugin) parseStats(body io.Reader) (map[string]interface{}, error) { 104 stat := make(map[string]interface{}) 105 106 r := bufio.NewReader(body) 107 line, _, err := r.ReadLine() 108 if err != nil { 109 return nil, errors.New("cannot get values") 110 } 111 re := regexp.MustCompile("Active connections: ([0-9]+)") 112 res := re.FindStringSubmatch(string(line)) 113 if res == nil || len(res) != 2 { 114 return nil, errors.New("cannot get values") 115 } 116 stat["connections"], err = strconv.ParseFloat(res[1], 64) 117 if err != nil { 118 return nil, errors.New("cannot get values") 119 } 120 121 _, _, err = r.ReadLine() 122 if err != nil { 123 return nil, errors.New("cannot get values") 124 } 125 126 line, _, err = r.ReadLine() 127 if err != nil { 128 return nil, errors.New("cannot get values") 129 } 130 re = regexp.MustCompile("([0-9]+) ([0-9]+) ([0-9]+)") 131 res = re.FindStringSubmatch(string(line)) 132 if res == nil || len(res) != 4 { 133 return nil, errors.New("cannot get values") 134 } 135 stat["accepts"], err = strconv.ParseFloat(res[1], 64) 136 if err != nil { 137 return nil, errors.New("cannot get values") 138 } 139 stat["handled"], err = strconv.ParseFloat(res[2], 64) 140 if err != nil { 141 return nil, errors.New("cannot get values") 142 } 143 stat["requests"], err = strconv.ParseFloat(res[3], 64) 144 if err != nil { 145 return nil, errors.New("cannot get values") 146 } 147 148 line, _, err = r.ReadLine() 149 if err != nil { 150 return nil, errors.New("cannot get values") 151 } 152 re = regexp.MustCompile("Reading: ([0-9]+) Writing: ([0-9]+) Waiting: ([0-9]+)") 153 res = re.FindStringSubmatch(string(line)) 154 if res == nil || len(res) != 4 { 155 return nil, errors.New("cannot get values") 156 } 157 stat["reading"], err = strconv.ParseFloat(res[1], 64) 158 if err != nil { 159 return nil, errors.New("cannot get values") 160 } 161 stat["writing"], err = strconv.ParseFloat(res[2], 64) 162 if err != nil { 163 return nil, errors.New("cannot get values") 164 } 165 stat["waiting"], err = strconv.ParseFloat(res[3], 64) 166 if err != nil { 167 return nil, errors.New("cannot get values") 168 } 169 return stat, nil 170 } 171 172 // GraphDefinition interface for mackerelplugin 173 func (n NginxPlugin) GraphDefinition() map[string]mp.Graphs { 174 return graphdef 175 } 176 177 // Do the plugin 178 func Do() { 179 optURI := flag.String("uri", "", "URI") 180 optScheme := flag.String("scheme", "http", "Scheme") 181 optHost := flag.String("host", "localhost", "Hostname") 182 optPort := flag.String("port", "8080", "Port") 183 optPath := flag.String("path", "/nginx_status", "Path") 184 optTempfile := flag.String("tempfile", "", "Temp file name") 185 optHeader := &stringSlice{} 186 flag.Var(optHeader, "header", "Set http header (e.g. \"Host: servername\")") 187 flag.Parse() 188 189 var nginx NginxPlugin 190 if *optURI != "" { 191 nginx.URI = *optURI 192 } else { 193 nginx.URI = fmt.Sprintf("%s://%s:%s%s", *optScheme, *optHost, *optPort, *optPath) 194 } 195 nginx.Header = *optHeader 196 197 helper := mp.NewMackerelPlugin(nginx) 198 helper.Tempfile = *optTempfile 199 helper.Run() 200 }