summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/modules/zookeeper
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 11:19:16 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-07-24 09:53:24 +0000
commitb5f8ee61a7f7e9bd291dd26b0585d03eb686c941 (patch)
treed4d31289c39fc00da064a825df13a0b98ce95b10 /src/go/collectors/go.d.plugin/modules/zookeeper
parentAdding upstream version 1.44.3. (diff)
downloadnetdata-b5f8ee61a7f7e9bd291dd26b0585d03eb686c941.tar.xz
netdata-b5f8ee61a7f7e9bd291dd26b0585d03eb686c941.zip
Adding upstream version 1.46.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
l---------src/go/collectors/go.d.plugin/modules/zookeeper/README.md1
-rw-r--r--src/go/collectors/go.d.plugin/modules/zookeeper/charts.go111
-rw-r--r--src/go/collectors/go.d.plugin/modules/zookeeper/collect.go79
-rw-r--r--src/go/collectors/go.d.plugin/modules/zookeeper/config_schema.json95
-rw-r--r--src/go/collectors/go.d.plugin/modules/zookeeper/fetcher.go74
-rw-r--r--src/go/collectors/go.d.plugin/modules/zookeeper/fetcher_test.go49
-rw-r--r--src/go/collectors/go.d.plugin/modules/zookeeper/init.go41
-rw-r--r--src/go/collectors/go.d.plugin/modules/zookeeper/integrations/zookeeper.md215
-rw-r--r--src/go/collectors/go.d.plugin/modules/zookeeper/metadata.yaml202
-rw-r--r--src/go/collectors/go.d.plugin/modules/zookeeper/testdata/config.json10
-rw-r--r--src/go/collectors/go.d.plugin/modules/zookeeper/testdata/config.yaml8
-rw-r--r--src/go/collectors/go.d.plugin/modules/zookeeper/testdata/mntr.txt416
-rw-r--r--src/go/collectors/go.d.plugin/modules/zookeeper/testdata/mntr_notinwhitelist.txt1
-rw-r--r--src/go/collectors/go.d.plugin/modules/zookeeper/zookeeper.go103
-rw-r--r--src/go/collectors/go.d.plugin/modules/zookeeper/zookeeper_test.go174
15 files changed, 1579 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/zookeeper/README.md b/src/go/collectors/go.d.plugin/modules/zookeeper/README.md
new file mode 120000
index 000000000..ae81b3714
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/zookeeper/README.md
@@ -0,0 +1 @@
+integrations/zookeeper.md \ No newline at end of file
diff --git a/src/go/collectors/go.d.plugin/modules/zookeeper/charts.go b/src/go/collectors/go.d.plugin/modules/zookeeper/charts.go
new file mode 100644
index 000000000..2c2cf6a05
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/zookeeper/charts.go
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package zookeeper
+
+import "github.com/netdata/netdata/go/go.d.plugin/agent/module"
+
+type (
+ Charts = module.Charts
+ Dims = module.Dims
+ Vars = module.Vars
+)
+
+var charts = Charts{
+ {
+ ID: "requests",
+ Title: "Outstanding Requests",
+ Units: "requests",
+ Fam: "requests",
+ Ctx: "zookeeper.requests",
+ Dims: Dims{
+ {ID: "outstanding_requests", Name: "outstanding"},
+ },
+ },
+ {
+ ID: "requests_latency",
+ Title: "Requests Latency",
+ Units: "ms",
+ Fam: "requests",
+ Ctx: "zookeeper.requests_latency",
+ Dims: Dims{
+ {ID: "min_latency", Name: "min", Div: 1000},
+ {ID: "avg_latency", Name: "avg", Div: 1000},
+ {ID: "max_latency", Name: "max", Div: 1000},
+ },
+ },
+ {
+ ID: "connections",
+ Title: "Alive Connections",
+ Units: "connections",
+ Fam: "connections",
+ Ctx: "zookeeper.connections",
+ Dims: Dims{
+ {ID: "num_alive_connections", Name: "alive"},
+ },
+ },
+ {
+ ID: "packets",
+ Title: "Packets",
+ Units: "pps",
+ Fam: "net",
+ Ctx: "zookeeper.packets",
+ Dims: Dims{
+ {ID: "packets_received", Name: "received", Algo: module.Incremental},
+ {ID: "packets_sent", Name: "sent", Algo: module.Incremental, Mul: -1},
+ },
+ },
+ {
+ ID: "file_descriptor",
+ Title: "Open File Descriptors",
+ Units: "file descriptors",
+ Fam: "file descriptors",
+ Ctx: "zookeeper.file_descriptor",
+ Dims: Dims{
+ {ID: "open_file_descriptor_count", Name: "open"},
+ },
+ Vars: Vars{
+ {ID: "max_file_descriptor_count"},
+ },
+ },
+ {
+ ID: "nodes",
+ Title: "Number of Nodes",
+ Units: "nodes",
+ Fam: "data tree",
+ Ctx: "zookeeper.nodes",
+ Dims: Dims{
+ {ID: "znode_count", Name: "znode"},
+ {ID: "ephemerals_count", Name: "ephemerals"},
+ },
+ },
+ {
+ ID: "watches",
+ Title: "Number of Watches",
+ Units: "watches",
+ Fam: "data tree",
+ Ctx: "zookeeper.watches",
+ Dims: Dims{
+ {ID: "watch_count", Name: "watches"},
+ },
+ },
+ {
+ ID: "approximate_data_size",
+ Title: "Approximate Data Tree Size",
+ Units: "KiB",
+ Fam: "data tree",
+ Ctx: "zookeeper.approximate_data_size",
+ Dims: Dims{
+ {ID: "approximate_data_size", Name: "size", Div: 1024},
+ },
+ },
+ {
+ ID: "server_state",
+ Title: "Server State",
+ Units: "state",
+ Fam: "server state",
+ Ctx: "zookeeper.server_state",
+ Dims: Dims{
+ {ID: "server_state", Name: "state"},
+ },
+ },
+}
diff --git a/src/go/collectors/go.d.plugin/modules/zookeeper/collect.go b/src/go/collectors/go.d.plugin/modules/zookeeper/collect.go
new file mode 100644
index 000000000..86491e1b1
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/zookeeper/collect.go
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package zookeeper
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+func (z *Zookeeper) collect() (map[string]int64, error) {
+ return z.collectMntr()
+}
+
+func (z *Zookeeper) collectMntr() (map[string]int64, error) {
+ const command = "mntr"
+
+ lines, err := z.fetch("mntr")
+ if err != nil {
+ return nil, err
+ }
+
+ switch len(lines) {
+ case 0:
+ return nil, fmt.Errorf("'%s' command returned empty response", command)
+ case 1:
+ // mntr is not executed because it is not in the whitelist.
+ return nil, fmt.Errorf("'%s' command returned bad response: %s", command, lines[0])
+ }
+
+ mx := make(map[string]int64)
+
+ for _, line := range lines {
+ parts := strings.Fields(line)
+ if len(parts) != 2 || !strings.HasPrefix(parts[0], "zk_") {
+ continue
+ }
+
+ key, value := strings.TrimPrefix(parts[0], "zk_"), parts[1]
+ switch key {
+ case "version":
+ case "server_state":
+ mx[key] = convertServerState(value)
+ case "min_latency", "avg_latency", "max_latency":
+ v, err := strconv.ParseFloat(value, 64)
+ if err != nil {
+ continue
+ }
+ mx[key] = int64(v * 1000)
+ default:
+ v, err := strconv.ParseFloat(value, 64)
+ if err != nil {
+ continue
+ }
+ mx[key] = int64(v)
+ }
+ }
+
+ if len(mx) == 0 {
+ return nil, fmt.Errorf("'%s' command: failed to parse response", command)
+ }
+
+ return mx, nil
+}
+
+func convertServerState(state string) int64 {
+ switch state {
+ default:
+ return 0
+ case "leader":
+ return 1
+ case "follower":
+ return 2
+ case "observer":
+ return 3
+ case "standalone":
+ return 4
+ }
+}
diff --git a/src/go/collectors/go.d.plugin/modules/zookeeper/config_schema.json b/src/go/collectors/go.d.plugin/modules/zookeeper/config_schema.json
new file mode 100644
index 000000000..e07a27c29
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/zookeeper/config_schema.json
@@ -0,0 +1,95 @@
+{
+ "jsonSchema": {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Zookeeper collector configuration.",
+ "type": "object",
+ "properties": {
+ "update_every": {
+ "title": "Update every",
+ "description": "Data collection interval, measured in seconds.",
+ "type": "integer",
+ "minimum": 1,
+ "default": 1
+ },
+ "address": {
+ "title": "Address",
+ "description": "The IP address and port where the Zookeeper server listens for connections.",
+ "type": "string",
+ "default": "127.0.0.1:2181"
+ },
+ "timeout": {
+ "title": "Timeout",
+ "description": "The timeout, in seconds, for connection, read, write, and SSL handshake operations.",
+ "type": "number",
+ "minimum": 0.5,
+ "default": 1
+ },
+ "use_tls": {
+ "title": "Use TLS",
+ "description": "Indicates whether TLS should be used for secure communication.",
+ "type": "boolean"
+ },
+ "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
+ },
+ "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": [
+ "use_tls",
+ "tls_skip_verify",
+ "tls_ca",
+ "tls_cert",
+ "tls_key"
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/src/go/collectors/go.d.plugin/modules/zookeeper/fetcher.go b/src/go/collectors/go.d.plugin/modules/zookeeper/fetcher.go
new file mode 100644
index 000000000..be821e622
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/zookeeper/fetcher.go
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package zookeeper
+
+import (
+ "bytes"
+ "fmt"
+ "unsafe"
+
+ "github.com/netdata/netdata/go/go.d.plugin/pkg/socket"
+)
+
+const limitReadLines = 2000
+
+type zookeeperFetcher struct {
+ socket.Client
+}
+
+func (c *zookeeperFetcher) fetch(command string) (rows []string, err error) {
+ if err = c.Connect(); err != nil {
+ return nil, err
+ }
+ defer func() { _ = c.Disconnect() }()
+
+ var num int
+ clientErr := c.Command(command, func(b []byte) bool {
+ if !isZKLine(b) || isMntrLineOK(b) {
+ rows = append(rows, string(b))
+ }
+ if num += 1; num >= limitReadLines {
+ err = fmt.Errorf("read line limit exceeded (%d)", limitReadLines)
+ return false
+ }
+ return true
+ })
+ if clientErr != nil {
+ return nil, clientErr
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ return rows, nil
+}
+
+func isZKLine(line []byte) bool {
+ return bytes.HasPrefix(line, []byte("zk_"))
+}
+
+func isMntrLineOK(line []byte) bool {
+ idx := bytes.LastIndexByte(line, '\t')
+ return idx > 0 && collectedZKKeys[unsafeString(line)[:idx]]
+}
+
+func unsafeString(b []byte) string {
+ return *((*string)(unsafe.Pointer(&b)))
+}
+
+var collectedZKKeys = map[string]bool{
+ "zk_num_alive_connections": true,
+ "zk_outstanding_requests": true,
+ "zk_min_latency": true,
+ "zk_avg_latency": true,
+ "zk_max_latency": true,
+ "zk_packets_received": true,
+ "zk_packets_sent": true,
+ "zk_open_file_descriptor_count": true,
+ "zk_max_file_descriptor_count": true,
+ "zk_znode_count": true,
+ "zk_ephemerals_count": true,
+ "zk_watch_count": true,
+ "zk_approximate_data_size": true,
+ "zk_server_state": true,
+}
diff --git a/src/go/collectors/go.d.plugin/modules/zookeeper/fetcher_test.go b/src/go/collectors/go.d.plugin/modules/zookeeper/fetcher_test.go
new file mode 100644
index 000000000..dbc5174b9
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/zookeeper/fetcher_test.go
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package zookeeper
+
+import (
+ "testing"
+
+ "github.com/netdata/netdata/go/go.d.plugin/pkg/socket"
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_clientFetch(t *testing.T) {
+ c := &zookeeperFetcher{Client: &mockSocket{rowsNumResp: 10}}
+
+ rows, err := c.fetch("whatever\n")
+ assert.NoError(t, err)
+ assert.Len(t, rows, 10)
+
+ rows, err = c.fetch("whatever\n")
+ assert.NoError(t, err)
+ assert.Len(t, rows, 10)
+}
+
+func Test_clientFetchReadLineLimitExceeded(t *testing.T) {
+ c := &zookeeperFetcher{Client: &mockSocket{rowsNumResp: limitReadLines + 1}}
+
+ rows, err := c.fetch("whatever\n")
+ assert.Error(t, err)
+ assert.Len(t, rows, 0)
+}
+
+type mockSocket struct {
+ rowsNumResp int
+}
+
+func (m *mockSocket) Connect() error {
+ return nil
+}
+
+func (m *mockSocket) Disconnect() error {
+ return nil
+}
+
+func (m *mockSocket) Command(command string, process socket.Processor) error {
+ for i := 0; i < m.rowsNumResp; i++ {
+ process([]byte(command))
+ }
+ return nil
+}
diff --git a/src/go/collectors/go.d.plugin/modules/zookeeper/init.go b/src/go/collectors/go.d.plugin/modules/zookeeper/init.go
new file mode 100644
index 000000000..1910e9a0b
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/zookeeper/init.go
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package zookeeper
+
+import (
+ "crypto/tls"
+ "errors"
+ "fmt"
+
+ "github.com/netdata/netdata/go/go.d.plugin/pkg/socket"
+ "github.com/netdata/netdata/go/go.d.plugin/pkg/tlscfg"
+)
+
+func (z *Zookeeper) verifyConfig() error {
+ if z.Address == "" {
+ return errors.New("address not set")
+ }
+ return nil
+}
+
+func (z *Zookeeper) initZookeeperFetcher() (fetcher, error) {
+ var tlsConf *tls.Config
+ var err error
+
+ if z.UseTLS {
+ tlsConf, err = tlscfg.NewTLSConfig(z.TLSConfig)
+ if err != nil {
+ return nil, fmt.Errorf("creating tls config : %v", err)
+ }
+ }
+
+ sock := socket.New(socket.Config{
+ Address: z.Address,
+ ConnectTimeout: z.Timeout.Duration(),
+ ReadTimeout: z.Timeout.Duration(),
+ WriteTimeout: z.Timeout.Duration(),
+ TLSConf: tlsConf,
+ })
+
+ return &zookeeperFetcher{Client: sock}, nil
+}
diff --git a/src/go/collectors/go.d.plugin/modules/zookeeper/integrations/zookeeper.md b/src/go/collectors/go.d.plugin/modules/zookeeper/integrations/zookeeper.md
new file mode 100644
index 000000000..45eeb0fc9
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/zookeeper/integrations/zookeeper.md
@@ -0,0 +1,215 @@
+<!--startmeta
+custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/zookeeper/README.md"
+meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/zookeeper/metadata.yaml"
+sidebar_label: "ZooKeeper"
+learn_status: "Published"
+learn_rel_path: "Collecting Metrics/Service Discovery / Registry"
+most_popular: False
+message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE"
+endmeta-->
+
+# ZooKeeper
+
+
+<img src="https://netdata.cloud/img/zookeeper.svg" width="150"/>
+
+
+Plugin: go.d.plugin
+Module: zookeeper
+
+<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" />
+
+## Overview
+
+
+
+It connects to the Zookeeper instance via a TCP and executes the following commands:
+
+- [mntr](https://zookeeper.apache.org/doc/r3.4.8/zookeeperAdmin.html#sc_zkCommands).
+
+
+This collector is supported on all platforms.
+
+This collector supports collecting metrics from multiple instances of this integration, including remote instances.
+
+
+### Default Behavior
+
+#### Auto-Detection
+
+By default, it detects instances running on localhost by attempting to connect using known ZooKeeper TCP sockets:
+
+- 127.0.0.1:2181
+- 127.0.0.1:2182
+
+
+#### 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 ZooKeeper instance
+
+These metrics refer to the entire monitored application.
+
+This scope has no labels.
+
+Metrics:
+
+| Metric | Dimensions | Unit |
+|:------|:----------|:----|
+| zookeeper.requests | outstanding | requests |
+| zookeeper.requests_latency | min, avg, max | ms |
+| zookeeper.connections | alive | connections |
+| zookeeper.packets | received, sent | pps |
+| zookeeper.file_descriptor | open | file descriptors |
+| zookeeper.nodes | znode, ephemerals | nodes |
+| zookeeper.watches | watches | watches |
+| zookeeper.approximate_data_size | size | KiB |
+| zookeeper.server_state | state | state |
+
+
+
+## Alerts
+
+There are no alerts configured by default for this integration.
+
+
+## Setup
+
+### Prerequisites
+
+#### Whitelist `mntr` command
+
+Add `mntr` to Zookeeper's [4lw.commands.whitelist](https://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_4lw).
+
+
+
+### Configuration
+
+#### File
+
+The configuration file name for this integration is `go.d/zookeeper.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/zookeeper.conf
+```
+#### Options
+
+The following options can be defined globally: update_every, autodetection_retry.
+
+
+<details open><summary>Config options</summary>
+
+| Name | Description | Default | Required |
+|:----|:-----------|:-------|:--------:|
+| update_every | Data collection frequency. | 1 | no |
+| autodetection_retry | Recheck interval in seconds. Zero means no recheck will be scheduled. | 0 | no |
+| address | Server address. The format is IP:PORT. | 127.0.0.1:2181 | yes |
+| timeout | Connection/read/write/ssl handshake timeout. | 1 | no |
+| use_tls | Whether to use TLS or not. | 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
+
+Local server.
+
+<details open><summary>Config</summary>
+
+```yaml
+jobs:
+ - name: local
+ address: 127.0.0.1:2181
+
+```
+</details>
+
+##### TLS with self-signed certificate
+
+Zookeeper with TLS and self-signed certificate.
+
+<details open><summary>Config</summary>
+
+```yaml
+jobs:
+ - name: local
+ address: 127.0.0.1:2181
+ use_tls: yes
+ tls_skip_verify: yes
+
+```
+</details>
+
+##### Multi-instance
+
+> **Note**: When you define multiple jobs, their names must be unique.
+
+Collecting metrics from local and remote instances.
+
+
+<details open><summary>Config</summary>
+
+```yaml
+jobs:
+ - name: local
+ address: 127.0.0.1:2181
+
+ - name: remote
+ address: 192.0.2.1:2181
+
+```
+</details>
+
+
+
+## Troubleshooting
+
+### Debug Mode
+
+To troubleshoot issues with the `zookeeper` 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 zookeeper
+ ```
+
+
diff --git a/src/go/collectors/go.d.plugin/modules/zookeeper/metadata.yaml b/src/go/collectors/go.d.plugin/modules/zookeeper/metadata.yaml
new file mode 100644
index 000000000..527a55fb4
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/zookeeper/metadata.yaml
@@ -0,0 +1,202 @@
+plugin_name: go.d.plugin
+modules:
+ - meta:
+ id: collector-go.d.plugin-zookeeper
+ plugin_name: go.d.plugin
+ module_name: zookeeper
+ monitored_instance:
+ name: ZooKeeper
+ link: https://zookeeper.apache.org/
+ categories:
+ - data-collection.service-discovery-registry
+ icon_filename: zookeeper.svg
+ keywords:
+ - zookeeper
+ most_popular: false
+ info_provided_to_referring_integrations:
+ description: ""
+ related_resources:
+ integrations:
+ list:
+ - plugin_name: apps.plugin
+ module_name: apps
+ overview:
+ data_collection:
+ metrics_description: ""
+ method_description: |
+ It connects to the Zookeeper instance via a TCP and executes the following commands:
+
+ - [mntr](https://zookeeper.apache.org/doc/r3.4.8/zookeeperAdmin.html#sc_zkCommands).
+ default_behavior:
+ auto_detection:
+ description: |
+ By default, it detects instances running on localhost by attempting to connect using known ZooKeeper TCP sockets:
+
+ - 127.0.0.1:2181
+ - 127.0.0.1:2182
+ limits:
+ description: ""
+ performance_impact:
+ description: ""
+ additional_permissions:
+ description: ""
+ multi_instance: true
+ supported_platforms:
+ include: []
+ exclude: []
+ setup:
+ prerequisites:
+ list:
+ - title: Whitelist `mntr` command
+ description: |
+ Add `mntr` to Zookeeper's [4lw.commands.whitelist](https://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_4lw).
+ configuration:
+ file:
+ name: "go.d/zookeeper.conf"
+ options:
+ description: |
+ The following options can be defined globally: update_every, autodetection_retry.
+ folding:
+ title: Config options
+ enabled: true
+ list:
+ - name: update_every
+ description: Data collection frequency.
+ default_value: 1
+ required: false
+ - name: autodetection_retry
+ description: Recheck interval in seconds. Zero means no recheck will be scheduled.
+ default_value: 0
+ required: false
+ - name: address
+ description: Server address. The format is IP:PORT.
+ default_value: 127.0.0.1:2181
+ required: true
+ - name: timeout
+ description: Connection/read/write/ssl handshake timeout.
+ default_value: 1
+ required: false
+ - name: use_tls
+ description: Whether to use TLS or not.
+ 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: Local server.
+ config: |
+ jobs:
+ - name: local
+ address: 127.0.0.1:2181
+ - name: TLS with self-signed certificate
+ description: Zookeeper with TLS and self-signed certificate.
+ config: |
+ jobs:
+ - name: local
+ address: 127.0.0.1:2181
+ use_tls: yes
+ tls_skip_verify: yes
+ - name: Multi-instance
+ description: |
+ > **Note**: When you define multiple jobs, their names must be unique.
+
+ Collecting metrics from local and remote instances.
+ config: |
+ jobs:
+ - name: local
+ address: 127.0.0.1:2181
+
+ - name: remote
+ address: 192.0.2.1:2181
+ 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: zookeeper.requests
+ description: Outstanding Requests
+ unit: requests
+ chart_type: line
+ dimensions:
+ - name: outstanding
+ - name: zookeeper.requests_latency
+ description: Requests Latency
+ unit: ms
+ chart_type: line
+ dimensions:
+ - name: min
+ - name: avg
+ - name: max
+ - name: zookeeper.connections
+ description: Alive Connections
+ unit: connections
+ chart_type: line
+ dimensions:
+ - name: alive
+ - name: zookeeper.packets
+ description: Packets
+ unit: pps
+ chart_type: line
+ dimensions:
+ - name: received
+ - name: sent
+ - name: zookeeper.file_descriptor
+ description: Open File Descriptors
+ unit: file descriptors
+ chart_type: line
+ dimensions:
+ - name: open
+ - name: zookeeper.nodes
+ description: Number of Nodes
+ unit: nodes
+ chart_type: line
+ dimensions:
+ - name: znode
+ - name: ephemerals
+ - name: zookeeper.watches
+ description: Number of Watches
+ unit: watches
+ chart_type: line
+ dimensions:
+ - name: watches
+ - name: zookeeper.approximate_data_size
+ description: Approximate Data Tree Size
+ unit: KiB
+ chart_type: line
+ dimensions:
+ - name: size
+ - name: zookeeper.server_state
+ description: Server State
+ unit: state
+ chart_type: line
+ dimensions:
+ - name: state
diff --git a/src/go/collectors/go.d.plugin/modules/zookeeper/testdata/config.json b/src/go/collectors/go.d.plugin/modules/zookeeper/testdata/config.json
new file mode 100644
index 000000000..0cf6c4727
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/zookeeper/testdata/config.json
@@ -0,0 +1,10 @@
+{
+ "update_every": 123,
+ "address": "ok",
+ "timeout": 123.123,
+ "use_tls": true,
+ "tls_ca": "ok",
+ "tls_cert": "ok",
+ "tls_key": "ok",
+ "tls_skip_verify": true
+}
diff --git a/src/go/collectors/go.d.plugin/modules/zookeeper/testdata/config.yaml b/src/go/collectors/go.d.plugin/modules/zookeeper/testdata/config.yaml
new file mode 100644
index 000000000..54456cc80
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/zookeeper/testdata/config.yaml
@@ -0,0 +1,8 @@
+update_every: 123
+address: "ok"
+timeout: 123.123
+use_tls: yes
+tls_ca: "ok"
+tls_cert: "ok"
+tls_key: "ok"
+tls_skip_verify: yes
diff --git a/src/go/collectors/go.d.plugin/modules/zookeeper/testdata/mntr.txt b/src/go/collectors/go.d.plugin/modules/zookeeper/testdata/mntr.txt
new file mode 100644
index 000000000..8e10c287d
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/zookeeper/testdata/mntr.txt
@@ -0,0 +1,416 @@
+zk_version 3.6.1--104dcb3e3fb464b30c5186d229e00af9f332524b, built on 04/21/2020 15:01 GMT
+zk_server_state standalone
+zk_ephemerals_count 0
+zk_min_latency 0.1
+zk_avg_latency 0.1
+zk_num_alive_connections 1
+zk_max_file_descriptor_count 1048576
+zk_outstanding_requests 0
+zk_approximate_data_size 44
+zk_znode_count 5
+zk_open_file_descriptor_count 63
+zk_global_sessions 0
+zk_local_sessions 0
+zk_uptime 27595191
+zk_last_client_response_size -1
+zk_max_latency 0.1
+zk_packets_sent 182
+zk_outstanding_tls_handshake 0
+zk_packets_received 92
+zk_max_client_response_size -1
+zk_connection_drop_probability 0.0
+zk_watch_count 0
+zk_min_client_response_size -1
+zk_proposal_count 0
+zk_outstanding_changes_removed 0
+zk_stale_requests_dropped 0
+zk_large_requests_rejected 0
+zk_connection_rejected 0
+zk_sessionless_connections_expired 0
+zk_looking_count 0
+zk_dead_watchers_queued 0
+zk_stale_requests 0
+zk_connection_drop_count 0
+zk_learner_proposal_received_count 0
+zk_digest_mismatches_count 0
+zk_dead_watchers_cleared 0
+zk_response_packet_cache_hits 0
+zk_bytes_received_count 368
+zk_add_dead_watcher_stall_time 0
+zk_request_throttle_wait_count 0
+zk_response_packet_cache_misses 0
+zk_ensemble_auth_success 0
+zk_prep_processor_request_queued 0
+zk_learner_commit_received_count 0
+zk_stale_replies 0
+zk_connection_request_count 0
+zk_ensemble_auth_fail 0
+zk_diff_count 0
+zk_response_packet_get_children_cache_misses 0
+zk_connection_revalidate_count 0
+zk_quit_leading_due_to_disloyal_voter 0
+zk_snap_count 0
+zk_unrecoverable_error_count 0
+zk_commit_count 0
+zk_stale_sessions_expired 0
+zk_response_packet_get_children_cache_hits 0
+zk_sync_processor_request_queued 0
+zk_outstanding_changes_queued 0
+zk_request_commit_queued 0
+zk_ensemble_auth_skip 0
+zk_tls_handshake_exceeded 0
+zk_revalidate_count 0
+zk_avg_node_created_watch_count 0.0
+zk_min_node_created_watch_count 0
+zk_max_node_created_watch_count 0
+zk_cnt_node_created_watch_count 0
+zk_sum_node_created_watch_count 0
+zk_avg_session_queues_drained 0.0
+zk_min_session_queues_drained 0
+zk_max_session_queues_drained 0
+zk_cnt_session_queues_drained 0
+zk_sum_session_queues_drained 0
+zk_avg_write_commit_proc_req_queued 0.0
+zk_min_write_commit_proc_req_queued 0
+zk_max_write_commit_proc_req_queued 0
+zk_cnt_write_commit_proc_req_queued 0
+zk_sum_write_commit_proc_req_queued 0
+zk_avg_connection_token_deficit 0.0
+zk_min_connection_token_deficit 0
+zk_max_connection_token_deficit 0
+zk_cnt_connection_token_deficit 0
+zk_sum_connection_token_deficit 0
+zk_avg_read_commit_proc_req_queued 0.0
+zk_min_read_commit_proc_req_queued 0
+zk_max_read_commit_proc_req_queued 0
+zk_cnt_read_commit_proc_req_queued 0
+zk_sum_read_commit_proc_req_queued 0
+zk_avg_node_deleted_watch_count 0.0
+zk_min_node_deleted_watch_count 0
+zk_max_node_deleted_watch_count 0
+zk_cnt_node_deleted_watch_count 0
+zk_sum_node_deleted_watch_count 0
+zk_avg_startup_txns_load_time 0.0
+zk_min_startup_txns_load_time 0
+zk_max_startup_txns_load_time 0
+zk_cnt_startup_txns_load_time 0
+zk_sum_startup_txns_load_time 0
+zk_avg_sync_processor_queue_size 0.0
+zk_min_sync_processor_queue_size 0
+zk_max_sync_processor_queue_size 0
+zk_cnt_sync_processor_queue_size 1
+zk_sum_sync_processor_queue_size 0
+zk_avg_follower_sync_time 0.0
+zk_min_follower_sync_time 0
+zk_max_follower_sync_time 0
+zk_cnt_follower_sync_time 0
+zk_sum_follower_sync_time 0
+zk_avg_prep_processor_queue_size 0.0
+zk_min_prep_processor_queue_size 0
+zk_max_prep_processor_queue_size 0
+zk_cnt_prep_processor_queue_size 1
+zk_sum_prep_processor_queue_size 0
+zk_avg_fsynctime 0.0
+zk_min_fsynctime 0
+zk_max_fsynctime 0
+zk_cnt_fsynctime 0
+zk_sum_fsynctime 0
+zk_avg_reads_issued_from_session_queue 0.0
+zk_min_reads_issued_from_session_queue 0
+zk_max_reads_issued_from_session_queue 0
+zk_cnt_reads_issued_from_session_queue 0
+zk_sum_reads_issued_from_session_queue 0
+zk_avg_snapshottime 0.0
+zk_min_snapshottime 0
+zk_max_snapshottime 0
+zk_cnt_snapshottime 1
+zk_sum_snapshottime 0
+zk_avg_startup_txns_loaded 0.0
+zk_min_startup_txns_loaded 0
+zk_max_startup_txns_loaded 0
+zk_cnt_startup_txns_loaded 0
+zk_sum_startup_txns_loaded 0
+zk_avg_reads_after_write_in_session_queue 0.0
+zk_min_reads_after_write_in_session_queue 0
+zk_max_reads_after_write_in_session_queue 0
+zk_cnt_reads_after_write_in_session_queue 0
+zk_sum_reads_after_write_in_session_queue 0
+zk_avg_requests_in_session_queue 0.0
+zk_min_requests_in_session_queue 0
+zk_max_requests_in_session_queue 0
+zk_cnt_requests_in_session_queue 0
+zk_sum_requests_in_session_queue 0
+zk_avg_write_commit_proc_issued 0.0
+zk_min_write_commit_proc_issued 0
+zk_max_write_commit_proc_issued 0
+zk_cnt_write_commit_proc_issued 0
+zk_sum_write_commit_proc_issued 0
+zk_avg_prep_process_time 0.0
+zk_min_prep_process_time 0
+zk_max_prep_process_time 0
+zk_cnt_prep_process_time 0
+zk_sum_prep_process_time 0
+zk_avg_pending_session_queue_size 0.0
+zk_min_pending_session_queue_size 0
+zk_max_pending_session_queue_size 0
+zk_cnt_pending_session_queue_size 0
+zk_sum_pending_session_queue_size 0
+zk_avg_time_waiting_empty_pool_in_commit_processor_read_ms 0.0
+zk_min_time_waiting_empty_pool_in_commit_processor_read_ms 0
+zk_max_time_waiting_empty_pool_in_commit_processor_read_ms 0
+zk_cnt_time_waiting_empty_pool_in_commit_processor_read_ms 0
+zk_sum_time_waiting_empty_pool_in_commit_processor_read_ms 0
+zk_avg_commit_process_time 0.0
+zk_min_commit_process_time 0
+zk_max_commit_process_time 0
+zk_cnt_commit_process_time 0
+zk_sum_commit_process_time 0
+zk_avg_dbinittime 6.0
+zk_min_dbinittime 6
+zk_max_dbinittime 6
+zk_cnt_dbinittime 1
+zk_sum_dbinittime 6
+zk_avg_netty_queued_buffer_capacity 0.0
+zk_min_netty_queued_buffer_capacity 0
+zk_max_netty_queued_buffer_capacity 0
+zk_cnt_netty_queued_buffer_capacity 0
+zk_sum_netty_queued_buffer_capacity 0
+zk_avg_election_time 0.0
+zk_min_election_time 0
+zk_max_election_time 0
+zk_cnt_election_time 0
+zk_sum_election_time 0
+zk_avg_commit_commit_proc_req_queued 0.0
+zk_min_commit_commit_proc_req_queued 0
+zk_max_commit_commit_proc_req_queued 0
+zk_cnt_commit_commit_proc_req_queued 0
+zk_sum_commit_commit_proc_req_queued 0
+zk_avg_sync_processor_batch_size 0.0
+zk_min_sync_processor_batch_size 0
+zk_max_sync_processor_batch_size 0
+zk_cnt_sync_processor_batch_size 0
+zk_sum_sync_processor_batch_size 0
+zk_avg_node_children_watch_count 0.0
+zk_min_node_children_watch_count 0
+zk_max_node_children_watch_count 0
+zk_cnt_node_children_watch_count 0
+zk_sum_node_children_watch_count 0
+zk_avg_write_batch_time_in_commit_processor 0.0
+zk_min_write_batch_time_in_commit_processor 0
+zk_max_write_batch_time_in_commit_processor 0
+zk_cnt_write_batch_time_in_commit_processor 0
+zk_sum_write_batch_time_in_commit_processor 0
+zk_avg_read_commit_proc_issued 0.0
+zk_min_read_commit_proc_issued 0
+zk_max_read_commit_proc_issued 0
+zk_cnt_read_commit_proc_issued 0
+zk_sum_read_commit_proc_issued 0
+zk_avg_concurrent_request_processing_in_commit_processor 0.0
+zk_min_concurrent_request_processing_in_commit_processor 0
+zk_max_concurrent_request_processing_in_commit_processor 0
+zk_cnt_concurrent_request_processing_in_commit_processor 0
+zk_sum_concurrent_request_processing_in_commit_processor 0
+zk_avg_node_changed_watch_count 0.0
+zk_min_node_changed_watch_count 0
+zk_max_node_changed_watch_count 0
+zk_cnt_node_changed_watch_count 0
+zk_sum_node_changed_watch_count 0
+zk_avg_sync_process_time 0.0
+zk_min_sync_process_time 0
+zk_max_sync_process_time 0
+zk_cnt_sync_process_time 0
+zk_sum_sync_process_time 0
+zk_avg_startup_snap_load_time 5.0
+zk_min_startup_snap_load_time 5
+zk_max_startup_snap_load_time 5
+zk_cnt_startup_snap_load_time 1
+zk_sum_startup_snap_load_time 5
+zk_avg_prep_processor_queue_time_ms 0.0
+zk_min_prep_processor_queue_time_ms 0
+zk_max_prep_processor_queue_time_ms 0
+zk_cnt_prep_processor_queue_time_ms 0
+zk_sum_prep_processor_queue_time_ms 0
+zk_p50_prep_processor_queue_time_ms 0
+zk_p95_prep_processor_queue_time_ms 0
+zk_p99_prep_processor_queue_time_ms 0
+zk_p999_prep_processor_queue_time_ms 0
+zk_avg_close_session_prep_time 0.0
+zk_min_close_session_prep_time 0
+zk_max_close_session_prep_time 0
+zk_cnt_close_session_prep_time 0
+zk_sum_close_session_prep_time 0
+zk_p50_close_session_prep_time 0
+zk_p95_close_session_prep_time 0
+zk_p99_close_session_prep_time 0
+zk_p999_close_session_prep_time 0
+zk_avg_read_commitproc_time_ms 0.0
+zk_min_read_commitproc_time_ms 0
+zk_max_read_commitproc_time_ms 0
+zk_cnt_read_commitproc_time_ms 0
+zk_sum_read_commitproc_time_ms 0
+zk_p50_read_commitproc_time_ms 0
+zk_p95_read_commitproc_time_ms 0
+zk_p99_read_commitproc_time_ms 0
+zk_p999_read_commitproc_time_ms 0
+zk_avg_updatelatency 0.0
+zk_min_updatelatency 0
+zk_max_updatelatency 0
+zk_cnt_updatelatency 0
+zk_sum_updatelatency 0
+zk_p50_updatelatency 0
+zk_p95_updatelatency 0
+zk_p99_updatelatency 0
+zk_p999_updatelatency 0
+zk_avg_local_write_committed_time_ms 0.0
+zk_min_local_write_committed_time_ms 0
+zk_max_local_write_committed_time_ms 0
+zk_cnt_local_write_committed_time_ms 0
+zk_sum_local_write_committed_time_ms 0
+zk_p50_local_write_committed_time_ms 0
+zk_p95_local_write_committed_time_ms 0
+zk_p99_local_write_committed_time_ms 0
+zk_p999_local_write_committed_time_ms 0
+zk_avg_readlatency 0.0
+zk_min_readlatency 0
+zk_max_readlatency 0
+zk_cnt_readlatency 0
+zk_sum_readlatency 0
+zk_p50_readlatency 0
+zk_p95_readlatency 0
+zk_p99_readlatency 0
+zk_p999_readlatency 0
+zk_avg_quorum_ack_latency 0.0
+zk_min_quorum_ack_latency 0
+zk_max_quorum_ack_latency 0
+zk_cnt_quorum_ack_latency 0
+zk_sum_quorum_ack_latency 0
+zk_p50_quorum_ack_latency 0
+zk_p95_quorum_ack_latency 0
+zk_p99_quorum_ack_latency 0
+zk_p999_quorum_ack_latency 0
+zk_avg_om_commit_process_time_ms 0.0
+zk_min_om_commit_process_time_ms 0
+zk_max_om_commit_process_time_ms 0
+zk_cnt_om_commit_process_time_ms 0
+zk_sum_om_commit_process_time_ms 0
+zk_p50_om_commit_process_time_ms 0
+zk_p95_om_commit_process_time_ms 0
+zk_p99_om_commit_process_time_ms 0
+zk_p999_om_commit_process_time_ms 0
+zk_avg_read_final_proc_time_ms 0.0
+zk_min_read_final_proc_time_ms 0
+zk_max_read_final_proc_time_ms 0
+zk_cnt_read_final_proc_time_ms 0
+zk_sum_read_final_proc_time_ms 0
+zk_p50_read_final_proc_time_ms 0
+zk_p95_read_final_proc_time_ms 0
+zk_p99_read_final_proc_time_ms 0
+zk_p999_read_final_proc_time_ms 0
+zk_avg_commit_propagation_latency 0.0
+zk_min_commit_propagation_latency 0
+zk_max_commit_propagation_latency 0
+zk_cnt_commit_propagation_latency 0
+zk_sum_commit_propagation_latency 0
+zk_p50_commit_propagation_latency 0
+zk_p95_commit_propagation_latency 0
+zk_p99_commit_propagation_latency 0
+zk_p999_commit_propagation_latency 0
+zk_avg_dead_watchers_cleaner_latency 0.0
+zk_min_dead_watchers_cleaner_latency 0
+zk_max_dead_watchers_cleaner_latency 0
+zk_cnt_dead_watchers_cleaner_latency 0
+zk_sum_dead_watchers_cleaner_latency 0
+zk_p50_dead_watchers_cleaner_latency 0
+zk_p95_dead_watchers_cleaner_latency 0
+zk_p99_dead_watchers_cleaner_latency 0
+zk_p999_dead_watchers_cleaner_latency 0
+zk_avg_write_final_proc_time_ms 0.0
+zk_min_write_final_proc_time_ms 0
+zk_max_write_final_proc_time_ms 0
+zk_cnt_write_final_proc_time_ms 0
+zk_sum_write_final_proc_time_ms 0
+zk_p50_write_final_proc_time_ms 0
+zk_p95_write_final_proc_time_ms 0
+zk_p99_write_final_proc_time_ms 0
+zk_p999_write_final_proc_time_ms 0
+zk_avg_proposal_ack_creation_latency 0.0
+zk_min_proposal_ack_creation_latency 0
+zk_max_proposal_ack_creation_latency 0
+zk_cnt_proposal_ack_creation_latency 0
+zk_sum_proposal_ack_creation_latency 0
+zk_p50_proposal_ack_creation_latency 0
+zk_p95_proposal_ack_creation_latency 0
+zk_p99_proposal_ack_creation_latency 0
+zk_p999_proposal_ack_creation_latency 0
+zk_avg_proposal_latency 0.0
+zk_min_proposal_latency 0
+zk_max_proposal_latency 0
+zk_cnt_proposal_latency 0
+zk_sum_proposal_latency 0
+zk_p50_proposal_latency 0
+zk_p95_proposal_latency 0
+zk_p99_proposal_latency 0
+zk_p999_proposal_latency 0
+zk_avg_om_proposal_process_time_ms 0.0
+zk_min_om_proposal_process_time_ms 0
+zk_max_om_proposal_process_time_ms 0
+zk_cnt_om_proposal_process_time_ms 0
+zk_sum_om_proposal_process_time_ms 0
+zk_p50_om_proposal_process_time_ms 0
+zk_p95_om_proposal_process_time_ms 0
+zk_p99_om_proposal_process_time_ms 0
+zk_p999_om_proposal_process_time_ms 0
+zk_avg_sync_processor_queue_and_flush_time_ms 0.0
+zk_min_sync_processor_queue_and_flush_time_ms 0
+zk_max_sync_processor_queue_and_flush_time_ms 0
+zk_cnt_sync_processor_queue_and_flush_time_ms 0
+zk_sum_sync_processor_queue_and_flush_time_ms 0
+zk_p50_sync_processor_queue_and_flush_time_ms 0
+zk_p95_sync_processor_queue_and_flush_time_ms 0
+zk_p99_sync_processor_queue_and_flush_time_ms 0
+zk_p999_sync_processor_queue_and_flush_time_ms 0
+zk_avg_propagation_latency 0.0
+zk_min_propagation_latency 0
+zk_max_propagation_latency 0
+zk_cnt_propagation_latency 0
+zk_sum_propagation_latency 0
+zk_p50_propagation_latency 0
+zk_p95_propagation_latency 0
+zk_p99_propagation_latency 0
+zk_p999_propagation_latency 0
+zk_avg_server_write_committed_time_ms 0.0
+zk_min_server_write_committed_time_ms 0
+zk_max_server_write_committed_time_ms 0
+zk_cnt_server_write_committed_time_ms 0
+zk_sum_server_write_committed_time_ms 0
+zk_p50_server_write_committed_time_ms 0
+zk_p95_server_write_committed_time_ms 0
+zk_p99_server_write_committed_time_ms 0
+zk_p999_server_write_committed_time_ms 0
+zk_avg_sync_processor_queue_time_ms 0.0
+zk_min_sync_processor_queue_time_ms 0
+zk_max_sync_processor_queue_time_ms 0
+zk_cnt_sync_processor_queue_time_ms 0
+zk_sum_sync_processor_queue_time_ms 0
+zk_p50_sync_processor_queue_time_ms 0
+zk_p95_sync_processor_queue_time_ms 0
+zk_p99_sync_processor_queue_time_ms 0
+zk_p999_sync_processor_queue_time_ms 0
+zk_avg_sync_processor_queue_flush_time_ms 0.0
+zk_min_sync_processor_queue_flush_time_ms 0
+zk_max_sync_processor_queue_flush_time_ms 0
+zk_cnt_sync_processor_queue_flush_time_ms 0
+zk_sum_sync_processor_queue_flush_time_ms 0
+zk_p50_sync_processor_queue_flush_time_ms 0
+zk_p95_sync_processor_queue_flush_time_ms 0
+zk_p99_sync_processor_queue_flush_time_ms 0
+zk_p999_sync_processor_queue_flush_time_ms 0
+zk_avg_write_commitproc_time_ms 0.0
+zk_min_write_commitproc_time_ms 0
+zk_max_write_commitproc_time_ms 0
+zk_cnt_write_commitproc_time_ms 0
+zk_sum_write_commitproc_time_ms 0
+zk_p50_write_commitproc_time_ms 0
+zk_p95_write_commitproc_time_ms 0
+zk_p99_write_commitproc_time_ms 0
+zk_p999_write_commitproc_time_ms 0 \ No newline at end of file
diff --git a/src/go/collectors/go.d.plugin/modules/zookeeper/testdata/mntr_notinwhitelist.txt b/src/go/collectors/go.d.plugin/modules/zookeeper/testdata/mntr_notinwhitelist.txt
new file mode 100644
index 000000000..1fd1983b7
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/zookeeper/testdata/mntr_notinwhitelist.txt
@@ -0,0 +1 @@
+mntr is not executed because it is not in the whitelist. \ No newline at end of file
diff --git a/src/go/collectors/go.d.plugin/modules/zookeeper/zookeeper.go b/src/go/collectors/go.d.plugin/modules/zookeeper/zookeeper.go
new file mode 100644
index 000000000..bf2a43310
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/zookeeper/zookeeper.go
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package zookeeper
+
+import (
+ _ "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"
+)
+
+//go:embed "config_schema.json"
+var configSchema string
+
+func init() {
+ module.Register("zookeeper", module.Creator{
+ JobConfigSchema: configSchema,
+ Create: func() module.Module { return New() },
+ Config: func() any { return &Config{} },
+ })
+}
+
+func New() *Zookeeper {
+ return &Zookeeper{
+ Config: Config{
+ Address: "127.0.0.1:2181",
+ Timeout: web.Duration(time.Second),
+ UseTLS: false,
+ }}
+}
+
+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:""`
+ UseTLS bool `yaml:"use_tls,omitempty" json:"use_tls"`
+}
+
+type (
+ Zookeeper struct {
+ module.Base
+ Config `yaml:",inline" json:""`
+
+ fetcher
+ }
+ fetcher interface {
+ fetch(command string) ([]string, error)
+ }
+)
+
+func (z *Zookeeper) Configuration() any {
+ return z.Config
+}
+
+func (z *Zookeeper) Init() error {
+ if err := z.verifyConfig(); err != nil {
+ z.Error(err)
+ return err
+ }
+
+ f, err := z.initZookeeperFetcher()
+ if err != nil {
+ z.Error(err)
+ return err
+ }
+ z.fetcher = f
+
+ return nil
+}
+
+func (z *Zookeeper) Check() error {
+ mx, err := z.collect()
+ if err != nil {
+ z.Error(err)
+ return err
+ }
+ if len(mx) == 0 {
+ return errors.New("no metrics collected")
+ }
+ return nil
+}
+
+func (z *Zookeeper) Charts() *Charts {
+ return charts.Copy()
+}
+
+func (z *Zookeeper) Collect() map[string]int64 {
+ mx, err := z.collect()
+ if err != nil {
+ z.Error(err)
+ }
+
+ if len(mx) == 0 {
+ return nil
+ }
+ return mx
+}
+
+func (z *Zookeeper) Cleanup() {}
diff --git a/src/go/collectors/go.d.plugin/modules/zookeeper/zookeeper_test.go b/src/go/collectors/go.d.plugin/modules/zookeeper/zookeeper_test.go
new file mode 100644
index 000000000..d33673fc3
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/zookeeper/zookeeper_test.go
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package zookeeper
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "os"
+ "testing"
+
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+var (
+ dataConfigJSON, _ = os.ReadFile("testdata/config.json")
+ dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
+
+ dataMntrMetrics, _ = os.ReadFile("testdata/mntr.txt")
+ dataMntrNotInWhiteListResponse, _ = os.ReadFile("testdata/mntr_notinwhitelist.txt")
+)
+
+func Test_testDataIsValid(t *testing.T) {
+ for name, data := range map[string][]byte{
+ "dataConfigJSON": dataConfigJSON,
+ "dataConfigYAML": dataConfigYAML,
+ "dataMntrMetrics": dataMntrMetrics,
+ "dataMntrNotInWhiteListResponse": dataMntrNotInWhiteListResponse,
+ } {
+ assert.NotNil(t, data, name)
+ }
+}
+
+func TestZookeeper_ConfigurationSerialize(t *testing.T) {
+ module.TestConfigurationSerialize(t, &Zookeeper{}, dataConfigJSON, dataConfigYAML)
+}
+
+func TestZookeeper_Init(t *testing.T) {
+ job := New()
+
+ assert.NoError(t, job.Init())
+ assert.NotNil(t, job.fetcher)
+}
+
+func TestZookeeper_InitErrorOnCreatingTLSConfig(t *testing.T) {
+ job := New()
+ job.UseTLS = true
+ job.TLSConfig.TLSCA = "testdata/tls"
+
+ assert.Error(t, job.Init())
+}
+
+func TestZookeeper_Check(t *testing.T) {
+ job := New()
+ require.NoError(t, job.Init())
+ job.fetcher = &mockZookeeperFetcher{data: dataMntrMetrics}
+
+ assert.NoError(t, job.Check())
+}
+
+func TestZookeeper_CheckErrorOnFetch(t *testing.T) {
+ job := New()
+ require.NoError(t, job.Init())
+ job.fetcher = &mockZookeeperFetcher{err: true}
+
+ assert.Error(t, job.Check())
+}
+
+func TestZookeeper_Charts(t *testing.T) {
+ assert.NotNil(t, New().Charts())
+}
+
+func TestZookeeper_Cleanup(t *testing.T) {
+ New().Cleanup()
+}
+
+func TestZookeeper_Collect(t *testing.T) {
+ job := New()
+ require.NoError(t, job.Init())
+ job.fetcher = &mockZookeeperFetcher{data: dataMntrMetrics}
+
+ expected := map[string]int64{
+ "approximate_data_size": 44,
+ "avg_latency": 100,
+ "ephemerals_count": 0,
+ "max_file_descriptor_count": 1048576,
+ "max_latency": 100,
+ "min_latency": 100,
+ "num_alive_connections": 1,
+ "open_file_descriptor_count": 63,
+ "outstanding_requests": 0,
+ "packets_received": 92,
+ "packets_sent": 182,
+ "server_state": 4,
+ "watch_count": 0,
+ "znode_count": 5,
+ }
+
+ collected := job.Collect()
+
+ assert.Equal(t, expected, collected)
+ ensureCollectedHasAllChartsDimsVarsIDs(t, job, collected)
+}
+
+func TestZookeeper_CollectMntrNotInWhiteList(t *testing.T) {
+ job := New()
+ require.NoError(t, job.Init())
+ job.fetcher = &mockZookeeperFetcher{data: dataMntrNotInWhiteListResponse}
+
+ assert.Nil(t, job.Collect())
+}
+
+func TestZookeeper_CollectMntrEmptyResponse(t *testing.T) {
+ job := New()
+ require.NoError(t, job.Init())
+ job.fetcher = &mockZookeeperFetcher{}
+
+ assert.Nil(t, job.Collect())
+}
+
+func TestZookeeper_CollectMntrInvalidData(t *testing.T) {
+ job := New()
+ require.NoError(t, job.Init())
+ job.fetcher = &mockZookeeperFetcher{data: []byte("hello \nand good buy\n")}
+
+ assert.Nil(t, job.Collect())
+}
+
+func TestZookeeper_CollectMntrReceiveError(t *testing.T) {
+ job := New()
+ require.NoError(t, job.Init())
+ job.fetcher = &mockZookeeperFetcher{err: true}
+
+ assert.Nil(t, job.Collect())
+}
+
+func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, zk *Zookeeper, collected map[string]int64) {
+ for _, chart := range *zk.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)
+ }
+ }
+}
+
+type mockZookeeperFetcher struct {
+ data []byte
+ err bool
+}
+
+func (m mockZookeeperFetcher) fetch(_ string) ([]string, error) {
+ if m.err {
+ return nil, errors.New("mock fetch error")
+ }
+
+ var lines []string
+ s := bufio.NewScanner(bytes.NewReader(m.data))
+ for s.Scan() {
+ if !isZKLine(s.Bytes()) || isMntrLineOK(s.Bytes()) {
+ lines = append(lines, s.Text())
+ }
+ }
+ return lines, nil
+}