diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-26 08:15:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-26 08:15:35 +0000 |
commit | f09848204fa5283d21ea43e262ee41aa578e1808 (patch) | |
tree | c62385d7adf209fa6a798635954d887f718fb3fb /src/go/plugin/go.d/modules/apache | |
parent | Releasing debian version 1.46.3-2. (diff) | |
download | netdata-f09848204fa5283d21ea43e262ee41aa578e1808.tar.xz netdata-f09848204fa5283d21ea43e262ee41aa578e1808.zip |
Merging upstream version 1.47.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/go/plugin/go.d/modules/apache')
17 files changed, 2085 insertions, 0 deletions
diff --git a/src/go/plugin/go.d/modules/apache/README.md b/src/go/plugin/go.d/modules/apache/README.md new file mode 120000 index 000000000..066ee4162 --- /dev/null +++ b/src/go/plugin/go.d/modules/apache/README.md @@ -0,0 +1 @@ +integrations/apache.md
\ No newline at end of file diff --git a/src/go/plugin/go.d/modules/apache/apache.go b/src/go/plugin/go.d/modules/apache/apache.go new file mode 100644 index 000000000..d0869353d --- /dev/null +++ b/src/go/plugin/go.d/modules/apache/apache.go @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package apache + +import ( + _ "embed" + "errors" + "net/http" + "sync" + "time" + + "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module" + "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web" +) + +//go:embed "config_schema.json" +var configSchema string + +func init() { + module.Register("apache", module.Creator{ + Create: func() module.Module { return New() }, + JobConfigSchema: configSchema, + Config: func() any { return &Config{} }, + }) +} + +func New() *Apache { + return &Apache{ + Config: Config{ + HTTP: web.HTTP{ + Request: web.Request{ + URL: "http://127.0.0.1/server-status?auto", + }, + Client: web.Client{ + Timeout: web.Duration(time.Second), + }, + }, + }, + charts: &module.Charts{}, + once: &sync.Once{}, + } +} + +type Config struct { + UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"` + web.HTTP `yaml:",inline" json:""` +} + +type Apache struct { + module.Base + Config `yaml:",inline" json:""` + + charts *module.Charts + + httpClient *http.Client + + once *sync.Once +} + +func (a *Apache) Configuration() any { + return a.Config +} + +func (a *Apache) Init() error { + if err := a.validateConfig(); err != nil { + a.Errorf("config validation: %v", err) + return err + } + + httpClient, err := a.initHTTPClient() + if err != nil { + a.Errorf("init HTTP client: %v", err) + return err + } + a.httpClient = httpClient + + a.Debugf("using URL %s", a.URL) + a.Debugf("using timeout: %s", a.Timeout) + + return nil +} + +func (a *Apache) Check() error { + mx, err := a.collect() + if err != nil { + a.Error(err) + return err + } + if len(mx) == 0 { + return errors.New("no metrics collected") + + } + return nil +} + +func (a *Apache) Charts() *module.Charts { + return a.charts +} + +func (a *Apache) Collect() map[string]int64 { + mx, err := a.collect() + if err != nil { + a.Error(err) + } + + if len(mx) == 0 { + return nil + } + return mx +} + +func (a *Apache) Cleanup() { + if a.httpClient != nil { + a.httpClient.CloseIdleConnections() + } +} diff --git a/src/go/plugin/go.d/modules/apache/apache_test.go b/src/go/plugin/go.d/modules/apache/apache_test.go new file mode 100644 index 000000000..64fa6ed96 --- /dev/null +++ b/src/go/plugin/go.d/modules/apache/apache_test.go @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package apache + +import ( + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module" + "github.com/netdata/netdata/go/plugins/plugin/go.d/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") + + dataSimpleStatusMPMEvent, _ = os.ReadFile("testdata/simple-status-mpm-event.txt") + dataExtendedStatusMPMEvent, _ = os.ReadFile("testdata/extended-status-mpm-event.txt") + dataExtendedStatusMPMPrefork, _ = os.ReadFile("testdata/extended-status-mpm-prefork.txt") + dataLighttpdStatus, _ = os.ReadFile("testdata/lighttpd-status.txt") +) + +func Test_testDataIsValid(t *testing.T) { + for name, data := range map[string][]byte{ + "dataConfigJSON": dataConfigJSON, + "dataConfigYAML": dataConfigYAML, + "dataSimpleStatusMPMEvent": dataSimpleStatusMPMEvent, + "dataExtendedStatusMPMEvent": dataExtendedStatusMPMEvent, + "dataExtendedStatusMPMPrefork": dataExtendedStatusMPMPrefork, + "dataLighttpdStatus": dataLighttpdStatus, + } { + require.NotNil(t, data, name) + + } +} + +func TestApache_ConfigurationSerialize(t *testing.T) { + module.TestConfigurationSerialize(t, &Apache{}, dataConfigJSON, dataConfigYAML) +} + +func TestApache_Init(t *testing.T) { + tests := map[string]struct { + wantFail bool + config Config + }{ + "success with default": { + wantFail: false, + config: New().Config, + }, + "fail when URL not set": { + wantFail: true, + config: Config{ + HTTP: web.HTTP{ + Request: web.Request{URL: ""}, + }, + }, + }, + "fail when URL has no wantMetrics suffix": { + wantFail: true, + config: Config{ + HTTP: web.HTTP{ + Request: web.Request{URL: "http://127.0.0.1:38001"}, + }, + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + apache := New() + apache.Config = test.config + + if test.wantFail { + assert.Error(t, apache.Init()) + } else { + assert.NoError(t, apache.Init()) + } + }) + } +} + +func TestApache_Check(t *testing.T) { + tests := map[string]struct { + wantFail bool + prepare func(t *testing.T) (apache *Apache, cleanup func()) + }{ + "success on simple status MPM Event": { + wantFail: false, + prepare: caseMPMEventSimpleStatus, + }, + "success on extended status MPM Event": { + wantFail: false, + prepare: caseMPMEventExtendedStatus, + }, + "success on extended status MPM Prefork": { + wantFail: false, + prepare: caseMPMPreforkExtendedStatus, + }, + "fail on Lighttpd response": { + wantFail: true, + prepare: caseLighttpdResponse, + }, + "fail on invalid data response": { + wantFail: true, + prepare: caseInvalidDataResponse, + }, + "fail on connection refused": { + wantFail: true, + prepare: caseConnectionRefused, + }, + "fail on 404 response": { + wantFail: true, + prepare: case404, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + apache, cleanup := test.prepare(t) + defer cleanup() + + if test.wantFail { + assert.Error(t, apache.Check()) + } else { + assert.NoError(t, apache.Check()) + } + }) + } +} + +func TestApache_Charts(t *testing.T) { + assert.NotNil(t, New().Charts()) +} + +func TestApache_Collect(t *testing.T) { + tests := map[string]struct { + prepare func(t *testing.T) (apache *Apache, cleanup func()) + wantNumOfCharts int + wantMetrics map[string]int64 + }{ + "success on simple status MPM Event": { + prepare: caseMPMEventSimpleStatus, + wantNumOfCharts: len(baseCharts), + wantMetrics: map[string]int64{ + "busy_workers": 1, + "conns_async_closing": 0, + "conns_async_keep_alive": 0, + "conns_async_writing": 0, + "conns_total": 0, + "idle_workers": 74, + "scoreboard_closing": 0, + "scoreboard_dns_lookup": 0, + "scoreboard_finishing": 0, + "scoreboard_idle_cleanup": 0, + "scoreboard_keepalive": 0, + "scoreboard_logging": 0, + "scoreboard_open": 325, + "scoreboard_reading": 0, + "scoreboard_sending": 1, + "scoreboard_starting": 0, + "scoreboard_waiting": 74, + }, + }, + "success on extended status MPM Event": { + prepare: caseMPMEventExtendedStatus, + wantNumOfCharts: len(baseCharts) + len(extendedCharts), + wantMetrics: map[string]int64{ + "busy_workers": 1, + "bytes_per_req": 136533000, + "bytes_per_sec": 4800000, + "conns_async_closing": 0, + "conns_async_keep_alive": 0, + "conns_async_writing": 0, + "conns_total": 0, + "idle_workers": 99, + "req_per_sec": 3515, + "scoreboard_closing": 0, + "scoreboard_dns_lookup": 0, + "scoreboard_finishing": 0, + "scoreboard_idle_cleanup": 0, + "scoreboard_keepalive": 0, + "scoreboard_logging": 0, + "scoreboard_open": 300, + "scoreboard_reading": 0, + "scoreboard_sending": 1, + "scoreboard_starting": 0, + "scoreboard_waiting": 99, + "total_accesses": 9, + "total_kBytes": 12, + "uptime": 256, + }, + }, + "success on extended status MPM Prefork": { + prepare: caseMPMPreforkExtendedStatus, + wantNumOfCharts: len(baseCharts) + len(extendedCharts) - 2, + wantMetrics: map[string]int64{ + "busy_workers": 70, + "bytes_per_req": 3617880000, + "bytes_per_sec": 614250000000, + "idle_workers": 1037, + "req_per_sec": 16978100, + "scoreboard_closing": 0, + "scoreboard_dns_lookup": 0, + "scoreboard_finishing": 0, + "scoreboard_idle_cleanup": 0, + "scoreboard_keepalive": 0, + "scoreboard_logging": 0, + "scoreboard_open": 3, + "scoreboard_reading": 0, + "scoreboard_sending": 0, + "scoreboard_starting": 0, + "scoreboard_waiting": 3, + "total_accesses": 120358784, + "total_kBytes": 4252382776, + "uptime": 708904, + }, + }, + "fail on Lighttpd response": { + prepare: caseLighttpdResponse, + wantNumOfCharts: 0, + wantMetrics: nil, + }, + "fail on invalid data response": { + prepare: caseInvalidDataResponse, + wantNumOfCharts: 0, + wantMetrics: nil, + }, + "fail on connection refused": { + prepare: caseConnectionRefused, + wantNumOfCharts: 0, + wantMetrics: nil, + }, + "fail on 404 response": { + prepare: case404, + wantNumOfCharts: 0, + wantMetrics: nil, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + apache, cleanup := test.prepare(t) + defer cleanup() + + _ = apache.Check() + + collected := apache.Collect() + + require.Equal(t, test.wantMetrics, collected) + assert.Equal(t, test.wantNumOfCharts, len(*apache.Charts())) + }) + } +} + +func caseMPMEventSimpleStatus(t *testing.T) (*Apache, func()) { + t.Helper() + srv := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write(dataSimpleStatusMPMEvent) + })) + apache := New() + apache.URL = srv.URL + "/server-status?auto" + require.NoError(t, apache.Init()) + + return apache, srv.Close +} + +func caseMPMEventExtendedStatus(t *testing.T) (*Apache, func()) { + t.Helper() + srv := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write(dataExtendedStatusMPMEvent) + })) + apache := New() + apache.URL = srv.URL + "/server-status?auto" + require.NoError(t, apache.Init()) + + return apache, srv.Close +} + +func caseMPMPreforkExtendedStatus(t *testing.T) (*Apache, func()) { + t.Helper() + srv := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write(dataExtendedStatusMPMPrefork) + })) + apache := New() + apache.URL = srv.URL + "/server-status?auto" + require.NoError(t, apache.Init()) + + return apache, srv.Close +} + +func caseLighttpdResponse(t *testing.T) (*Apache, func()) { + t.Helper() + srv := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write(dataLighttpdStatus) + })) + apache := New() + apache.URL = srv.URL + "/server-status?auto" + require.NoError(t, apache.Init()) + + return apache, srv.Close +} + +func caseInvalidDataResponse(t *testing.T) (*Apache, func()) { + t.Helper() + srv := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte("hello and\n goodbye")) + })) + apache := New() + apache.URL = srv.URL + "/server-status?auto" + require.NoError(t, apache.Init()) + + return apache, srv.Close +} + +func caseConnectionRefused(t *testing.T) (*Apache, func()) { + t.Helper() + apache := New() + apache.URL = "http://127.0.0.1:65001/server-status?auto" + require.NoError(t, apache.Init()) + + return apache, func() {} +} + +func case404(t *testing.T) (*Apache, func()) { + t.Helper() + srv := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + })) + apache := New() + apache.URL = srv.URL + "/server-status?auto" + require.NoError(t, apache.Init()) + + return apache, srv.Close +} diff --git a/src/go/plugin/go.d/modules/apache/charts.go b/src/go/plugin/go.d/modules/apache/charts.go new file mode 100644 index 000000000..ad83112d2 --- /dev/null +++ b/src/go/plugin/go.d/modules/apache/charts.go @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package apache + +import "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module" + +const ( + prioRequests = module.Priority + iota + prioConnection + prioConnsAsync + prioScoreboard + prioNet + prioWorkers + prioReqPerSec + prioBytesPerSec + prioBytesPerReq + prioUptime +) + +var baseCharts = module.Charts{ + chartConnections.Copy(), + chartConnsAsync.Copy(), + chartWorkers.Copy(), + chartScoreboard.Copy(), +} + +var extendedCharts = module.Charts{ + chartRequests.Copy(), + chartBandwidth.Copy(), + chartReqPerSec.Copy(), + chartBytesPerSec.Copy(), + chartBytesPerReq.Copy(), + chartUptime.Copy(), +} + +func newCharts(s *serverStatus) *module.Charts { + charts := baseCharts.Copy() + + // ServerMPM: prefork + if s.Connections.Total == nil { + _ = charts.Remove(chartConnections.ID) + } + if s.Connections.Async.KeepAlive == nil { + _ = charts.Remove(chartConnsAsync.ID) + } + + if s.Total.Accesses != nil { + _ = charts.Add(*extendedCharts.Copy()...) + } + + return charts +} + +// simple status +var ( + chartConnections = module.Chart{ + ID: "connections", + Title: "Connections", + Units: "connections", + Fam: "connections", + Ctx: "apache.connections", + Priority: prioConnection, + Dims: module.Dims{ + {ID: "conns_total", Name: "connections"}, + }, + } + chartConnsAsync = module.Chart{ + ID: "conns_async", + Title: "Async Connections", + Units: "connections", + Fam: "connections", + Ctx: "apache.conns_async", + Type: module.Stacked, + Priority: prioConnsAsync, + Dims: module.Dims{ + {ID: "conns_async_keep_alive", Name: "keepalive"}, + {ID: "conns_async_closing", Name: "closing"}, + {ID: "conns_async_writing", Name: "writing"}, + }, + } + chartWorkers = module.Chart{ + ID: "workers", + Title: "Workers Threads", + Units: "workers", + Fam: "workers", + Ctx: "apache.workers", + Type: module.Stacked, + Priority: prioWorkers, + Dims: module.Dims{ + {ID: "idle_workers", Name: "idle"}, + {ID: "busy_workers", Name: "busy"}, + }, + } + chartScoreboard = module.Chart{ + ID: "scoreboard", + Title: "Scoreboard", + Units: "connections", + Fam: "connections", + Ctx: "apache.scoreboard", + Priority: prioScoreboard, + Dims: module.Dims{ + {ID: "scoreboard_waiting", Name: "waiting"}, + {ID: "scoreboard_starting", Name: "starting"}, + {ID: "scoreboard_reading", Name: "reading"}, + {ID: "scoreboard_sending", Name: "sending"}, + {ID: "scoreboard_keepalive", Name: "keepalive"}, + {ID: "scoreboard_dns_lookup", Name: "dns_lookup"}, + {ID: "scoreboard_closing", Name: "closing"}, + {ID: "scoreboard_logging", Name: "logging"}, + {ID: "scoreboard_finishing", Name: "finishing"}, + {ID: "scoreboard_idle_cleanup", Name: "idle_cleanup"}, + {ID: "scoreboard_open", Name: "open"}, + }, + } +) + +// extended status +var ( + chartRequests = module.Chart{ + ID: "requests", + Title: "Requests", + Units: "requests/s", + Fam: "requests", + Ctx: "apache.requests", + Priority: prioRequests, + Dims: module.Dims{ + {ID: "total_accesses", Name: "requests", Algo: module.Incremental}, + }, + } + chartBandwidth = module.Chart{ + ID: "net", + Title: "Bandwidth", + Units: "kilobits/s", + Fam: "bandwidth", + Ctx: "apache.net", + Type: module.Area, + Priority: prioNet, + Dims: module.Dims{ + {ID: "total_kBytes", Name: "sent", Algo: module.Incremental, Mul: 8}, + }, + } + chartReqPerSec = module.Chart{ + ID: "reqpersec", + Title: "Lifetime Average Number Of Requests Per Second", + Units: "requests/s", + Fam: "statistics", + Ctx: "apache.reqpersec", + Type: module.Area, + Priority: prioReqPerSec, + Dims: module.Dims{ + {ID: "req_per_sec", Name: "requests", Div: 100000}, + }, + } + chartBytesPerSec = module.Chart{ + ID: "bytespersec", + Title: "Lifetime Average Number Of Bytes Served Per Second", + Units: "KiB/s", + Fam: "statistics", + Ctx: "apache.bytespersec", + Type: module.Area, + Priority: prioBytesPerSec, + Dims: module.Dims{ + {ID: "bytes_per_sec", Name: "served", Mul: 8, Div: 1024 * 100000}, + }, + } + chartBytesPerReq = module.Chart{ + ID: "bytesperreq", + Title: "Lifetime Average Response Size", + Units: "KiB", + Fam: "statistics", + Ctx: "apache.bytesperreq", + Type: module.Area, + Priority: prioBytesPerReq, + Dims: module.Dims{ + {ID: "bytes_per_req", Name: "size", Div: 1024 * 100000}, + }, + } + chartUptime = module.Chart{ + ID: "uptime", + Title: "Uptime", + Units: "seconds", + Fam: "availability", + Ctx: "apache.uptime", + Priority: prioUptime, + Dims: module.Dims{ + {ID: "uptime"}, + }, + } +) diff --git a/src/go/plugin/go.d/modules/apache/collect.go b/src/go/plugin/go.d/modules/apache/collect.go new file mode 100644 index 000000000..79de7722a --- /dev/null +++ b/src/go/plugin/go.d/modules/apache/collect.go @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package apache + +import ( + "bufio" + "fmt" + "io" + "net/http" + "strconv" + "strings" + + "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/stm" + "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web" +) + +func (a *Apache) collect() (map[string]int64, error) { + status, err := a.scrapeStatus() + if err != nil { + return nil, err + } + + mx := stm.ToMap(status) + if len(mx) == 0 { + return nil, fmt.Errorf("nothing was collected from %s", a.URL) + } + + a.once.Do(func() { a.charts = newCharts(status) }) + + return mx, nil +} + +func (a *Apache) scrapeStatus() (*serverStatus, error) { + req, err := web.NewHTTPRequest(a.Request) + if err != nil { + return nil, err + } + + resp, err := a.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("error on HTTP request '%s': %v", req.URL, err) + } + defer closeBody(resp) + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode) + } + + return parseResponse(resp.Body) +} + +func parseResponse(r io.Reader) (*serverStatus, error) { + s := bufio.NewScanner(r) + var status serverStatus + + for s.Scan() { + parts := strings.Split(s.Text(), ":") + if len(parts) != 2 { + continue + } + + key, value := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]) + + switch key { + default: + case "BusyServers", "IdleServers": + return nil, fmt.Errorf("found '%s', Lighttpd data", key) + case "BusyWorkers": + status.Workers.Busy = parseInt(value) + case "IdleWorkers": + status.Workers.Idle = parseInt(value) + case "ConnsTotal": + status.Connections.Total = parseInt(value) + case "ConnsAsyncWriting": + status.Connections.Async.Writing = parseInt(value) + case "ConnsAsyncKeepAlive": + status.Connections.Async.KeepAlive = parseInt(value) + case "ConnsAsyncClosing": + status.Connections.Async.Closing = parseInt(value) + case "Total Accesses": + status.Total.Accesses = parseInt(value) + case "Total kBytes": + status.Total.KBytes = parseInt(value) + case "Uptime": + status.Uptime = parseInt(value) + case "ReqPerSec": + status.Averages.ReqPerSec = parseFloat(value) + case "BytesPerSec": + status.Averages.BytesPerSec = parseFloat(value) + case "BytesPerReq": + status.Averages.BytesPerReq = parseFloat(value) + case "Scoreboard": + status.Scoreboard = parseScoreboard(value) + } + } + + return &status, nil +} + +func parseScoreboard(line string) *scoreboard { + // “_” Waiting for Connection + // “S” Starting up + // “R” Reading Request + // “W” Sending Reply + // “K” Keepalive (read) + // “D” DNS Lookup + // “C” Closing connection + // “L” Logging + // “G” Gracefully finishing + // “I” Idle cleanup of worker + // “.” Open slot with no current process + var sb scoreboard + for _, s := range strings.Split(line, "") { + switch s { + case "_": + sb.Waiting++ + case "S": + sb.Starting++ + case "R": + sb.Reading++ + case "W": + sb.Sending++ + case "K": + sb.KeepAlive++ + case "D": + sb.DNSLookup++ + case "C": + sb.Closing++ + case "L": + sb.Logging++ + case "G": + sb.Finishing++ + case "I": + sb.IdleCleanup++ + case ".": + sb.Open++ + } + } + return &sb +} + +func parseInt(value string) *int64 { + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return nil + } + return &v +} + +func parseFloat(value string) *float64 { + v, err := strconv.ParseFloat(value, 64) + if err != nil { + return nil + } + return &v +} + +func closeBody(resp *http.Response) { + if resp != nil && resp.Body != nil { + _, _ = io.Copy(io.Discard, resp.Body) + _ = resp.Body.Close() + } +} diff --git a/src/go/plugin/go.d/modules/apache/config_schema.json b/src/go/plugin/go.d/modules/apache/config_schema.json new file mode 100644 index 000000000..b92363e93 --- /dev/null +++ b/src/go/plugin/go.d/modules/apache/config_schema.json @@ -0,0 +1,183 @@ +{ + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Apache 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 Apache machine readable [status page](https://httpd.apache.org/docs/2.4/mod/mod_status.html).", + "type": "string", + "default": "http://127.0.0.1/server-status?auto", + "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": { + "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)." + }, + "username": { + "ui:widget": "password" + }, + "proxy_username": { + "ui:widget": "password" + }, + "password": { + "ui:widget": "password" + }, + "proxy_password": { + "ui:widget": "password" + }, + "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" + ] + } + ] + } + } +} diff --git a/src/go/plugin/go.d/modules/apache/init.go b/src/go/plugin/go.d/modules/apache/init.go new file mode 100644 index 000000000..e13186f01 --- /dev/null +++ b/src/go/plugin/go.d/modules/apache/init.go @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package apache + +import ( + "errors" + "net/http" + "strings" + + "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web" +) + +func (a *Apache) validateConfig() error { + if a.URL == "" { + return errors.New("url not set") + } + if !strings.HasSuffix(a.URL, "?auto") { + return errors.New("invalid URL, should ends in '?auto'") + } + return nil +} + +func (a *Apache) initHTTPClient() (*http.Client, error) { + return web.NewHTTPClient(a.Client) +} diff --git a/src/go/plugin/go.d/modules/apache/integrations/apache.md b/src/go/plugin/go.d/modules/apache/integrations/apache.md new file mode 100644 index 000000000..ec9f88883 --- /dev/null +++ b/src/go/plugin/go.d/modules/apache/integrations/apache.md @@ -0,0 +1,273 @@ +<!--startmeta +custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/plugin/go.d/modules/apache/integrations/apache.md" +meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/plugin/go.d/modules/apache/metadata.yaml" +sidebar_label: "Apache" +learn_status: "Published" +learn_rel_path: "Collecting Metrics/Web Servers and Web Proxies" +most_popular: True +message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE" +endmeta--> + +# Apache + + +<img src="https://netdata.cloud/img/apache.svg" width="150"/> + + +Plugin: go.d.plugin +Module: apache + +<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" /> + +## Overview + +This collector monitors the activity and performance of Apache servers, and collects metrics such as the number of connections, workers, requests and more. + + +It sends HTTP requests to the Apache location [server-status](https://httpd.apache.org/docs/2.4/mod/mod_status.html), +which is a built-in location that provides metrics about the Apache server. + + +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 + +By default, it detects Apache instances running on localhost that are listening on port 80. +On startup, it tries to collect metrics from: + +- http://localhost/server-status?auto +- http://127.0.0.1/server-status?auto + + +#### 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. + +All metrics available only if [ExtendedStatus](https://httpd.apache.org/docs/2.4/mod/core.html#extendedstatus) is on. + + +### Per Apache instance + +These metrics refer to the entire monitored application. + +This scope has no labels. + +Metrics: + +| Metric | Dimensions | Unit | Basic | Extended | +|:------|:----------|:----|:---:|:---:| +| apache.connections | connections | connections | • | • | +| apache.conns_async | keepalive, closing, writing | connections | • | • | +| apache.workers | idle, busy | workers | • | • | +| apache.scoreboard | waiting, starting, reading, sending, keepalive, dns_lookup, closing, logging, finishing, idle_cleanup, open | connections | • | • | +| apache.requests | requests | requests/s | | • | +| apache.net | sent | kilobit/s | | • | +| apache.reqpersec | requests | requests/s | | • | +| apache.bytespersec | served | KiB/s | | • | +| apache.bytesperreq | size | KiB | | • | +| apache.uptime | uptime | seconds | | • | + + + +## Alerts + +There are no alerts configured by default for this integration. + + +## Setup + +### Prerequisites + +#### Enable Apache status support + +- Enable and configure [status_module](https://httpd.apache.org/docs/2.4/mod/mod_status.html). +- Ensure that you have [ExtendedStatus](https://httpd.apache.org/docs/2.4/mod/mod_status.html#troubleshoot) set on (enabled by default since Apache v2.3.6). + + + +### Configuration + +#### File + +The configuration file name for this integration is `go.d/apache.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/apache.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/server-status?auto | 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. + +```yaml +jobs: + - name: local + url: http://127.0.0.1/server-status?auto + +``` +##### HTTP authentication + +Basic HTTP authentication. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + url: http://127.0.0.1/server-status?auto + username: username + password: password + +``` +</details> + +##### HTTPS with self-signed certificate + +Apache with enabled HTTPS and self-signed certificate. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + url: https://127.0.0.1/server-status?auto + 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/server-status?auto + + - name: remote + url: http://192.0.2.1/server-status?auto + +``` +</details> + + + +## Troubleshooting + +### Debug Mode + +**Important**: Debug mode is not supported for data collection jobs created via the UI using the Dyncfg feature. + +To troubleshoot issues with the `apache` 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 apache + ``` + +### Getting Logs + +If you're encountering problems with the `apache` collector, follow these steps to retrieve logs and identify potential issues: + +- **Run the command** specific to your system (systemd, non-systemd, or Docker container). +- **Examine the output** for any warnings or error messages that might indicate issues. These messages should provide clues about the root cause of the problem. + +#### System with systemd + +Use the following command to view logs generated since the last Netdata service restart: + +```bash +journalctl _SYSTEMD_INVOCATION_ID="$(systemctl show --value --property=InvocationID netdata)" --namespace=netdata --grep apache +``` + +#### System without systemd + +Locate the collector log file, typically at `/var/log/netdata/collector.log`, and use `grep` to filter for collector's name: + +```bash +grep apache /var/log/netdata/collector.log +``` + +**Note**: This method shows logs from all restarts. Focus on the **latest entries** for troubleshooting current issues. + +#### Docker Container + +If your Netdata runs in a Docker container named "netdata" (replace if different), use this command: + +```bash +docker logs netdata 2>&1 | grep apache +``` + + diff --git a/src/go/plugin/go.d/modules/apache/integrations/httpd.md b/src/go/plugin/go.d/modules/apache/integrations/httpd.md new file mode 100644 index 000000000..258365180 --- /dev/null +++ b/src/go/plugin/go.d/modules/apache/integrations/httpd.md @@ -0,0 +1,273 @@ +<!--startmeta +custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/plugin/go.d/modules/apache/integrations/httpd.md" +meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/plugin/go.d/modules/apache/metadata.yaml" +sidebar_label: "HTTPD" +learn_status: "Published" +learn_rel_path: "Collecting Metrics/Web Servers and Web Proxies" +most_popular: True +message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE" +endmeta--> + +# HTTPD + + +<img src="https://netdata.cloud/img/apache.svg" width="150"/> + + +Plugin: go.d.plugin +Module: apache + +<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" /> + +## Overview + +This collector monitors the activity and performance of Apache servers, and collects metrics such as the number of connections, workers, requests and more. + + +It sends HTTP requests to the Apache location [server-status](https://httpd.apache.org/docs/2.4/mod/mod_status.html), +which is a built-in location that provides metrics about the Apache server. + + +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 + +By default, it detects Apache instances running on localhost that are listening on port 80. +On startup, it tries to collect metrics from: + +- http://localhost/server-status?auto +- http://127.0.0.1/server-status?auto + + +#### 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. + +All metrics available only if [ExtendedStatus](https://httpd.apache.org/docs/2.4/mod/core.html#extendedstatus) is on. + + +### Per Apache instance + +These metrics refer to the entire monitored application. + +This scope has no labels. + +Metrics: + +| Metric | Dimensions | Unit | Basic | Extended | +|:------|:----------|:----|:---:|:---:| +| apache.connections | connections | connections | • | • | +| apache.conns_async | keepalive, closing, writing | connections | • | • | +| apache.workers | idle, busy | workers | • | • | +| apache.scoreboard | waiting, starting, reading, sending, keepalive, dns_lookup, closing, logging, finishing, idle_cleanup, open | connections | • | • | +| apache.requests | requests | requests/s | | • | +| apache.net | sent | kilobit/s | | • | +| apache.reqpersec | requests | requests/s | | • | +| apache.bytespersec | served | KiB/s | | • | +| apache.bytesperreq | size | KiB | | • | +| apache.uptime | uptime | seconds | | • | + + + +## Alerts + +There are no alerts configured by default for this integration. + + +## Setup + +### Prerequisites + +#### Enable Apache status support + +- Enable and configure [status_module](https://httpd.apache.org/docs/2.4/mod/mod_status.html). +- Ensure that you have [ExtendedStatus](https://httpd.apache.org/docs/2.4/mod/mod_status.html#troubleshoot) set on (enabled by default since Apache v2.3.6). + + + +### Configuration + +#### File + +The configuration file name for this integration is `go.d/apache.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/apache.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/server-status?auto | 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. + +```yaml +jobs: + - name: local + url: http://127.0.0.1/server-status?auto + +``` +##### HTTP authentication + +Basic HTTP authentication. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + url: http://127.0.0.1/server-status?auto + username: username + password: password + +``` +</details> + +##### HTTPS with self-signed certificate + +Apache with enabled HTTPS and self-signed certificate. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + url: https://127.0.0.1/server-status?auto + 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/server-status?auto + + - name: remote + url: http://192.0.2.1/server-status?auto + +``` +</details> + + + +## Troubleshooting + +### Debug Mode + +**Important**: Debug mode is not supported for data collection jobs created via the UI using the Dyncfg feature. + +To troubleshoot issues with the `apache` 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 apache + ``` + +### Getting Logs + +If you're encountering problems with the `apache` collector, follow these steps to retrieve logs and identify potential issues: + +- **Run the command** specific to your system (systemd, non-systemd, or Docker container). +- **Examine the output** for any warnings or error messages that might indicate issues. These messages should provide clues about the root cause of the problem. + +#### System with systemd + +Use the following command to view logs generated since the last Netdata service restart: + +```bash +journalctl _SYSTEMD_INVOCATION_ID="$(systemctl show --value --property=InvocationID netdata)" --namespace=netdata --grep apache +``` + +#### System without systemd + +Locate the collector log file, typically at `/var/log/netdata/collector.log`, and use `grep` to filter for collector's name: + +```bash +grep apache /var/log/netdata/collector.log +``` + +**Note**: This method shows logs from all restarts. Focus on the **latest entries** for troubleshooting current issues. + +#### Docker Container + +If your Netdata runs in a Docker container named "netdata" (replace if different), use this command: + +```bash +docker logs netdata 2>&1 | grep apache +``` + + diff --git a/src/go/plugin/go.d/modules/apache/metadata.yaml b/src/go/plugin/go.d/modules/apache/metadata.yaml new file mode 100644 index 000000000..bfab73fcf --- /dev/null +++ b/src/go/plugin/go.d/modules/apache/metadata.yaml @@ -0,0 +1,302 @@ +plugin_name: go.d.plugin +modules: + - &module + meta: &meta + id: collector-go.d.plugin-apache + plugin_name: go.d.plugin + module_name: apache + monitored_instance: + name: Apache + link: https://httpd.apache.org/ + icon_filename: apache.svg + categories: + - data-collection.web-servers-and-web-proxies + keywords: + - webserver + related_resources: + integrations: + list: + - plugin_name: go.d.plugin + module_name: weblog + - plugin_name: go.d.plugin + module_name: httpcheck + - plugin_name: apps.plugin + module_name: apps + info_provided_to_referring_integrations: + description: "" + most_popular: true + overview: + data_collection: + metrics_description: | + This collector monitors the activity and performance of Apache servers, and collects metrics such as the number of connections, workers, requests and more. + method_description: | + It sends HTTP requests to the Apache location [server-status](https://httpd.apache.org/docs/2.4/mod/mod_status.html), + which is a built-in location that provides metrics about the Apache server. + supported_platforms: + include: [] + exclude: [] + multi_instance: true + additional_permissions: + description: "" + default_behavior: + auto_detection: + description: | + By default, it detects Apache instances running on localhost that are listening on port 80. + On startup, it tries to collect metrics from: + + - http://localhost/server-status?auto + - http://127.0.0.1/server-status?auto + limits: + description: "" + performance_impact: + description: "" + setup: + prerequisites: + list: + - title: Enable Apache status support + description: | + - Enable and configure [status_module](https://httpd.apache.org/docs/2.4/mod/mod_status.html). + - Ensure that you have [ExtendedStatus](https://httpd.apache.org/docs/2.4/mod/mod_status.html#troubleshoot) set on (enabled by default since Apache v2.3.6). + configuration: + file: + name: go.d/apache.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/server-status?auto + 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: no + required: false + - name: tls_skip_verify + description: Server certificate chain and hostname validation policy. Controls whether the client performs this check. + default_value: no + 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 + folding: + enabled: false + description: A basic example configuration. + config: | + jobs: + - name: local + url: http://127.0.0.1/server-status?auto + - name: HTTP authentication + description: Basic HTTP authentication. + config: | + jobs: + - name: local + url: http://127.0.0.1/server-status?auto + username: username + password: password + - name: HTTPS with self-signed certificate + description: Apache with enabled HTTPS and self-signed certificate. + config: | + jobs: + - name: local + url: https://127.0.0.1/server-status?auto + 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/server-status?auto + + - name: remote + url: http://192.0.2.1/server-status?auto + troubleshooting: + problems: + list: [] + alerts: [] + metrics: + folding: + title: Metrics + enabled: false + description: | + All metrics available only if [ExtendedStatus](https://httpd.apache.org/docs/2.4/mod/core.html#extendedstatus) is on. + availability: + - Basic + - Extended + scopes: + - name: global + description: These metrics refer to the entire monitored application. + labels: [] + metrics: + - name: apache.connections + availability: + - Basic + - Extended + description: Connections + unit: connections + chart_type: line + dimensions: + - name: connections + - name: apache.conns_async + availability: + - Basic + - Extended + description: Active Connections + unit: connections + chart_type: stacked + dimensions: + - name: keepalive + - name: closing + - name: writing + - name: apache.workers + availability: + - Basic + - Extended + description: Workers Threads + unit: workers + chart_type: stacked + dimensions: + - name: idle + - name: busy + - name: apache.scoreboard + availability: + - Basic + - Extended + description: Scoreboard + unit: connections + chart_type: line + dimensions: + - name: waiting + - name: starting + - name: reading + - name: sending + - name: keepalive + - name: dns_lookup + - name: closing + - name: logging + - name: finishing + - name: idle_cleanup + - name: open + - name: apache.requests + availability: + - Extended + description: Requests + unit: requests/s + chart_type: line + dimensions: + - name: requests + - name: apache.net + availability: + - Extended + description: Bandwidth + unit: kilobit/s + chart_type: area + dimensions: + - name: sent + - name: apache.reqpersec + availability: + - Extended + description: Lifetime Average Number Of Requests Per Second + unit: requests/s + chart_type: area + dimensions: + - name: requests + - name: apache.bytespersec + availability: + - Extended + description: Lifetime Average Number Of Bytes Served Per Second + unit: KiB/s + chart_type: area + dimensions: + - name: served + - name: apache.bytesperreq + availability: + - Extended + description: Lifetime Average Response Size + unit: KiB + chart_type: area + dimensions: + - name: size + - name: apache.uptime + availability: + - Extended + description: Uptime + unit: seconds + chart_type: line + dimensions: + - name: uptime + - <<: *module + meta: + <<: *meta + id: collector-go.d.plugin-httpd + monitored_instance: + name: HTTPD + link: https://httpd.apache.org/ + icon_filename: apache.svg + categories: + - data-collection.web-servers-and-web-proxies diff --git a/src/go/plugin/go.d/modules/apache/metrics.go b/src/go/plugin/go.d/modules/apache/metrics.go new file mode 100644 index 000000000..953bd42c3 --- /dev/null +++ b/src/go/plugin/go.d/modules/apache/metrics.go @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package apache + +type ( + serverStatus struct { + // ExtendedStatus + Total struct { + // Total number of accesses. + Accesses *int64 `stm:"accesses"` + // Total number of byte count served. + // This metric reflects the bytes that should have been served, + // which is not necessarily equal to the bytes actually (successfully) served. + KBytes *int64 `stm:"kBytes"` + } `stm:"total"` + Averages struct { + //Average number of requests per second. + ReqPerSec *float64 `stm:"req_per_sec,100000,1"` + // Average number of bytes served per second. + BytesPerSec *float64 `stm:"bytes_per_sec,100000,1"` + // Average number of bytes per request. + BytesPerReq *float64 `stm:"bytes_per_req,100000,1"` + } `stm:""` + Uptime *int64 `stm:"uptime"` + + Workers struct { + // Total number of busy worker threads/processes. + // A worker is considered “busy” if it is in any of the following states: + // reading, writing, keep-alive, logging, closing, or gracefully finishing. + Busy *int64 `stm:"busy_workers"` + // Total number of idle worker threads/processes. + // An “idle” worker is not in any of the busy states. + Idle *int64 `stm:"idle_workers"` + } `stm:""` + Connections struct { + Total *int64 `stm:"total"` + Async struct { + // Number of async connections in writing state (only applicable to event MPM). + Writing *int64 `stm:"writing"` + // Number of async connections in keep-alive state (only applicable to event MPM). + KeepAlive *int64 `stm:"keep_alive"` + // Number of async connections in closing state (only applicable to event MPM). + Closing *int64 `stm:"closing"` + } `stm:"async"` + } `stm:"conns"` + Scoreboard *scoreboard `stm:"scoreboard"` + } + scoreboard struct { + Waiting int64 `stm:"waiting"` + Starting int64 `stm:"starting"` + Reading int64 `stm:"reading"` + Sending int64 `stm:"sending"` + KeepAlive int64 `stm:"keepalive"` + DNSLookup int64 `stm:"dns_lookup"` + Closing int64 `stm:"closing"` + Logging int64 `stm:"logging"` + Finishing int64 `stm:"finishing"` + IdleCleanup int64 `stm:"idle_cleanup"` + Open int64 `stm:"open"` + } +) diff --git a/src/go/plugin/go.d/modules/apache/testdata/config.json b/src/go/plugin/go.d/modules/apache/testdata/config.json new file mode 100644 index 000000000..984c3ed6e --- /dev/null +++ b/src/go/plugin/go.d/modules/apache/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/plugin/go.d/modules/apache/testdata/config.yaml b/src/go/plugin/go.d/modules/apache/testdata/config.yaml new file mode 100644 index 000000000..8558b61cc --- /dev/null +++ b/src/go/plugin/go.d/modules/apache/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/plugin/go.d/modules/apache/testdata/extended-status-mpm-event.txt b/src/go/plugin/go.d/modules/apache/testdata/extended-status-mpm-event.txt new file mode 100644 index 000000000..136b69363 --- /dev/null +++ b/src/go/plugin/go.d/modules/apache/testdata/extended-status-mpm-event.txt @@ -0,0 +1,39 @@ +127.0.0.1 +ServerVersion: Apache/2.4.37 (Unix) +ServerMPM: event +Server Built: Oct 23 2018 18:27:46 +CurrentTime: Sunday, 13-Jan-2019 20:39:30 MSK +RestartTime: Sunday, 13-Jan-2019 20:35:13 MSK +ParentServerConfigGeneration: 1 +ParentServerMPMGeneration: 0 +ServerUptimeSeconds: 256 +ServerUptime: 4 minutes 16 seconds +Load1: 1.02 +Load5: 1.30 +Load15: 1.41 +Total Accesses: 9 +Total kBytes: 12 +Total Duration: 1 +CPUUser: 0 +CPUSystem: .01 +CPUChildrenUser: 0 +CPUChildrenSystem: 0 +CPULoad: .00390625 +Uptime: 256 +ReqPerSec: .0351563 +BytesPerSec: 48 +BytesPerReq: 1365.33 +DurationPerReq: .111111 +BusyWorkers: 1 +IdleWorkers: 99 +Processes: 4 +Stopping: 0 +BusyWorkers: 1 +IdleWorkers: 99 +ConnsTotal: 0 +ConnsAsyncWriting: 0 +ConnsAsyncKeepAlive: 0 +ConnsAsyncClosing: 0 +Scoreboard: ____________________________________________________________W_______________________________________............................................................................................................................................................................................................................................................................................................ +Using GnuTLS version: 3.6.5 +Built against GnuTLS version: 3.5.19
\ No newline at end of file diff --git a/src/go/plugin/go.d/modules/apache/testdata/extended-status-mpm-prefork.txt b/src/go/plugin/go.d/modules/apache/testdata/extended-status-mpm-prefork.txt new file mode 100644 index 000000000..eeafb4983 --- /dev/null +++ b/src/go/plugin/go.d/modules/apache/testdata/extended-status-mpm-prefork.txt @@ -0,0 +1,48 @@ +some.host.name +ServerVersion: Apache/2.4.53 (Unix) OpenSSL/1.1.1 +ServerMPM: prefork +Server Built: Apr 6 2022 16:30:59 +CurrentTime: Monday, 18-Apr-2022 11:52:39 CEST +RestartTime: Sunday, 10-Apr-2022 06:57:34 CEST +ParentServerConfigGeneration: 9 +ParentServerMPMGeneration: 8 +ServerUptimeSeconds: 708904 +ServerUptime: 8 days 4 hours 55 minutes 4 seconds +Load1: 7.18 +Load5: 7.29 +Load15: 8.25 +Total Accesses: 120358784 +Total kBytes: 4252382776 +Total Duration: 35583107177 +CPUUser: 4549.96 +CPUSystem: 4142.92 +CPUChildrenUser: 776666 +CPUChildrenSystem: 609619 +CPULoad: 196.78 +Uptime: 708904 +ReqPerSec: 169.781 +BytesPerSec: 6142500 +BytesPerReq: 36178.8 +DurationPerReq: 295.642 +BusyWorkers: 70 +IdleWorkers: 1037 +Scoreboard: ___... +TLSSessionCacheStatus +CacheType: SHMCB +CacheSharedMemory: 512000 +CacheCurrentEntries: 1969 +CacheSubcaches: 32 +CacheIndexesPerSubcaches: 88 +CacheTimeLeftOldestAvg: 295 +CacheTimeLeftOldestMin: 295 +CacheTimeLeftOldestMax: 296 +CacheIndexUsage: 69% +CacheUsage: 99% +CacheStoreCount: 22984008 +CacheReplaceCount: 0 +CacheExpireCount: 0 +CacheDiscardCount: 22976594 +CacheRetrieveHitCount: 5501 +CacheRetrieveMissCount: 4630 +CacheRemoveHitCount: 5491 +CacheRemoveMissCount: 51
\ No newline at end of file diff --git a/src/go/plugin/go.d/modules/apache/testdata/lighttpd-status.txt b/src/go/plugin/go.d/modules/apache/testdata/lighttpd-status.txt new file mode 100644 index 000000000..07d8e06e8 --- /dev/null +++ b/src/go/plugin/go.d/modules/apache/testdata/lighttpd-status.txt @@ -0,0 +1,6 @@ +Total Accesses: 12 +Total kBytes: 4 +Uptime: 11 +BusyServers: 3 +IdleServers: 125 +Scoreboard: khr_____________________________________________________________________________________________________________________________
\ No newline at end of file diff --git a/src/go/plugin/go.d/modules/apache/testdata/simple-status-mpm-event.txt b/src/go/plugin/go.d/modules/apache/testdata/simple-status-mpm-event.txt new file mode 100644 index 000000000..8093eacf9 --- /dev/null +++ b/src/go/plugin/go.d/modules/apache/testdata/simple-status-mpm-event.txt @@ -0,0 +1,24 @@ +127.0.0.1 +ServerVersion: Apache/2.4.37 (Unix) +ServerMPM: event +Server Built: Oct 23 2018 18:27:46 +CurrentTime: Sunday, 13-Jan-2019 21:43:56 MSK +RestartTime: Sunday, 13-Jan-2019 21:43:53 MSK +ParentServerConfigGeneration: 1 +ParentServerMPMGeneration: 0 +ServerUptimeSeconds: 2 +ServerUptime: 2 seconds +Load1: 0.77 +Load5: 0.93 +Load15: 1.03 +BusyWorkers: 1 +IdleWorkers: 74 +Processes: 3 +Stopping: 0 +BusyWorkers: 1 +IdleWorkers: 74 +ConnsTotal: 0 +ConnsAsyncWriting: 0 +ConnsAsyncKeepAlive: 0 +ConnsAsyncClosing: 0 +Scoreboard: ________________________________________________________________W__________.....................................................................................................................................................................................................................................................................................................................................
\ No newline at end of file |