diff options
Diffstat (limited to '')
17 files changed, 4075 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/README.md b/src/go/collectors/go.d.plugin/modules/couchdb/README.md new file mode 120000 index 000000000..14cff4d36 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/README.md @@ -0,0 +1 @@ +integrations/couchdb.md
\ No newline at end of file diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/charts.go b/src/go/collectors/go.d.plugin/modules/couchdb/charts.go new file mode 100644 index 000000000..3dedec7db --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/charts.go @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package couchdb + +import ( + "github.com/netdata/netdata/go/go.d.plugin/agent/module" +) + +type ( + Charts = module.Charts + Dims = module.Dims + Vars = module.Vars +) + +var dbActivityCharts = Charts{ + { + ID: "activity", + Title: "Overall Activity", + Units: "requests/s", + Fam: "dbactivity", + Ctx: "couchdb.activity", + Type: module.Stacked, + Dims: Dims{ + {ID: "couchdb_database_reads", Name: "DB reads", Algo: module.Incremental}, + {ID: "couchdb_database_writes", Name: "DB writes", Algo: module.Incremental}, + {ID: "couchdb_httpd_view_reads", Name: "View reads", Algo: module.Incremental}, + }, + }, +} + +var httpTrafficBreakdownCharts = Charts{ + { + ID: "request_methods", + Title: "HTTP request methods", + Units: "requests/s", + Fam: "httptraffic", + Ctx: "couchdb.request_methods", + Type: module.Stacked, + Dims: Dims{ + {ID: "couchdb_httpd_request_methods_COPY", Name: "COPY", Algo: module.Incremental}, + {ID: "couchdb_httpd_request_methods_DELETE", Name: "DELETE", Algo: module.Incremental}, + {ID: "couchdb_httpd_request_methods_GET", Name: "GET", Algo: module.Incremental}, + {ID: "couchdb_httpd_request_methods_HEAD", Name: "HEAD", Algo: module.Incremental}, + {ID: "couchdb_httpd_request_methods_OPTIONS", Name: "OPTIONS", Algo: module.Incremental}, + {ID: "couchdb_httpd_request_methods_POST", Name: "POST", Algo: module.Incremental}, + {ID: "couchdb_httpd_request_methods_PUT", Name: "PUT", Algo: module.Incremental}, + }, + }, + { + ID: "response_codes", + Title: "HTTP response status codes", + Units: "responses/s", + Fam: "httptraffic", + Ctx: "couchdb.response_codes", + Type: module.Stacked, + Dims: Dims{ + {ID: "couchdb_httpd_status_codes_200", Name: "200 OK", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_201", Name: "201 Created", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_202", Name: "202 Accepted", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_204", Name: "204 No Content", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_206", Name: "206 Partial Content", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_301", Name: "301 Moved Permanently", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_302", Name: "302 Found", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_304", Name: "304 Not Modified", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_400", Name: "400 Bad Request", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_401", Name: "401 Unauthorized", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_403", Name: "403 Forbidden", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_404", Name: "404 Not Found", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_406", Name: "406 Not Acceptable", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_409", Name: "409 Conflict", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_412", Name: "412 Precondition Failed", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_413", Name: "413 Request Entity Too Long", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_414", Name: "414 Request URI Too Long", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_415", Name: "415 Unsupported Media Type", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_416", Name: "416 Requested Range Not Satisfiable", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_417", Name: "417 Expectation Failed", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_500", Name: "500 Internal Server Error", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_501", Name: "501 Not Implemented", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_503", Name: "503 Service Unavailable", Algo: module.Incremental}, + }, + }, + { + ID: "response_code_classes", + Title: "HTTP response status code classes", + Units: "responses/s", + Fam: "httptraffic", + Ctx: "couchdb.response_code_classes", + Type: module.Stacked, + Dims: Dims{ + {ID: "couchdb_httpd_status_codes_2xx", Name: "2xx Success", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_3xx", Name: "3xx Redirection", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_4xx", Name: "4xx Client error", Algo: module.Incremental}, + {ID: "couchdb_httpd_status_codes_5xx", Name: "5xx Server error", Algo: module.Incremental}, + }, + }, +} + +var serverOperationsCharts = Charts{ + { + ID: "active_tasks", + Title: "Active task breakdown", + Units: "tasks", + Fam: "ops", + Ctx: "couchdb.active_tasks", + Type: module.Stacked, + Dims: Dims{ + {ID: "active_tasks_indexer", Name: "Indexer"}, + {ID: "active_tasks_database_compaction", Name: "DB Compaction"}, + {ID: "active_tasks_replication", Name: "Replication"}, + {ID: "active_tasks_view_compaction", Name: "View Compaction"}, + }, + }, + { + ID: "replicator_jobs", + Title: "Replicator job breakdown", + Units: "jobs", + Fam: "ops", + Ctx: "couchdb.replicator_jobs", + Type: module.Stacked, + Dims: Dims{ + {ID: "couch_replicator_jobs_running", Name: "Running"}, + {ID: "couch_replicator_jobs_pending", Name: "Pending"}, + {ID: "couch_replicator_jobs_crashed", Name: "Crashed"}, + {ID: "internal_replication_jobs", Name: "Internal replication jobs"}, + }, + }, + { + ID: "open_files", + Title: "Open files", + Units: "files", + Fam: "ops", + Ctx: "couchdb.open_files", + Dims: Dims{ + {ID: "couchdb_open_os_files", Name: "# files"}, + }, + }, +} + +var erlangStatisticsCharts = Charts{ + { + ID: "erlang_memory", + Title: "Erlang VM memory usage", + Units: "B", + Fam: "erlang", + Ctx: "couchdb.erlang_vm_memory", + Type: module.Stacked, + Dims: Dims{ + {ID: "memory_atom", Name: "atom"}, + {ID: "memory_binary", Name: "binaries"}, + {ID: "memory_code", Name: "code"}, + {ID: "memory_ets", Name: "ets"}, + {ID: "memory_processes", Name: "procs"}, + {ID: "memory_other", Name: "other"}, + }, + }, + { + ID: "erlang_proc_counts", + Title: "Process counts", + Units: "processes", + Fam: "erlang", + Ctx: "couchdb.proccounts", + Dims: Dims{ + {ID: "os_proc_count", Name: "OS procs"}, + {ID: "process_count", Name: "erl procs"}, + }, + }, + { + ID: "erlang_peak_msg_queue", + Title: "Peak message queue size", + Units: "messages", + Fam: "erlang", + Ctx: "couchdb.peakmsgqueue", + Dims: Dims{ + {ID: "peak_msg_queue", Name: "peak size"}, + }, + }, + { + ID: "erlang_reductions", + Title: "Erlang reductions", + Units: "reductions", + Fam: "erlang", + Ctx: "couchdb.reductions", + Type: module.Stacked, + Dims: Dims{ + {ID: "reductions", Name: "reductions", Algo: module.Incremental}, + }, + }, +} + +var ( + dbSpecificCharts = Charts{ + { + ID: "db_sizes_file", + Title: "Database sizes (file)", + Units: "KiB", + Fam: "perdbstats", + Ctx: "couchdb.db_sizes_file", + }, + { + ID: "db_sizes_external", + Title: "Database sizes (external)", + Units: "KiB", + Fam: "perdbstats", + Ctx: "couchdb.db_sizes_external", + }, + { + ID: "db_sizes_active", + Title: "Database sizes (active)", + Units: "KiB", + Fam: "perdbstats", + Ctx: "couchdb.db_sizes_active", + }, + { + ID: "db_doc_counts", + Title: "Database # of docs", + Units: "docs", + Fam: "perdbstats", + Ctx: "couchdb.db_doc_count", + }, + { + ID: "db_doc_del_counts", + Title: "Database # of deleted docs", + Units: "docs", + Fam: "perdbstats", + Ctx: "couchdb.db_doc_del_count", + }, + } +) diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/collect.go b/src/go/collectors/go.d.plugin/modules/couchdb/collect.go new file mode 100644 index 000000000..5c722fd0c --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/collect.go @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package couchdb + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "math" + "net/http" + "strings" + "sync" + + "github.com/netdata/netdata/go/go.d.plugin/pkg/stm" + "github.com/netdata/netdata/go/go.d.plugin/pkg/web" +) + +const ( + urlPathActiveTasks = "/_active_tasks" + urlPathOverviewStats = "/_node/%s/_stats" + urlPathSystemStats = "/_node/%s/_system" + urlPathDatabases = "/_dbs_info" + + httpStatusCodePrefix = "couchdb_httpd_status_codes_" + httpStatusCodePrefixLen = len(httpStatusCodePrefix) +) + +func (cdb *CouchDB) collect() (map[string]int64, error) { + ms := cdb.scrapeCouchDB() + if ms.empty() { + return nil, nil + } + + collected := make(map[string]int64) + cdb.collectNodeStats(collected, ms) + cdb.collectSystemStats(collected, ms) + cdb.collectActiveTasks(collected, ms) + cdb.collectDBStats(collected, ms) + + return collected, nil +} + +func (cdb *CouchDB) collectNodeStats(collected map[string]int64, ms *cdbMetrics) { + if !ms.hasNodeStats() { + return + } + + for metric, value := range stm.ToMap(ms.NodeStats) { + collected[metric] = value + if strings.HasPrefix(metric, httpStatusCodePrefix) { + code := metric[httpStatusCodePrefixLen:] + collected["couchdb_httpd_status_codes_"+string(code[0])+"xx"] += value + } + } +} + +func (cdb *CouchDB) collectSystemStats(collected map[string]int64, ms *cdbMetrics) { + if !ms.hasNodeSystem() { + return + } + + for metric, value := range stm.ToMap(ms.NodeSystem) { + collected[metric] = value + } + + collected["peak_msg_queue"] = findMaxMQSize(ms.NodeSystem.MessageQueues) +} + +func (cdb *CouchDB) collectActiveTasks(collected map[string]int64, ms *cdbMetrics) { + collected["active_tasks_indexer"] = 0 + collected["active_tasks_database_compaction"] = 0 + collected["active_tasks_replication"] = 0 + collected["active_tasks_view_compaction"] = 0 + + if !ms.hasActiveTasks() { + return + } + + for _, task := range ms.ActiveTasks { + collected["active_tasks_"+task.Type]++ + } +} + +func (cdb *CouchDB) collectDBStats(collected map[string]int64, ms *cdbMetrics) { + if !ms.hasDBStats() { + return + } + + for _, dbStats := range ms.DBStats { + if dbStats.Error != "" { + cdb.Warning("database '", dbStats.Key, "' doesn't exist") + continue + } + merge(collected, stm.ToMap(dbStats.Info), "db_"+dbStats.Key) + } +} + +func (cdb *CouchDB) scrapeCouchDB() *cdbMetrics { + ms := &cdbMetrics{} + wg := &sync.WaitGroup{} + + wg.Add(1) + go func() { defer wg.Done(); cdb.scrapeNodeStats(ms) }() + + wg.Add(1) + go func() { defer wg.Done(); cdb.scrapeSystemStats(ms) }() + + wg.Add(1) + go func() { defer wg.Done(); cdb.scrapeActiveTasks(ms) }() + + if len(cdb.databases) > 0 { + wg.Add(1) + go func() { defer wg.Done(); cdb.scrapeDBStats(ms) }() + } + + wg.Wait() + return ms +} + +func (cdb *CouchDB) scrapeNodeStats(ms *cdbMetrics) { + req, _ := web.NewHTTPRequest(cdb.Request) + req.URL.Path = fmt.Sprintf(urlPathOverviewStats, cdb.Config.Node) + + var stats cdbNodeStats + if err := cdb.doOKDecode(req, &stats); err != nil { + cdb.Warning(err) + return + } + ms.NodeStats = &stats +} + +func (cdb *CouchDB) scrapeSystemStats(ms *cdbMetrics) { + req, _ := web.NewHTTPRequest(cdb.Request) + req.URL.Path = fmt.Sprintf(urlPathSystemStats, cdb.Config.Node) + + var stats cdbNodeSystem + if err := cdb.doOKDecode(req, &stats); err != nil { + cdb.Warning(err) + return + } + ms.NodeSystem = &stats +} + +func (cdb *CouchDB) scrapeActiveTasks(ms *cdbMetrics) { + req, _ := web.NewHTTPRequest(cdb.Request) + req.URL.Path = urlPathActiveTasks + + var stats []cdbActiveTask + if err := cdb.doOKDecode(req, &stats); err != nil { + cdb.Warning(err) + return + } + ms.ActiveTasks = stats +} + +func (cdb *CouchDB) scrapeDBStats(ms *cdbMetrics) { + req, _ := web.NewHTTPRequest(cdb.Request) + req.URL.Path = urlPathDatabases + req.Method = http.MethodPost + req.Header.Add("Accept", "application/json") + req.Header.Add("Content-Type", "application/json") + + var q struct { + Keys []string `json:"keys"` + } + q.Keys = cdb.databases + body, err := json.Marshal(q) + if err != nil { + cdb.Error(err) + return + } + req.Body = io.NopCloser(bytes.NewReader(body)) + + var stats []cdbDBStats + if err := cdb.doOKDecode(req, &stats); err != nil { + cdb.Warning(err) + return + } + ms.DBStats = stats +} + +func findMaxMQSize(MessageQueues map[string]interface{}) int64 { + var max float64 + for _, mq := range MessageQueues { + switch mqSize := mq.(type) { + case float64: + max = math.Max(max, mqSize) + case map[string]interface{}: + if v, ok := mqSize["count"].(float64); ok { + max = math.Max(max, v) + } + } + } + return int64(max) +} + +func (cdb *CouchDB) pingCouchDB() error { + req, _ := web.NewHTTPRequest(cdb.Request) + + var info struct{ Couchdb string } + if err := cdb.doOKDecode(req, &info); err != nil { + return err + } + + if info.Couchdb != "Welcome" { + return errors.New("not a CouchDB endpoint") + } + + return nil +} + +func (cdb *CouchDB) doOKDecode(req *http.Request, in interface{}) error { + resp, err := cdb.httpClient.Do(req) + if err != nil { + return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err) + } + defer closeBody(resp) + + // TODO: read resp body, it contains reason + // ex.: {"error":"bad_request","reason":"`keys` member must exist."} (400) + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode) + } + + if err := json.NewDecoder(resp.Body).Decode(in); err != nil { + return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err) + } + return nil +} + +func closeBody(resp *http.Response) { + if resp != nil && resp.Body != nil { + _, _ = io.Copy(io.Discard, resp.Body) + _ = resp.Body.Close() + } +} + +func merge(dst, src map[string]int64, prefix string) { + for k, v := range src { + dst[prefix+"_"+k] = v + } +} diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/config_schema.json b/src/go/collectors/go.d.plugin/modules/couchdb/config_schema.json new file mode 100644 index 000000000..2f9df722a --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/config_schema.json @@ -0,0 +1,191 @@ +{ + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CouchDB 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 CouchDB web server.", + "type": "string", + "default": "http://127.0.0.1:5984", + "format": "uri" + }, + "timeout": { + "title": "Timeout", + "description": "The timeout in seconds for the HTTP request.", + "type": "number", + "minimum": 0.5, + "default": 2 + }, + "not_follow_redirects": { + "title": "Not follow redirects", + "description": "If set, the client will not follow HTTP redirects automatically.", + "type": "boolean" + }, + "node": { + "title": "Node name", + "description": "CouchDB node name. Same as -name vm.args argument.", + "type": "string", + "default": "_local" + }, + "databases": { + "title": "Databases", + "description": "A space-separated list of database names for which you want to collect data. Leave blank to exclude database statistics.", + "type": "string" + }, + "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", + "node" + ], + "additionalProperties": false, + "patternProperties": { + "^name$": {} + } + }, + "uiSchema": { + "ui:flavour": "tabs", + "ui:options": { + "tabs": [ + { + "title": "Base", + "fields": [ + "update_every", + "url", + "timeout", + "not_follow_redirects", + "node", + "databases" + ] + }, + { + "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/couchdb/couchdb.go b/src/go/collectors/go.d.plugin/modules/couchdb/couchdb.go new file mode 100644 index 000000000..459251e9d --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/couchdb.go @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package couchdb + +import ( + _ "embed" + "errors" + "net/http" + "strings" + "time" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/web" +) + +//go:embed "config_schema.json" +var configSchema string + +func init() { + module.Register("couchdb", module.Creator{ + JobConfigSchema: configSchema, + Defaults: module.Defaults{ + UpdateEvery: 10, + }, + Create: func() module.Module { return New() }, + Config: func() any { return &Config{} }, + }) +} + +func New() *CouchDB { + return &CouchDB{ + Config: Config{ + HTTP: web.HTTP{ + Request: web.Request{ + URL: "http://127.0.0.1:5984", + }, + Client: web.Client{ + Timeout: web.Duration(time.Second * 2), + }, + }, + Node: "_local", + }, + } +} + +type Config struct { + UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"` + web.HTTP `yaml:",inline" json:""` + Node string `yaml:"node,omitempty" json:"node"` + Databases string `yaml:"databases,omitempty" json:"databases"` +} + +type CouchDB struct { + module.Base + Config `yaml:",inline" json:""` + + charts *module.Charts + + httpClient *http.Client + + databases []string +} + +func (cdb *CouchDB) Configuration() any { + return cdb.Config +} + +func (cdb *CouchDB) Init() error { + err := cdb.validateConfig() + if err != nil { + cdb.Errorf("check configuration: %v", err) + return err + } + + cdb.databases = strings.Fields(cdb.Config.Databases) + + httpClient, err := cdb.initHTTPClient() + if err != nil { + cdb.Errorf("init HTTP client: %v", err) + return err + } + cdb.httpClient = httpClient + + charts, err := cdb.initCharts() + if err != nil { + cdb.Errorf("init charts: %v", err) + return err + } + cdb.charts = charts + + return nil +} + +func (cdb *CouchDB) Check() error { + if err := cdb.pingCouchDB(); err != nil { + cdb.Error(err) + return err + } + + mx, err := cdb.collect() + if err != nil { + cdb.Error(err) + return err + } + + if len(mx) == 0 { + return errors.New("no metrics collected") + } + + return nil +} + +func (cdb *CouchDB) Charts() *Charts { + return cdb.charts +} + +func (cdb *CouchDB) Collect() map[string]int64 { + mx, err := cdb.collect() + if err != nil { + cdb.Error(err) + } + + if len(mx) == 0 { + return nil + } + return mx +} + +func (cdb *CouchDB) Cleanup() { + if cdb.httpClient == nil { + return + } + cdb.httpClient.CloseIdleConnections() +} diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/couchdb_test.go b/src/go/collectors/go.d.plugin/modules/couchdb/couchdb_test.go new file mode 100644 index 000000000..15e7aa0a1 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/couchdb_test.go @@ -0,0 +1,464 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package couchdb + +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") + + dataVer311Root, _ = os.ReadFile("testdata/v3.1.1/root.json") + dataVer311ActiveTasks, _ = os.ReadFile("testdata/v3.1.1/active_tasks.json") + dataVer311NodeStats, _ = os.ReadFile("testdata/v3.1.1/node_stats.json") + dataVer311NodeSystem, _ = os.ReadFile("testdata/v3.1.1/node_system.json") + dataVer311DbsInfo, _ = os.ReadFile("testdata/v3.1.1/dbs_info.json") +) + +func Test_testDataIsValid(t *testing.T) { + for name, data := range map[string][]byte{ + "dataConfigJSON": dataConfigJSON, + "dataConfigYAML": dataConfigYAML, + "dataVer311Root": dataVer311Root, + "dataVer311ActiveTasks": dataVer311ActiveTasks, + "dataVer311NodeStats": dataVer311NodeStats, + "dataVer311NodeSystem": dataVer311NodeSystem, + "dataVer311DbsInfo": dataVer311DbsInfo, + } { + require.NotNil(t, data, name) + } +} + +func TestCouchDB_ConfigurationSerialize(t *testing.T) { + module.TestConfigurationSerialize(t, &CouchDB{}, dataConfigJSON, dataConfigYAML) +} + +func TestCouchDB_Init(t *testing.T) { + tests := map[string]struct { + config Config + wantNumOfCharts int + wantFail bool + }{ + "default": { + wantNumOfCharts: numOfCharts( + dbActivityCharts, + httpTrafficBreakdownCharts, + serverOperationsCharts, + erlangStatisticsCharts, + ), + config: New().Config, + }, + "URL not set": { + wantFail: true, + config: Config{ + HTTP: web.HTTP{ + Request: web.Request{URL: ""}, + }}, + }, + "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) { + es := New() + es.Config = test.config + + if test.wantFail { + assert.Error(t, es.Init()) + } else { + assert.NoError(t, es.Init()) + assert.Equal(t, test.wantNumOfCharts, len(*es.Charts())) + } + }) + } +} + +func TestCouchDB_Check(t *testing.T) { + tests := map[string]struct { + prepare func(*testing.T) (cdb *CouchDB, cleanup func()) + wantFail bool + }{ + "valid data": {prepare: prepareCouchDBValidData}, + "invalid data": {prepare: prepareCouchDBInvalidData, wantFail: true}, + "404": {prepare: prepareCouchDB404, wantFail: true}, + "connection refused": {prepare: prepareCouchDBConnectionRefused, wantFail: true}, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + cdb, cleanup := test.prepare(t) + defer cleanup() + + if test.wantFail { + assert.Error(t, cdb.Check()) + } else { + assert.NoError(t, cdb.Check()) + } + }) + } +} + +func TestCouchDB_Charts(t *testing.T) { + assert.Nil(t, New().Charts()) +} + +func TestCouchDB_Cleanup(t *testing.T) { + assert.NotPanics(t, New().Cleanup) +} + +func TestCouchDB_Collect(t *testing.T) { + tests := map[string]struct { + prepare func() *CouchDB + wantCollected map[string]int64 + checkCharts bool + }{ + "all stats": { + prepare: func() *CouchDB { + cdb := New() + cdb.Config.Databases = "db1 db2" + return cdb + }, + wantCollected: map[string]int64{ + + // node stats + "couch_replicator_jobs_crashed": 1, + "couch_replicator_jobs_pending": 1, + "couch_replicator_jobs_running": 1, + "couchdb_database_reads": 1, + "couchdb_database_writes": 14, + "couchdb_httpd_request_methods_COPY": 1, + "couchdb_httpd_request_methods_DELETE": 1, + "couchdb_httpd_request_methods_GET": 75544, + "couchdb_httpd_request_methods_HEAD": 1, + "couchdb_httpd_request_methods_OPTIONS": 1, + "couchdb_httpd_request_methods_POST": 15, + "couchdb_httpd_request_methods_PUT": 3, + "couchdb_httpd_status_codes_200": 75294, + "couchdb_httpd_status_codes_201": 15, + "couchdb_httpd_status_codes_202": 1, + "couchdb_httpd_status_codes_204": 1, + "couchdb_httpd_status_codes_206": 1, + "couchdb_httpd_status_codes_301": 1, + "couchdb_httpd_status_codes_302": 1, + "couchdb_httpd_status_codes_304": 1, + "couchdb_httpd_status_codes_400": 1, + "couchdb_httpd_status_codes_401": 20, + "couchdb_httpd_status_codes_403": 1, + "couchdb_httpd_status_codes_404": 225, + "couchdb_httpd_status_codes_405": 1, + "couchdb_httpd_status_codes_406": 1, + "couchdb_httpd_status_codes_409": 1, + "couchdb_httpd_status_codes_412": 3, + "couchdb_httpd_status_codes_413": 1, + "couchdb_httpd_status_codes_414": 1, + "couchdb_httpd_status_codes_415": 1, + "couchdb_httpd_status_codes_416": 1, + "couchdb_httpd_status_codes_417": 1, + "couchdb_httpd_status_codes_500": 1, + "couchdb_httpd_status_codes_501": 1, + "couchdb_httpd_status_codes_503": 1, + "couchdb_httpd_status_codes_2xx": 75312, + "couchdb_httpd_status_codes_3xx": 3, + "couchdb_httpd_status_codes_4xx": 258, + "couchdb_httpd_status_codes_5xx": 3, + "couchdb_httpd_view_reads": 1, + "couchdb_open_os_files": 1, + + // node system + "context_switches": 22614499, + "ets_table_count": 116, + "internal_replication_jobs": 1, + "io_input": 49674812, + "io_output": 686400800, + "memory_atom_used": 488328, + "memory_atom": 504433, + "memory_binary": 297696, + "memory_code": 11252688, + "memory_ets": 1579120, + "memory_other": 20427855, + "memory_processes": 9161448, + "os_proc_count": 1, + "peak_msg_queue": 2, + "process_count": 296, + "reductions": 43211228312, + "run_queue": 1, + + // active tasks + "active_tasks_database_compaction": 1, + "active_tasks_indexer": 2, + "active_tasks_replication": 1, + "active_tasks_view_compaction": 1, + + // databases + "db_db1_db_doc_counts": 14, + "db_db1_db_doc_del_counts": 1, + "db_db1_db_sizes_active": 2818, + "db_db1_db_sizes_external": 588, + "db_db1_db_sizes_file": 74115, + + "db_db2_db_doc_counts": 15, + "db_db2_db_doc_del_counts": 1, + "db_db2_db_sizes_active": 1818, + "db_db2_db_sizes_external": 288, + "db_db2_db_sizes_file": 7415, + }, + checkCharts: true, + }, + "wrong node": { + prepare: func() *CouchDB { + cdb := New() + cdb.Config.Node = "bad_node@bad_host" + cdb.Config.Databases = "db1 db2" + return cdb + }, + wantCollected: map[string]int64{ + + // node stats + + // node system + + // active tasks + "active_tasks_database_compaction": 1, + "active_tasks_indexer": 2, + "active_tasks_replication": 1, + "active_tasks_view_compaction": 1, + + // databases + "db_db1_db_doc_counts": 14, + "db_db1_db_doc_del_counts": 1, + "db_db1_db_sizes_active": 2818, + "db_db1_db_sizes_external": 588, + "db_db1_db_sizes_file": 74115, + + "db_db2_db_doc_counts": 15, + "db_db2_db_doc_del_counts": 1, + "db_db2_db_sizes_active": 1818, + "db_db2_db_sizes_external": 288, + "db_db2_db_sizes_file": 7415, + }, + checkCharts: false, + }, + "wrong database": { + prepare: func() *CouchDB { + cdb := New() + cdb.Config.Databases = "bad_db db1 db2" + return cdb + }, + wantCollected: map[string]int64{ + + // node stats + "couch_replicator_jobs_crashed": 1, + "couch_replicator_jobs_pending": 1, + "couch_replicator_jobs_running": 1, + "couchdb_database_reads": 1, + "couchdb_database_writes": 14, + "couchdb_httpd_request_methods_COPY": 1, + "couchdb_httpd_request_methods_DELETE": 1, + "couchdb_httpd_request_methods_GET": 75544, + "couchdb_httpd_request_methods_HEAD": 1, + "couchdb_httpd_request_methods_OPTIONS": 1, + "couchdb_httpd_request_methods_POST": 15, + "couchdb_httpd_request_methods_PUT": 3, + "couchdb_httpd_status_codes_200": 75294, + "couchdb_httpd_status_codes_201": 15, + "couchdb_httpd_status_codes_202": 1, + "couchdb_httpd_status_codes_204": 1, + "couchdb_httpd_status_codes_206": 1, + "couchdb_httpd_status_codes_301": 1, + "couchdb_httpd_status_codes_302": 1, + "couchdb_httpd_status_codes_304": 1, + "couchdb_httpd_status_codes_400": 1, + "couchdb_httpd_status_codes_401": 20, + "couchdb_httpd_status_codes_403": 1, + "couchdb_httpd_status_codes_404": 225, + "couchdb_httpd_status_codes_405": 1, + "couchdb_httpd_status_codes_406": 1, + "couchdb_httpd_status_codes_409": 1, + "couchdb_httpd_status_codes_412": 3, + "couchdb_httpd_status_codes_413": 1, + "couchdb_httpd_status_codes_414": 1, + "couchdb_httpd_status_codes_415": 1, + "couchdb_httpd_status_codes_416": 1, + "couchdb_httpd_status_codes_417": 1, + "couchdb_httpd_status_codes_500": 1, + "couchdb_httpd_status_codes_501": 1, + "couchdb_httpd_status_codes_503": 1, + "couchdb_httpd_status_codes_2xx": 75312, + "couchdb_httpd_status_codes_3xx": 3, + "couchdb_httpd_status_codes_4xx": 258, + "couchdb_httpd_status_codes_5xx": 3, + "couchdb_httpd_view_reads": 1, + "couchdb_open_os_files": 1, + + // node system + "context_switches": 22614499, + "ets_table_count": 116, + "internal_replication_jobs": 1, + "io_input": 49674812, + "io_output": 686400800, + "memory_atom_used": 488328, + "memory_atom": 504433, + "memory_binary": 297696, + "memory_code": 11252688, + "memory_ets": 1579120, + "memory_other": 20427855, + "memory_processes": 9161448, + "os_proc_count": 1, + "peak_msg_queue": 2, + "process_count": 296, + "reductions": 43211228312, + "run_queue": 1, + + // active tasks + "active_tasks_database_compaction": 1, + "active_tasks_indexer": 2, + "active_tasks_replication": 1, + "active_tasks_view_compaction": 1, + + // databases + "db_db1_db_doc_counts": 14, + "db_db1_db_doc_del_counts": 1, + "db_db1_db_sizes_active": 2818, + "db_db1_db_sizes_external": 588, + "db_db1_db_sizes_file": 74115, + + "db_db2_db_doc_counts": 15, + "db_db2_db_doc_del_counts": 1, + "db_db2_db_sizes_active": 1818, + "db_db2_db_sizes_external": 288, + "db_db2_db_sizes_file": 7415, + }, + checkCharts: false, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + cdb, cleanup := prepareCouchDB(t, test.prepare) + defer cleanup() + + var collected map[string]int64 + for i := 0; i < 10; i++ { + collected = cdb.Collect() + } + + assert.Equal(t, test.wantCollected, collected) + if test.checkCharts { + ensureCollectedHasAllChartsDimsVarsIDs(t, cdb, collected) + } + }) + } +} + +func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, cdb *CouchDB, collected map[string]int64) { + for _, chart := range *cdb.Charts() { + if chart.Obsolete { + continue + } + for _, dim := range chart.Dims { + _, ok := collected[dim.ID] + assert.Truef(t, ok, "collected metrics has no data for dim '%s' chart '%s'", dim.ID, chart.ID) + } + for _, v := range chart.Vars { + _, ok := collected[v.ID] + assert.Truef(t, ok, "collected metrics has no data for var '%s' chart '%s'", v.ID, chart.ID) + } + } +} + +func prepareCouchDB(t *testing.T, createCDB func() *CouchDB) (cdb *CouchDB, cleanup func()) { + t.Helper() + cdb = createCDB() + srv := prepareCouchDBEndpoint() + cdb.URL = srv.URL + + require.NoError(t, cdb.Init()) + + return cdb, srv.Close +} + +func prepareCouchDBValidData(t *testing.T) (cdb *CouchDB, cleanup func()) { + return prepareCouchDB(t, New) +} + +func prepareCouchDBInvalidData(t *testing.T) (*CouchDB, func()) { + t.Helper() + srv := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte("hello and\n goodbye")) + })) + cdb := New() + cdb.URL = srv.URL + require.NoError(t, cdb.Init()) + + return cdb, srv.Close +} + +func prepareCouchDB404(t *testing.T) (*CouchDB, func()) { + t.Helper() + srv := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + })) + cdb := New() + cdb.URL = srv.URL + require.NoError(t, cdb.Init()) + + return cdb, srv.Close +} + +func prepareCouchDBConnectionRefused(t *testing.T) (*CouchDB, func()) { + t.Helper() + cdb := New() + cdb.URL = "http://127.0.0.1:38001" + require.NoError(t, cdb.Init()) + + return cdb, func() {} +} + +func prepareCouchDBEndpoint() *httptest.Server { + return httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/_node/_local/_stats": + _, _ = w.Write(dataVer311NodeStats) + case "/_node/_local/_system": + _, _ = w.Write(dataVer311NodeSystem) + case urlPathActiveTasks: + _, _ = w.Write(dataVer311ActiveTasks) + case "/_dbs_info": + _, _ = w.Write(dataVer311DbsInfo) + case "/": + _, _ = w.Write(dataVer311Root) + default: + w.WriteHeader(http.StatusNotFound) + } + })) +} + +func numOfCharts(charts ...Charts) (num int) { + for _, v := range charts { + num += len(v) + } + return num +} diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/init.go b/src/go/collectors/go.d.plugin/modules/couchdb/init.go new file mode 100644 index 000000000..028848152 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/init.go @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package couchdb + +import ( + "errors" + "net/http" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/web" +) + +func (cdb *CouchDB) validateConfig() error { + if cdb.URL == "" { + return errors.New("URL not set") + } + if cdb.Node == "" { + return errors.New("'node' not set") + } + if _, err := web.NewHTTPRequest(cdb.Request); err != nil { + return err + } + return nil +} + +func (cdb *CouchDB) initHTTPClient() (*http.Client, error) { + return web.NewHTTPClient(cdb.Client) +} + +func (cdb *CouchDB) initCharts() (*Charts, error) { + charts := module.Charts{} + + if err := charts.Add(*dbActivityCharts.Copy()...); err != nil { + return nil, err + } + if err := charts.Add(*httpTrafficBreakdownCharts.Copy()...); err != nil { + return nil, err + } + if err := charts.Add(*serverOperationsCharts.Copy()...); err != nil { + return nil, err + } + if len(cdb.databases) != 0 { + dbCharts := dbSpecificCharts.Copy() + + if err := charts.Add(*dbCharts...); err != nil { + return nil, err + } + + for _, chart := range *dbCharts { + for _, db := range cdb.databases { + if err := chart.AddDim(&module.Dim{ID: "db_" + db + "_" + chart.ID, Name: db}); err != nil { + return nil, err + } + } + } + + } + if err := charts.Add(*erlangStatisticsCharts.Copy()...); err != nil { + return nil, err + } + + if len(charts) == 0 { + return nil, errors.New("zero charts") + } + return &charts, nil +} diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/integrations/couchdb.md b/src/go/collectors/go.d.plugin/modules/couchdb/integrations/couchdb.md new file mode 100644 index 000000000..8a59d181e --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/integrations/couchdb.md @@ -0,0 +1,225 @@ +<!--startmeta +custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/couchdb/README.md" +meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/couchdb/metadata.yaml" +sidebar_label: "CouchDB" +learn_status: "Published" +learn_rel_path: "Collecting Metrics/Databases" +most_popular: False +message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE" +endmeta--> + +# CouchDB + + +<img src="https://netdata.cloud/img/couchdb.svg" width="150"/> + + +Plugin: go.d.plugin +Module: couchdb + +<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" /> + +## Overview + +This collector monitors CouchDB 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 CouchDB instance + +These metrics refer to the entire monitored application. + +This scope has no labels. + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| couchdb.activity | db_reads, db_writes, view_reads | requests/s | +| couchdb.request_methods | copy, delete, get, head, options, post, put | requests/s | +| couchdb.response_codes | 200, 201, 202, 204, 206, 301, 302, 304, 400, 401, 403, 404, 406, 409, 412, 413, 414, 415, 416, 417, 500, 501, 503 | responses/s | +| couchdb.response_code_classes | 2xx, 3xx, 4xx, 5xx | responses/s | +| couchdb.active_tasks | indexer, db_compaction, replication, view_compaction | tasks | +| couchdb.replicator_jobs | running, pending, crashed, internal_replication_jobs | jobs | +| couchdb.open_files | files | files | +| couchdb.erlang_vm_memory | atom, binaries, code, ets, procs, other | B | +| couchdb.proccounts | os_procs, erl_procs | processes | +| couchdb.peakmsgqueue | peak_size | messages | +| couchdb.reductions | reductions | reductions | +| couchdb.db_sizes_file | a dimension per database | KiB | +| couchdb.db_sizes_external | a dimension per database | KiB | +| couchdb.db_sizes_active | a dimension per database | KiB | +| couchdb.db_doc_count | a dimension per database | docs | +| couchdb.db_doc_del_count | a dimension per database | docs | + + + +## Alerts + +There are no alerts configured by default for this integration. + + +## Setup + +### Prerequisites + +No action required. + +### Configuration + +#### File + +The configuration file name for this integration is `go.d/couchdb.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/couchdb.conf +``` +#### Options + +The following options can be defined globally: update_every, autodetection_retry. + + +<details open><summary>Config</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:5984 | yes | +| node | CouchDB node name. Same as -name vm.args argument. | _local | no | +| databases | List of database names for which db-specific stats should be displayed, space separated. | | 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 | +| timeout | HTTP request timeout. | 2 | 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 + +An example configuration. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + url: http://127.0.0.1:5984 + +``` +</details> + +##### Basic HTTP auth + +Local server with basic HTTP authentication, node name and multiple databases defined. Make sure to match the node name with the `NODENAME` value in your CouchDB's `etc/vm.args` file. Typically, this is of the form `couchdb@fully.qualified.domain.name` in a cluster, or `couchdb@127.0.0.1` for a single-node server. + + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + url: http://127.0.0.1:5984 + node: couchdb@127.0.0.1 + databases: my-db other-db + username: foo + password: bar + +``` +</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:5984 + + - name: remote + url: http://203.0.113.0:5984 + +``` +</details> + + + +## Troubleshooting + +### Debug Mode + +To troubleshoot issues with the `couchdb` 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 couchdb + ``` + + diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/metadata.yaml b/src/go/collectors/go.d.plugin/modules/couchdb/metadata.yaml new file mode 100644 index 000000000..2f0036db2 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/metadata.yaml @@ -0,0 +1,323 @@ +plugin_name: go.d.plugin +modules: + - meta: + id: collector-go.d.plugin-couchdb + plugin_name: go.d.plugin + module_name: couchdb + monitored_instance: + name: CouchDB + link: https://couchdb.apache.org/ + icon_filename: couchdb.svg + categories: + - data-collection.database-servers + keywords: + - couchdb + - databases + related_resources: + integrations: + list: [] + info_provided_to_referring_integrations: + description: "" + most_popular: false + overview: + data_collection: + metrics_description: | + This collector monitors CouchDB 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: [] + configuration: + file: + name: go.d/couchdb.conf + options: + description: | + The following options can be defined globally: update_every, autodetection_retry. + folding: + title: Config + 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:5984 + required: true + - name: node + description: CouchDB node name. Same as -name vm.args argument. + default_value: "_local" + required: false + - name: databases + description: List of database names for which db-specific stats should be displayed, space separated. + default_value: "" + 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: timeout + description: HTTP request timeout. + default_value: 2 + 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: An example configuration. + folding: + title: Example + enabled: true + config: | + jobs: + - name: local + url: http://127.0.0.1:5984 + - name: Basic HTTP auth + description: > + Local server with basic HTTP authentication, node name and multiple databases defined. + Make sure to match the node name with the `NODENAME` value in your CouchDB's `etc/vm.args` file. + Typically, this is of the form `couchdb@fully.qualified.domain.name` in a cluster, or `couchdb@127.0.0.1` for a single-node server. + config: | + jobs: + - name: local + url: http://127.0.0.1:5984 + node: couchdb@127.0.0.1 + databases: my-db other-db + username: foo + password: bar + - 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:5984 + + - name: remote + url: http://203.0.113.0:5984 + 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: couchdb.activity + description: Overall Activity + unit: requests/s + chart_type: stacked + dimensions: + - name: db_reads + - name: db_writes + - name: view_reads + - name: couchdb.request_methods + description: HTTP request methods + unit: requests/s + chart_type: stacked + dimensions: + - name: copy + - name: delete + - name: get + - name: head + - name: options + - name: post + - name: put + - name: couchdb.response_codes + description: HTTP response status codes + unit: responses/s + chart_type: stacked + dimensions: + - name: "200" + - name: "201" + - name: "202" + - name: "204" + - name: "206" + - name: "301" + - name: "302" + - name: "304" + - name: "400" + - name: "401" + - name: "403" + - name: "404" + - name: "406" + - name: "409" + - name: "412" + - name: "413" + - name: "414" + - name: "415" + - name: "416" + - name: "417" + - name: "500" + - name: "501" + - name: "503" + - name: couchdb.response_code_classes + description: HTTP response status code classes + unit: responses/s + chart_type: stacked + dimensions: + - name: 2xx + - name: 3xx + - name: 4xx + - name: 5xx + - name: couchdb.active_tasks + description: Active task breakdown + unit: tasks + chart_type: stacked + dimensions: + - name: indexer + - name: db_compaction + - name: replication + - name: view_compaction + - name: couchdb.replicator_jobs + description: Replicator job breakdown + unit: jobs + chart_type: stacked + dimensions: + - name: running + - name: pending + - name: crashed + - name: internal_replication_jobs + - name: couchdb.open_files + description: Open files + unit: files + chart_type: line + dimensions: + - name: files + - name: couchdb.erlang_vm_memory + description: Erlang VM memory usage + unit: B + chart_type: stacked + dimensions: + - name: atom + - name: binaries + - name: code + - name: ets + - name: procs + - name: other + - name: couchdb.proccounts + description: Process counts + unit: processes + chart_type: line + dimensions: + - name: os_procs + - name: erl_procs + - name: couchdb.peakmsgqueue + description: Peak message queue size + unit: messages + chart_type: line + dimensions: + - name: peak_size + - name: couchdb.reductions + description: Erlang reductions + unit: reductions + chart_type: line + dimensions: + - name: reductions + - name: couchdb.db_sizes_file + description: Database sizes (file) + unit: KiB + chart_type: line + dimensions: + - name: a dimension per database + - name: couchdb.db_sizes_external + description: Database sizes (external) + unit: KiB + chart_type: line + dimensions: + - name: a dimension per database + - name: couchdb.db_sizes_active + description: Database sizes (active) + unit: KiB + chart_type: line + dimensions: + - name: a dimension per database + - name: couchdb.db_doc_count + description: 'Database # of docs' + unit: docs + chart_type: line + dimensions: + - name: a dimension per database + - name: couchdb.db_doc_del_count + description: 'Database # of deleted docs' + unit: docs + chart_type: line + dimensions: + - name: a dimension per database diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/metrics.go b/src/go/collectors/go.d.plugin/modules/couchdb/metrics.go new file mode 100644 index 000000000..4d2f02679 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/metrics.go @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package couchdb + +// https://docs.couchdb.org/en/stable/api/index.html + +type cdbMetrics struct { + // https://docs.couchdb.org/en/stable/api/server/common.html#active-tasks + ActiveTasks []cdbActiveTask + // https://docs.couchdb.org/en/stable/api/server/common.html#node-node-name-stats + NodeStats *cdbNodeStats + // https://docs.couchdb.org/en/stable/api/server/common.html#node-node-name-system + NodeSystem *cdbNodeSystem + // https://docs.couchdb.org/en/stable/api/database/common.html + DBStats []cdbDBStats +} + +func (m cdbMetrics) empty() bool { + switch { + case m.hasActiveTasks(), m.hasNodeStats(), m.hasNodeSystem(), m.hasDBStats(): + return false + } + return true +} + +func (m cdbMetrics) hasActiveTasks() bool { return m.ActiveTasks != nil } +func (m cdbMetrics) hasNodeStats() bool { return m.NodeStats != nil } +func (m cdbMetrics) hasNodeSystem() bool { return m.NodeSystem != nil } +func (m cdbMetrics) hasDBStats() bool { return m.DBStats != nil } + +type cdbActiveTask struct { + Type string `json:"type"` +} + +type cdbNodeStats struct { + CouchDB struct { + DatabaseReads struct { + Value float64 `stm:"" json:"value"` + } `stm:"database_reads" json:"database_reads"` + DatabaseWrites struct { + Value float64 `stm:"" json:"value"` + } `stm:"database_writes" json:"database_writes"` + HTTPd struct { + ViewReads struct { + Value float64 `stm:"" json:"value"` + } `stm:"view_reads" json:"view_reads"` + } `stm:"httpd" json:"httpd"` + HTTPdRequestMethods struct { + Copy struct { + Value float64 `stm:"" json:"value"` + } `stm:"COPY" json:"COPY"` + Delete struct { + Value float64 `stm:"" json:"value"` + } `stm:"DELETE" json:"DELETE"` + Get struct { + Value float64 `stm:"" json:"value"` + } `stm:"GET" json:"GET"` + Head struct { + Value float64 `stm:"" json:"value"` + } `stm:"HEAD" json:"HEAD"` + Options struct { + Value float64 `stm:"" json:"value"` + } `stm:"OPTIONS" json:"OPTIONS"` + Post struct { + Value float64 `stm:"" json:"value"` + } `stm:"POST" json:"POST"` + Put struct { + Value float64 `stm:"" json:"value"` + } `stm:"PUT" json:"PUT"` + } `stm:"httpd_request_methods" json:"httpd_request_methods"` + HTTPdStatusCodes struct { + Code200 struct { + Value float64 `stm:"" json:"value"` + } `stm:"200" json:"200"` + Code201 struct { + Value float64 `stm:"" json:"value"` + } `stm:"201" json:"201"` + Code202 struct { + Value float64 `stm:"" json:"value"` + } `stm:"202" json:"202"` + Code204 struct { + Value float64 `stm:"" json:"value"` + } `stm:"204" json:"204"` + Code206 struct { + Value float64 `stm:"" json:"value"` + } `stm:"206" json:"206"` + Code301 struct { + Value float64 `stm:"" json:"value"` + } `stm:"301" json:"301"` + Code302 struct { + Value float64 `stm:"" json:"value"` + } `stm:"302" json:"302"` + Code304 struct { + Value float64 `stm:"" json:"value"` + } `stm:"304" json:"304"` + Code400 struct { + Value float64 `stm:"" json:"value"` + } `stm:"400" json:"400"` + Code401 struct { + Value float64 `stm:"" json:"value"` + } `stm:"401" json:"401"` + Code403 struct { + Value float64 `stm:"" json:"value"` + } `stm:"403" json:"403"` + Code404 struct { + Value float64 `stm:"" json:"value"` + } `stm:"404" json:"404"` + Code405 struct { + Value float64 `stm:"" json:"value"` + } `stm:"405" json:"405"` + Code406 struct { + Value float64 `stm:"" json:"value"` + } `stm:"406" json:"406"` + Code409 struct { + Value float64 `stm:"" json:"value"` + } `stm:"409" json:"409"` + Code412 struct { + Value float64 `stm:"" json:"value"` + } `stm:"412" json:"412"` + Code413 struct { + Value float64 `stm:"" json:"value"` + } `stm:"413" json:"413"` + Code414 struct { + Value float64 `stm:"" json:"value"` + } `stm:"414" json:"414"` + Code415 struct { + Value float64 `stm:"" json:"value"` + } `stm:"415" json:"415"` + Code416 struct { + Value float64 `stm:"" json:"value"` + } `stm:"416" json:"416"` + Code417 struct { + Value float64 `stm:"" json:"value"` + } `stm:"417" json:"417"` + Code500 struct { + Value float64 `stm:"" json:"value"` + } `stm:"500" json:"500"` + Code501 struct { + Value float64 `stm:"" json:"value"` + } `stm:"501" json:"501"` + Code503 struct { + Value float64 `stm:"" json:"value"` + } `stm:"503" json:"503"` + } `stm:"httpd_status_codes" json:"httpd_status_codes"` + OpenOSFiles struct { + Value float64 `stm:"" json:"value"` + } `stm:"open_os_files" json:"open_os_files"` + } `stm:"couchdb" json:"couchdb"` + CouchReplicator struct { + Jobs struct { + Running struct { + Value float64 `stm:"" json:"value"` + } `stm:"running" json:"running"` + Pending struct { + Value float64 `stm:"" json:"value"` + } `stm:"pending" json:"pending"` + Crashed struct { + Value float64 `stm:"" json:"value"` + } `stm:"crashed" json:"crashed"` + } `stm:"jobs" json:"jobs"` + } `stm:"couch_replicator" json:"couch_replicator"` +} + +type cdbNodeSystem struct { + Memory struct { + Other float64 `stm:"other" json:"other"` + Atom float64 `stm:"atom" json:"atom"` + AtomUsed float64 `stm:"atom_used" json:"atom_used"` + Processes float64 `stm:"processes" json:"processes"` + Binary float64 `stm:"binary" json:"binary"` + Code float64 `stm:"code" json:"code"` + Ets float64 `stm:"ets" json:"ets"` + } `stm:"memory" json:"memory"` + + RunQueue float64 `stm:"run_queue" json:"run_queue"` + EtsTableCount float64 `stm:"ets_table_count" json:"ets_table_count"` + ContextSwitches float64 `stm:"context_switches" json:"context_switches"` + Reductions float64 `stm:"reductions" json:"reductions"` + IOInput float64 `stm:"io_input" json:"io_input"` + IOOutput float64 `stm:"io_output" json:"io_output"` + OSProcCount float64 `stm:"os_proc_count" json:"os_proc_count"` + ProcessCount float64 `stm:"process_count" json:"process_count"` + InternalReplicationJobs float64 `stm:"internal_replication_jobs" json:"internal_replication_jobs"` + + MessageQueues map[string]interface{} `json:"message_queues"` +} + +type cdbDBStats struct { + Key string + Error string + Info struct { + Sizes struct { + File float64 `stm:"file" json:"file"` + External float64 `stm:"external" json:"external"` + Active float64 `stm:"active" json:"active"` + } `stm:"db_sizes" json:"sizes"` + DocDelCount float64 `stm:"db_doc_del_counts" json:"doc_del_count"` + DocCount float64 `stm:"db_doc_counts" json:"doc_count"` + } +} diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/testdata/config.json b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/config.json new file mode 100644 index 000000000..0fa716e5d --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/config.json @@ -0,0 +1,22 @@ +{ + "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, + "node": "ok", + "databases": "ok" +} diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/testdata/config.yaml b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/config.yaml new file mode 100644 index 000000000..4968ed263 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/config.yaml @@ -0,0 +1,19 @@ +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 +node: "ok" +databases: "ok" diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/active_tasks.json b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/active_tasks.json new file mode 100644 index 000000000..788fe5642 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/active_tasks.json @@ -0,0 +1,63 @@ +[ + { + "changes_done": 64438, + "database": "mailbox", + "pid": "<0.12986.1>", + "progress": 84, + "started_on": 1376116576, + "total_changes": 76215, + "type": "database_compaction", + "updated_on": 1376116619 + }, + { + "changes_done": 26534, + "database": "mailbox", + "pid": "<0.12943.2>", + "progress": 23, + "started_on": 1376116592, + "total_changes": 76215, + "type": "view_compaction", + "updated_on": 1376116637 + }, + { + "changes_done": 14443, + "database": "mailbox", + "design_document": "c9753817b3ba7c674d92361f24f59b9f", + "pid": "<0.10461.3>", + "progress": 18, + "started_on": 1376116621, + "total_changes": 76215, + "type": "indexer", + "updated_on": 1376116650 + }, + { + "changes_done": 5454, + "database": "mailbox", + "design_document": "_design/meta", + "pid": "<0.6838.4>", + "progress": 7, + "started_on": 1376116632, + "total_changes": 76215, + "type": "indexer", + "updated_on": 1376116651 + }, + { + "checkpointed_source_seq": 68585, + "continuous": false, + "doc_id": null, + "doc_write_failures": 1, + "docs_read": 4524, + "docs_written": 4524, + "missing_revisions_found": 4524, + "pid": "<0.1538.5>", + "progress": 44, + "replication_id": "9bc1727d74d49d9e157e260bb8bbd1d5", + "revisions_checked": 4524, + "source": "mailbox", + "source_seq": 154419, + "started_on": 1376116644, + "target": "http://mailsrv:5984/mailbox", + "type": "replication", + "updated_on": 1376116651 + } +] diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/dbs_info.json b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/dbs_info.json new file mode 100644 index 000000000..9ca43a53c --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/dbs_info.json @@ -0,0 +1,52 @@ +[ + { + "key": "db1", + "info": { + "db_name": "db1", + "purge_seq": "0-g1AAAABPeJzLYWBgYMpgTmHgzcvPy09JdcjLz8gvLskBCeexAEmGBiD1HwiyEhlwqEtkSKqHKMgCAIT2GV4", + "update_seq": "14-g1AAAABPeJzLYWBgYMpgTmHgzcvPy09JdcjLz8gvLskBCeexAEmGBiD1HwiyEjlxqEtkSKoHK2DNAgCGOxls", + "sizes": { + "file": 74115, + "external": 588, + "active": 2818 + }, + "props": {}, + "doc_del_count": 1, + "doc_count": 14, + "disk_format_version": 8, + "compact_running": false, + "cluster": { + "q": 2, + "n": 1, + "w": 1, + "r": 1 + }, + "instance_start_time": "0" + } + }, + { + "key": "db2", + "info": { + "db_name": "db2", + "purge_seq": "0-g1AAAABPeJzLYWBgYMpgTmHgzcvPy09JdcjLz8gvLskBCeexAEmGBiD1HwiyEhlwqEtkSKqHKMgCAIT2GV5", + "update_seq": "14-g1AAAABPeJzLYWBgYMpgTmHgzcvPy09JdcjLz8gvLskBCeexAEmGBiD1HwiyEjlxqEtkSKoHK2DNAgCGOxlt", + "sizes": { + "file": 7415, + "external": 288, + "active": 1818 + }, + "props": {}, + "doc_del_count": 1, + "doc_count": 15, + "disk_format_version": 8, + "compact_running": false, + "cluster": { + "q": 2, + "n": 1, + "w": 1, + "r": 1 + }, + "instance_start_time": "0" + } + } +] diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/node_stats.json b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/node_stats.json new file mode 100644 index 000000000..ae31366af --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/node_stats.json @@ -0,0 +1,1651 @@ +{ + "global_changes": { + "db_writes": { + "value": 1, + "type": "counter", + "desc": "number of db writes performed by global changes" + }, + "event_doc_conflict": { + "value": 1, + "type": "counter", + "desc": "number of conflicted event docs encountered by global changes" + }, + "listener_pending_updates": { + "value": 1, + "type": "gauge", + "desc": "number of global changes updates pending writes in global_changes_listener" + }, + "rpcs": { + "value": 1, + "type": "counter", + "desc": "number of rpc operations performed by global_changes" + }, + "server_pending_updates": { + "value": 1, + "type": "gauge", + "desc": "number of global changes updates pending writes in global_changes_server" + } + }, + "couchdb": { + "httpd": { + "aborted_requests": { + "value": 1, + "type": "counter", + "desc": "number of aborted requests" + }, + "bulk_docs": { + "value": { + "min": 0.0, + "max": 0.0, + "arithmetic_mean": 0.0, + "geometric_mean": 0.0, + "harmonic_mean": 0.0, + "median": 0.0, + "variance": 0.0, + "standard_deviation": 0.0, + "skewness": 0.0, + "kurtosis": 0.0, + "percentile": [ + [ + 50, + 0.0 + ], + [ + 75, + 0.0 + ], + [ + 90, + 0.0 + ], + [ + 95, + 0.0 + ], + [ + 99, + 0.0 + ], + [ + 999, + 0.0 + ] + ], + "histogram": [ + [ + 0, + 0 + ] + ], + "n": 0 + }, + "type": "histogram", + "desc": "distribution of the number of docs in _bulk_docs requests" + }, + "bulk_requests": { + "value": 1, + "type": "counter", + "desc": "number of bulk requests" + }, + "requests": { + "value": 75562, + "type": "counter", + "desc": "number of HTTP requests" + }, + "view_timeouts": { + "value": 1, + "type": "counter", + "desc": "number of HTTP view timeouts" + }, + "find_timeouts": { + "value": 1, + "type": "counter", + "desc": "number of HTTP find timeouts" + }, + "explain_timeouts": { + "value": 1, + "type": "counter", + "desc": "number of HTTP _explain timeouts" + }, + "all_docs_timeouts": { + "value": 1, + "type": "counter", + "desc": "number of HTTP all_docs timeouts" + }, + "partition_view_requests": { + "value": 1, + "type": "counter", + "desc": "number of partition HTTP view requests" + }, + "partition_find_requests": { + "value": 1, + "type": "counter", + "desc": "number of partition HTTP _find requests" + }, + "partition_explain_requests": { + "value": 1, + "type": "counter", + "desc": "number of partition HTTP _explain requests" + }, + "partition_all_docs_requests": { + "value": 1, + "type": "counter", + "desc": "number of partition HTTP _all_docs requests" + }, + "partition_view_timeouts": { + "value": 1, + "type": "counter", + "desc": "number of partition HTTP view timeouts" + }, + "partition_find_timeouts": { + "value": 1, + "type": "counter", + "desc": "number of partition HTTP find timeouts" + }, + "partition_explain_timeouts": { + "value": 1, + "type": "counter", + "desc": "number of partition HTTP _explain timeouts" + }, + "partition_all_docs_timeouts": { + "value": 1, + "type": "counter", + "desc": "number of partition HTTP all_docs timeouts" + }, + "temporary_view_reads": { + "value": 1, + "type": "counter", + "desc": "number of temporary view reads" + }, + "view_reads": { + "value": 1, + "type": "counter", + "desc": "number of view reads" + }, + "clients_requesting_changes": { + "value": 1, + "type": "counter", + "desc": "number of clients for continuous _changes" + }, + "purge_requests": { + "value": 1, + "type": "counter", + "desc": "number of purge requests" + } + }, + "dbinfo": { + "value": { + "min": 0.0, + "max": 0.0, + "arithmetic_mean": 0.0, + "geometric_mean": 0.0, + "harmonic_mean": 0.0, + "median": 0.0, + "variance": 0.0, + "standard_deviation": 0.0, + "skewness": 0.0, + "kurtosis": 0.0, + "percentile": [ + [ + 50, + 0.0 + ], + [ + 75, + 0.0 + ], + [ + 90, + 0.0 + ], + [ + 95, + 0.0 + ], + [ + 99, + 0.0 + ], + [ + 999, + 0.0 + ] + ], + "histogram": [ + [ + 0, + 0 + ] + ], + "n": 0 + }, + "type": "histogram", + "desc": "distribution of latencies for calls to retrieve DB info" + }, + "io_queue": { + "search": { + "value": 1, + "type": "counter", + "desc": "Search IO directly triggered by client requests" + } + }, + "io_queue2": { + "search": { + "count": { + "value": 1, + "type": "counter", + "desc": "Search IO directly triggered by client requests" + } + } + }, + "auth_cache_hits": { + "value": 1, + "type": "counter", + "desc": "number of authentication cache hits" + }, + "auth_cache_misses": { + "value": 2, + "type": "counter", + "desc": "number of authentication cache misses" + }, + "collect_results_time": { + "value": { + "min": 0.0, + "max": 0.0, + "arithmetic_mean": 0.0, + "geometric_mean": 0.0, + "harmonic_mean": 0.0, + "median": 0.0, + "variance": 0.0, + "standard_deviation": 0.0, + "skewness": 0.0, + "kurtosis": 0.0, + "percentile": [ + [ + 50, + 0.0 + ], + [ + 75, + 0.0 + ], + [ + 90, + 0.0 + ], + [ + 95, + 0.0 + ], + [ + 99, + 0.0 + ], + [ + 999, + 0.0 + ] + ], + "histogram": [ + [ + 0, + 0 + ] + ], + "n": 0 + }, + "type": "histogram", + "desc": "microsecond latency for calls to couch_db:collect_results/3" + }, + "database_writes": { + "value": 14, + "type": "counter", + "desc": "number of times a database was changed" + }, + "database_reads": { + "value": 1, + "type": "counter", + "desc": "number of times a document was read from a database" + }, + "database_purges": { + "value": 1, + "type": "counter", + "desc": "number of times a database was purged" + }, + "db_open_time": { + "value": { + "min": 0.0, + "max": 0.0, + "arithmetic_mean": 0.0, + "geometric_mean": 0.0, + "harmonic_mean": 0.0, + "median": 0.0, + "variance": 0.0, + "standard_deviation": 0.0, + "skewness": 0.0, + "kurtosis": 0.0, + "percentile": [ + [ + 50, + 0.0 + ], + [ + 75, + 0.0 + ], + [ + 90, + 0.0 + ], + [ + 95, + 0.0 + ], + [ + 99, + 0.0 + ], + [ + 999, + 0.0 + ] + ], + "histogram": [ + [ + 0, + 0 + ] + ], + "n": 0 + }, + "type": "histogram", + "desc": "milliseconds required to open a database" + }, + "document_inserts": { + "value": 17, + "type": "counter", + "desc": "number of documents inserted" + }, + "document_writes": { + "value": 17, + "type": "counter", + "desc": "number of document write operations" + }, + "document_purges": { + "total": { + "value": 1, + "type": "counter", + "desc": "number of total document purge operations" + }, + "success": { + "value": 1, + "type": "counter", + "desc": "number of successful document purge operations" + }, + "failure": { + "value": 1, + "type": "counter", + "desc": "number of failed document purge operations" + } + }, + "local_document_writes": { + "value": 1, + "type": "counter", + "desc": "number of _local document write operations" + }, + "httpd_request_methods": { + "COPY": { + "value": 1, + "type": "counter", + "desc": "number of HTTP COPY requests" + }, + "DELETE": { + "value": 1, + "type": "counter", + "desc": "number of HTTP DELETE requests" + }, + "GET": { + "value": 75544, + "type": "counter", + "desc": "number of HTTP GET requests" + }, + "HEAD": { + "value": 1, + "type": "counter", + "desc": "number of HTTP HEAD requests" + }, + "OPTIONS": { + "value": 1, + "type": "counter", + "desc": "number of HTTP OPTIONS requests" + }, + "POST": { + "value": 15, + "type": "counter", + "desc": "number of HTTP POST requests" + }, + "PUT": { + "value": 3, + "type": "counter", + "desc": "number of HTTP PUT requests" + } + }, + "httpd_status_codes": { + "200": { + "value": 75294, + "type": "counter", + "desc": "number of HTTP 200 OK responses" + }, + "201": { + "value": 15, + "type": "counter", + "desc": "number of HTTP 201 Created responses" + }, + "202": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 202 Accepted responses" + }, + "204": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 204 No Content responses" + }, + "206": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 206 Partial Content" + }, + "301": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 301 Moved Permanently responses" + }, + "302": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 302 Found responses" + }, + "304": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 304 Not Modified responses" + }, + "400": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 400 Bad Request responses" + }, + "401": { + "value": 20, + "type": "counter", + "desc": "number of HTTP 401 Unauthorized responses" + }, + "403": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 403 Forbidden responses" + }, + "404": { + "value": 225, + "type": "counter", + "desc": "number of HTTP 404 Not Found responses" + }, + "405": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 405 Method Not Allowed responses" + }, + "406": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 406 Not Acceptable responses" + }, + "409": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 409 Conflict responses" + }, + "412": { + "value": 3, + "type": "counter", + "desc": "number of HTTP 412 Precondition Failed responses" + }, + "413": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 413 Request Entity Too Long responses" + }, + "414": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 414 Request URI Too Long responses" + }, + "415": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 415 Unsupported Media Type responses" + }, + "416": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 416 Requested Range Not Satisfiable responses" + }, + "417": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 417 Expectation Failed responses" + }, + "500": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 500 Internal Server Error responses" + }, + "501": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 501 Not Implemented responses" + }, + "503": { + "value": 1, + "type": "counter", + "desc": "number of HTTP 503 Service unavailable responses" + } + }, + "open_databases": { + "value": 1, + "type": "counter", + "desc": "number of open databases" + }, + "open_os_files": { + "value": 1, + "type": "counter", + "desc": "number of file descriptors CouchDB has open" + }, + "request_time": { + "value": { + "min": 0.0, + "max": 0.0, + "arithmetic_mean": 0.0, + "geometric_mean": 0.0, + "harmonic_mean": 0.0, + "median": 0.0, + "variance": 0.0, + "standard_deviation": 0.0, + "skewness": 0.0, + "kurtosis": 0.0, + "percentile": [ + [ + 50, + 0.0 + ], + [ + 75, + 0.0 + ], + [ + 90, + 0.0 + ], + [ + 95, + 0.0 + ], + [ + 99, + 0.0 + ], + [ + 999, + 0.0 + ] + ], + "histogram": [ + [ + 0, + 0 + ] + ], + "n": 0 + }, + "type": "histogram", + "desc": "length of a request inside CouchDB without MochiWeb" + }, + "couch_server": { + "lru_skip": { + "value": 1, + "type": "counter", + "desc": "number of couch_server LRU operations skipped" + } + }, + "query_server": { + "vdu_rejects": { + "value": 1, + "type": "counter", + "desc": "number of rejections by validate_doc_update function" + }, + "vdu_process_time": { + "value": { + "min": 0.0, + "max": 0.0, + "arithmetic_mean": 0.0, + "geometric_mean": 0.0, + "harmonic_mean": 0.0, + "median": 0.0, + "variance": 0.0, + "standard_deviation": 0.0, + "skewness": 0.0, + "kurtosis": 0.0, + "percentile": [ + [ + 50, + 0.0 + ], + [ + 75, + 0.0 + ], + [ + 90, + 0.0 + ], + [ + 95, + 0.0 + ], + [ + 99, + 0.0 + ], + [ + 999, + 0.0 + ] + ], + "histogram": [ + [ + 0, + 0 + ] + ], + "n": 0 + }, + "type": "histogram", + "desc": "duration of validate_doc_update function calls" + } + }, + "mrview": { + "map_doc": { + "value": 1, + "type": "counter", + "desc": "number of documents mapped in the view server" + }, + "emits": { + "value": 1, + "type": "counter", + "desc": "number of invocations of `emit' in map functions in the view server" + } + } + }, + "mem3": { + "shard_cache": { + "eviction": { + "value": 1, + "type": "counter", + "desc": "number of shard cache evictions" + }, + "hit": { + "value": 185, + "type": "counter", + "desc": "number of shard cache hits" + }, + "miss": { + "value": 252470, + "type": "counter", + "desc": "number of shard cache misses" + } + } + }, + "ddoc_cache": { + "hit": { + "value": 1, + "type": "counter", + "desc": "number of design doc cache hits" + }, + "miss": { + "value": 3, + "type": "counter", + "desc": "number of design doc cache misses" + }, + "recovery": { + "value": 1, + "type": "counter", + "desc": "number of design doc cache recoveries" + } + }, + "couch_log": { + "level": { + "alert": { + "value": 1, + "type": "counter", + "desc": "number of logged alert messages" + }, + "critical": { + "value": 1, + "type": "counter", + "desc": "number of logged critical messages" + }, + "debug": { + "value": 1, + "type": "counter", + "desc": "number of logged debug messages" + }, + "emergency": { + "value": 1, + "type": "counter", + "desc": "number of logged emergency messages" + }, + "error": { + "value": 2, + "type": "counter", + "desc": "number of logged error messages" + }, + "info": { + "value": 8, + "type": "counter", + "desc": "number of logged info messages" + }, + "notice": { + "value": 126250, + "type": "counter", + "desc": "number of logged notice messages" + }, + "warning": { + "value": 8, + "type": "counter", + "desc": "number of logged warning messages" + } + } + }, + "dreyfus": { + "httpd": { + "search": { + "value": { + "min": 0.0, + "max": 0.0, + "arithmetic_mean": 0.0, + "geometric_mean": 0.0, + "harmonic_mean": 0.0, + "median": 0.0, + "variance": 0.0, + "standard_deviation": 0.0, + "skewness": 0.0, + "kurtosis": 0.0, + "percentile": [ + [ + 50, + 0.0 + ], + [ + 75, + 0.0 + ], + [ + 90, + 0.0 + ], + [ + 95, + 0.0 + ], + [ + 99, + 0.0 + ], + [ + 999, + 0.0 + ] + ], + "histogram": [ + [ + 0, + 0 + ] + ], + "n": 0 + }, + "type": "histogram", + "desc": "Distribution of overall search request latency as experienced by the end user" + } + }, + "rpc": { + "search": { + "value": { + "min": 0.0, + "max": 0.0, + "arithmetic_mean": 0.0, + "geometric_mean": 0.0, + "harmonic_mean": 0.0, + "median": 0.0, + "variance": 0.0, + "standard_deviation": 0.0, + "skewness": 0.0, + "kurtosis": 0.0, + "percentile": [ + [ + 50, + 0.0 + ], + [ + 75, + 0.0 + ], + [ + 90, + 0.0 + ], + [ + 95, + 0.0 + ], + [ + 99, + 0.0 + ], + [ + 999, + 0.0 + ] + ], + "histogram": [ + [ + 0, + 0 + ] + ], + "n": 0 + }, + "type": "histogram", + "desc": "length of a search RPC worker" + }, + "group1": { + "value": { + "min": 0.0, + "max": 0.0, + "arithmetic_mean": 0.0, + "geometric_mean": 0.0, + "harmonic_mean": 0.0, + "median": 0.0, + "variance": 0.0, + "standard_deviation": 0.0, + "skewness": 0.0, + "kurtosis": 0.0, + "percentile": [ + [ + 50, + 0.0 + ], + [ + 75, + 0.0 + ], + [ + 90, + 0.0 + ], + [ + 95, + 0.0 + ], + [ + 99, + 0.0 + ], + [ + 999, + 0.0 + ] + ], + "histogram": [ + [ + 0, + 0 + ] + ], + "n": 0 + }, + "type": "histogram", + "desc": "length of a group1 RPC worker" + }, + "group2": { + "value": { + "min": 0.0, + "max": 0.0, + "arithmetic_mean": 0.0, + "geometric_mean": 0.0, + "harmonic_mean": 0.0, + "median": 0.0, + "variance": 0.0, + "standard_deviation": 0.0, + "skewness": 0.0, + "kurtosis": 0.0, + "percentile": [ + [ + 50, + 0.0 + ], + [ + 75, + 0.0 + ], + [ + 90, + 0.0 + ], + [ + 95, + 0.0 + ], + [ + 99, + 0.0 + ], + [ + 999, + 0.0 + ] + ], + "histogram": [ + [ + 0, + 0 + ] + ], + "n": 0 + }, + "type": "histogram", + "desc": "length of a group2 RPC worker" + }, + "info": { + "value": { + "min": 0.0, + "max": 0.0, + "arithmetic_mean": 0.0, + "geometric_mean": 0.0, + "harmonic_mean": 0.0, + "median": 0.0, + "variance": 0.0, + "standard_deviation": 0.0, + "skewness": 0.0, + "kurtosis": 0.0, + "percentile": [ + [ + 50, + 0.0 + ], + [ + 75, + 0.0 + ], + [ + 90, + 0.0 + ], + [ + 95, + 0.0 + ], + [ + 99, + 0.0 + ], + [ + 999, + 0.0 + ] + ], + "histogram": [ + [ + 0, + 0 + ] + ], + "n": 0 + }, + "type": "histogram", + "desc": "length of an info RPC worker" + } + }, + "index": { + "await": { + "value": { + "min": 0.0, + "max": 0.0, + "arithmetic_mean": 0.0, + "geometric_mean": 0.0, + "harmonic_mean": 0.0, + "median": 0.0, + "variance": 0.0, + "standard_deviation": 0.0, + "skewness": 0.0, + "kurtosis": 0.0, + "percentile": [ + [ + 50, + 0.0 + ], + [ + 75, + 0.0 + ], + [ + 90, + 0.0 + ], + [ + 95, + 0.0 + ], + [ + 99, + 0.0 + ], + [ + 999, + 0.0 + ] + ], + "histogram": [ + [ + 0, + 0 + ] + ], + "n": 0 + }, + "type": "histogram", + "desc": "length of an dreyfus_index await request" + }, + "search": { + "value": { + "min": 0.0, + "max": 0.0, + "arithmetic_mean": 0.0, + "geometric_mean": 0.0, + "harmonic_mean": 0.0, + "median": 0.0, + "variance": 0.0, + "standard_deviation": 0.0, + "skewness": 0.0, + "kurtosis": 0.0, + "percentile": [ + [ + 50, + 0.0 + ], + [ + 75, + 0.0 + ], + [ + 90, + 0.0 + ], + [ + 95, + 0.0 + ], + [ + 99, + 0.0 + ], + [ + 999, + 0.0 + ] + ], + "histogram": [ + [ + 0, + 0 + ] + ], + "n": 0 + }, + "type": "histogram", + "desc": "length of an dreyfus_index search request" + }, + "group1": { + "value": { + "min": 0.0, + "max": 0.0, + "arithmetic_mean": 0.0, + "geometric_mean": 0.0, + "harmonic_mean": 0.0, + "median": 0.0, + "variance": 0.0, + "standard_deviation": 0.0, + "skewness": 0.0, + "kurtosis": 0.0, + "percentile": [ + [ + 50, + 0.0 + ], + [ + 75, + 0.0 + ], + [ + 90, + 0.0 + ], + [ + 95, + 0.0 + ], + [ + 99, + 0.0 + ], + [ + 999, + 0.0 + ] + ], + "histogram": [ + [ + 0, + 0 + ] + ], + "n": 0 + }, + "type": "histogram", + "desc": "length of an dreyfus_index group1 request" + }, + "group2": { + "value": { + "min": 0.0, + "max": 0.0, + "arithmetic_mean": 0.0, + "geometric_mean": 0.0, + "harmonic_mean": 0.0, + "median": 0.0, + "variance": 0.0, + "standard_deviation": 0.0, + "skewness": 0.0, + "kurtosis": 0.0, + "percentile": [ + [ + 50, + 0.0 + ], + [ + 75, + 0.0 + ], + [ + 90, + 0.0 + ], + [ + 95, + 0.0 + ], + [ + 99, + 0.0 + ], + [ + 999, + 0.0 + ] + ], + "histogram": [ + [ + 0, + 0 + ] + ], + "n": 0 + }, + "type": "histogram", + "desc": "length of an dreyfus_index group2 request" + }, + "info": { + "value": { + "min": 0.0, + "max": 0.0, + "arithmetic_mean": 0.0, + "geometric_mean": 0.0, + "harmonic_mean": 0.0, + "median": 0.0, + "variance": 0.0, + "standard_deviation": 0.0, + "skewness": 0.0, + "kurtosis": 0.0, + "percentile": [ + [ + 50, + 0.0 + ], + [ + 75, + 0.0 + ], + [ + 90, + 0.0 + ], + [ + 95, + 0.0 + ], + [ + 99, + 0.0 + ], + [ + 999, + 0.0 + ] + ], + "histogram": [ + [ + 0, + 0 + ] + ], + "n": 0 + }, + "type": "histogram", + "desc": "length of an dreyfus_index info request" + } + } + }, + "fabric": { + "worker": { + "timeouts": { + "value": 1, + "type": "counter", + "desc": "number of worker timeouts" + } + }, + "open_shard": { + "timeouts": { + "value": 1, + "type": "counter", + "desc": "number of open shard timeouts" + } + }, + "read_repairs": { + "success": { + "value": 1, + "type": "counter", + "desc": "number of successful read repair operations" + }, + "failure": { + "value": 1, + "type": "counter", + "desc": "number of failed read repair operations" + } + }, + "doc_update": { + "errors": { + "value": 1, + "type": "counter", + "desc": "number of document update errors" + }, + "mismatched_errors": { + "value": 1, + "type": "counter", + "desc": "number of document update errors with multiple error types" + }, + "write_quorum_errors": { + "value": 1, + "type": "counter", + "desc": "number of write quorum errors" + } + } + }, + "rexi": { + "buffered": { + "value": 1, + "type": "counter", + "desc": "number of rexi messages buffered" + }, + "down": { + "value": 1, + "type": "counter", + "desc": "number of rexi_DOWN messages handled" + }, + "dropped": { + "value": 1, + "type": "counter", + "desc": "number of rexi messages dropped from buffers" + }, + "streams": { + "timeout": { + "init_stream": { + "value": 1, + "type": "counter", + "desc": "number of rexi stream initialization timeouts" + }, + "stream": { + "value": 1, + "type": "counter", + "desc": "number of rexi stream timeouts" + }, + "wait_for_ack": { + "value": 1, + "type": "counter", + "desc": "number of rexi stream timeouts while waiting for acks" + } + } + } + }, + "couch_replicator": { + "changes_read_failures": { + "value": 1, + "type": "counter", + "desc": "number of failed replicator changes read failures" + }, + "changes_reader_deaths": { + "value": 1, + "type": "counter", + "desc": "number of failed replicator changes readers" + }, + "changes_manager_deaths": { + "value": 1, + "type": "counter", + "desc": "number of failed replicator changes managers" + }, + "changes_queue_deaths": { + "value": 1, + "type": "counter", + "desc": "number of failed replicator changes work queues" + }, + "checkpoints": { + "success": { + "value": 1, + "type": "counter", + "desc": "number of checkpoints successfully saves" + }, + "failure": { + "value": 1, + "type": "counter", + "desc": "number of failed checkpoint saves" + } + }, + "failed_starts": { + "value": 1, + "type": "counter", + "desc": "number of replications that have failed to start" + }, + "requests": { + "value": 1, + "type": "counter", + "desc": "number of HTTP requests made by the replicator" + }, + "responses": { + "failure": { + "value": 1, + "type": "counter", + "desc": "number of failed HTTP responses received by the replicator" + }, + "success": { + "value": 1, + "type": "counter", + "desc": "number of successful HTTP responses received by the replicator" + } + }, + "stream_responses": { + "failure": { + "value": 1, + "type": "counter", + "desc": "number of failed streaming HTTP responses received by the replicator" + }, + "success": { + "value": 1, + "type": "counter", + "desc": "number of successful streaming HTTP responses received by the replicator" + } + }, + "worker_deaths": { + "value": 1, + "type": "counter", + "desc": "number of failed replicator workers" + }, + "workers_started": { + "value": 1, + "type": "counter", + "desc": "number of replicator workers started" + }, + "cluster_is_stable": { + "value": 1, + "type": "gauge", + "desc": "1 if cluster is stable, 0 if unstable" + }, + "db_scans": { + "value": 1, + "type": "counter", + "desc": "number of times replicator db scans have been started" + }, + "docs": { + "dbs_created": { + "value": 1, + "type": "counter", + "desc": "number of db shard creations seen by replicator doc processor" + }, + "dbs_deleted": { + "value": 1, + "type": "counter", + "desc": "number of db shard deletions seen by replicator doc processor" + }, + "dbs_found": { + "value": 1, + "type": "counter", + "desc": "number of db shard found by replicator doc processor" + }, + "db_changes": { + "value": 1, + "type": "counter", + "desc": "number of db changes processed by replicator doc processor" + }, + "failed_state_updates": { + "value": 1, + "type": "counter", + "desc": "number of 'failed' state document updates" + }, + "completed_state_updates": { + "value": 1, + "type": "counter", + "desc": "number of 'completed' state document updates" + } + }, + "jobs": { + "adds": { + "value": 1, + "type": "counter", + "desc": "number of jobs added to replicator scheduler" + }, + "duplicate_adds": { + "value": 1, + "type": "counter", + "desc": "number of duplicate jobs added to replicator scheduler" + }, + "removes": { + "value": 1, + "type": "counter", + "desc": "number of jobs removed from replicator scheduler" + }, + "starts": { + "value": 1, + "type": "counter", + "desc": "number of jobs started by replicator scheduler" + }, + "stops": { + "value": 1, + "type": "counter", + "desc": "number of jobs stopped by replicator scheduler" + }, + "crashes": { + "value": 1, + "type": "counter", + "desc": "number of job crashed noticed by replicator scheduler" + }, + "running": { + "value": 1, + "type": "gauge", + "desc": "replicator scheduler running jobs" + }, + "pending": { + "value": 1, + "type": "gauge", + "desc": "replicator scheduler pending jobs" + }, + "crashed": { + "value": 1, + "type": "gauge", + "desc": "replicator scheduler crashed jobs" + }, + "total": { + "value": 1, + "type": "gauge", + "desc": "total number of replicator scheduler jobs" + } + }, + "connection": { + "acquires": { + "value": 1, + "type": "counter", + "desc": "number of times connections are shared" + }, + "creates": { + "value": 1, + "type": "counter", + "desc": "number of connections created" + }, + "releases": { + "value": 1, + "type": "counter", + "desc": "number of times ownership of a connection is released" + }, + "owner_crashes": { + "value": 1, + "type": "counter", + "desc": "number of times a connection owner crashes while owning at least one connection" + }, + "worker_crashes": { + "value": 1, + "type": "counter", + "desc": "number of times a worker unexpectedly terminates" + }, + "closes": { + "value": 1, + "type": "counter", + "desc": "number of times a worker is gracefully shut down" + } + } + }, + "pread": { + "exceed_eof": { + "value": 1, + "type": "counter", + "desc": "number of the attempts to read beyond end of db file" + }, + "exceed_limit": { + "value": 1, + "type": "counter", + "desc": "number of the attempts to read beyond set limit" + } + }, + "mango": { + "unindexed_queries": { + "value": 1, + "type": "counter", + "desc": "number of mango queries that could not use an index" + }, + "query_invalid_index": { + "value": 1, + "type": "counter", + "desc": "number of mango queries that generated an invalid index warning" + }, + "too_many_docs_scanned": { + "value": 1, + "type": "counter", + "desc": "number of mango queries that generated an index scan warning" + }, + "docs_examined": { + "value": 1, + "type": "counter", + "desc": "number of documents examined by mango queries coordinated by this node" + }, + "quorum_docs_examined": { + "value": 1, + "type": "counter", + "desc": "number of documents examined by mango queries, using cluster quorum" + }, + "results_returned": { + "value": 1, + "type": "counter", + "desc": "number of rows returned by mango queries" + }, + "query_time": { + "value": { + "min": 0.0, + "max": 0.0, + "arithmetic_mean": 0.0, + "geometric_mean": 0.0, + "harmonic_mean": 0.0, + "median": 0.0, + "variance": 0.0, + "standard_deviation": 0.0, + "skewness": 0.0, + "kurtosis": 0.0, + "percentile": [ + [ + 50, + 0.0 + ], + [ + 75, + 0.0 + ], + [ + 90, + 0.0 + ], + [ + 95, + 0.0 + ], + [ + 99, + 0.0 + ], + [ + 999, + 0.0 + ] + ], + "histogram": [ + [ + 0, + 0 + ] + ], + "n": 0 + }, + "type": "histogram", + "desc": "length of time processing a mango query" + }, + "evaluate_selector": { + "value": 1, + "type": "counter", + "desc": "number of mango selector evaluations" + } + } +} diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/node_system.json b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/node_system.json new file mode 100644 index 000000000..7084645a4 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/node_system.json @@ -0,0 +1,176 @@ +{ + "uptime": 253571, + "memory": { + "other": 20427855, + "atom": 504433, + "atom_used": 488328, + "processes": 9161448, + "processes_used": 9160864, + "binary": 297696, + "code": 11252688, + "ets": 1579120 + }, + "run_queue": 1, + "ets_table_count": 116, + "context_switches": 22614499, + "reductions": 43211228312, + "garbage_collection_count": 11416345, + "words_reclaimed": 20241272866, + "io_input": 49674812, + "io_output": 686400800, + "os_proc_count": 1, + "stale_proc_count": 1, + "process_count": 296, + "process_limit": 262144, + "message_queues": { + "couch_file": { + "count": 2, + "min": 1, + "max": 1, + "50": 1, + "90": 1, + "99": 1 + }, + "couch_db_updater": { + "count": 2, + "min": 1, + "max": 1, + "50": 1, + "90": 1, + "99": 1 + }, + "httpc_manager": 1, + "httpc_handler_sup": 1, + "ken_sup": 1, + "ken_server": 1, + "couch_replication": 1, + "standard_error_sup": 1, + "chttpd_auth_cache_lru": 1, + "couch_index_sup": 1, + "ioq_sup": 1, + "couch_index_server": 1, + "mem3_events": 1, + "jwtf_sup": 1, + "jwtf_keystore": 1, + "ioq": 1, + "couch_uuids": 1, + "ftp_sup": 1, + "ibrowse_sup": 1, + "couch_secondary_services": 1, + "couch_primary_services": 1, + "couch_task_status": 1, + "couch_sup": 1, + "global_changes_sup": 1, + "global_changes_server": 1, + "couch_server": 1, + "couch_epi_functions_gen_couch_index": 1, + "couch_plugin": 1, + "ibrowse": 1, + "config_event": 1, + "couch_epi_functions_gen_chttpd_auth": 1, + "chttpd_sup": 1, + "couch_epi_functions_gen_couch_db": 1, + "couch_epi_data_gen_flags_config": 1, + "couch_epi_functions_gen_global_changes": 1, + "couch_proc_manager": 1, + "release_handler": 1, + "sasl_sup": 1, + "couch_epi_functions_gen_chttpd_handlers": 1, + "couch_epi_functions_gen_feature_flags": 1, + "couch_epi_functions_gen_chttpd": 1, + "dreyfus_sup": 1, + "sasl_safe_sup": 1, + "couch_event_sup2": 1, + "alarm_handler": 1, + "couch_event_server": 1, + "dreyfus_index_manager": 1, + "timer_server": 1, + "runtime_tools_sup": 1, + "couch_httpd_vhost": 1, + "chttpd_auth_cache": 1, + "couch_stats_sup": 1, + "couch_stats_process_tracker": 1, + "chttpd": 1, + "kernel_safe_sup": 1, + "tftp_sup": 1, + "couch_stats_aggregator": 1, + "rex": 1, + "folsom_sup": 1, + "inet_gethost_native_sup": 1, + "kernel_sup": 1, + "ddoc_cache_sup": 1, + "global_name_server": 1, + "ddoc_cache_opener": 1, + "folsom_sample_slide_sup": 1, + "ddoc_cache_lru": 1, + "file_server_2": 1, + "standard_error": 1, + "rexi_buffer_nonode@nohost": 1, + "rexi_server_nonode@nohost": 1, + "couch_drv": 1, + "couch_peruser_sup": 1, + "tls_connection_sup": 1, + "couch_peruser": 1, + "folsom_metrics_histogram_ets": 1, + "couch_replicator_sup": 1, + "ssl_sup": 1, + "couch_replicator_scheduler_sup": 1, + "smoosh_sup": 1, + "folsom_meter_timer_server": 1, + "smoosh_server": 1, + "couch_replicator_scheduler": 1, + "couch_epi_data_gen_dreyfus_black_list": 1, + "mem3_sync_nodes": 1, + "couch_replicator_rate_limiter": 1, + "inet_gethost_native": 1, + "inets_sup": 1, + "setup_sup": 1, + "inet_db": 1, + "ssl_pem_cache": 1, + "mem3_sync": 1, + "ssl_manager": 1, + "mem3_sup": 1, + "ssl_listen_tracker_sup": 1, + "mem3_shards": 1, + "mem3_seeds": 1, + "httpd_sup": 1, + "couch_log_sup": 1, + "mem3_reshard_sup": 1, + "mango_sup": 1, + "couch_log_server": 1, + "mem3_reshard_job_sup": 1, + "erts_code_purger": 1, + "global_group": 1, + "error_logger": 1, + "couch_replicator_doc_processor": 1, + "ssl_connection_sup": 1, + "init": 1, + "mem3_reshard_dbdoc": 1, + "couch_replicator_connection": 1, + "erl_signal_server": 1, + "couch_replicator_clustering": 1, + "config": 1, + "mem3_reshard": 1, + "user": 1, + "couch_epi_sup": 1, + "mem3_nodes": 1, + "ssl_admin_sup": 1, + "mochiweb_clock": 1, + "rexi_buffer_mon": 1, + "dtls_udp_sup": 1, + "rexi_buffer_sup": 1, + "erl_prim_loader": 1, + "code_server": 1, + "httpc_sup": 1, + "rexi_sup": 1, + "dtls_connection_sup": 1, + "rexi_server_sup": 1, + "rexi_server_mon": 1, + "application_controller": 1, + "httpc_profile_sup": 1, + "config_sup": 1, + "rexi_server": 1 + }, + "internal_replication_jobs": 1, + "distribution": {} +} diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/root.json b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/root.json new file mode 100644 index 000000000..e7feb41c7 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/testdata/v3.1.1/root.json @@ -0,0 +1,16 @@ +{ + "couchdb": "Welcome", + "version": "3.1.1", + "git_sha": "ce596c65d", + "uuid": "d7bc2230b8e4de7f20680091bd7a21c7", + "features": [ + "access-ready", + "partitioned", + "pluggable-storage-engines", + "reshard", + "scheduler" + ], + "vendor": { + "name": "The Apache Software Foundation" + } +} |