diff options
Diffstat (limited to '')
12 files changed, 1746 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/haproxy/README.md b/src/go/collectors/go.d.plugin/modules/haproxy/README.md new file mode 120000 index 000000000..2f52cf846 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/haproxy/README.md @@ -0,0 +1 @@ +integrations/haproxy.md
\ No newline at end of file diff --git a/src/go/collectors/go.d.plugin/modules/haproxy/charts.go b/src/go/collectors/go.d.plugin/modules/haproxy/charts.go new file mode 100644 index 000000000..503094c58 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/haproxy/charts.go @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package haproxy + +import ( + "fmt" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" +) + +var charts = module.Charts{ + chartBackendCurrentSessions.Copy(), + chartBackendSessions.Copy(), + + chartBackendResponseTimeAverage.Copy(), + + chartBackendQueueTimeAverage.Copy(), + chartBackendCurrentQueue.Copy(), +} + +var ( + chartBackendCurrentSessions = module.Chart{ + ID: "backend_current_sessions", + Title: "Current number of active sessions", + Units: "sessions", + Fam: "backend sessions", + Ctx: "haproxy.backend_current_sessions", + } + chartBackendSessions = module.Chart{ + ID: "backend_sessions", + Title: "Sessions rate", + Units: "sessions/s", + Fam: "backend sessions", + Ctx: "haproxy.backend_sessions", + } +) + +var ( + chartBackendResponseTimeAverage = module.Chart{ + ID: "backend_response_time_average", + Title: "Average response time for last 1024 successful connections", + Units: "milliseconds", + Fam: "backend responses", + Ctx: "haproxy.backend_response_time_average", + } + chartTemplateBackendHTTPResponses = module.Chart{ + ID: "backend_http_responses_proxy_%s", + Title: "HTTP responses by code class for <code>%s</code> proxy", + Units: "responses/s", + Fam: "backend responses", + Ctx: "haproxy.backend_http_responses", + Type: module.Stacked, + Dims: module.Dims{ + {ID: "haproxy_backend_http_responses_1xx_proxy_%s", Name: "1xx", Algo: module.Incremental}, + {ID: "haproxy_backend_http_responses_2xx_proxy_%s", Name: "2xx", Algo: module.Incremental}, + {ID: "haproxy_backend_http_responses_3xx_proxy_%s", Name: "3xx", Algo: module.Incremental}, + {ID: "haproxy_backend_http_responses_4xx_proxy_%s", Name: "4xx", Algo: module.Incremental}, + {ID: "haproxy_backend_http_responses_5xx_proxy_%s", Name: "5xx", Algo: module.Incremental}, + {ID: "haproxy_backend_http_responses_other_proxy_%s", Name: "other", Algo: module.Incremental}, + }, + } +) + +var ( + chartBackendQueueTimeAverage = module.Chart{ + ID: "backend_queue_time_average", + Title: "Average queue time for last 1024 successful connections", + Units: "milliseconds", + Fam: "backend queue", + Ctx: "haproxy.backend_queue_time_average", + } + chartBackendCurrentQueue = module.Chart{ + ID: "backend_current_queue", + Title: "Current number of queued requests", + Units: "requests", + Fam: "backend queue", + Ctx: "haproxy.backend_current_queue", + } +) + +var ( + chartTemplateBackendNetworkIO = module.Chart{ + ID: "backend_network_io_proxy_%s", + Title: "Network traffic for <code>%s</code> proxy", + Units: "bytes/s", + Fam: "backend network", + Ctx: "haproxy.backend_network_io", + Type: module.Area, + Dims: module.Dims{ + {ID: "haproxy_backend_bytes_in_proxy_%s", Name: "in", Algo: module.Incremental}, + {ID: "haproxy_backend_bytes_out_proxy_%s", Name: "out", Algo: module.Incremental, Mul: -1}, + }, + } +) + +func newChartBackendHTTPResponses(proxy string) *module.Chart { + return newBackendChartFromTemplate(chartTemplateBackendHTTPResponses, proxy) +} + +func newChartBackendNetworkIO(proxy string) *module.Chart { + return newBackendChartFromTemplate(chartTemplateBackendNetworkIO, proxy) +} + +func newBackendChartFromTemplate(tpl module.Chart, proxy string) *module.Chart { + c := tpl.Copy() + c.ID = fmt.Sprintf(c.ID, proxy) + c.Title = fmt.Sprintf(c.Title, proxy) + for _, d := range c.Dims { + d.ID = fmt.Sprintf(d.ID, proxy) + } + return c +} diff --git a/src/go/collectors/go.d.plugin/modules/haproxy/collect.go b/src/go/collectors/go.d.plugin/modules/haproxy/collect.go new file mode 100644 index 000000000..203ff1ec4 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/haproxy/collect.go @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package haproxy + +import ( + "errors" + "strings" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/prometheus" +) + +const ( + metricBackendSessionsTotal = "haproxy_backend_sessions_total" + metricBackendCurrentSessions = "haproxy_backend_current_sessions" + metricBackendHTTPResponsesTotal = "haproxy_backend_http_responses_total" + metricBackendResponseTimeAverageSeconds = "haproxy_backend_response_time_average_seconds" + metricBackendCurrentQueue = "haproxy_backend_current_queue" + metricBackendQueueTimeAverageSeconds = "haproxy_backend_queue_time_average_seconds" + metricBackendBytesInTotal = "haproxy_backend_bytes_in_total" + metricBackendBytesOutTotal = "haproxy_backend_bytes_out_total" +) + +func isHaproxyMetrics(pms prometheus.Series) bool { + for _, pm := range pms { + if strings.HasPrefix(pm.Name(), "haproxy_") { + return true + } + } + return false +} + +func (h *Haproxy) collect() (map[string]int64, error) { + pms, err := h.prom.ScrapeSeries() + if err != nil { + return nil, err + } + + if h.validateMetrics && !isHaproxyMetrics(pms) { + return nil, errors.New("unexpected metrics (not HAProxy)") + } + h.validateMetrics = false + + mx := make(map[string]int64) + for _, pm := range pms { + proxy := pm.Labels.Get("proxy") + if proxy == "" { + continue + } + + if !h.proxies[proxy] { + h.proxies[proxy] = true + h.addProxyToCharts(proxy) + } + + mx[dimID(pm)] = int64(pm.Value * multiplier(pm)) + } + + return mx, nil +} + +func (h *Haproxy) addProxyToCharts(proxy string) { + h.addDimToChart(chartBackendCurrentSessions.ID, &module.Dim{ + ID: proxyDimID(metricBackendCurrentSessions, proxy), + Name: proxy, + }) + h.addDimToChart(chartBackendSessions.ID, &module.Dim{ + ID: proxyDimID(metricBackendSessionsTotal, proxy), + Name: proxy, + Algo: module.Incremental, + }) + + h.addDimToChart(chartBackendResponseTimeAverage.ID, &module.Dim{ + ID: proxyDimID(metricBackendResponseTimeAverageSeconds, proxy), + Name: proxy, + }) + if err := h.Charts().Add(newChartBackendHTTPResponses(proxy)); err != nil { + h.Warning(err) + } + + h.addDimToChart(chartBackendCurrentQueue.ID, &module.Dim{ + ID: proxyDimID(metricBackendCurrentQueue, proxy), + Name: proxy, + }) + h.addDimToChart(chartBackendQueueTimeAverage.ID, &module.Dim{ + ID: proxyDimID(metricBackendQueueTimeAverageSeconds, proxy), + Name: proxy, + }) + + if err := h.Charts().Add(newChartBackendNetworkIO(proxy)); err != nil { + h.Warning(err) + } +} + +func (h *Haproxy) addDimToChart(chartID string, dim *module.Dim) { + chart := h.Charts().Get(chartID) + if chart == nil { + h.Warningf("error on adding '%s' dimension: can not find '%s' chart", dim.ID, chartID) + return + } + if err := chart.AddDim(dim); err != nil { + h.Warning(err) + return + } + chart.MarkNotCreated() +} + +func multiplier(pm prometheus.SeriesSample) float64 { + switch pm.Name() { + case metricBackendResponseTimeAverageSeconds, + metricBackendQueueTimeAverageSeconds: + // to milliseconds + return 1000 + } + return 1 +} + +func dimID(pm prometheus.SeriesSample) string { + proxy := pm.Labels.Get("proxy") + if proxy == "" { + return "" + } + + name := cleanMetricName(pm.Name()) + if pm.Name() == metricBackendHTTPResponsesTotal { + name += "_" + pm.Labels.Get("code") + } + return proxyDimID(name, proxy) +} + +func proxyDimID(metric, proxy string) string { + return cleanMetricName(metric) + "_proxy_" + proxy +} + +func cleanMetricName(name string) string { + if strings.HasSuffix(name, "_total") { + return name[:len(name)-6] + } + if strings.HasSuffix(name, "_seconds") { + return name[:len(name)-8] + } + return name +} diff --git a/src/go/collectors/go.d.plugin/modules/haproxy/config_schema.json b/src/go/collectors/go.d.plugin/modules/haproxy/config_schema.json new file mode 100644 index 000000000..a5d43236c --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/haproxy/config_schema.json @@ -0,0 +1,177 @@ +{ + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "HAProxy collector configuration.", + "type": "object", + "properties": { + "update_every": { + "title": "Update every", + "description": "Data collection interval, measured in seconds.", + "type": "integer", + "minimum": 1, + "default": 1 + }, + "url": { + "title": "URL", + "description": "The URL of the HAProxy [Prometheus endpoint](https://www.haproxy.com/documentation/haproxy-configuration-tutorials/alerts-and-monitoring/prometheus/).", + "type": "string", + "default": "http://127.0.0.1:8404/metrics", + "format": "uri" + }, + "timeout": { + "title": "Timeout", + "description": "The timeout in seconds for the HTTP request.", + "type": "number", + "minimum": 0.5, + "default": 1 + }, + "not_follow_redirects": { + "title": "Not follow redirects", + "description": "If set, the client will not follow HTTP redirects automatically.", + "type": "boolean" + }, + "username": { + "title": "Username", + "description": "The username for basic authentication.", + "type": "string", + "sensitive": true + }, + "password": { + "title": "Password", + "description": "The password for basic authentication.", + "type": "string", + "sensitive": true + }, + "proxy_url": { + "title": "Proxy URL", + "description": "The URL of the proxy server.", + "type": "string" + }, + "proxy_username": { + "title": "Proxy username", + "description": "The username for proxy authentication.", + "type": "string", + "sensitive": true + }, + "proxy_password": { + "title": "Proxy password", + "description": "The password for proxy authentication.", + "type": "string", + "sensitive": true + }, + "headers": { + "title": "Headers", + "description": "Additional HTTP headers to include in the request.", + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "string" + } + }, + "tls_skip_verify": { + "title": "Skip TLS verification", + "description": "If set, TLS certificate verification will be skipped.", + "type": "boolean" + }, + "tls_ca": { + "title": "TLS CA", + "description": "The path to the CA certificate file for TLS verification.", + "type": "string", + "pattern": "^$|^/" + }, + "tls_cert": { + "title": "TLS certificate", + "description": "The path to the client certificate file for TLS authentication.", + "type": "string", + "pattern": "^$|^/" + }, + "tls_key": { + "title": "TLS key", + "description": "The path to the client key file for TLS authentication.", + "type": "string", + "pattern": "^$|^/" + }, + "body": { + "title": "Body", + "type": "string" + }, + "method": { + "title": "Method", + "type": "string" + } + }, + "required": [ + "url" + ], + "additionalProperties": false, + "patternProperties": { + "^name$": {} + } + }, + "uiSchema": { + "ui:flavour": "tabs", + "ui:options": { + "tabs": [ + { + "title": "Base", + "fields": [ + "update_every", + "url", + "timeout", + "not_follow_redirects" + ] + }, + { + "title": "Auth", + "fields": [ + "username", + "password" + ] + }, + { + "title": "TLS", + "fields": [ + "tls_skip_verify", + "tls_ca", + "tls_cert", + "tls_key" + ] + }, + { + "title": "Proxy", + "fields": [ + "proxy_url", + "proxy_username", + "proxy_password" + ] + }, + { + "title": "Headers", + "fields": [ + "headers" + ] + } + ] + }, + "uiOptions": { + "fullPage": true + }, + "body": { + "ui:widget": "hidden" + }, + "method": { + "ui:widget": "hidden" + }, + "timeout": { + "ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)." + }, + "password": { + "ui:widget": "password" + }, + "proxy_password": { + "ui:widget": "password" + } + } +} diff --git a/src/go/collectors/go.d.plugin/modules/haproxy/haproxy.go b/src/go/collectors/go.d.plugin/modules/haproxy/haproxy.go new file mode 100644 index 000000000..217eb3c22 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/haproxy/haproxy.go @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package haproxy + +import ( + _ "embed" + "errors" + "time" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/prometheus" + "github.com/netdata/netdata/go/go.d.plugin/pkg/web" +) + +//go:embed "config_schema.json" +var configSchema string + +func init() { + module.Register("haproxy", module.Creator{ + JobConfigSchema: configSchema, + Create: func() module.Module { return New() }, + Config: func() any { return &Config{} }, + }) +} + +func New() *Haproxy { + return &Haproxy{ + Config: Config{ + HTTP: web.HTTP{ + Request: web.Request{ + URL: "http://127.0.0.1:8404/metrics", + }, + Client: web.Client{ + Timeout: web.Duration(time.Second), + }, + }, + }, + + charts: charts.Copy(), + proxies: make(map[string]bool), + validateMetrics: true, + } +} + +type Config struct { + web.HTTP `yaml:",inline" json:""` + UpdateEvery int `yaml:"update_every" json:"update_every"` +} + +type Haproxy struct { + module.Base + Config `yaml:",inline" json:""` + + charts *module.Charts + + prom prometheus.Prometheus + + validateMetrics bool + proxies map[string]bool +} + +func (h *Haproxy) Configuration() any { + return h.Config +} + +func (h *Haproxy) Init() error { + if err := h.validateConfig(); err != nil { + h.Errorf("config validation: %v", err) + return err + } + + prom, err := h.initPrometheusClient() + if err != nil { + h.Errorf("prometheus client initialization: %v", err) + return err + } + h.prom = prom + + return nil +} + +func (h *Haproxy) Check() error { + mx, err := h.collect() + if err != nil { + h.Error(err) + return err + } + if len(mx) == 0 { + return errors.New("no metrics collected") + } + return nil +} + +func (h *Haproxy) Charts() *module.Charts { + return h.charts +} + +func (h *Haproxy) Collect() map[string]int64 { + mx, err := h.collect() + if err != nil { + h.Error(err) + return nil + } + + if len(mx) == 0 { + return nil + } + return mx +} + +func (h *Haproxy) Cleanup() { + if h.prom != nil && h.prom.HTTPClient() != nil { + h.prom.HTTPClient().CloseIdleConnections() + } +} diff --git a/src/go/collectors/go.d.plugin/modules/haproxy/haproxy_test.go b/src/go/collectors/go.d.plugin/modules/haproxy/haproxy_test.go new file mode 100644 index 000000000..3566d17e4 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/haproxy/haproxy_test.go @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package haproxy + +import ( + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/tlscfg" + "github.com/netdata/netdata/go/go.d.plugin/pkg/web" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + dataConfigJSON, _ = os.ReadFile("testdata/config.json") + dataConfigYAML, _ = os.ReadFile("testdata/config.yaml") + + dataVer2310Metrics, _ = os.ReadFile("testdata/v2.3.10/metrics.txt") +) + +func Test_testDataIsValid(t *testing.T) { + for name, data := range map[string][]byte{ + "dataConfigJSON": dataConfigJSON, + "dataConfigYAML": dataConfigYAML, + "dataVer2310Metrics": dataVer2310Metrics, + } { + require.NotNil(t, data, name) + } +} + +func TestHaproxy_ConfigurationSerialize(t *testing.T) { + module.TestConfigurationSerialize(t, &Haproxy{}, dataConfigJSON, dataConfigYAML) +} + +func TestHaproxy_Init(t *testing.T) { + tests := map[string]struct { + config Config + wantFail bool + }{ + "success on default config": { + config: New().Config, + }, + "fails on unset 'url'": { + wantFail: true, + config: Config{HTTP: web.HTTP{ + Request: web.Request{}, + }}, + }, + "fails on invalid TLSCA": { + wantFail: true, + config: Config{ + HTTP: web.HTTP{ + Client: web.Client{ + TLSConfig: tlscfg.TLSConfig{TLSCA: "testdata/tls"}, + }, + }}, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + rdb := New() + rdb.Config = test.config + + if test.wantFail { + assert.Error(t, rdb.Init()) + } else { + assert.NoError(t, rdb.Init()) + } + }) + } +} + +func TestHaproxy_Charts(t *testing.T) { + assert.NotNil(t, New().Charts()) +} + +func TestHaproxy_Cleanup(t *testing.T) { + assert.NotPanics(t, New().Cleanup) +} + +func TestHaproxy_Check(t *testing.T) { + tests := map[string]struct { + wantFail bool + prepare func(t *testing.T) (h *Haproxy, cleanup func()) + }{ + "success on valid response v2.3.1": { + wantFail: false, + prepare: prepareCaseHaproxyV231Metrics, + }, + "fails on response with unexpected metrics (not HAProxy)": { + wantFail: true, + prepare: prepareCaseNotHaproxyMetrics, + }, + "fails on 404 response": { + wantFail: true, + prepare: prepareCase404Response, + }, + "fails on connection refused": { + wantFail: true, + prepare: prepareCaseConnectionRefused, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + h, cleanup := test.prepare(t) + defer cleanup() + + if test.wantFail { + assert.Error(t, h.Check()) + } else { + assert.NoError(t, h.Check()) + } + }) + } +} + +func TestHaproxy_Collect(t *testing.T) { + tests := map[string]struct { + prepare func(t *testing.T) (h *Haproxy, cleanup func()) + wantCollected map[string]int64 + }{ + "success on valid response v2.3.1": { + prepare: prepareCaseHaproxyV231Metrics, + wantCollected: map[string]int64{ + "haproxy_backend_bytes_in_proxy_proxy1": 21057046294, + "haproxy_backend_bytes_in_proxy_proxy2": 2493759083896, + "haproxy_backend_bytes_out_proxy_proxy1": 41352782609, + "haproxy_backend_bytes_out_proxy_proxy2": 5131407558, + "haproxy_backend_current_queue_proxy_proxy1": 1, + "haproxy_backend_current_queue_proxy_proxy2": 1, + "haproxy_backend_current_sessions_proxy_proxy1": 1, + "haproxy_backend_current_sessions_proxy_proxy2": 1322, + "haproxy_backend_http_responses_1xx_proxy_proxy1": 1, + "haproxy_backend_http_responses_1xx_proxy_proxy2": 4130401, + "haproxy_backend_http_responses_2xx_proxy_proxy1": 21338013, + "haproxy_backend_http_responses_2xx_proxy_proxy2": 1, + "haproxy_backend_http_responses_3xx_proxy_proxy1": 10004, + "haproxy_backend_http_responses_3xx_proxy_proxy2": 1, + "haproxy_backend_http_responses_4xx_proxy_proxy1": 10170758, + "haproxy_backend_http_responses_4xx_proxy_proxy2": 1, + "haproxy_backend_http_responses_5xx_proxy_proxy1": 3075, + "haproxy_backend_http_responses_5xx_proxy_proxy2": 1, + "haproxy_backend_http_responses_other_proxy_proxy1": 5657, + "haproxy_backend_http_responses_other_proxy_proxy2": 1, + "haproxy_backend_queue_time_average_proxy_proxy1": 0, + "haproxy_backend_queue_time_average_proxy_proxy2": 0, + "haproxy_backend_response_time_average_proxy_proxy1": 52, + "haproxy_backend_response_time_average_proxy_proxy2": 1, + "haproxy_backend_sessions_proxy_proxy1": 31527507, + "haproxy_backend_sessions_proxy_proxy2": 4131723, + }, + }, + "fails on response with unexpected metrics (not HAProxy)": { + prepare: prepareCaseNotHaproxyMetrics, + }, + "fails on 404 response": { + prepare: prepareCase404Response, + }, + "fails on connection refused": { + prepare: prepareCaseConnectionRefused, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + h, cleanup := test.prepare(t) + defer cleanup() + + ms := h.Collect() + + assert.Equal(t, test.wantCollected, ms) + if len(test.wantCollected) > 0 { + ensureCollectedHasAllChartsDimsVarsIDs(t, h, ms) + } + }) + } +} + +func prepareCaseHaproxyV231Metrics(t *testing.T) (*Haproxy, func()) { + t.Helper() + srv := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write(dataVer2310Metrics) + })) + h := New() + h.URL = srv.URL + require.NoError(t, h.Init()) + + return h, srv.Close +} + +func prepareCaseNotHaproxyMetrics(t *testing.T) (*Haproxy, func()) { + t.Helper() + srv := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte(` +# HELP haproxy_backend_http_responses_total Total number of HTTP responses. +# TYPE haproxy_backend_http_responses_total counter +application_backend_http_responses_total{proxy="infra-traefik-web",code="1xx"} 0 +application_backend_http_responses_total{proxy="infra-vernemq-ws",code="1xx"} 4130401 +application_backend_http_responses_total{proxy="infra-traefik-web",code="2xx"} 21338013 +application_backend_http_responses_total{proxy="infra-vernemq-ws",code="2xx"} 0 +application_backend_http_responses_total{proxy="infra-traefik-web",code="3xx"} 10004 +application_backend_http_responses_total{proxy="infra-vernemq-ws",code="3xx"} 0 +application_backend_http_responses_total{proxy="infra-traefik-web",code="4xx"} 10170758 +application_backend_http_responses_total{proxy="infra-vernemq-ws",code="4xx"} 0 +application_backend_http_responses_total{proxy="infra-traefik-web",code="5xx"} 3075 +application_backend_http_responses_total{proxy="infra-vernemq-ws",code="5xx"} 0 +application_backend_http_responses_total{proxy="infra-traefik-web",code="other"} 5657 +application_backend_http_responses_total{proxy="infra-vernemq-ws",code="other"} 0 +`)) + })) + h := New() + h.URL = srv.URL + require.NoError(t, h.Init()) + + return h, srv.Close +} + +func prepareCase404Response(t *testing.T) (*Haproxy, func()) { + t.Helper() + srv := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + })) + h := New() + h.URL = srv.URL + require.NoError(t, h.Init()) + + return h, srv.Close +} + +func prepareCaseConnectionRefused(t *testing.T) (*Haproxy, func()) { + t.Helper() + h := New() + h.URL = "http://127.0.0.1:38001" + require.NoError(t, h.Init()) + + return h, func() {} +} + +func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, h *Haproxy, ms map[string]int64) { + for _, chart := range *h.Charts() { + if chart.Obsolete { + continue + } + for _, dim := range chart.Dims { + _, ok := ms[dim.ID] + assert.Truef(t, ok, "chart '%s' dim '%s': no dim in collected", dim.ID, chart.ID) + } + for _, v := range chart.Vars { + _, ok := ms[v.ID] + assert.Truef(t, ok, "chart '%s' dim '%s': no dim in collected", v.ID, chart.ID) + } + } +} diff --git a/src/go/collectors/go.d.plugin/modules/haproxy/init.go b/src/go/collectors/go.d.plugin/modules/haproxy/init.go new file mode 100644 index 000000000..55c669000 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/haproxy/init.go @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package haproxy + +import ( + "errors" + + "github.com/netdata/netdata/go/go.d.plugin/pkg/prometheus" + "github.com/netdata/netdata/go/go.d.plugin/pkg/prometheus/selector" + "github.com/netdata/netdata/go/go.d.plugin/pkg/web" +) + +func (h *Haproxy) validateConfig() error { + if h.URL == "" { + return errors.New("'url' is not set") + } + if _, err := web.NewHTTPRequest(h.Request); err != nil { + return err + } + return nil +} + +func (h *Haproxy) initPrometheusClient() (prometheus.Prometheus, error) { + httpClient, err := web.NewHTTPClient(h.Client) + if err != nil { + return nil, err + } + + prom := prometheus.NewWithSelector(httpClient, h.Request, sr) + return prom, nil +} + +var sr, _ = selector.Expr{ + Allow: []string{ + metricBackendHTTPResponsesTotal, + metricBackendCurrentQueue, + metricBackendQueueTimeAverageSeconds, + metricBackendBytesInTotal, + metricBackendResponseTimeAverageSeconds, + metricBackendSessionsTotal, + metricBackendCurrentSessions, + metricBackendBytesOutTotal, + }, +}.Parse() diff --git a/src/go/collectors/go.d.plugin/modules/haproxy/integrations/haproxy.md b/src/go/collectors/go.d.plugin/modules/haproxy/integrations/haproxy.md new file mode 100644 index 000000000..af6d3c9be --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/haproxy/integrations/haproxy.md @@ -0,0 +1,241 @@ +<!--startmeta +custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/haproxy/README.md" +meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/haproxy/metadata.yaml" +sidebar_label: "HAProxy" +learn_status: "Published" +learn_rel_path: "Collecting Metrics/Web Servers and Web Proxies" +most_popular: False +message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE" +endmeta--> + +# HAProxy + + +<img src="https://netdata.cloud/img/haproxy.svg" width="150"/> + + +Plugin: go.d.plugin +Module: haproxy + +<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" /> + +## Overview + +This collector monitors HAProxy servers. + + + + +This collector is supported on all platforms. + +This collector supports collecting metrics from multiple instances of this integration, including remote instances. + + +### Default Behavior + +#### Auto-Detection + +This integration doesn't support auto-detection. + +#### Limits + +The default configuration for this integration does not impose any limits on data collection. + +#### Performance Impact + +The default configuration for this integration is not expected to impose a significant performance impact on the system. + + +## Metrics + +Metrics grouped by *scope*. + +The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels. + + + +### Per HAProxy instance + +These metrics refer to the entire monitored application. + +This scope has no labels. + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| haproxy.backend_current_sessions | a dimension per proxy | sessions | +| haproxy.backend_sessions | a dimension per proxy | sessions/s | +| haproxy.backend_response_time_average | a dimension per proxy | milliseconds | +| haproxy.backend_queue_time_average | a dimension per proxy | milliseconds | +| haproxy.backend_current_queue | a dimension per proxy | requests | + +### Per proxy + +These metrics refer to the Proxy. + +This scope has no labels. + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| haproxy.backend_http_responses | 1xx, 2xx, 3xx, 4xx, 5xx, other | responses/s | +| haproxy.backend_network_io | in, out | bytes/s | + + + +## Alerts + +There are no alerts configured by default for this integration. + + +## Setup + +### Prerequisites + +#### Enable PROMEX addon. + +To enable PROMEX addon, follow the [official documentation](https://github.com/haproxy/haproxy/tree/master/addons/promex). + + + +### Configuration + +#### File + +The configuration file name for this integration is `go.d/haproxy.conf`. + + +You can edit the configuration file using the `edit-config` script from the +Netdata [config directory](/docs/netdata-agent/configuration/README.md#the-netdata-config-directory). + +```bash +cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata +sudo ./edit-config go.d/haproxy.conf +``` +#### Options + +The following options can be defined globally: update_every, autodetection_retry. + + +<details open><summary>Config options</summary> + +| Name | Description | Default | Required | +|:----|:-----------|:-------|:--------:| +| update_every | Data collection frequency. | 1 | no | +| autodetection_retry | Recheck interval in seconds. Zero means no recheck will be scheduled. | 0 | no | +| url | Server URL. | http://127.0.0.1 | yes | +| timeout | HTTP request timeout. | 1 | no | +| username | Username for basic HTTP authentication. | | no | +| password | Password for basic HTTP authentication. | | no | +| proxy_url | Proxy URL. | | no | +| proxy_username | Username for proxy basic HTTP authentication. | | no | +| proxy_password | Password for proxy basic HTTP authentication. | | no | +| method | HTTP request method. | GET | no | +| body | HTTP request body. | | no | +| headers | HTTP request headers. | | no | +| not_follow_redirects | Redirect handling policy. Controls whether the client follows redirects. | no | no | +| tls_skip_verify | Server certificate chain and hostname validation policy. Controls whether the client performs this check. | no | no | +| tls_ca | Certification authority that the client uses when verifying the server's certificates. | | no | +| tls_cert | Client TLS certificate. | | no | +| tls_key | Client TLS key. | | no | + +</details> + +#### Examples + +##### Basic + +A basic example configuration. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + url: http://127.0.0.1:8404/metrics + +``` +</details> + +##### HTTP authentication + +Basic HTTP authentication. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + url: http://127.0.0.1:8404/metrics + username: username + password: password + +``` +</details> + +##### HTTPS with self-signed certificate + +NGINX Plus with enabled HTTPS and self-signed certificate. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + url: https://127.0.0.1:8404/metrics + tls_skip_verify: yes + +``` +</details> + +##### Multi-instance + +> **Note**: When you define multiple jobs, their names must be unique. + +Collecting metrics from local and remote instances. + + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + url: http://127.0.0.1:8404/metrics + + - name: remote + url: http://192.0.2.1:8404/metrics + +``` +</details> + + + +## Troubleshooting + +### Debug Mode + +To troubleshoot issues with the `haproxy` collector, run the `go.d.plugin` with the debug option enabled. The output +should give you clues as to why the collector isn't working. + +- Navigate to the `plugins.d` directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on + your system, open `netdata.conf` and look for the `plugins` setting under `[directories]`. + + ```bash + cd /usr/libexec/netdata/plugins.d/ + ``` + +- Switch to the `netdata` user. + + ```bash + sudo -u netdata -s + ``` + +- Run the `go.d.plugin` to debug the collector: + + ```bash + ./go.d.plugin -d -m haproxy + ``` + + diff --git a/src/go/collectors/go.d.plugin/modules/haproxy/metadata.yaml b/src/go/collectors/go.d.plugin/modules/haproxy/metadata.yaml new file mode 100644 index 000000000..adc879602 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/haproxy/metadata.yaml @@ -0,0 +1,231 @@ +plugin_name: go.d.plugin +modules: + - meta: + id: collector-go.d.plugin-haproxy + plugin_name: go.d.plugin + module_name: haproxy + monitored_instance: + name: HAProxy + link: https://www.haproxy.org/ + icon_filename: haproxy.svg + categories: + - data-collection.web-servers-and-web-proxies + keywords: + - haproxy + - web + - webserver + - http + - proxy + related_resources: + integrations: + list: [] + info_provided_to_referring_integrations: + description: "" + most_popular: false + overview: + data_collection: + metrics_description: | + This collector monitors HAProxy servers. + method_description: "" + supported_platforms: + include: [] + exclude: [] + multi_instance: true + additional_permissions: + description: "" + default_behavior: + auto_detection: + description: "" + limits: + description: "" + performance_impact: + description: "" + setup: + prerequisites: + list: + - title: Enable PROMEX addon. + description: | + To enable PROMEX addon, follow the [official documentation](https://github.com/haproxy/haproxy/tree/master/addons/promex). + configuration: + file: + name: go.d/haproxy.conf + options: + description: | + The following options can be defined globally: update_every, autodetection_retry. + folding: + title: Config options + enabled: true + list: + - name: update_every + description: Data collection frequency. + default_value: 1 + required: false + - name: autodetection_retry + description: Recheck interval in seconds. Zero means no recheck will be scheduled. + default_value: 0 + required: false + - name: url + description: Server URL. + default_value: http://127.0.0.1 + required: true + - name: timeout + description: HTTP request timeout. + default_value: 1 + required: false + - name: username + description: Username for basic HTTP authentication. + default_value: "" + required: false + - name: password + description: Password for basic HTTP authentication. + default_value: "" + required: false + - name: proxy_url + description: Proxy URL. + default_value: "" + required: false + - name: proxy_username + description: Username for proxy basic HTTP authentication. + default_value: "" + required: false + - name: proxy_password + description: Password for proxy basic HTTP authentication. + default_value: "" + required: false + - name: method + description: HTTP request method. + default_value: GET + required: false + - name: body + description: HTTP request body. + default_value: "" + required: false + - name: headers + description: HTTP request headers. + default_value: "" + required: false + - name: not_follow_redirects + description: Redirect handling policy. Controls whether the client follows redirects. + default_value: false + required: false + - name: tls_skip_verify + description: Server certificate chain and hostname validation policy. Controls whether the client performs this check. + default_value: false + required: false + - name: tls_ca + description: Certification authority that the client uses when verifying the server's certificates. + default_value: "" + required: false + - name: tls_cert + description: Client TLS certificate. + default_value: "" + required: false + - name: tls_key + description: Client TLS key. + default_value: "" + required: false + examples: + folding: + title: Config + enabled: true + list: + - name: Basic + description: A basic example configuration. + config: | + jobs: + - name: local + url: http://127.0.0.1:8404/metrics + - name: HTTP authentication + description: Basic HTTP authentication. + config: | + jobs: + - name: local + url: http://127.0.0.1:8404/metrics + username: username + password: password + - name: HTTPS with self-signed certificate + description: NGINX Plus with enabled HTTPS and self-signed certificate. + config: | + jobs: + - name: local + url: https://127.0.0.1:8404/metrics + tls_skip_verify: yes + - name: Multi-instance + description: | + > **Note**: When you define multiple jobs, their names must be unique. + + Collecting metrics from local and remote instances. + config: | + jobs: + - name: local + url: http://127.0.0.1:8404/metrics + + - name: remote + url: http://192.0.2.1:8404/metrics + troubleshooting: + problems: + list: [] + alerts: [] + metrics: + folding: + title: Metrics + enabled: false + description: "" + availability: [] + scopes: + - name: global + description: These metrics refer to the entire monitored application. + labels: [] + metrics: + - name: haproxy.backend_current_sessions + description: Current number of active sessions + unit: sessions + chart_type: line + dimensions: + - name: a dimension per proxy + - name: haproxy.backend_sessions + description: Sessions rate + unit: sessions/s + chart_type: line + dimensions: + - name: a dimension per proxy + - name: haproxy.backend_response_time_average + description: Average response time for last 1024 successful connections + unit: milliseconds + chart_type: line + dimensions: + - name: a dimension per proxy + - name: haproxy.backend_queue_time_average + description: Average queue time for last 1024 successful connections + unit: milliseconds + chart_type: line + dimensions: + - name: a dimension per proxy + - name: haproxy.backend_current_queue + description: Current number of queued requests + unit: requests + chart_type: line + dimensions: + - name: a dimension per proxy + - name: proxy + description: These metrics refer to the Proxy. + labels: [] + metrics: + - name: haproxy.backend_http_responses + description: HTTP responses by code class + unit: responses/s + chart_type: stacked + dimensions: + - name: 1xx + - name: 2xx + - name: 3xx + - name: 4xx + - name: 5xx + - name: other + - name: haproxy.backend_network_io + description: Network traffic + unit: bytes/s + chart_type: area + dimensions: + - name: in + - name: out diff --git a/src/go/collectors/go.d.plugin/modules/haproxy/testdata/config.json b/src/go/collectors/go.d.plugin/modules/haproxy/testdata/config.json new file mode 100644 index 000000000..984c3ed6e --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/haproxy/testdata/config.json @@ -0,0 +1,20 @@ +{ + "update_every": 123, + "url": "ok", + "body": "ok", + "method": "ok", + "headers": { + "ok": "ok" + }, + "username": "ok", + "password": "ok", + "proxy_url": "ok", + "proxy_username": "ok", + "proxy_password": "ok", + "timeout": 123.123, + "not_follow_redirects": true, + "tls_ca": "ok", + "tls_cert": "ok", + "tls_key": "ok", + "tls_skip_verify": true +} diff --git a/src/go/collectors/go.d.plugin/modules/haproxy/testdata/config.yaml b/src/go/collectors/go.d.plugin/modules/haproxy/testdata/config.yaml new file mode 100644 index 000000000..8558b61cc --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/haproxy/testdata/config.yaml @@ -0,0 +1,17 @@ +update_every: 123 +url: "ok" +body: "ok" +method: "ok" +headers: + ok: "ok" +username: "ok" +password: "ok" +proxy_url: "ok" +proxy_username: "ok" +proxy_password: "ok" +timeout: 123.123 +not_follow_redirects: yes +tls_ca: "ok" +tls_cert: "ok" +tls_key: "ok" +tls_skip_verify: yes diff --git a/src/go/collectors/go.d.plugin/modules/haproxy/testdata/v2.3.10/metrics.txt b/src/go/collectors/go.d.plugin/modules/haproxy/testdata/v2.3.10/metrics.txt new file mode 100644 index 000000000..a156485d9 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/haproxy/testdata/v2.3.10/metrics.txt @@ -0,0 +1,382 @@ +# HELP haproxy_frontend_status Current status of the service (frontend: 0=STOP, 1=UP - backend: 0=DOWN, 1=UP - server: 0=DOWN, 1=UP, 2=MAINT, 3=DRAIN, 4=NOLB). +# TYPE haproxy_frontend_status gauge +haproxy_frontend_status{proxy="healthz"} 1 +haproxy_frontend_status{proxy="http"} 1 +haproxy_frontend_status{proxy="https"} 1 +haproxy_frontend_status{proxy="stats"} 1 +# HELP haproxy_frontend_current_sessions Current number of active sessions. +# TYPE haproxy_frontend_current_sessions gauge +haproxy_frontend_current_sessions{proxy="healthz"} 1 +haproxy_frontend_current_sessions{proxy="http"} 1 +haproxy_frontend_current_sessions{proxy="https"} 1348 +haproxy_frontend_current_sessions{proxy="stats"} 2 +# HELP haproxy_frontend_max_sessions Maximum observed number of active sessions. +# TYPE haproxy_frontend_max_sessions gauge +haproxy_frontend_max_sessions{proxy="healthz"} 10 +haproxy_frontend_max_sessions{proxy="http"} 5 +haproxy_frontend_max_sessions{proxy="https"} 1389 +haproxy_frontend_max_sessions{proxy="stats"} 8 +# HELP haproxy_frontend_limit_sessions Configured session limit. +# TYPE haproxy_frontend_limit_sessions gauge +haproxy_frontend_limit_sessions{proxy="healthz"} 524181 +haproxy_frontend_limit_sessions{proxy="http"} 524181 +haproxy_frontend_limit_sessions{proxy="https"} 524181 +haproxy_frontend_limit_sessions{proxy="stats"} 524181 +# HELP haproxy_frontend_sessions_total Total number of sessions. +# TYPE haproxy_frontend_sessions_total counter +haproxy_frontend_sessions_total{proxy="healthz"} 723971 +haproxy_frontend_sessions_total{proxy="http"} 1392 +haproxy_frontend_sessions_total{proxy="https"} 23433914 +haproxy_frontend_sessions_total{proxy="stats"} 4207 +# HELP haproxy_frontend_limit_session_rate Configured limit on new sessions per second. +# TYPE haproxy_frontend_limit_session_rate gauge +haproxy_frontend_limit_session_rate{proxy="healthz"} 1 +haproxy_frontend_limit_session_rate{proxy="http"} 1 +haproxy_frontend_limit_session_rate{proxy="https"} 1 +haproxy_frontend_limit_session_rate{proxy="stats"} 1 +# HELP haproxy_frontend_max_session_rate Maximum observed number of sessions per second. +# TYPE haproxy_frontend_max_session_rate gauge +haproxy_frontend_max_session_rate{proxy="healthz"} 1 +haproxy_frontend_max_session_rate{proxy="http"} 12 +haproxy_frontend_max_session_rate{proxy="https"} 96 +haproxy_frontend_max_session_rate{proxy="stats"} 2 +# HELP haproxy_frontend_connections_rate_max Maximum observed number of connections per second. +# TYPE haproxy_frontend_connections_rate_max gauge +haproxy_frontend_connections_rate_max{proxy="healthz"} 1 +haproxy_frontend_connections_rate_max{proxy="http"} 12 +haproxy_frontend_connections_rate_max{proxy="https"} 85 +haproxy_frontend_connections_rate_max{proxy="stats"} 2 +# HELP haproxy_frontend_connections_total Total number of connections. +# TYPE haproxy_frontend_connections_total counter +haproxy_frontend_connections_total{proxy="healthz"} 723971 +haproxy_frontend_connections_total{proxy="http"} 1392 +haproxy_frontend_connections_total{proxy="https"} 23476808 +haproxy_frontend_connections_total{proxy="stats"} 4207 +# HELP haproxy_frontend_bytes_in_total Current total of incoming bytes. +# TYPE haproxy_frontend_bytes_in_total counter +haproxy_frontend_bytes_in_total{proxy="healthz"} 79636810 +haproxy_frontend_bytes_in_total{proxy="http"} 73990 +haproxy_frontend_bytes_in_total{proxy="https"} 2514816135823 +haproxy_frontend_bytes_in_total{proxy="stats"} 14694474 +# HELP haproxy_frontend_bytes_out_total Current total of outgoing bytes. +# TYPE haproxy_frontend_bytes_out_total counter +haproxy_frontend_bytes_out_total{proxy="healthz"} 112215505 +haproxy_frontend_bytes_out_total{proxy="http"} 260431 +haproxy_frontend_bytes_out_total{proxy="https"} 46485344378 +haproxy_frontend_bytes_out_total{proxy="stats"} 23646727611 +# HELP haproxy_frontend_requests_denied_total Total number of denied requests. +# TYPE haproxy_frontend_requests_denied_total counter +haproxy_frontend_requests_denied_total{proxy="healthz"} 1 +haproxy_frontend_requests_denied_total{proxy="http"} 1 +haproxy_frontend_requests_denied_total{proxy="https"} 1 +haproxy_frontend_requests_denied_total{proxy="stats"} 1 +# HELP haproxy_frontend_responses_denied_total Total number of denied responses. +# TYPE haproxy_frontend_responses_denied_total counter +haproxy_frontend_responses_denied_total{proxy="healthz"} 1 +haproxy_frontend_responses_denied_total{proxy="http"} 1 +haproxy_frontend_responses_denied_total{proxy="https"} 1 +haproxy_frontend_responses_denied_total{proxy="stats"} 1 +# HELP haproxy_frontend_request_errors_total Total number of request errors. +# TYPE haproxy_frontend_request_errors_total counter +haproxy_frontend_request_errors_total{proxy="healthz"} 1 +haproxy_frontend_request_errors_total{proxy="http"} 1107 +haproxy_frontend_request_errors_total{proxy="https"} 5922 +haproxy_frontend_request_errors_total{proxy="stats"} 12 +# HELP haproxy_frontend_denied_connections_total Total number of requests denied by "tcp-request connection" rules. +# TYPE haproxy_frontend_denied_connections_total counter +haproxy_frontend_denied_connections_total{proxy="healthz"} 1 +haproxy_frontend_denied_connections_total{proxy="http"} 1 +haproxy_frontend_denied_connections_total{proxy="https"} 1 +haproxy_frontend_denied_connections_total{proxy="stats"} 1 +# HELP haproxy_frontend_denied_sessions_total Total number of requests denied by "tcp-request session" rules. +# TYPE haproxy_frontend_denied_sessions_total counter +haproxy_frontend_denied_sessions_total{proxy="healthz"} 1 +haproxy_frontend_denied_sessions_total{proxy="http"} 1 +haproxy_frontend_denied_sessions_total{proxy="https"} 1 +haproxy_frontend_denied_sessions_total{proxy="stats"} 1 +# HELP haproxy_frontend_failed_header_rewriting_total Total number of failed header rewriting warnings. +# TYPE haproxy_frontend_failed_header_rewriting_total counter +haproxy_frontend_failed_header_rewriting_total{proxy="healthz"} 1 +haproxy_frontend_failed_header_rewriting_total{proxy="http"} 1 +haproxy_frontend_failed_header_rewriting_total{proxy="https"} 1 +haproxy_frontend_failed_header_rewriting_total{proxy="stats"} 1 +# HELP haproxy_frontend_internal_errors_total Total number of internal errors. +# TYPE haproxy_frontend_internal_errors_total counter +haproxy_frontend_internal_errors_total{proxy="healthz"} 1 +haproxy_frontend_internal_errors_total{proxy="http"} 1 +haproxy_frontend_internal_errors_total{proxy="https"} 1 +haproxy_frontend_internal_errors_total{proxy="stats"} 1 +# HELP haproxy_frontend_http_requests_rate_max Maximum observed number of HTTP requests per second. +# TYPE haproxy_frontend_http_requests_rate_max gauge +haproxy_frontend_http_requests_rate_max{proxy="healthz"} 1 +haproxy_frontend_http_requests_rate_max{proxy="http"} 12 +haproxy_frontend_http_requests_rate_max{proxy="https"} 101 +haproxy_frontend_http_requests_rate_max{proxy="stats"} 2 +# HELP haproxy_frontend_http_requests_total Total number of HTTP requests received. +# TYPE haproxy_frontend_http_requests_total counter +haproxy_frontend_http_requests_total{proxy="healthz"} 723971 +haproxy_frontend_http_requests_total{proxy="http"} 1402 +haproxy_frontend_http_requests_total{proxy="https"} 35664484 +haproxy_frontend_http_requests_total{proxy="stats"} 60011 +# HELP haproxy_frontend_http_responses_total Total number of HTTP responses. +# TYPE haproxy_frontend_http_responses_total counter +haproxy_frontend_http_responses_total{proxy="healthz",code="1xx"} 1 +haproxy_frontend_http_responses_total{proxy="http",code="1xx"} 1 +haproxy_frontend_http_responses_total{proxy="https",code="1xx"} 4130401 +haproxy_frontend_http_responses_total{proxy="stats",code="1xx"} 1 +haproxy_frontend_http_responses_total{proxy="healthz",code="2xx"} 723971 +haproxy_frontend_http_responses_total{proxy="http",code="2xx"} 1 +haproxy_frontend_http_responses_total{proxy="https",code="2xx"} 21338013 +haproxy_frontend_http_responses_total{proxy="stats",code="2xx"} 59998 +haproxy_frontend_http_responses_total{proxy="healthz",code="3xx"} 1 +haproxy_frontend_http_responses_total{proxy="http",code="3xx"} 147 +haproxy_frontend_http_responses_total{proxy="https",code="3xx"} 10004 +haproxy_frontend_http_responses_total{proxy="stats",code="3xx"} 1 +haproxy_frontend_http_responses_total{proxy="healthz",code="4xx"} 1 +haproxy_frontend_http_responses_total{proxy="http",code="4xx"} 1107 +haproxy_frontend_http_responses_total{proxy="https",code="4xx"} 10175979 +haproxy_frontend_http_responses_total{proxy="stats",code="4xx"} 12 +haproxy_frontend_http_responses_total{proxy="healthz",code="5xx"} 1 +haproxy_frontend_http_responses_total{proxy="http",code="5xx"} 148 +haproxy_frontend_http_responses_total{proxy="https",code="5xx"} 3108 +haproxy_frontend_http_responses_total{proxy="stats",code="5xx"} 1 +haproxy_frontend_http_responses_total{proxy="healthz",code="other"} 1 +haproxy_frontend_http_responses_total{proxy="http",code="other"} 1 +haproxy_frontend_http_responses_total{proxy="https",code="other"} 5657 +haproxy_frontend_http_responses_total{proxy="stats",code="other"} 1 +# HELP haproxy_frontend_intercepted_requests_total Total number of intercepted HTTP requests. +# TYPE haproxy_frontend_intercepted_requests_total counter +haproxy_frontend_intercepted_requests_total{proxy="healthz"} 723971 +haproxy_frontend_intercepted_requests_total{proxy="http"} 147 +haproxy_frontend_intercepted_requests_total{proxy="https"} 1 +haproxy_frontend_intercepted_requests_total{proxy="stats"} 59999 +# HELP haproxy_frontend_http_cache_lookups_total Total number of HTTP cache lookups. +# TYPE haproxy_frontend_http_cache_lookups_total counter +haproxy_frontend_http_cache_lookups_total{proxy="healthz"} 1 +haproxy_frontend_http_cache_lookups_total{proxy="http"} 1 +haproxy_frontend_http_cache_lookups_total{proxy="https"} 1 +haproxy_frontend_http_cache_lookups_total{proxy="stats"} 1 +# HELP haproxy_frontend_http_cache_hits_total Total number of HTTP cache hits. +# TYPE haproxy_frontend_http_cache_hits_total counter +haproxy_frontend_http_cache_hits_total{proxy="healthz"} 1 +haproxy_frontend_http_cache_hits_total{proxy="http"} 1 +haproxy_frontend_http_cache_hits_total{proxy="https"} 1 +haproxy_frontend_http_cache_hits_total{proxy="stats"} 1 +# HELP haproxy_frontend_http_comp_bytes_in_total Total number of HTTP response bytes fed to the compressor. +# TYPE haproxy_frontend_http_comp_bytes_in_total counter +haproxy_frontend_http_comp_bytes_in_total{proxy="healthz"} 1 +haproxy_frontend_http_comp_bytes_in_total{proxy="http"} 1 +haproxy_frontend_http_comp_bytes_in_total{proxy="https"} 1 +haproxy_frontend_http_comp_bytes_in_total{proxy="stats"} 1 +# HELP haproxy_frontend_http_comp_bytes_out_total Total number of HTTP response bytes emitted by the compressor. +# TYPE haproxy_frontend_http_comp_bytes_out_total counter +haproxy_frontend_http_comp_bytes_out_total{proxy="healthz"} 1 +haproxy_frontend_http_comp_bytes_out_total{proxy="http"} 1 +haproxy_frontend_http_comp_bytes_out_total{proxy="https"} 1 +haproxy_frontend_http_comp_bytes_out_total{proxy="stats"} 1 +# HELP haproxy_frontend_http_comp_bytes_bypassed_total Total number of bytes that bypassed the HTTP compressor (CPU/BW limit). +# TYPE haproxy_frontend_http_comp_bytes_bypassed_total counter +haproxy_frontend_http_comp_bytes_bypassed_total{proxy="healthz"} 1 +haproxy_frontend_http_comp_bytes_bypassed_total{proxy="http"} 1 +haproxy_frontend_http_comp_bytes_bypassed_total{proxy="https"} 1 +haproxy_frontend_http_comp_bytes_bypassed_total{proxy="stats"} 1 +# HELP haproxy_frontend_http_comp_responses_total Total number of HTTP responses that were compressed. +# TYPE haproxy_frontend_http_comp_responses_total counter +haproxy_frontend_http_comp_responses_total{proxy="healthz"} 1 +haproxy_frontend_http_comp_responses_total{proxy="http"} 1 +haproxy_frontend_http_comp_responses_total{proxy="https"} 1 +haproxy_frontend_http_comp_responses_total{proxy="stats"} 1 +# HELP haproxy_backend_status Current status of the service (frontend: 0=STOP, 1=UP - backend: 0=DOWN, 1=UP - server: 0=DOWN, 1=UP, 2=MAINT, 3=DRAIN, 4=NOLB). +# TYPE haproxy_backend_status gauge +haproxy_backend_status{proxy="proxy1"} 1 +haproxy_backend_status{proxy="proxy2"} 1 +# HELP haproxy_backend_current_sessions Current number of active sessions. +# TYPE haproxy_backend_current_sessions gauge +haproxy_backend_current_sessions{proxy="proxy1"} 1 +haproxy_backend_current_sessions{proxy="proxy2"} 1322 +# HELP haproxy_backend_max_sessions Maximum observed number of active sessions. +# TYPE haproxy_backend_max_sessions gauge +haproxy_backend_max_sessions{proxy="proxy1"} 112 +haproxy_backend_max_sessions{proxy="proxy2"} 1367 +# HELP haproxy_backend_limit_sessions Configured session limit. +# TYPE haproxy_backend_limit_sessions gauge +haproxy_backend_limit_sessions{proxy="proxy1"} 1 +haproxy_backend_limit_sessions{proxy="proxy2"} 1 +# HELP haproxy_backend_sessions_total Total number of sessions. +# TYPE haproxy_backend_sessions_total counter +haproxy_backend_sessions_total{proxy="proxy1"} 31527507 +haproxy_backend_sessions_total{proxy="proxy2"} 4131723 +# HELP haproxy_backend_max_session_rate Maximum observed number of sessions per second. +# TYPE haproxy_backend_max_session_rate gauge +haproxy_backend_max_session_rate{proxy="proxy1"} 82 +haproxy_backend_max_session_rate{proxy="proxy2"} 41 +# HELP haproxy_backend_last_session_seconds Number of seconds since last session assigned to server/backend. +# TYPE haproxy_backend_last_session_seconds gauge +haproxy_backend_last_session_seconds{proxy="proxy1"} 1 +haproxy_backend_last_session_seconds{proxy="proxy2"} 3 +# HELP haproxy_backend_current_queue Current number of queued requests. +# TYPE haproxy_backend_current_queue gauge +haproxy_backend_current_queue{proxy="proxy1"} 1 +haproxy_backend_current_queue{proxy="proxy2"} 1 +# HELP haproxy_backend_max_queue Maximum observed number of queued requests. +# TYPE haproxy_backend_max_queue gauge +haproxy_backend_max_queue{proxy="proxy1"} 1 +haproxy_backend_max_queue{proxy="proxy2"} 1 +# HELP haproxy_backend_connection_attempts_total Total number of connection establishment attempts. +# TYPE haproxy_backend_connection_attempts_total counter +haproxy_backend_connection_attempts_total{proxy="proxy1"} 19864884 +haproxy_backend_connection_attempts_total{proxy="proxy2"} 4131723 +# HELP haproxy_backend_connection_reuses_total Total number of connection reuses. +# TYPE haproxy_backend_connection_reuses_total counter +haproxy_backend_connection_reuses_total{proxy="proxy1"} 11661922 +haproxy_backend_connection_reuses_total{proxy="proxy2"} 1 +# HELP haproxy_backend_bytes_in_total Current total of incoming bytes. +# TYPE haproxy_backend_bytes_in_total counter +haproxy_backend_bytes_in_total{proxy="proxy1"} 21057046294 +haproxy_backend_bytes_in_total{proxy="proxy2"} 2493759083896 +# HELP haproxy_backend_bytes_out_total Current total of outgoing bytes. +# TYPE haproxy_backend_bytes_out_total counter +haproxy_backend_bytes_out_total{proxy="proxy1"} 41352782609 +haproxy_backend_bytes_out_total{proxy="proxy2"} 5131407558 +# HELP haproxy_backend_queue_time_average_seconds Avg. queue time for last 1024 successful connections. +# TYPE haproxy_backend_queue_time_average_seconds gauge +haproxy_backend_queue_time_average_seconds{proxy="proxy1"} 0.000000 +haproxy_backend_queue_time_average_seconds{proxy="proxy2"} 0.000000 +# HELP haproxy_backend_connect_time_average_seconds Avg. connect time for last 1024 successful connections. +# TYPE haproxy_backend_connect_time_average_seconds gauge +haproxy_backend_connect_time_average_seconds{proxy="proxy1"} 0.000000 +haproxy_backend_connect_time_average_seconds{proxy="proxy2"} 0.001000 +# HELP haproxy_backend_response_time_average_seconds Avg. response time for last 1024 successful connections. +# TYPE haproxy_backend_response_time_average_seconds gauge +haproxy_backend_response_time_average_seconds{proxy="proxy1"} 0.052000 +haproxy_backend_response_time_average_seconds{proxy="proxy2"} 0.001000 +# HELP haproxy_backend_total_time_average_seconds Avg. total time for last 1024 successful connections. +# TYPE haproxy_backend_total_time_average_seconds gauge +haproxy_backend_total_time_average_seconds{proxy="proxy1"} 1.746000 +haproxy_backend_total_time_average_seconds{proxy="proxy2"} 198.639000 +# HELP haproxy_backend_max_queue_time_seconds Maximum observed time spent in the queue +# TYPE haproxy_backend_max_queue_time_seconds gauge +haproxy_backend_max_queue_time_seconds{proxy="proxy1"} 0.000000 +haproxy_backend_max_queue_time_seconds{proxy="proxy2"} 0.000000 +# HELP haproxy_backend_max_connect_time_seconds Maximum observed time spent waiting for a connection to complete +# TYPE haproxy_backend_max_connect_time_seconds gauge +haproxy_backend_max_connect_time_seconds{proxy="proxy1"} 1.063000 +haproxy_backend_max_connect_time_seconds{proxy="proxy2"} 1.061000 +# HELP haproxy_backend_max_response_time_seconds Maximum observed time spent waiting for a server response +# TYPE haproxy_backend_max_response_time_seconds gauge +haproxy_backend_max_response_time_seconds{proxy="proxy1"} 74.050000 +haproxy_backend_max_response_time_seconds{proxy="proxy2"} 1.396000 +# HELP haproxy_backend_max_total_time_seconds Maximum observed total request+response time (request+queue+connect+response+processing) +# TYPE haproxy_backend_max_total_time_seconds gauge +haproxy_backend_max_total_time_seconds{proxy="proxy1"} 331.297000 +haproxy_backend_max_total_time_seconds{proxy="proxy2"} 3116820.243000 +# HELP haproxy_backend_requests_denied_total Total number of denied requests. +# TYPE haproxy_backend_requests_denied_total counter +haproxy_backend_requests_denied_total{proxy="proxy1"} 1 +haproxy_backend_requests_denied_total{proxy="proxy2"} 1 +# HELP haproxy_backend_responses_denied_total Total number of denied responses. +# TYPE haproxy_backend_responses_denied_total counter +haproxy_backend_responses_denied_total{proxy="proxy1"} 1 +haproxy_backend_responses_denied_total{proxy="proxy2"} 1 +# HELP haproxy_backend_connection_errors_total Total number of connection errors. +# TYPE haproxy_backend_connection_errors_total counter +haproxy_backend_connection_errors_total{proxy="proxy1"} 1 +haproxy_backend_connection_errors_total{proxy="proxy2"} 1 +# HELP haproxy_backend_response_errors_total Total number of response errors. +# TYPE haproxy_backend_response_errors_total counter +haproxy_backend_response_errors_total{proxy="proxy1"} 13 +haproxy_backend_response_errors_total{proxy="proxy2"} 4122625 +# HELP haproxy_backend_retry_warnings_total Total number of retry warnings. +# TYPE haproxy_backend_retry_warnings_total counter +haproxy_backend_retry_warnings_total{proxy="proxy1"} 1 +haproxy_backend_retry_warnings_total{proxy="proxy2"} 1 +# HELP haproxy_backend_redispatch_warnings_total Total number of redispatch warnings. +# TYPE haproxy_backend_redispatch_warnings_total counter +haproxy_backend_redispatch_warnings_total{proxy="proxy1"} 1 +haproxy_backend_redispatch_warnings_total{proxy="proxy2"} 1 +# HELP haproxy_backend_failed_header_rewriting_total Total number of failed header rewriting warnings. +# TYPE haproxy_backend_failed_header_rewriting_total counter +haproxy_backend_failed_header_rewriting_total{proxy="proxy1"} 1 +haproxy_backend_failed_header_rewriting_total{proxy="proxy2"} 1 +# HELP haproxy_backend_internal_errors_total Total number of internal errors. +# TYPE haproxy_backend_internal_errors_total counter +haproxy_backend_internal_errors_total{proxy="proxy1"} 1 +haproxy_backend_internal_errors_total{proxy="proxy2"} 1 +# HELP haproxy_backend_client_aborts_total Total number of data transfers aborted by the client. +# TYPE haproxy_backend_client_aborts_total counter +haproxy_backend_client_aborts_total{proxy="proxy1"} 27231 +haproxy_backend_client_aborts_total{proxy="proxy2"} 7777 +# HELP haproxy_backend_server_aborts_total Total number of data transfers aborted by the server. +# TYPE haproxy_backend_server_aborts_total counter +haproxy_backend_server_aborts_total{proxy="proxy1"} 1 +haproxy_backend_server_aborts_total{proxy="proxy2"} 4122625 +# HELP haproxy_backend_weight Service weight. +# TYPE haproxy_backend_weight gauge +haproxy_backend_weight{proxy="proxy1"} 256 +haproxy_backend_weight{proxy="proxy2"} 640 +# HELP haproxy_backend_active_servers Current number of active servers. +# TYPE haproxy_backend_active_servers gauge +haproxy_backend_active_servers{proxy="proxy1"} 2 +haproxy_backend_active_servers{proxy="proxy2"} 5 +# HELP haproxy_backend_backup_servers Current number of backup servers. +# TYPE haproxy_backend_backup_servers gauge +haproxy_backend_backup_servers{proxy="proxy1"} 1 +haproxy_backend_backup_servers{proxy="proxy2"} 1 +# HELP haproxy_backend_check_up_down_total Total number of UP->DOWN transitions. +# TYPE haproxy_backend_check_up_down_total counter +haproxy_backend_check_up_down_total{proxy="proxy1"} 1 +haproxy_backend_check_up_down_total{proxy="proxy2"} 1 +# HELP haproxy_backend_check_last_change_seconds Number of seconds since the last UP<->DOWN transition. +# TYPE haproxy_backend_check_last_change_seconds gauge +haproxy_backend_check_last_change_seconds{proxy="proxy1"} 3619864 +haproxy_backend_check_last_change_seconds{proxy="proxy2"} 3619864 +# HELP haproxy_backend_downtime_seconds_total Total downtime (in seconds) for the service. +# TYPE haproxy_backend_downtime_seconds_total counter +haproxy_backend_downtime_seconds_total{proxy="proxy1"} 1 +haproxy_backend_downtime_seconds_total{proxy="proxy2"} 1 +# HELP haproxy_backend_loadbalanced_total Total number of times a service was selected, either for new sessions, or when redispatching. +# TYPE haproxy_backend_loadbalanced_total counter +haproxy_backend_loadbalanced_total{proxy="proxy1"} 31526806 +haproxy_backend_loadbalanced_total{proxy="proxy2"} 4131723 +# HELP haproxy_backend_http_requests_total Total number of HTTP requests received. +# TYPE haproxy_backend_http_requests_total counter +haproxy_backend_http_requests_total{proxy="proxy1"} 31527507 +haproxy_backend_http_requests_total{proxy="proxy2"} 4130401 +# HELP haproxy_backend_http_responses_total Total number of HTTP responses. +# TYPE haproxy_backend_http_responses_total counter +haproxy_backend_http_responses_total{proxy="proxy1",code="1xx"} 1 +haproxy_backend_http_responses_total{proxy="proxy2",code="1xx"} 4130401 +haproxy_backend_http_responses_total{proxy="proxy1",code="2xx"} 21338013 +haproxy_backend_http_responses_total{proxy="proxy2",code="2xx"} 1 +haproxy_backend_http_responses_total{proxy="proxy1",code="3xx"} 10004 +haproxy_backend_http_responses_total{proxy="proxy2",code="3xx"} 1 +haproxy_backend_http_responses_total{proxy="proxy1",code="4xx"} 10170758 +haproxy_backend_http_responses_total{proxy="proxy2",code="4xx"} 1 +haproxy_backend_http_responses_total{proxy="proxy1",code="5xx"} 3075 +haproxy_backend_http_responses_total{proxy="proxy2",code="5xx"} 1 +haproxy_backend_http_responses_total{proxy="proxy1",code="other"} 5657 +haproxy_backend_http_responses_total{proxy="proxy2",code="other"} 1 +# HELP haproxy_backend_http_cache_lookups_total Total number of HTTP cache lookups. +# TYPE haproxy_backend_http_cache_lookups_total counter +haproxy_backend_http_cache_lookups_total{proxy="proxy1"} 1 +haproxy_backend_http_cache_lookups_total{proxy="proxy2"} 1 +# HELP haproxy_backend_http_cache_hits_total Total number of HTTP cache hits. +# TYPE haproxy_backend_http_cache_hits_total counter +haproxy_backend_http_cache_hits_total{proxy="proxy1"} 1 +haproxy_backend_http_cache_hits_total{proxy="proxy2"} 1 +# HELP haproxy_backend_http_comp_bytes_in_total Total number of HTTP response bytes fed to the compressor. +# TYPE haproxy_backend_http_comp_bytes_in_total counter +haproxy_backend_http_comp_bytes_in_total{proxy="proxy1"} 1 +haproxy_backend_http_comp_bytes_in_total{proxy="proxy2"} 1 +# HELP haproxy_backend_http_comp_bytes_out_total Total number of HTTP response bytes emitted by the compressor. +# TYPE haproxy_backend_http_comp_bytes_out_total counter +haproxy_backend_http_comp_bytes_out_total{proxy="proxy1"} 1 +haproxy_backend_http_comp_bytes_out_total{proxy="proxy2"} 1 +# HELP haproxy_backend_http_comp_bytes_bypassed_total Total number of bytes that bypassed the HTTP compressor (CPU/BW limit). +# TYPE haproxy_backend_http_comp_bytes_bypassed_total counter +haproxy_backend_http_comp_bytes_bypassed_total{proxy="proxy1"} 1 +haproxy_backend_http_comp_bytes_bypassed_total{proxy="proxy2"} 1 +# HELP haproxy_backend_http_comp_responses_total Total number of HTTP responses that were compressed. +# TYPE haproxy_backend_http_comp_responses_total counter +haproxy_backend_http_comp_responses_total{proxy="proxy1"} 1 +haproxy_backend_http_comp_responses_total{proxy="proxy2"} 1
\ No newline at end of file |