package collector import ( "os" "os/exec" "strconv" "strings" ) type HAProxyInfo struct { Uptime string `json:"uptime"` CurrentConns int `json:"current_conns"` TotalConns int `json:"total_conns"` TotalReqs int `json:"total_requests"` CurrentSSL int `json:"current_ssl"` TotalSSL int `json:"total_ssl"` MaxConns int `json:"max_conns"` } type HAProxyEntry struct { ProxyName string `json:"proxy_name"` ServerName string `json:"server_name"` Type string `json:"type"` Status string `json:"status"` CurrentSessions int `json:"current_sessions"` TotalSessions int `json:"total_sessions"` BytesIn int64 `json:"bytes_in"` BytesOut int64 `json:"bytes_out"` RequestsTotal int `json:"requests_total"` ErrorsReq int `json:"errors_req"` ErrorsConn int `json:"errors_conn"` ErrorsResp int `json:"errors_resp"` } type HAProxyData struct { Info HAProxyInfo `json:"info"` Entries []HAProxyEntry `json:"entries"` } const haproxySocket = "/var/run/haproxy.socket" func CollectHAProxy() *HAProxyData { if _, err := os.Stat(haproxySocket); err != nil { return nil } data := &HAProxyData{} // show info if out, err := exec.Command("/bin/sh", "-c", `echo "show info" | /usr/bin/nc -U `+haproxySocket).Output(); err == nil { info := parseHAProxyInfo(string(out)) data.Info = info } // show stat if out, err := exec.Command("/bin/sh", "-c", `echo "show stat" | /usr/bin/nc -U `+haproxySocket).Output(); err == nil { data.Entries = parseHAProxyStats(string(out)) } return data } func parseHAProxyInfo(output string) HAProxyInfo { info := HAProxyInfo{} for _, line := range strings.Split(output, "\n") { parts := strings.SplitN(line, ":", 2) if len(parts) != 2 { continue } key := strings.TrimSpace(parts[0]) val := strings.TrimSpace(parts[1]) switch key { case "Uptime": info.Uptime = val case "CurrConns": info.CurrentConns, _ = strconv.Atoi(val) case "CumConns": info.TotalConns, _ = strconv.Atoi(val) case "CumReq": info.TotalReqs, _ = strconv.Atoi(val) case "CurrSslConns": info.CurrentSSL, _ = strconv.Atoi(val) case "CumSslConns": info.TotalSSL, _ = strconv.Atoi(val) case "Maxconn": info.MaxConns, _ = strconv.Atoi(val) } } return info } func parseHAProxyStats(output string) []HAProxyEntry { var entries []HAProxyEntry lines := strings.Split(output, "\n") for _, line := range lines { if strings.HasPrefix(line, "#") || strings.TrimSpace(line) == "" { continue } fields := strings.Split(line, ",") if len(fields) < 18 { continue } svname := fields[1] entryType := "server" if svname == "FRONTEND" { entryType = "frontend" } else if svname == "BACKEND" { entryType = "backend" } e := HAProxyEntry{ ProxyName: fields[0], ServerName: svname, Type: entryType, Status: fields[17], } e.CurrentSessions, _ = strconv.Atoi(fields[4]) e.TotalSessions, _ = strconv.Atoi(fields[7]) e.BytesIn, _ = strconv.ParseInt(fields[8], 10, 64) e.BytesOut, _ = strconv.ParseInt(fields[9], 10, 64) e.ErrorsReq, _ = strconv.Atoi(fields[12]) e.ErrorsConn, _ = strconv.Atoi(fields[13]) e.ErrorsResp, _ = strconv.Atoi(fields[14]) if len(fields) > 48 { e.RequestsTotal, _ = strconv.Atoi(fields[48]) } entries = append(entries, e) } return entries }