add missing fields to haproxy input (#2323)
This commit is contained in:
committed by
Cameron Sparr
parent
a610f8bd03
commit
55d3f70771
@@ -14,80 +14,17 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/internal/errchan"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
//CSV format: https://cbonte.github.io/haproxy-dconv/1.5/configuration.html#9.1
|
||||
const (
|
||||
HF_PXNAME = 0 // 0. pxname [LFBS]: proxy name
|
||||
HF_SVNAME = 1 // 1. svname [LFBS]: service name (FRONTEND for frontend, BACKEND for backend, any name for server/listener)
|
||||
HF_QCUR = 2 //2. qcur [..BS]: current queued requests. For the backend this reports the number queued without a server assigned.
|
||||
HF_QMAX = 3 //3. qmax [..BS]: max value of qcur
|
||||
HF_SCUR = 4 // 4. scur [LFBS]: current sessions
|
||||
HF_SMAX = 5 //5. smax [LFBS]: max sessions
|
||||
HF_SLIM = 6 //6. slim [LFBS]: configured session limit
|
||||
HF_STOT = 7 //7. stot [LFBS]: cumulative number of connections
|
||||
HF_BIN = 8 //8. bin [LFBS]: bytes in
|
||||
HF_BOUT = 9 //9. bout [LFBS]: bytes out
|
||||
HF_DREQ = 10 //10. dreq [LFB.]: requests denied because of security concerns.
|
||||
HF_DRESP = 11 //11. dresp [LFBS]: responses denied because of security concerns.
|
||||
HF_EREQ = 12 //12. ereq [LF..]: request errors. Some of the possible causes are:
|
||||
HF_ECON = 13 //13. econ [..BS]: number of requests that encountered an error trying to
|
||||
HF_ERESP = 14 //14. eresp [..BS]: response errors. srv_abrt will be counted here also. Some other errors are: - write error on the client socket (won't be counted for the server stat) - failure applying filters to the response.
|
||||
HF_WRETR = 15 //15. wretr [..BS]: number of times a connection to a server was retried.
|
||||
HF_WREDIS = 16 //16. wredis [..BS]: number of times a request was redispatched to another server. The server value counts the number of times that server was switched away from.
|
||||
HF_STATUS = 17 //17. status [LFBS]: status (UP/DOWN/NOLB/MAINT/MAINT(via)...)
|
||||
HF_WEIGHT = 18 //18. weight [..BS]: total weight (backend), server weight (server)
|
||||
HF_ACT = 19 //19. act [..BS]: number of active servers (backend), server is active (server)
|
||||
HF_BCK = 20 //20. bck [..BS]: number of backup servers (backend), server is backup (server)
|
||||
HF_CHKFAIL = 21 //21. chkfail [...S]: number of failed checks. (Only counts checks failed when the server is up.)
|
||||
HF_CHKDOWN = 22 //22. chkdown [..BS]: number of UP->DOWN transitions. The backend counter counts transitions to the whole backend being down, rather than the sum of the counters for each server.
|
||||
HF_LASTCHG = 23 //23. lastchg [..BS]: number of seconds since the last UP<->DOWN transition
|
||||
HF_DOWNTIME = 24 //24. downtime [..BS]: total downtime (in seconds). The value for the backend is the downtime for the whole backend, not the sum of the server downtime.
|
||||
HF_QLIMIT = 25 //25. qlimit [...S]: configured maxqueue for the server, or nothing in the value is 0 (default, meaning no limit)
|
||||
HF_PID = 26 //26. pid [LFBS]: process id (0 for first instance, 1 for second, ...)
|
||||
HF_IID = 27 //27. iid [LFBS]: unique proxy id
|
||||
HF_SID = 28 //28. sid [L..S]: server id (unique inside a proxy)
|
||||
HF_THROTTLE = 29 //29. throttle [...S]: current throttle percentage for the server, when slowstart is active, or no value if not in slowstart.
|
||||
HF_LBTOT = 30 //30. lbtot [..BS]: total number of times a server was selected, either for new sessions, or when re-dispatching. The server counter is the number of times that server was selected.
|
||||
HF_TRACKED = 31 //31. tracked [...S]: id of proxy/server if tracking is enabled.
|
||||
HF_TYPE = 32 //32. type [LFBS]: (0 = frontend, 1 = backend, 2 = server, 3 = socket/listener)
|
||||
HF_RATE = 33 //33. rate [.FBS]: number of sessions per second over last elapsed second
|
||||
HF_RATE_LIM = 34 //34. rate_lim [.F..]: configured limit on new sessions per second
|
||||
HF_RATE_MAX = 35 //35. rate_max [.FBS]: max number of new sessions per second
|
||||
HF_CHECK_STATUS = 36 //36. check_status [...S]: status of last health check, one of:
|
||||
HF_CHECK_CODE = 37 //37. check_code [...S]: layer5-7 code, if available
|
||||
HF_CHECK_DURATION = 38 //38. check_duration [...S]: time in ms took to finish last health check
|
||||
HF_HRSP_1xx = 39 //39. hrsp_1xx [.FBS]: http responses with 1xx code
|
||||
HF_HRSP_2xx = 40 //40. hrsp_2xx [.FBS]: http responses with 2xx code
|
||||
HF_HRSP_3xx = 41 //41. hrsp_3xx [.FBS]: http responses with 3xx code
|
||||
HF_HRSP_4xx = 42 //42. hrsp_4xx [.FBS]: http responses with 4xx code
|
||||
HF_HRSP_5xx = 43 //43. hrsp_5xx [.FBS]: http responses with 5xx code
|
||||
HF_HRSP_OTHER = 44 //44. hrsp_other [.FBS]: http responses with other codes (protocol error)
|
||||
HF_HANAFAIL = 45 //45. hanafail [...S]: failed health checks details
|
||||
HF_REQ_RATE = 46 //46. req_rate [.F..]: HTTP requests per second over last elapsed second
|
||||
HF_REQ_RATE_MAX = 47 //47. req_rate_max [.F..]: max number of HTTP requests per second observed
|
||||
HF_REQ_TOT = 48 //48. req_tot [.F..]: total number of HTTP requests received
|
||||
HF_CLI_ABRT = 49 //49. cli_abrt [..BS]: number of data transfers aborted by the client
|
||||
HF_SRV_ABRT = 50 //50. srv_abrt [..BS]: number of data transfers aborted by the server (inc. in eresp)
|
||||
HF_COMP_IN = 51 //51. comp_in [.FB.]: number of HTTP response bytes fed to the compressor
|
||||
HF_COMP_OUT = 52 //52. comp_out [.FB.]: number of HTTP response bytes emitted by the compressor
|
||||
HF_COMP_BYP = 53 //53. comp_byp [.FB.]: number of bytes that bypassed the HTTP compressor (CPU/BW limit)
|
||||
HF_COMP_RSP = 54 //54. comp_rsp [.FB.]: number of HTTP responses that were compressed
|
||||
HF_LASTSESS = 55 //55. lastsess [..BS]: number of seconds since last session assigned to server/backend
|
||||
HF_LAST_CHK = 56 //56. last_chk [...S]: last health check contents or textual error
|
||||
HF_LAST_AGT = 57 //57. last_agt [...S]: last agent check contents or textual error
|
||||
HF_QTIME = 58 //58. qtime [..BS]:
|
||||
HF_CTIME = 59 //59. ctime [..BS]:
|
||||
HF_RTIME = 60 //60. rtime [..BS]: (0 for TCP)
|
||||
HF_TTIME = 61 //61. ttime [..BS]: the average total session time in ms over the 1024 last requests
|
||||
)
|
||||
|
||||
type haproxy struct {
|
||||
Servers []string
|
||||
|
||||
client *http.Client
|
||||
|
||||
KeepFieldNames bool
|
||||
}
|
||||
|
||||
var sampleConfig = `
|
||||
@@ -103,6 +40,11 @@ var sampleConfig = `
|
||||
## Server address not starting with 'http' will be treated as a possible
|
||||
## socket, so both examples below are valid.
|
||||
## servers = ["socket:/run/haproxy/admin.sock", "/run/haproxy/*.sock"]
|
||||
#
|
||||
## By default, some of the fields are renamed from what haproxy calls them.
|
||||
## Setting this option to true results in the plugin keeping the original
|
||||
## field names.
|
||||
## keep_field_names = true
|
||||
`
|
||||
|
||||
func (r *haproxy) SampleConfig() string {
|
||||
@@ -147,17 +89,18 @@ func (g *haproxy) Gather(acc telegraf.Accumulator) error {
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
errChan := errchan.New(len(endpoints))
|
||||
wg.Add(len(endpoints))
|
||||
for _, server := range endpoints {
|
||||
go func(serv string) {
|
||||
defer wg.Done()
|
||||
errChan.C <- g.gatherServer(serv, acc)
|
||||
if err := g.gatherServer(serv, acc); err != nil {
|
||||
acc.AddError(err)
|
||||
}
|
||||
}(server)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
return errChan.Error()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *haproxy) gatherServerSocket(addr string, acc telegraf.Accumulator) error {
|
||||
@@ -175,7 +118,7 @@ func (g *haproxy) gatherServerSocket(addr string, acc telegraf.Accumulator) erro
|
||||
return fmt.Errorf("Could not write to socket '%s': %s", addr, errw)
|
||||
}
|
||||
|
||||
return importCsvResult(c, acc, socketPath)
|
||||
return g.importCsvResult(c, acc, socketPath)
|
||||
}
|
||||
|
||||
func (g *haproxy) gatherServer(addr string, acc telegraf.Accumulator) error {
|
||||
@@ -216,7 +159,11 @@ func (g *haproxy) gatherServer(addr string, acc telegraf.Accumulator) error {
|
||||
return fmt.Errorf("Unable to get valid stat result from '%s', http response code : %d", addr, res.StatusCode)
|
||||
}
|
||||
|
||||
return importCsvResult(res.Body, acc, u.Host)
|
||||
if err := g.importCsvResult(res.Body, acc, u.Host); err != nil {
|
||||
return fmt.Errorf("Unable to parse stat result from '%s': %s", addr, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSocketAddr(sock string) string {
|
||||
@@ -229,205 +176,96 @@ func getSocketAddr(sock string) string {
|
||||
}
|
||||
}
|
||||
|
||||
func importCsvResult(r io.Reader, acc telegraf.Accumulator, host string) error {
|
||||
csv := csv.NewReader(r)
|
||||
result, err := csv.ReadAll()
|
||||
var typeNames = []string{"frontend", "backend", "server", "listener"}
|
||||
var fieldRenames = map[string]string{
|
||||
"pxname": "proxy",
|
||||
"svname": "sv",
|
||||
"act": "active_servers",
|
||||
"bck": "backup_servers",
|
||||
"cli_abrt": "cli_abort",
|
||||
"srv_abrt": "srv_abort",
|
||||
"hrsp_1xx": "http_response.1xx",
|
||||
"hrsp_2xx": "http_response.2xx",
|
||||
"hrsp_3xx": "http_response.3xx",
|
||||
"hrsp_4xx": "http_response.4xx",
|
||||
"hrsp_5xx": "http_response.5xx",
|
||||
"hrsp_other": "http_response.other",
|
||||
}
|
||||
|
||||
func (g *haproxy) importCsvResult(r io.Reader, acc telegraf.Accumulator, host string) error {
|
||||
csvr := csv.NewReader(r)
|
||||
now := time.Now()
|
||||
|
||||
for _, row := range result {
|
||||
headers, err := csvr.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(headers[0]) <= 2 || headers[0][:2] != "# " {
|
||||
return fmt.Errorf("did not receive standard haproxy headers")
|
||||
}
|
||||
headers[0] = headers[0][2:]
|
||||
|
||||
for {
|
||||
row, err := csvr.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields := make(map[string]interface{})
|
||||
tags := map[string]string{
|
||||
"server": host,
|
||||
"proxy": row[HF_PXNAME],
|
||||
"sv": row[HF_SVNAME],
|
||||
}
|
||||
for field, v := range row {
|
||||
switch field {
|
||||
case HF_QCUR:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["qcur"] = ival
|
||||
|
||||
if len(row) != len(headers) {
|
||||
return fmt.Errorf("number of columns does not match number of headers. headers=%d columns=%d", len(headers), len(row))
|
||||
}
|
||||
for i, v := range row {
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
colName := headers[i]
|
||||
fieldName := colName
|
||||
if !g.KeepFieldNames {
|
||||
if fieldRename, ok := fieldRenames[colName]; ok {
|
||||
fieldName = fieldRename
|
||||
}
|
||||
case HF_QMAX:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["qmax"] = ival
|
||||
}
|
||||
|
||||
switch colName {
|
||||
case "pxname", "svname":
|
||||
tags[fieldName] = v
|
||||
case "type":
|
||||
vi, err := strconv.ParseInt(v, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse type value '%s'", v)
|
||||
}
|
||||
case HF_SCUR:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["scur"] = ival
|
||||
if int(vi) >= len(typeNames) {
|
||||
return fmt.Errorf("received unknown type value: %d", vi)
|
||||
}
|
||||
case HF_SMAX:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["smax"] = ival
|
||||
tags[fieldName] = typeNames[vi]
|
||||
case "check_desc", "agent_desc":
|
||||
// do nothing. These fields are just a more verbose description of the check_status & agent_status fields
|
||||
case "status", "check_status", "last_chk", "mode", "tracked", "agent_status", "last_agt", "addr", "cookie":
|
||||
// these are string fields
|
||||
fields[fieldName] = v
|
||||
case "lastsess":
|
||||
vi, err := strconv.ParseInt(v, 10, 64)
|
||||
if err != nil {
|
||||
//TODO log the error. And just once (per column) so we don't spam the log
|
||||
continue
|
||||
}
|
||||
case HF_SLIM:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["slim"] = ival
|
||||
}
|
||||
case HF_STOT:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["stot"] = ival
|
||||
}
|
||||
case HF_BIN:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["bin"] = ival
|
||||
}
|
||||
case HF_BOUT:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["bout"] = ival
|
||||
}
|
||||
case HF_DREQ:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["dreq"] = ival
|
||||
}
|
||||
case HF_DRESP:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["dresp"] = ival
|
||||
}
|
||||
case HF_EREQ:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["ereq"] = ival
|
||||
}
|
||||
case HF_ECON:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["econ"] = ival
|
||||
}
|
||||
case HF_ERESP:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["eresp"] = ival
|
||||
}
|
||||
case HF_WRETR:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["wretr"] = ival
|
||||
}
|
||||
case HF_WREDIS:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["wredis"] = ival
|
||||
}
|
||||
case HF_ACT:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["active_servers"] = ival
|
||||
}
|
||||
case HF_BCK:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["backup_servers"] = ival
|
||||
}
|
||||
case HF_DOWNTIME:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["downtime"] = ival
|
||||
}
|
||||
case HF_THROTTLE:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["throttle"] = ival
|
||||
}
|
||||
case HF_LBTOT:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["lbtot"] = ival
|
||||
}
|
||||
case HF_RATE:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["rate"] = ival
|
||||
}
|
||||
case HF_RATE_MAX:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["rate_max"] = ival
|
||||
}
|
||||
case HF_CHECK_DURATION:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["check_duration"] = ival
|
||||
}
|
||||
case HF_HRSP_1xx:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["http_response.1xx"] = ival
|
||||
}
|
||||
case HF_HRSP_2xx:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["http_response.2xx"] = ival
|
||||
}
|
||||
case HF_HRSP_3xx:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["http_response.3xx"] = ival
|
||||
}
|
||||
case HF_HRSP_4xx:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["http_response.4xx"] = ival
|
||||
}
|
||||
case HF_HRSP_5xx:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["http_response.5xx"] = ival
|
||||
}
|
||||
case HF_REQ_RATE:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["req_rate"] = ival
|
||||
}
|
||||
case HF_REQ_RATE_MAX:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["req_rate_max"] = ival
|
||||
}
|
||||
case HF_REQ_TOT:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["req_tot"] = ival
|
||||
}
|
||||
case HF_CLI_ABRT:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["cli_abort"] = ival
|
||||
}
|
||||
case HF_SRV_ABRT:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["srv_abort"] = ival
|
||||
}
|
||||
case HF_QTIME:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["qtime"] = ival
|
||||
}
|
||||
case HF_CTIME:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["ctime"] = ival
|
||||
}
|
||||
case HF_RTIME:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["rtime"] = ival
|
||||
}
|
||||
case HF_TTIME:
|
||||
ival, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil {
|
||||
fields["ttime"] = ival
|
||||
fields[fieldName] = vi
|
||||
default:
|
||||
vi, err := strconv.ParseUint(v, 10, 64)
|
||||
if err != nil {
|
||||
//TODO log the error. And just once (per column) so we don't spam the log
|
||||
continue
|
||||
}
|
||||
fields[fieldName] = vi
|
||||
}
|
||||
}
|
||||
acc.AddFields("haproxy", fields, tags, now)
|
||||
|
||||
Reference in New Issue
Block a user