github.com/mackerelio/mackerel-agent-plugins@v0.89.3/mackerel-plugin-haproxy/lib/haproxy.go (about) 1 package mphaproxy 2 3 import ( 4 "bufio" 5 "encoding/csv" 6 "errors" 7 "flag" 8 "fmt" 9 "io" 10 "net" 11 "net/http" 12 "os" 13 "strconv" 14 "time" 15 16 mp "github.com/mackerelio/go-mackerel-plugin" 17 ) 18 19 var graphdef = map[string]mp.Graphs{ 20 "haproxy.total.sessions": { 21 Label: "HAProxy Total Sessions", 22 Unit: "integer", 23 Metrics: []mp.Metrics{ 24 {Name: "sessions", Label: "Sessions", Diff: true}, 25 }, 26 }, 27 "haproxy.total.bytes": { 28 Label: "HAProxy Total Bytes", 29 Unit: "integer", 30 Metrics: []mp.Metrics{ 31 {Name: "bytes_in", Label: "Bytes In", Diff: true}, 32 {Name: "bytes_out", Label: "Bytes Out", Diff: true}, 33 }, 34 }, 35 "haproxy.total.connection_errors": { 36 Label: "HAProxy Total Connection Errors", 37 Unit: "integer", 38 Metrics: []mp.Metrics{ 39 {Name: "connection_errors", Label: "Connection Errors", Diff: true}, 40 }, 41 }, 42 } 43 44 // HAProxyPlugin mackerel plugin for haproxy 45 type HAProxyPlugin struct { 46 URI string 47 Username string 48 Password string 49 Socket string 50 } 51 52 // FetchMetrics interface for mackerelplugin 53 func (p HAProxyPlugin) FetchMetrics() (map[string]float64, error) { 54 var metrics map[string]float64 55 var err error 56 if p.Socket == "" { 57 metrics, err = p.fetchMetricsFromTCP() 58 } else { 59 metrics, err = p.fetchMetricsFromSocket() 60 } 61 return metrics, err 62 } 63 64 func (p HAProxyPlugin) fetchMetricsFromTCP() (map[string]float64, error) { 65 client := &http.Client{ 66 Timeout: time.Duration(5) * time.Second, 67 } 68 69 requestURI := p.URI + ";csv;norefresh" 70 req, err := http.NewRequest("GET", requestURI, nil) 71 if err != nil { 72 return nil, err 73 } 74 if p.Username != "" { 75 req.SetBasicAuth(p.Username, p.Password) 76 } 77 req.Header.Set("User-Agent", "mackerel-plugin-haproxy") 78 79 resp, err := client.Do(req) 80 if err != nil { 81 return nil, err 82 } 83 defer resp.Body.Close() 84 85 if resp.StatusCode != 200 { 86 return nil, fmt.Errorf("Request failed. Status: %s, URI: %s", resp.Status, requestURI) // nolint 87 } 88 89 return p.parseStats(resp.Body) 90 } 91 92 func (p HAProxyPlugin) fetchMetricsFromSocket() (map[string]float64, error) { 93 client, err := net.Dial("unix", p.Socket) 94 if err != nil { 95 return nil, err 96 } 97 defer client.Close() 98 99 fmt.Fprintln(client, "show stat") 100 101 return p.parseStats(bufio.NewReader(client)) 102 } 103 104 func (p HAProxyPlugin) parseStats(statsBody io.Reader) (map[string]float64, error) { 105 stat := make(map[string]float64) 106 reader := csv.NewReader(statsBody) 107 108 for { 109 columns, err := reader.Read() 110 if err == io.EOF { 111 break 112 } 113 114 if len(columns) < 60 { 115 return nil, errors.New("length of stats csv is too short (specified uri/socket may be wrong)") 116 } 117 118 if columns[1] != "BACKEND" { 119 continue 120 } 121 122 var data float64 123 124 data, err = strconv.ParseFloat(columns[7], 64) 125 if err != nil { 126 return nil, errors.New("cannot get values") 127 } 128 stat["sessions"] += data 129 130 data, err = strconv.ParseFloat(columns[8], 64) 131 if err != nil { 132 return nil, errors.New("cannot get values") 133 } 134 stat["bytes_in"] += data 135 136 data, err = strconv.ParseFloat(columns[9], 64) 137 if err != nil { 138 return nil, errors.New("cannot get values") 139 } 140 stat["bytes_out"] += data 141 142 data, err = strconv.ParseFloat(columns[13], 64) 143 if err != nil { 144 return nil, errors.New("cannot get values") 145 } 146 stat["connection_errors"] += data 147 } 148 149 return stat, nil 150 } 151 152 // GraphDefinition interface for mackerelplugin 153 func (p HAProxyPlugin) GraphDefinition() map[string]mp.Graphs { 154 return graphdef 155 } 156 157 // Do the plugin 158 func Do() { 159 optURI := flag.String("uri", "", "URI") 160 optScheme := flag.String("scheme", "http", "Scheme") 161 optHost := flag.String("host", "localhost", "Hostname") 162 optPort := flag.String("port", "80", "Port") 163 optPath := flag.String("path", "/", "Path") 164 optUsername := flag.String("username", "", "Username for Basic Auth") 165 optPassword := flag.String("password", os.Getenv("HAPROXY_PASSWORD"), "Password for Basic Auth") 166 optTempfile := flag.String("tempfile", "", "Temp file name") 167 optSocket := flag.String("socket", "", "Unix Domain Socket") 168 flag.Parse() 169 170 var haproxy HAProxyPlugin 171 if *optURI != "" { 172 haproxy.URI = *optURI 173 } else { 174 haproxy.URI = fmt.Sprintf("%s://%s:%s%s", *optScheme, *optHost, *optPort, *optPath) 175 } 176 177 if *optUsername != "" { 178 haproxy.Username = *optUsername 179 } 180 181 if *optPassword != "" { 182 haproxy.Password = *optPassword 183 } 184 185 if *optSocket != "" { 186 haproxy.Socket = *optSocket 187 } 188 189 helper := mp.NewMackerelPlugin(haproxy) 190 helper.Tempfile = *optTempfile 191 192 helper.Run() 193 }