diff options
Diffstat (limited to '')
14 files changed, 1853 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/pika/README.md b/src/go/collectors/go.d.plugin/modules/pika/README.md new file mode 120000 index 000000000..5e3a8da77 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/pika/README.md @@ -0,0 +1 @@ +integrations/pika.md
\ No newline at end of file diff --git a/src/go/collectors/go.d.plugin/modules/pika/charts.go b/src/go/collectors/go.d.plugin/modules/pika/charts.go new file mode 100644 index 000000000..cdaa68f6e --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/pika/charts.go @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package pika + +import "github.com/netdata/netdata/go/go.d.plugin/agent/module" + +var pikaCharts = module.Charts{ + chartConnections.Copy(), + chartClients.Copy(), + + chartMemory.Copy(), + + chartConnectedReplicas.Copy(), + + chartCommands.Copy(), + chartCommandsCalls.Copy(), + + chartDbStringsKeys.Copy(), + chartDbStringsExpiresKeys.Copy(), + chartDbStringsInvalidKeys.Copy(), + chartDbHashesKeys.Copy(), + chartDbHashesExpiresKeys.Copy(), + chartDbHashesInvalidKeys.Copy(), + chartDbListsKeys.Copy(), + chartDbListsExpiresKeys.Copy(), + chartDbListsInvalidKeys.Copy(), + chartDbZsetsKeys.Copy(), + chartDbZsetsExpiresKeys.Copy(), + chartDbZsetsInvalidKeys.Copy(), + chartDbSetsKeys.Copy(), + chartDbSetsExpiresKeys.Copy(), + chartDbSetsInvalidKeys.Copy(), + + chartUptime.Copy(), +} + +var ( + chartConnections = module.Chart{ + ID: "connections", + Title: "Connections", + Units: "connections/s", + Fam: "connections", + Ctx: "pika.connections", + Dims: module.Dims{ + {ID: "total_connections_received", Name: "accepted", Algo: module.Incremental}, + }, + } + chartClients = module.Chart{ + ID: "clients", + Title: "Clients", + Units: "clients", + Fam: "connections", + Ctx: "pika.clients", + Dims: module.Dims{ + {ID: "connected_clients", Name: "connected"}, + }, + } +) + +var ( + chartMemory = module.Chart{ + ID: "memory", + Title: "Memory usage", + Units: "bytes", + Fam: "memory", + Ctx: "pika.memory", + Type: module.Area, + Dims: module.Dims{ + {ID: "used_memory", Name: "used"}, + }, + } +) + +var ( + chartConnectedReplicas = module.Chart{ + ID: "connected_replicas", + Title: "Connected replicas", + Units: "replicas", + Fam: "replication", + Ctx: "pika.connected_replicas", + Dims: module.Dims{ + {ID: "connected_slaves", Name: "connected"}, + }, + } +) + +var ( + chartCommands = module.Chart{ + ID: "commands", + Title: "Processed commands", + Units: "commands/s", + Fam: "commands", + Ctx: "pika.commands", + Dims: module.Dims{ + {ID: "total_commands_processed", Name: "processed", Algo: module.Incremental}, + }, + } + chartCommandsCalls = module.Chart{ + ID: "commands_calls", + Title: "Calls per command", + Units: "calls/s", + Fam: "commands", + Ctx: "pika.commands_calls", + Type: module.Stacked, + } +) + +var ( + chartDbStringsKeys = module.Chart{ + ID: "database_strings_keys", + Title: "Strings type keys per database", + Units: "keys", + Fam: "keyspace strings", + Ctx: "pika.database_strings_keys", + Type: module.Stacked, + } + chartDbStringsExpiresKeys = module.Chart{ + ID: "database_strings_expires_keys", + Title: "Strings type expires keys per database", + Units: "keys", + Fam: "keyspace strings", + Ctx: "pika.database_strings_expires_keys", + Type: module.Stacked, + } + chartDbStringsInvalidKeys = module.Chart{ + ID: "database_strings_invalid_keys", + Title: "Strings type invalid keys per database", + Units: "keys", + Fam: "keyspace strings", + Ctx: "pika.database_strings_invalid_keys", + Type: module.Stacked, + } + + chartDbHashesKeys = module.Chart{ + ID: "database_hashes_keys", + Title: "Hashes type keys per database", + Units: "keys", + Fam: "keyspace hashes", + Ctx: "pika.database_hashes_keys", + Type: module.Stacked, + } + chartDbHashesExpiresKeys = module.Chart{ + ID: "database_hashes_expires_keys", + Title: "Hashes type expires keys per database", + Units: "keys", + Fam: "keyspace hashes", + Ctx: "pika.database_hashes_expires_keys", + Type: module.Stacked, + } + chartDbHashesInvalidKeys = module.Chart{ + ID: "database_hashes_invalid_keys", + Title: "Hashes type invalid keys per database", + Units: "keys", + Fam: "keyspace hashes", + Ctx: "pika.database_hashes_invalid_keys", + Type: module.Stacked, + } + + chartDbListsKeys = module.Chart{ + ID: "database_lists_keys", + Title: "Lists type keys per database", + Units: "keys", + Fam: "keyspace lists", + Ctx: "pika.database_lists_keys", + Type: module.Stacked, + } + chartDbListsExpiresKeys = module.Chart{ + ID: "database_lists_expires_keys", + Title: "Lists type expires keys per database", + Units: "keys", + Fam: "keyspace lists", + Ctx: "pika.database_lists_expires_keys", + Type: module.Stacked, + } + chartDbListsInvalidKeys = module.Chart{ + ID: "database_lists_invalid_keys", + Title: "Lists type invalid keys per database", + Units: "keys", + Fam: "keyspace lists", + Ctx: "pika.database_lists_invalid_keys", + Type: module.Stacked, + } + + chartDbZsetsKeys = module.Chart{ + ID: "database_zsets_keys", + Title: "Zsets type keys per database", + Units: "keys", + Fam: "keyspace zsets", + Ctx: "pika.database_zsets_keys", + Type: module.Stacked, + } + chartDbZsetsExpiresKeys = module.Chart{ + ID: "database_zsets_expires_keys", + Title: "Zsets type expires keys per database", + Units: "keys", + Fam: "keyspace zsets", + Ctx: "pika.database_zsets_expires_keys", + Type: module.Stacked, + } + chartDbZsetsInvalidKeys = module.Chart{ + ID: "database_zsets_invalid_keys", + Title: "Zsets type invalid keys per database", + Units: "keys", + Fam: "keyspace zsets", + Ctx: "pika.database_zsets_invalid_keys", + Type: module.Stacked, + } + + chartDbSetsKeys = module.Chart{ + ID: "database_sets_keys", + Title: "Sets type keys per database", + Units: "keys", + Fam: "keyspace sets", + Ctx: "pika.database_sets_keys", + Type: module.Stacked, + } + chartDbSetsExpiresKeys = module.Chart{ + ID: "database_sets_expires_keys", + Title: "Sets type expires keys per database", + Units: "keys", + Fam: "keyspace sets", + Ctx: "pika.database_sets_expires_keys", + Type: module.Stacked, + } + chartDbSetsInvalidKeys = module.Chart{ + ID: "database_sets_invalid_keys", + Title: "Sets invalid keys per database", + Units: "keys", + Fam: "keyspace sets", + Ctx: "pika.database_sets_invalid_keys", + Type: module.Stacked, + } +) + +var ( + chartUptime = module.Chart{ + ID: "uptime", + Title: "Uptime", + Units: "seconds", + Fam: "uptime", + Ctx: "pika.uptime", + Dims: module.Dims{ + {ID: "uptime_in_seconds", Name: "uptime"}, + }, + } +) diff --git a/src/go/collectors/go.d.plugin/modules/pika/collect.go b/src/go/collectors/go.d.plugin/modules/pika/collect.go new file mode 100644 index 000000000..72a4961dd --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/pika/collect.go @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package pika + +import ( + "bufio" + "context" + "errors" + "fmt" + "regexp" + "strings" + + "github.com/blang/semver/v4" +) + +const precision = 1000 // float values multiplier and dimensions divisor + +func (p *Pika) collect() (map[string]int64, error) { + info, err := p.pdb.Info(context.Background(), "all").Result() + if err != nil { + return nil, err + } + + if p.server == "" { + s, v, err := extractServerVersion(info) + if err != nil { + return nil, fmt.Errorf("can not extract server app and version: %v", err) + } + p.server, p.version = s, v + p.Debugf(`server="%s",version="%s"`, s, v) + } + + if p.server != "pika" { + return nil, fmt.Errorf("unsupported server app, want=pika, got=%s", p.server) + } + + ms := make(map[string]int64) + p.collectInfo(ms, info) + + return ms, nil +} + +// pika_version:3.4.0 +var reVersion = regexp.MustCompile(`([a-z]+)_version:(\d+\.\d+\.\d+)`) + +func extractServerVersion(info string) (string, *semver.Version, error) { + var versionLine string + for sc := bufio.NewScanner(strings.NewReader(info)); sc.Scan(); { + line := sc.Text() + if strings.Contains(line, "_version") { + versionLine = strings.TrimSpace(line) + break + } + } + if versionLine == "" { + return "", nil, errors.New("no version property") + } + + match := reVersion.FindStringSubmatch(versionLine) + if match == nil { + return "", nil, fmt.Errorf("can not parse version property '%s'", versionLine) + } + + server, version := match[1], match[2] + ver, err := semver.New(version) + if err != nil { + return "", nil, err + } + + return server, ver, nil +} diff --git a/src/go/collectors/go.d.plugin/modules/pika/collect_info.go b/src/go/collectors/go.d.plugin/modules/pika/collect_info.go new file mode 100644 index 000000000..2dc68f529 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/pika/collect_info.go @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package pika + +import ( + "bufio" + "regexp" + "strconv" + "strings" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" +) + +// https://github.com/Qihoo360/pika/blob/master/src/pika_admin.cc +// https://github.com/Qihoo360/pika/blob/a0dbdcf5897dd7800ba8a4d1eafce1595619ddc8/src/pika_admin.cc#L694-L710 + +const ( + infoSectionServer = "# Server" + infoSectionData = "# Data" + infoSectionClients = "# Clients" + infoSectionStats = "# Stats" + infoSectionCommandExecCount = "# Command_Exec_Count" + infoSectionCPU = "# CPU" + infoSectionReplMaster = "# Replication(MASTER)" + infoSectionReplSlave = "# Replication(SLAVE)" + infoSectionReplMasterSlave = "# Replication(Master && SLAVE)" + infoSectionKeyspace = "# Keyspace" +) + +var infoSections = map[string]struct{}{ + infoSectionServer: {}, + infoSectionData: {}, + infoSectionClients: {}, + infoSectionStats: {}, + infoSectionCommandExecCount: {}, + infoSectionCPU: {}, + infoSectionReplMaster: {}, + infoSectionReplSlave: {}, + infoSectionReplMasterSlave: {}, + infoSectionKeyspace: {}, +} + +func isInfoSection(line string) bool { _, ok := infoSections[line]; return ok } + +func (p *Pika) collectInfo(ms map[string]int64, info string) { + var curSection string + + sc := bufio.NewScanner(strings.NewReader(info)) + for sc.Scan() { + line := strings.TrimSpace(sc.Text()) + if len(line) == 0 { + curSection = "" + continue + } + if strings.HasPrefix(line, "#") { + if isInfoSection(line) { + curSection = line + } + continue + } + + field, value, ok := parseProperty(line) + if !ok { + continue + } + + switch curSection { + case infoSectionCommandExecCount: + p.collectInfoCommandExecCountProperty(ms, field, value) + case infoSectionKeyspace: + p.collectInfoKeyspaceProperty(ms, field, value) + default: + collectNumericValue(ms, field, value) + } + } +} + +var reKeyspaceValue = regexp.MustCompile(`^(.+)_keys=(\d+), expires=(\d+), invalid_keys=(\d+)`) + +func (p *Pika) collectInfoKeyspaceProperty(ms map[string]int64, field, value string) { + match := reKeyspaceValue.FindStringSubmatch(value) + if match == nil { + return + } + + dataType, keys, expires, invalid := strings.ToLower(match[1]), match[2], match[3], match[4] + collectNumericValue(ms, field+"_"+dataType+"_keys", keys) + collectNumericValue(ms, field+"_"+dataType+"_expires_keys", expires) + collectNumericValue(ms, field+"_"+dataType+"_invalid_keys", invalid) + + if !p.collectedDbs[field] { + p.collectedDbs[field] = true + p.addDbToKeyspaceCharts(field) + } +} + +func (p *Pika) collectInfoCommandExecCountProperty(ms map[string]int64, field, value string) { + collectNumericValue(ms, "cmd_"+field+"_calls", value) + + if !p.collectedCommands[field] { + p.collectedCommands[field] = true + p.addCmdToCommandsCharts(field) + } +} + +func (p *Pika) addCmdToCommandsCharts(cmd string) { + p.addDimToChart(chartCommandsCalls.ID, &module.Dim{ + ID: "cmd_" + cmd + "_calls", + Name: cmd, + Algo: module.Incremental, + }) +} + +func (p *Pika) addDbToKeyspaceCharts(db string) { + p.addDimToChart(chartDbStringsKeys.ID, &module.Dim{ + ID: db + "_strings_keys", + Name: db, + }) + p.addDimToChart(chartDbStringsExpiresKeys.ID, &module.Dim{ + ID: db + "_strings_expires_keys", + Name: db, + }) + p.addDimToChart(chartDbStringsInvalidKeys.ID, &module.Dim{ + ID: db + "_strings_invalid_keys", + Name: db, + }) + + p.addDimToChart(chartDbHashesKeys.ID, &module.Dim{ + ID: db + "_hashes_keys", + Name: db, + }) + p.addDimToChart(chartDbHashesExpiresKeys.ID, &module.Dim{ + ID: db + "_hashes_expires_keys", + Name: db, + }) + p.addDimToChart(chartDbHashesInvalidKeys.ID, &module.Dim{ + ID: db + "_hashes_invalid_keys", + Name: db, + }) + + p.addDimToChart(chartDbListsKeys.ID, &module.Dim{ + ID: db + "_lists_keys", + Name: db, + }) + p.addDimToChart(chartDbListsExpiresKeys.ID, &module.Dim{ + ID: db + "_lists_expires_keys", + Name: db, + }) + p.addDimToChart(chartDbListsInvalidKeys.ID, &module.Dim{ + ID: db + "_lists_invalid_keys", + Name: db, + }) + + p.addDimToChart(chartDbZsetsKeys.ID, &module.Dim{ + ID: db + "_zsets_keys", + Name: db, + }) + p.addDimToChart(chartDbZsetsExpiresKeys.ID, &module.Dim{ + ID: db + "_zsets_expires_keys", + Name: db, + }) + p.addDimToChart(chartDbZsetsInvalidKeys.ID, &module.Dim{ + ID: db + "_zsets_invalid_keys", + Name: db, + }) + + p.addDimToChart(chartDbSetsKeys.ID, &module.Dim{ + ID: db + "_sets_keys", + Name: db, + }) + p.addDimToChart(chartDbSetsExpiresKeys.ID, &module.Dim{ + ID: db + "_sets_expires_keys", + Name: db, + }) + p.addDimToChart(chartDbSetsInvalidKeys.ID, &module.Dim{ + ID: db + "_sets_invalid_keys", + Name: db, + }) +} + +func (p *Pika) addDimToChart(chartID string, dim *module.Dim) { + chart := p.Charts().Get(chartID) + if chart == nil { + p.Warningf("error on adding '%s' dimension: can not find '%s' chart", dim.ID, chartID) + return + } + if err := chart.AddDim(dim); err != nil { + p.Warning(err) + return + } + chart.MarkNotCreated() +} + +func parseProperty(prop string) (field, value string, ok bool) { + var sep byte + if strings.HasPrefix(prop, "db") { + sep = ' ' + } else { + sep = ':' + } + i := strings.IndexByte(prop, sep) + if i == -1 { + return "", "", false + } + field, value = prop[:i], prop[i+1:] + return field, value, field != "" && value != "" +} + +func collectNumericValue(ms map[string]int64, field, value string) { + v, err := strconv.ParseFloat(value, 64) + if err != nil { + return + } + if strings.IndexByte(value, '.') == -1 { + ms[field] = int64(v) + } else { + ms[field] = int64(v * precision) + } +} diff --git a/src/go/collectors/go.d.plugin/modules/pika/config_schema.json b/src/go/collectors/go.d.plugin/modules/pika/config_schema.json new file mode 100644 index 000000000..885cbed0f --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/pika/config_schema.json @@ -0,0 +1,93 @@ +{ + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "Pika collector configuration.", + "properties": { + "update_every": { + "title": "Update every", + "description": "Data collection interval, measured in seconds.", + "type": "integer", + "minimum": 1, + "default": 1 + }, + "address": { + "title": "URI", + "description": "The URI specifying the connection details for the Pika server.", + "type": "string", + "default": "redis://@localhost:9221" + }, + "timeout": { + "title": "Timeout", + "description": "Timeout for establishing a connection and communication (reading and writing) in seconds.", + "type": "number", + "minimum": 0.5, + "default": 1 + }, + "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": "^$|^/" + } + }, + "required": [ + "address" + ], + "additionalProperties": false, + "patternProperties": { + "^name$": {} + } + }, + "uiSchema": { + "uiOptions": { + "fullPage": true + }, + "address": { + "ui:placeholder": "redis://user:password@host:port", + "ui:help": "Tcp connection: `redis://user:password@host:port`. Unix connection: `unix://user:password@/path/to/redis.sock`." + }, + "timeout": { + "ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)." + }, + "ui:flavour": "tabs", + "ui:options": { + "tabs": [ + { + "title": "Base", + "fields": [ + "update_every", + "address", + "timeout" + ] + }, + { + "title": "TLS", + "fields": [ + "tls_skip_verify", + "tls_ca", + "tls_cert", + "tls_key" + ] + } + ] + } + } +} diff --git a/src/go/collectors/go.d.plugin/modules/pika/init.go b/src/go/collectors/go.d.plugin/modules/pika/init.go new file mode 100644 index 000000000..8cb62aa52 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/pika/init.go @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package pika + +import ( + "errors" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/tlscfg" + + "github.com/go-redis/redis/v8" +) + +func (p *Pika) validateConfig() error { + if p.Address == "" { + return errors.New("'address' not set") + } + return nil +} + +func (p *Pika) initRedisClient() (*redis.Client, error) { + opts, err := redis.ParseURL(p.Address) + if err != nil { + return nil, err + } + + tlsConfig, err := tlscfg.NewTLSConfig(p.TLSConfig) + if err != nil { + return nil, err + } + + if opts.TLSConfig != nil && tlsConfig != nil { + tlsConfig.ServerName = opts.TLSConfig.ServerName + } + + opts.PoolSize = 1 + opts.TLSConfig = tlsConfig + opts.DialTimeout = p.Timeout.Duration() + opts.ReadTimeout = p.Timeout.Duration() + opts.WriteTimeout = p.Timeout.Duration() + + return redis.NewClient(opts), nil +} + +func (p *Pika) initCharts() (*module.Charts, error) { + return pikaCharts.Copy(), nil +} diff --git a/src/go/collectors/go.d.plugin/modules/pika/integrations/pika.md b/src/go/collectors/go.d.plugin/modules/pika/integrations/pika.md new file mode 100644 index 000000000..1214dcad7 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/pika/integrations/pika.md @@ -0,0 +1,221 @@ +<!--startmeta +custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/pika/README.md" +meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/pika/metadata.yaml" +sidebar_label: "Pika" +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--> + +# Pika + + +<img src="https://netdata.cloud/img/pika.svg" width="150"/> + + +Plugin: go.d.plugin +Module: pika + +<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" /> + +## Overview + +This collector monitors Pika servers. + +It collects information and statistics about the server executing the following commands: + +- [`INFO ALL`](https://github.com/OpenAtomFoundation/pika/wiki/pika-info%E4%BF%A1%E6%81%AF%E8%AF%B4%E6%98%8E) + + + + +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 Pika instance + +These metrics refer to the entire monitored application. + +This scope has no labels. + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| pika.connections | accepted | connections | +| pika.clients | connected | clients | +| pika.memory | used | bytes | +| pika.connected_replicas | connected | replicas | +| pika.commands | processed | commands/s | +| pika.commands_calls | a dimension per command | calls/s | +| pika.database_strings_keys | a dimension per database | keys | +| pika.database_strings_expires_keys | a dimension per database | keys | +| pika.database_strings_invalid_keys | a dimension per database | keys | +| pika.database_hashes_keys | a dimension per database | keys | +| pika.database_hashes_expires_keys | a dimension per database | keys | +| pika.database_hashes_invalid_keys | a dimension per database | keys | +| pika.database_lists_keys | a dimension per database | keys | +| pika.database_lists_expires_keys | a dimension per database | keys | +| pika.database_lists_invalid_keys | a dimension per database | keys | +| pika.database_zsets_keys | a dimension per database | keys | +| pika.database_zsets_expires_keys | a dimension per database | keys | +| pika.database_zsets_invalid_keys | a dimension per database | keys | +| pika.database_sets_keys | a dimension per database | keys | +| pika.database_sets_expires_keys | a dimension per database | keys | +| pika.database_sets_invalid_keys | a dimension per database | keys | +| pika.uptime | uptime | seconds | + + + +## 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/pika.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/pika.conf +``` +#### Options + +The following options can be defined globally: update_every, autodetection_retry. + + +<details open><summary>Config options</summary> + +| Name | Description | Default | Required | +|:----|:-----------|:-------|:--------:| +| update_every | Data collection frequency. | 5 | no | +| autodetection_retry | Recheck interval in seconds. Zero means no recheck will be scheduled. | 0 | no | +| address | Pika server address. | redis://@localhost:9221 | yes | +| timeout | Dial (establishing new connections), read (socket reads) and write (socket writes) timeout in seconds. | 1 | no | +| username | Username used for authentication. | | no | +| password | Password used for authentication. | | no | +| tls_skip_verify | Server certificate chain and hostname validation policy. Controls whether the client performs this check. | no | no | +| tls_ca | Certificate authority that client use when verifying server certificates. | | no | +| tls_cert | Client tls certificate. | | no | +| tls_key | Client tls key. | | no | + +</details> + +#### Examples + +##### TCP socket + +An example configuration. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + address: 'redis://@localhost:9221' + +``` +</details> + +##### TCP socket with password + +An example configuration. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + address: 'redis://:password@127.0.0.1:9221' + +``` +</details> + +##### Multi-instance + +> **Note**: When you define multiple jobs, their names must be unique. + +Local and remote instances. + + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: local + address: 'redis://:password@127.0.0.1:9221' + + - name: remote + address: 'redis://user:password@203.0.113.0:9221' + +``` +</details> + + + +## Troubleshooting + +### Debug Mode + +To troubleshoot issues with the `pika` 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 pika + ``` + + diff --git a/src/go/collectors/go.d.plugin/modules/pika/metadata.yaml b/src/go/collectors/go.d.plugin/modules/pika/metadata.yaml new file mode 100644 index 000000000..c87cd9b27 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/pika/metadata.yaml @@ -0,0 +1,277 @@ +plugin_name: go.d.plugin +modules: + - meta: + id: collector-go.d.plugin-pika + plugin_name: go.d.plugin + module_name: pika + monitored_instance: + name: Pika + link: https://github.com/OpenAtomFoundation/pika + icon_filename: pika.svg + categories: + - data-collection.database-servers + keywords: + - pika + - databases + related_resources: + integrations: + list: [] + info_provided_to_referring_integrations: + description: "" + most_popular: false + overview: + data_collection: + metrics_description: | + This collector monitors Pika servers. + + It collects information and statistics about the server executing the following commands: + + - [`INFO ALL`](https://github.com/OpenAtomFoundation/pika/wiki/pika-info%E4%BF%A1%E6%81%AF%E8%AF%B4%E6%98%8E) + 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/pika.conf + options: + description: | + The following options can be defined globally: update_every, autodetection_retry. + folding: + title: Config options + enabled: true + list: + - name: update_every + description: Data collection frequency. + default_value: 5 + required: false + - name: autodetection_retry + description: Recheck interval in seconds. Zero means no recheck will be scheduled. + default_value: 0 + required: false + - name: address + description: Pika server address. + default_value: redis://@localhost:9221 + required: true + details: | + There are two connection types: by tcp socket and by unix socket. + + - Tcp connection: `redis://<user>:<password>@<host>:<port>/<db_number>` + - Unix connection: `unix://<user>:<password>@</path/to/redis.sock>?db=<db_number>` + - name: timeout + description: Dial (establishing new connections), read (socket reads) and write (socket writes) timeout in seconds. + default_value: 1 + required: false + - name: username + description: Username used for authentication. + default_value: "" + required: false + - name: password + description: Password used for authentication. + default_value: "" + 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: Certificate authority that client use when verifying server 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: TCP socket + description: An example configuration. + config: | + jobs: + - name: local + address: 'redis://@localhost:9221' + - name: TCP socket with password + description: An example configuration. + config: | + jobs: + - name: local + address: 'redis://:password@127.0.0.1:9221' + - name: Multi-instance + description: | + > **Note**: When you define multiple jobs, their names must be unique. + + Local and remote instances. + config: | + jobs: + - name: local + address: 'redis://:password@127.0.0.1:9221' + + - name: remote + address: 'redis://user:password@203.0.113.0:9221' + 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: pika.connections + description: Connections + unit: connections + chart_type: line + dimensions: + - name: accepted + - name: pika.clients + description: Clients + unit: clients + chart_type: line + dimensions: + - name: connected + - name: pika.memory + description: Memory usage + unit: bytes + chart_type: area + dimensions: + - name: used + - name: pika.connected_replicas + description: Connected replicas + unit: replicas + chart_type: line + dimensions: + - name: connected + - name: pika.commands + description: Processed commands + unit: commands/s + chart_type: line + dimensions: + - name: processed + - name: pika.commands_calls + description: Calls per command + unit: calls/s + chart_type: stacked + dimensions: + - name: a dimension per command + - name: pika.database_strings_keys + description: Strings type keys per database + unit: keys + chart_type: stacked + dimensions: + - name: a dimension per database + - name: pika.database_strings_expires_keys + description: Strings type expires keys per database + unit: keys + chart_type: stacked + dimensions: + - name: a dimension per database + - name: pika.database_strings_invalid_keys + description: Strings type invalid keys per database + unit: keys + chart_type: stacked + dimensions: + - name: a dimension per database + - name: pika.database_hashes_keys + description: Hashes type keys per database + unit: keys + chart_type: stacked + dimensions: + - name: a dimension per database + - name: pika.database_hashes_expires_keys + description: Hashes type expires keys per database + unit: keys + chart_type: stacked + dimensions: + - name: a dimension per database + - name: pika.database_hashes_invalid_keys + description: Hashes type invalid keys per database + unit: keys + chart_type: stacked + dimensions: + - name: a dimension per database + - name: pika.database_lists_keys + description: Lists type keys per database + unit: keys + chart_type: stacked + dimensions: + - name: a dimension per database + - name: pika.database_lists_expires_keys + description: Lists type expires keys per database + unit: keys + chart_type: stacked + dimensions: + - name: a dimension per database + - name: pika.database_lists_invalid_keys + description: Lists type invalid keys per database + unit: keys + chart_type: stacked + dimensions: + - name: a dimension per database + - name: pika.database_zsets_keys + description: Zsets type keys per database + unit: keys + chart_type: stacked + dimensions: + - name: a dimension per database + - name: pika.database_zsets_expires_keys + description: Zsets type expires keys per database + unit: keys + chart_type: stacked + dimensions: + - name: a dimension per database + - name: pika.database_zsets_invalid_keys + description: Zsets type invalid keys per database + unit: keys + chart_type: stacked + dimensions: + - name: a dimension per database + - name: pika.database_sets_keys + description: Sets type keys per database + unit: keys + chart_type: stacked + dimensions: + - name: a dimension per database + - name: pika.database_sets_expires_keys + description: Sets type expires keys per database + unit: keys + chart_type: stacked + dimensions: + - name: a dimension per database + - name: pika.database_sets_invalid_keys + description: Sets invalid keys per database + unit: keys + chart_type: stacked + dimensions: + - name: a dimension per database + - name: pika.uptime + description: Uptime + unit: seconds + chart_type: line + dimensions: + - name: uptime diff --git a/src/go/collectors/go.d.plugin/modules/pika/pika.go b/src/go/collectors/go.d.plugin/modules/pika/pika.go new file mode 100644 index 000000000..c7cbd019a --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/pika/pika.go @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package pika + +import ( + "context" + _ "embed" + "errors" + "time" + + "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/blang/semver/v4" + "github.com/go-redis/redis/v8" +) + +//go:embed "config_schema.json" +var configSchema string + +func init() { + module.Register("pika", module.Creator{ + JobConfigSchema: configSchema, + Create: func() module.Module { return New() }, + Config: func() any { return &Config{} }, + }) +} + +func New() *Pika { + return &Pika{ + Config: Config{ + Address: "redis://@localhost:9221", + Timeout: web.Duration(time.Second), + }, + + collectedCommands: make(map[string]bool), + collectedDbs: make(map[string]bool), + } +} + +type Config struct { + UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"` + Address string `yaml:"address" json:"address"` + Timeout web.Duration `yaml:"timeout,omitempty" json:"timeout"` + tlscfg.TLSConfig `yaml:",inline" json:""` +} + +type ( + Pika struct { + module.Base + Config `yaml:",inline" json:""` + + charts *module.Charts + + pdb redisClient + + server string + version *semver.Version + collectedCommands map[string]bool + collectedDbs map[string]bool + } + redisClient interface { + Info(ctx context.Context, section ...string) *redis.StringCmd + Close() error + } +) + +func (p *Pika) Configuration() any { + return p.Config +} + +func (p *Pika) Init() error { + err := p.validateConfig() + if err != nil { + p.Errorf("config validation: %v", err) + return err + } + + pdb, err := p.initRedisClient() + if err != nil { + p.Errorf("init redis client: %v", err) + return err + } + p.pdb = pdb + + charts, err := p.initCharts() + if err != nil { + p.Errorf("init charts: %v", err) + return err + } + p.charts = charts + + return nil +} + +func (p *Pika) Check() error { + mx, err := p.collect() + if err != nil { + p.Error(err) + return err + } + if len(mx) == 0 { + return errors.New("no metrics collected") + } + return nil +} + +func (p *Pika) Charts() *module.Charts { + return p.charts +} + +func (p *Pika) Collect() map[string]int64 { + ms, err := p.collect() + if err != nil { + p.Error(err) + } + + if len(ms) == 0 { + return nil + } + return ms +} + +func (p *Pika) Cleanup() { + if p.pdb == nil { + return + } + err := p.pdb.Close() + if err != nil { + p.Warningf("cleanup: error on closing redis client [%s]: %v", p.Address, err) + } + p.pdb = nil +} diff --git a/src/go/collectors/go.d.plugin/modules/pika/pika_test.go b/src/go/collectors/go.d.plugin/modules/pika/pika_test.go new file mode 100644 index 000000000..5a4e460d7 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/pika/pika_test.go @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package pika + +import ( + "context" + "errors" + "os" + "testing" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/tlscfg" + + "github.com/go-redis/redis/v8" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + dataConfigJSON, _ = os.ReadFile("testdata/config.json") + dataConfigYAML, _ = os.ReadFile("testdata/config.yaml") + + dataRedisInfoAll, _ = os.ReadFile("testdata/redis/info_all.txt") + dataVer340InfoAll, _ = os.ReadFile("testdata/v3.4.0/info_all.txt") +) + +func Test_testDataIsValid(t *testing.T) { + for name, data := range map[string][]byte{ + "dataConfigJSON": dataConfigJSON, + "dataConfigYAML": dataConfigYAML, + "dataRedisInfoAll": dataRedisInfoAll, + "dataVer340InfoAll": dataVer340InfoAll, + } { + require.NotNil(t, data, name) + } +} + +func TestPika_ConfigurationSerialize(t *testing.T) { + module.TestConfigurationSerialize(t, &Pika{}, dataConfigJSON, dataConfigYAML) +} + +func TestPika_Init(t *testing.T) { + tests := map[string]struct { + config Config + wantFail bool + }{ + "success on default config": { + config: New().Config, + }, + "fails on unset 'address'": { + wantFail: true, + config: Config{Address: ""}, + }, + "fails on invalid 'address' format": { + wantFail: true, + config: Config{Address: "127.0.0.1:9221"}, + }, + "fails on invalid TLSCA": { + wantFail: true, + config: Config{ + Address: "redis://@127.0.0.1:9221", + TLSConfig: tlscfg.TLSConfig{TLSCA: "testdata/tls"}, + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + pika := New() + pika.Config = test.config + + if test.wantFail { + assert.Error(t, pika.Init()) + } else { + assert.NoError(t, pika.Init()) + } + }) + } +} + +func TestPika_Check(t *testing.T) { + tests := map[string]struct { + prepare func(t *testing.T) *Pika + wantFail bool + }{ + "success on valid response v3.4.0": { + prepare: preparePikaV340, + }, + "fails on error on Info": { + wantFail: true, + prepare: preparePikaErrorOnInfo, + }, + "fails on response from not Pika instance": { + wantFail: true, + prepare: preparePikaWithRedisMetrics, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + pika := test.prepare(t) + + if test.wantFail { + assert.Error(t, pika.Check()) + } else { + assert.NoError(t, pika.Check()) + } + }) + } +} + +func TestPika_Charts(t *testing.T) { + pika := New() + require.NoError(t, pika.Init()) + + assert.NotNil(t, pika.Charts()) +} + +func TestPika_Cleanup(t *testing.T) { + pika := New() + assert.NotPanics(t, pika.Cleanup) + + require.NoError(t, pika.Init()) + m := &mockRedisClient{} + pika.pdb = m + + pika.Cleanup() + + assert.True(t, m.calledClose) +} + +func TestPika_Collect(t *testing.T) { + tests := map[string]struct { + prepare func(t *testing.T) *Pika + wantCollected map[string]int64 + }{ + "success on valid response v3.4.0": { + prepare: preparePikaV340, + wantCollected: map[string]int64{ + "cmd_INFO_calls": 1, + "cmd_SET_calls": 2, + "arch_bits": 64, + "connected_clients": 1, + "connected_slaves": 0, + "db0_hashes_expires_keys": 0, + "db0_hashes_invalid_keys": 0, + "db0_hashes_keys": 0, + "db0_lists_expires_keys": 0, + "db0_lists_invalid_keys": 0, + "db0_lists_keys": 0, + "db0_sets_expires_keys": 0, + "db0_sets_invalid_keys": 0, + "db0_sets_keys": 0, + "db0_strings_expires_keys": 0, + "db0_strings_invalid_keys": 0, + "db0_strings_keys": 0, + "db0_zsets_expires_keys": 0, + "db0_zsets_invalid_keys": 0, + "db0_zsets_keys": 0, + "instantaneous_ops_per_sec": 0, + "log_size": 4272814, + "process_id": 1, + "server_id": 1, + "sync_thread_num": 6, + "tcp_port": 9221, + "thread_num": 1, + "total_commands_processed": 3, + "total_connections_received": 3, + "uptime_in_days": 1, + "uptime_in_seconds": 1884, + "used_cpu_sys": 158200, + "used_cpu_sys_children": 30, + "used_cpu_user": 22050, + "used_cpu_user_children": 20, + "used_memory": 8198, + }, + }, + "fails on error on Info": { + prepare: preparePikaErrorOnInfo, + }, + "fails on response from not Pika instance": { + prepare: preparePikaWithRedisMetrics, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + pika := test.prepare(t) + + ms := pika.Collect() + + assert.Equal(t, test.wantCollected, ms) + if len(test.wantCollected) > 0 { + ensureCollectedHasAllChartsDimsVarsIDs(t, pika, ms) + ensureCollectedCommandsAddedToCharts(t, pika) + ensureCollectedDbsAddedToCharts(t, pika) + } + }) + } +} + +func preparePikaV340(t *testing.T) *Pika { + pika := New() + require.NoError(t, pika.Init()) + pika.pdb = &mockRedisClient{ + result: dataVer340InfoAll, + } + return pika +} + +func preparePikaErrorOnInfo(t *testing.T) *Pika { + pika := New() + require.NoError(t, pika.Init()) + pika.pdb = &mockRedisClient{ + errOnInfo: true, + } + return pika +} + +func preparePikaWithRedisMetrics(t *testing.T) *Pika { + pika := New() + require.NoError(t, pika.Init()) + pika.pdb = &mockRedisClient{ + result: dataRedisInfoAll, + } + return pika +} + +func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, pika *Pika, ms map[string]int64) { + for _, chart := range *pika.Charts() { + if chart.Obsolete { + continue + } + for _, dim := range chart.Dims { + _, ok := ms[dim.ID] + assert.Truef(t, ok, "chart '%s' dim '%s': no dim in collected", dim.ID, chart.ID) + } + for _, v := range chart.Vars { + _, ok := ms[v.ID] + assert.Truef(t, ok, "chart '%s' dim '%s': no dim in collected", v.ID, chart.ID) + } + } +} + +func ensureCollectedCommandsAddedToCharts(t *testing.T, pika *Pika) { + for _, id := range []string{ + chartCommandsCalls.ID, + } { + chart := pika.Charts().Get(id) + require.NotNilf(t, chart, "'%s' chart is not in charts", id) + assert.Lenf(t, chart.Dims, len(pika.collectedCommands), + "'%s' chart unexpected number of dimensions", id) + } +} + +func ensureCollectedDbsAddedToCharts(t *testing.T, pika *Pika) { + for _, id := range []string{ + chartDbStringsKeys.ID, + chartDbStringsExpiresKeys.ID, + chartDbStringsInvalidKeys.ID, + chartDbHashesKeys.ID, + chartDbHashesExpiresKeys.ID, + chartDbHashesInvalidKeys.ID, + chartDbListsKeys.ID, + chartDbListsExpiresKeys.ID, + chartDbListsInvalidKeys.ID, + chartDbZsetsKeys.ID, + chartDbZsetsExpiresKeys.ID, + chartDbZsetsInvalidKeys.ID, + chartDbSetsKeys.ID, + chartDbSetsExpiresKeys.ID, + chartDbSetsInvalidKeys.ID, + } { + chart := pika.Charts().Get(id) + require.NotNilf(t, chart, "'%s' chart is not in charts", id) + assert.Lenf(t, chart.Dims, len(pika.collectedDbs), + "'%s' chart unexpected number of dimensions", id) + } +} + +type mockRedisClient struct { + errOnInfo bool + result []byte + calledClose bool +} + +func (m *mockRedisClient) Info(_ context.Context, _ ...string) (cmd *redis.StringCmd) { + if m.errOnInfo { + cmd = redis.NewStringResult("", errors.New("error on Info")) + } else { + cmd = redis.NewStringResult(string(m.result), nil) + } + return cmd +} + +func (m *mockRedisClient) Close() error { + m.calledClose = true + return nil +} diff --git a/src/go/collectors/go.d.plugin/modules/pika/testdata/config.json b/src/go/collectors/go.d.plugin/modules/pika/testdata/config.json new file mode 100644 index 000000000..d8ba812ab --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/pika/testdata/config.json @@ -0,0 +1,9 @@ +{ + "update_every": 123, + "address": "ok", + "timeout": 123.123, + "tls_ca": "ok", + "tls_cert": "ok", + "tls_key": "ok", + "tls_skip_verify": true +} diff --git a/src/go/collectors/go.d.plugin/modules/pika/testdata/config.yaml b/src/go/collectors/go.d.plugin/modules/pika/testdata/config.yaml new file mode 100644 index 000000000..6a6f6ae69 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/pika/testdata/config.yaml @@ -0,0 +1,7 @@ +update_every: 123 +address: "ok" +timeout: 123.123 +tls_ca: "ok" +tls_cert: "ok" +tls_key: "ok" +tls_skip_verify: yes diff --git a/src/go/collectors/go.d.plugin/modules/pika/testdata/redis/info_all.txt b/src/go/collectors/go.d.plugin/modules/pika/testdata/redis/info_all.txt new file mode 100644 index 000000000..8ab381620 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/pika/testdata/redis/info_all.txt @@ -0,0 +1,165 @@ +$4050 +# Server +redis_version:6.0.9 +redis_git_sha1:00000000 +redis_git_dirty:0 +redis_build_id:12c354e6793cb936 +redis_mode:standalone +os:Linux 5.4.39-linuxkit x86_64 +arch_bits:64 +multiplexing_api:epoll +atomicvar_api:atomic-builtin +gcc_version:8.3.0 +process_id:1 +run_id:5d97fd948bbf6cb68458685fc747f9f9019c3fc4 +tcp_port:6379 +uptime_in_seconds:252812 +uptime_in_days:2 +hz:10 +configured_hz:10 +lru_clock:13181377 +executable:/data/redis-server +config_file: +io_threads_active:0 + +# Clients +connected_clients:1 +client_recent_max_input_buffer:8 +client_recent_max_output_buffer:0 +blocked_clients:0 +tracking_clients:0 +clients_in_timeout_table:0 + +# Memory +used_memory:867160 +used_memory_human:846.84K +used_memory_rss:3989504 +used_memory_rss_human:3.80M +used_memory_peak:923360 +used_memory_peak_human:901.72K +used_memory_peak_perc:93.91% +used_memory_overhead:803344 +used_memory_startup:803152 +used_memory_dataset:63816 +used_memory_dataset_perc:99.70% +allocator_allocated:903408 +allocator_active:1208320 +allocator_resident:3723264 +total_system_memory:2084032512 +total_system_memory_human:1.94G +used_memory_lua:37888 +used_memory_lua_human:37.00K +used_memory_scripts:0 +used_memory_scripts_human:0B +number_of_cached_scripts:0 +maxmemory:0 +maxmemory_human:0B +maxmemory_policy:noeviction +allocator_frag_ratio:1.34 +allocator_frag_bytes:304912 +allocator_rss_ratio:3.08 +allocator_rss_bytes:2514944 +rss_overhead_ratio:1.07 +rss_overhead_bytes:266240 +mem_fragmentation_ratio:4.96 +mem_fragmentation_bytes:3185848 +mem_not_counted_for_evict:0 +mem_replication_backlog:0 +mem_clients_slaves:0 +mem_clients_normal:0 +mem_aof_buffer:0 +mem_allocator:jemalloc-5.1.0 +active_defrag_running:0 +lazyfree_pending_objects:0 + +# Persistence +loading:0 +rdb_changes_since_last_save:0 +rdb_bgsave_in_progress:0 +rdb_last_save_time:1606951667 +rdb_last_bgsave_status:ok +rdb_last_bgsave_time_sec:0 +rdb_current_bgsave_time_sec:-1 +rdb_last_cow_size:290816 +aof_enabled:0 +aof_rewrite_in_progress:0 +aof_rewrite_scheduled:0 +aof_last_rewrite_time_sec:-1 +aof_current_rewrite_time_sec:-1 +aof_last_bgrewrite_status:ok +aof_last_write_status:ok +aof_last_cow_size:0 +module_fork_in_progress:0 +module_fork_last_cow_size:0 + +# Stats +total_connections_received:87 +total_commands_processed:161 +instantaneous_ops_per_sec:0 +total_net_input_bytes:2301 +total_net_output_bytes:507187 +instantaneous_input_kbps:0.00 +instantaneous_output_kbps:0.00 +rejected_connections:0 +sync_full:0 +sync_partial_ok:0 +sync_partial_err:0 +expired_keys:0 +expired_stale_perc:0.00 +expired_time_cap_reached_count:0 +expire_cycle_cpu_milliseconds:28362 +evicted_keys:0 +keyspace_hits:2 +keyspace_misses:0 +pubsub_channels:0 +pubsub_patterns:0 +latest_fork_usec:810 +migrate_cached_sockets:0 +slave_expires_tracked_keys:0 +active_defrag_hits:0 +active_defrag_misses:0 +active_defrag_key_hits:0 +active_defrag_key_misses:0 +tracking_total_keys:0 +tracking_total_items:0 +tracking_total_prefixes:0 +unexpected_error_replies:0 +total_reads_processed:250 +total_writes_processed:163 +io_threaded_reads_processed:0 +io_threaded_writes_processed:0 + +# Replication +role:master +connected_slaves:0 +master_replid:3f0ad529c9c59a17834bde8ae85f09f77609ecb1 +master_replid2:0000000000000000000000000000000000000000 +master_repl_offset:0 +second_repl_offset:-1 +repl_backlog_active:0 +repl_backlog_size:1048576 +repl_backlog_first_byte_offset:0 +repl_backlog_histlen:0 + +# CPU +used_cpu_sys:630.829091 +used_cpu_user:188.394908 +used_cpu_sys_children:0.020626 +used_cpu_user_children:0.002731 + +# Modules + +# Commandstats +cmdstat_set:calls=3,usec=140,usec_per_call=46.67 +cmdstat_command:calls=2,usec=2182,usec_per_call=1091.00 +cmdstat_get:calls=2,usec=29,usec_per_call=14.50 +cmdstat_hmset:calls=2,usec=408,usec_per_call=204.00 +cmdstat_hello:calls=1,usec=15,usec_per_call=15.00 +cmdstat_ping:calls=19,usec=286,usec_per_call=15.05 +cmdstat_info:calls=132,usec=37296,usec_per_call=282.55 + +# Cluster +cluster_enabled:0 + +# Keyspace +db0:keys=4,expires=0,avg_ttl=0 diff --git a/src/go/collectors/go.d.plugin/modules/pika/testdata/v3.4.0/info_all.txt b/src/go/collectors/go.d.plugin/modules/pika/testdata/v3.4.0/info_all.txt new file mode 100644 index 000000000..ec58524ce --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/pika/testdata/v3.4.0/info_all.txt @@ -0,0 +1,64 @@ +$1283 +# Server +pika_version:3.4.0 +pika_git_sha:bd30511bf82038c2c6531b3d84872c9825fe836a +pika_build_compile_date: Dec 1 2020 +os:Linux 5.4.39-linuxkit x86_64 +arch_bits:64 +process_id:1 +tcp_port:9221 +thread_num:1 +sync_thread_num:6 +uptime_in_seconds:1884 +uptime_in_days:1 +config_file:/pika/conf/pika.conf +server_id:1 + +# Data +db_size:645807 +db_size_human:0M +log_size:4272814 +log_size_human:4M +compression:snappy +used_memory:8198 +used_memory_human:0M +db_memtable_usage:8072 +db_tablereader_usage:126 +db_fatal:0 +db_fatal_msg:NULL + +# Clients +connected_clients:1 + +# Stats +total_connections_received:3 +instantaneous_ops_per_sec:0 +total_commands_processed:3 +is_bgsaving:No +is_scaning_keyspace:No +is_compact:No +compact_cron: +compact_interval: + +# Command_Exec_Count +INFO:1 +SET:2 + +# CPU +used_cpu_sys:158.20 +used_cpu_user:22.05 +used_cpu_sys_children:0.03 +used_cpu_user_children:0.02 + +# Replication(MASTER) +role:master +connected_slaves:0 +db0 binlog_offset=0 589,safety_purge=none + +# Keyspace +# Time:1970-01-01 08:00:00 +db0 Strings_keys=0, expires=0, invalid_keys=0 +db0 Hashes_keys=0, expires=0, invalid_keys=0 +db0 Lists_keys=0, expires=0, invalid_keys=0 +db0 Zsets_keys=0, expires=0, invalid_keys=0 +db0 Sets_keys=0, expires=0, invalid_keys=0 |