feat(go.d): add NATS collector (#19252)
add go.d/nats
Ilya Mashchenko committed
Dec 19, 2024 at 22:23 UTC
19c500963e31a99d1242e943be8c471265af706a
17 files changed
+1302
-4
src/go/plugin/go.d/README.md
+5
-4
@@ -14,8 +14,8 @@
14
15
All capabilities are set automatically during Netdata installation using the [official installation method](/packaging/installer/methods/kickstart.md).
16
17
-| Capability | Required by |
18
-|:--------------------|:-------------------------------------------------------------------------------------------------------:|
17
+| Capability | Required by |
18
+|:--------------------|:---------------------------------------------------------------------------------------------------------:|
19
| CAP_NET_RAW | [Ping](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/collector/ping#readme) |
20
| CAP_NET_ADMIN | [Wireguard](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/collector/wireguard#readme) |
21
| CAP_DAC_READ_SEARCH | [Filecheck](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/collector/filecheck#readme) |
@@ -25,8 +25,8 @@ All capabilities are set automatically during Netdata installation using the [of
25
<details>
26
<summary>Data Collection Modules</summary>
27
28
-| Name | Monitors |
29
-|:-------------------------------------------------------------------------------------------------------------------|:-----------------------------:|
28
+| Name | Monitors |
29
+|:---------------------------------------------------------------------------------------------------------------------|:-----------------------------:|
30
| [adaptec_raid](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/collector/adaptecraid) | Adaptec Hardware RAID |
31
| [activemq](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/collector/activemq) | ActiveMQ |
32
| [ap](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/collector/ap) | Wireless AP |
@@ -84,6 +84,7 @@ All capabilities are set automatically during Netdata installation using the [of
84
| [mongoDB](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/collector/mongodb) | MongoDB |
85
| [monit](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/collector/monit) | Monit |
86
| [mysql](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/collector/mysql) | MySQL |
87
+| [nats](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/collector/nats) | NATS |
88
| [nginx](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/collector/nginx) | NGINX |
89
| [nginxplus](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/collector/nginxplus) | NGINX Plus |
90
| [nginxunit](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/collector/nginxunit) | NGINX Unit |
src/go/plugin/go.d/collector/init.go
+1
@@ -61,6 +61,7 @@ import (
61
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/mongodb"
62
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/monit"
63
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/mysql"
64
+ _ "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/nats"
65
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/nginx"
66
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/nginxplus"
67
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/nginxunit"
src/go/plugin/go.d/collector/nats/charts.go
new
+161
@@ -0,0 +1,161 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package nats
4
+
5
+import (
6
+ "fmt"
7
+
8
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
9
+)
10
+
11
+const (
12
+ prioServerTraffic = module.Priority + iota
13
+ prioServerMessages
14
+ prioServerConnectionsCurrent
15
+ prioServerConnectionsRate
16
+ prioHttpEndpointRequests
17
+ prioServerHealthProbeStatus
18
+ prioServerCpuUsage
19
+ prioServerMemoryUsage
20
+ prioServerUptime
21
+)
22
+
23
+var serverCharts = func() module.Charts {
24
+ charts := module.Charts{
25
+ chartServerConnectionsCurrent.Copy(),
26
+ chartServerConnectionsRate.Copy(),
27
+ chartServerTraffic.Copy(),
28
+ chartServerMessages.Copy(),
29
+ chartServerHealthProbeStatus.Copy(),
30
+ chartServerCpuUsage.Copy(),
31
+ chartServerMemUsage.Copy(),
32
+ chartServerUptime.Copy(),
33
+ }
34
+ charts = append(charts, httpEndpointCharts()...)
35
+ return charts
36
+}()
37
+
38
+var (
39
+ chartServerTraffic = module.Chart{
40
+ ID: "server_traffic",
41
+ Title: "Server Traffic",
42
+ Units: "bytes/s",
43
+ Fam: "traffic",
44
+ Ctx: "nats.server_traffic",
45
+ Priority: prioServerTraffic,
46
+ Type: module.Area,
47
+ Dims: module.Dims{
48
+ {ID: "in_bytes", Name: "in", Algo: module.Incremental},
49
+ {ID: "out_bytes", Name: "out", Mul: -1, Algo: module.Incremental},
50
+ },
51
+ }
52
+ chartServerMessages = module.Chart{
53
+ ID: "server_messages",
54
+ Title: "Server Messages",
55
+ Units: "messages/s",
56
+ Fam: "traffic",
57
+ Ctx: "nats.server_messages",
58
+ Priority: prioServerMessages,
59
+ Dims: module.Dims{
60
+ {ID: "in_msgs", Name: "in", Algo: module.Incremental},
61
+ {ID: "out_msgs", Name: "out", Mul: -1, Algo: module.Incremental},
62
+ },
63
+ }
64
+ chartServerConnectionsCurrent = module.Chart{
65
+ ID: "server_connections_current",
66
+ Title: "Server Current Connections",
67
+ Units: "connections",
68
+ Fam: "connections",
69
+ Ctx: "nats.server_connections_current",
70
+ Priority: prioServerConnectionsCurrent,
71
+ Dims: module.Dims{
72
+ {ID: "connections", Name: "active"},
73
+ },
74
+ }
75
+ chartServerConnectionsRate = module.Chart{
76
+ ID: "server_connections_rate",
77
+ Title: "Server Connections",
78
+ Units: "connections/s",
79
+ Fam: "connections",
80
+ Ctx: "nats.server_connections_rate",
81
+ Priority: prioServerConnectionsRate,
82
+ Dims: module.Dims{
83
+ {ID: "total_connections", Name: "connections", Algo: module.Incremental},
84
+ },
85
+ }
86
+ chartServerHealthProbeStatus = module.Chart{
87
+ ID: "server_health_probe_status",
88
+ Title: "Server Health Probe Status",
89
+ Units: "status",
90
+ Fam: "health",
91
+ Ctx: "nats.server_health_probe_status",
92
+ Priority: prioServerHealthProbeStatus,
93
+ Dims: module.Dims{
94
+ {ID: "healthz_status_ok", Name: "ok"},
95
+ {ID: "healthz_status_error", Name: "error"},
96
+ },
97
+ }
98
+ chartServerCpuUsage = module.Chart{
99
+ ID: "server_cpu_usage",
100
+ Title: "Server CPU Usage",
101
+ Units: "percent",
102
+ Fam: "rusage",
103
+ Ctx: "nats.server_cpu_usage",
104
+ Priority: prioServerCpuUsage,
105
+ Type: module.Area,
106
+ Dims: module.Dims{
107
+ {ID: "cpu", Name: "used"},
108
+ },
109
+ }
110
+ chartServerMemUsage = module.Chart{
111
+ ID: "server_mem_usage",
112
+ Title: "Server Memory Usage",
113
+ Units: "bytes",
114
+ Fam: "rusage",
115
+ Ctx: "nats.server_mem_usage",
116
+ Priority: prioServerMemoryUsage,
117
+ Type: module.Area,
118
+ Dims: module.Dims{
119
+ {ID: "mem", Name: "used"},
120
+ },
121
+ }
122
+ chartServerUptime = module.Chart{
123
+ ID: "server_uptime",
124
+ Title: "Server Uptime",
125
+ Units: "seconds",
126
+ Fam: "uptime",
127
+ Ctx: "nats.server_uptime",
128
+ Priority: prioServerUptime,
129
+ Dims: module.Dims{
130
+ {ID: "uptime", Name: "uptime"},
131
+ },
132
+ }
133
+)
134
+
135
+func httpEndpointCharts() module.Charts {
136
+ var charts module.Charts
137
+ for _, path := range httpEndpoints {
138
+ chart := httpEndpointRequestsChartTmpl.Copy()
139
+ chart.ID = fmt.Sprintf(chart.ID, path)
140
+ chart.Labels = []module.Label{
141
+ {Key: "http_endpoint", Value: path},
142
+ }
143
+ for _, dim := range chart.Dims {
144
+ dim.ID = fmt.Sprintf(dim.ID, path)
145
+ }
146
+ charts = append(charts, chart)
147
+ }
148
+ return charts
149
+}
150
+
151
+var httpEndpointRequestsChartTmpl = module.Chart{
152
+ ID: "http_endpoint_%s_requests",
153
+ Title: "HTTP Endpoint Requests",
154
+ Units: "requests/s",
155
+ Fam: "http requests",
156
+ Ctx: "nats.http_endpoint_requests",
157
+ Priority: prioHttpEndpointRequests,
158
+ Dims: module.Dims{
159
+ {ID: "http_endpoint_%s_req", Name: "requests", Algo: module.Incremental},
160
+ },
161
+}
src/go/plugin/go.d/collector/nats/collect.go
new
+85
@@ -0,0 +1,85 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package nats
4
+
5
+import (
6
+ "fmt"
7
+ "net/http"
8
+
9
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/metrix"
10
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
11
+)
12
+
13
+const (
14
+ urlPathVarz = "/varz"
15
+ urlPathHealthz = "/healthz"
16
+)
17
+
18
+func (c *Collector) collect() (map[string]int64, error) {
19
+ mx := make(map[string]int64)
20
+
21
+ if err := c.collectVarz(mx); err != nil {
22
+ return nil, err
23
+ }
24
+ if err := c.collectHealthz(mx); err != nil {
25
+ return nil, err
26
+ }
27
+
28
+ return mx, nil
29
+}
30
+
31
+func (c *Collector) collectVarz(mx map[string]int64) error {
32
+ // https://docs.nats.io/running-a-nats-service/nats_admin/monitoring#general-information
33
+ req, err := web.NewHTTPRequestWithPath(c.RequestConfig, urlPathVarz)
34
+ if err != nil {
35
+ return err
36
+ }
37
+
38
+ var resp varzResponse
39
+ if err := web.DoHTTP(c.httpClient).RequestJSON(req, &resp); err != nil {
40
+ return err
41
+ }
42
+
43
+ mx["uptime"] = int64(resp.Now.Sub(resp.Start).Seconds())
44
+ mx["in_msgs"] = resp.InMsgs
45
+ mx["out_msgs"] = resp.OutMsgs
46
+ mx["in_bytes"] = resp.InBytes
47
+ mx["out_bytes"] = resp.OutBytes
48
+ mx["slow_consumers"] = resp.SlowConsumers
49
+ mx["subscriptions"] = int64(resp.Subscriptions)
50
+ mx["connections"] = int64(resp.Connections)
51
+ mx["total_connections"] = int64(resp.TotalConnections)
52
+ mx["routes"] = int64(resp.Routes)
53
+ mx["remotes"] = int64(resp.Remotes)
54
+ mx["cpu"] = int64(resp.CPU)
55
+ mx["mem"] = resp.Mem
56
+
57
+ for _, path := range httpEndpoints {
58
+ v := resp.HTTPReqStats[path]
59
+ mx[fmt.Sprintf("http_endpoint_%s_req", path)] = int64(v)
60
+ }
61
+
62
+ return nil
63
+}
64
+
65
+func (c *Collector) collectHealthz(mx map[string]int64) error {
66
+ // https://docs.nats.io/running-a-nats-service/nats_admin/monitoring#health
67
+ req, err := web.NewHTTPRequestWithPath(c.RequestConfig, urlPathHealthz)
68
+ if err != nil {
69
+ return err
70
+ }
71
+
72
+ var resp healthzResponse
73
+ client := web.DoHTTP(c.httpClient).OnNokCode(func(resp *http.Response) (bool, error) { return true, nil })
74
+ if err := client.RequestJSON(req, &resp); err != nil {
75
+ return err
76
+ }
77
+ if resp.Status == nil {
78
+ return fmt.Errorf("healthz response missing status")
79
+ }
80
+
81
+ mx["healthz_status_ok"] = metrix.Bool(*resp.Status == "ok")
82
+ mx["healthz_status_error"] = metrix.Bool(*resp.Status != "ok")
83
+
84
+ return nil
85
+}
src/go/plugin/go.d/collector/nats/collector.go
new
+113
@@ -0,0 +1,113 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package nats
4
+
5
+import (
6
+ "context"
7
+ _ "embed"
8
+ "errors"
9
+ "fmt"
10
+ "net/http"
11
+ "time"
12
+
13
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
14
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/confopt"
15
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
16
+)
17
+
18
+//go:embed "config_schema.json"
19
+var configSchema string
20
+
21
+func init() {
22
+ module.Register("nats", module.Creator{
23
+ Create: func() module.Module { return New() },
24
+ JobConfigSchema: configSchema,
25
+ Config: func() any { return &Config{} },
26
+ })
27
+}
28
+
29
+func New() *Collector {
30
+ return &Collector{
31
+ Config: Config{
32
+ HTTPConfig: web.HTTPConfig{
33
+ RequestConfig: web.RequestConfig{
34
+ URL: "http://127.0.0.1:8222",
35
+ },
36
+ ClientConfig: web.ClientConfig{
37
+ Timeout: confopt.Duration(time.Second),
38
+ },
39
+ },
40
+ },
41
+ charts: serverCharts.Copy(),
42
+ }
43
+}
44
+
45
+type Config struct {
46
+ Vnode string `yaml:"vnode,omitempty" json:"vnode"`
47
+ UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
48
+ web.HTTPConfig `yaml:",inline" json:""`
49
+}
50
+
51
+type Collector struct {
52
+ module.Base
53
+ Config `yaml:",inline" json:""`
54
+
55
+ charts *module.Charts
56
+
57
+ httpClient *http.Client
58
+}
59
+
60
+func (c *Collector) Configuration() any {
61
+ return c.Config
62
+}
63
+
64
+func (c *Collector) Init(context.Context) error {
65
+ if c.URL == "" {
66
+ return errors.New("URL required but not set")
67
+ }
68
+
69
+ httpClient, err := web.NewHTTPClient(c.ClientConfig)
70
+ if err != nil {
71
+ return fmt.Errorf("init HTTP client: %v", err)
72
+ }
73
+ c.httpClient = httpClient
74
+
75
+ c.Debugf("using URL %s", c.URL)
76
+ c.Debugf("using timeout: %s", c.Timeout)
77
+
78
+ return nil
79
+}
80
+
81
+func (c *Collector) Check(context.Context) error {
82
+ mx, err := c.collect()
83
+ if err != nil {
84
+ return err
85
+ }
86
+ if len(mx) == 0 {
87
+ return errors.New("no metrics collected")
88
+
89
+ }
90
+ return nil
91
+}
92
+
93
+func (c *Collector) Charts() *module.Charts {
94
+ return c.charts
95
+}
96
+
97
+func (c *Collector) Collect(context.Context) map[string]int64 {
98
+ mx, err := c.collect()
99
+ if err != nil {
100
+ c.Error(err)
101
+ }
102
+
103
+ if len(mx) == 0 {
104
+ return nil
105
+ }
106
+ return mx
107
+}
108
+
109
+func (c *Collector) Cleanup(context.Context) {
110
+ if c.httpClient != nil {
111
+ c.httpClient.CloseIdleConnections()
112
+ }
113
+}
src/go/plugin/go.d/collector/nats/collector_test.go
new
+282
@@ -0,0 +1,282 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package nats
4
+
5
+import (
6
+ "context"
7
+ "net/http"
8
+ "net/http/httptest"
9
+ "os"
10
+ "testing"
11
+
12
+ "github.com/stretchr/testify/assert"
13
+ "github.com/stretchr/testify/require"
14
+
15
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
16
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
17
+)
18
+
19
+var (
20
+ dataConfigJSON, _ = os.ReadFile("testdata/config.json")
21
+ dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
22
+
23
+ dataVer210Varz, _ = os.ReadFile("testdata/v2.10.24/varz.json")
24
+ dataVer210HealthzOk, _ = os.ReadFile("testdata/v2.10.24/healthz-ok.json")
25
+)
26
+
27
+func Test_testDataIsValid(t *testing.T) {
28
+ for name, data := range map[string][]byte{
29
+ "dataConfigJSON": dataConfigJSON,
30
+ "dataConfigYAML": dataConfigYAML,
31
+ "dataVer210Varz": dataVer210Varz,
32
+ "dataVer210HealthzOk": dataVer210HealthzOk,
33
+ } {
34
+ require.NotNil(t, data, name)
35
+ }
36
+}
37
+
38
+func TestCollector_ConfigurationSerialize(t *testing.T) {
39
+ module.TestConfigurationSerialize(t, &Collector{}, dataConfigJSON, dataConfigYAML)
40
+}
41
+
42
+func TestCollector_Init(t *testing.T) {
43
+ tests := map[string]struct {
44
+ wantFail bool
45
+ config Config
46
+ }{
47
+ "success with default": {
48
+ wantFail: false,
49
+ config: New().Config,
50
+ },
51
+ "fail when URL not set": {
52
+ wantFail: true,
53
+ config: Config{
54
+ HTTPConfig: web.HTTPConfig{
55
+ RequestConfig: web.RequestConfig{URL: ""},
56
+ },
57
+ },
58
+ },
59
+ }
60
+
61
+ for name, test := range tests {
62
+ t.Run(name, func(t *testing.T) {
63
+ collr := New()
64
+ collr.Config = test.config
65
+
66
+ if test.wantFail {
67
+ assert.Error(t, collr.Init(context.Background()))
68
+ } else {
69
+ assert.NoError(t, collr.Init(context.Background()))
70
+ }
71
+ })
72
+ }
73
+}
74
+
75
+func TestCollector_Check(t *testing.T) {
76
+ tests := map[string]struct {
77
+ wantFail bool
78
+ prepare func(t *testing.T) (nu *Collector, cleanup func())
79
+ }{
80
+ "success on valid response": {
81
+ wantFail: false,
82
+ prepare: caseOk,
83
+ },
84
+ "fail on unexpected JSON response": {
85
+ wantFail: true,
86
+ prepare: caseUnexpectedJsonResponse,
87
+ },
88
+ "fail on invalid data response": {
89
+ wantFail: true,
90
+ prepare: caseInvalidDataResponse,
91
+ },
92
+ "fail on connection refused": {
93
+ wantFail: true,
94
+ prepare: caseConnectionRefused,
95
+ },
96
+ "fail on 404 response": {
97
+ wantFail: true,
98
+ prepare: case404,
99
+ },
100
+ }
101
+
102
+ for name, test := range tests {
103
+ t.Run(name, func(t *testing.T) {
104
+ collr, cleanup := test.prepare(t)
105
+ defer cleanup()
106
+
107
+ if test.wantFail {
108
+ assert.Error(t, collr.Check(context.Background()))
109
+ } else {
110
+ assert.NoError(t, collr.Check(context.Background()))
111
+ }
112
+ })
113
+ }
114
+}
115
+
116
+func TestCollector_Charts(t *testing.T) {
117
+ assert.NotNil(t, New().Charts())
118
+}
119
+
120
+func TestCollector_Collect(t *testing.T) {
121
+ tests := map[string]struct {
122
+ prepare func(t *testing.T) (nu *Collector, cleanup func())
123
+ wantNumOfCharts int
124
+ wantMetrics map[string]int64
125
+ }{
126
+ "success on valid response": {
127
+ prepare: caseOk,
128
+ wantNumOfCharts: len(serverCharts),
129
+ wantMetrics: map[string]int64{
130
+ "connections": 0,
131
+ "cpu": 0,
132
+ "healthz_status_error": 0,
133
+ "healthz_status_ok": 1,
134
+ "http_endpoint_/_req": 3,
135
+ "http_endpoint_/accountz_req": 2,
136
+ "http_endpoint_/accstatz_req": 2,
137
+ "http_endpoint_/connz_req": 2,
138
+ "http_endpoint_/gatewayz_req": 2,
139
+ "http_endpoint_/healthz_req": 2017,
140
+ "http_endpoint_/ipqueuesz_req": 0,
141
+ "http_endpoint_/jsz_req": 3,
142
+ "http_endpoint_/leafz_req": 2,
143
+ "http_endpoint_/raftz_req": 0,
144
+ "http_endpoint_/routez_req": 2,
145
+ "http_endpoint_/stacksz_req": 0,
146
+ "http_endpoint_/subsz_req": 1,
147
+ "http_endpoint_/varz_req": 3750,
148
+ "in_bytes": 0,
149
+ "in_msgs": 0,
150
+ "mem": 21725184,
151
+ "out_bytes": 0,
152
+ "out_msgs": 0,
153
+ "remotes": 0,
154
+ "routes": 0,
155
+ "slow_consumers": 0,
156
+ "subscriptions": 57,
157
+ "total_connections": 0,
158
+ "uptime": 27513,
159
+ },
160
+ },
161
+ "fail on unexpected JSON response": {
162
+ prepare: caseUnexpectedJsonResponse,
163
+ wantMetrics: nil,
164
+ },
165
+ "fail on invalid data response": {
166
+ prepare: caseInvalidDataResponse,
167
+ wantMetrics: nil,
168
+ },
169
+ "fail on connection refused": {
170
+ prepare: caseConnectionRefused,
171
+ wantMetrics: nil,
172
+ },
173
+ "fail on 404 response": {
174
+ prepare: case404,
175
+ wantMetrics: nil,
176
+ },
177
+ }
178
+
179
+ for name, test := range tests {
180
+ t.Run(name, func(t *testing.T) {
181
+ collr, cleanup := test.prepare(t)
182
+ defer cleanup()
183
+
184
+ _ = collr.Check(context.Background())
185
+
186
+ mx := collr.Collect(context.Background())
187
+
188
+ require.Equal(t, test.wantMetrics, mx)
189
+
190
+ if len(test.wantMetrics) > 0 {
191
+ assert.Equal(t, test.wantNumOfCharts, len(*collr.Charts()), "want charts")
192
+
193
+ module.TestMetricsHasAllChartsDims(t, collr.Charts(), mx)
194
+ }
195
+ })
196
+ }
197
+}
198
+
199
+func caseOk(t *testing.T) (*Collector, func()) {
200
+ t.Helper()
201
+ srv := httptest.NewServer(http.HandlerFunc(
202
+ func(w http.ResponseWriter, r *http.Request) {
203
+ switch r.URL.Path {
204
+ case urlPathVarz:
205
+ _, _ = w.Write(dataVer210Varz)
206
+ case urlPathHealthz:
207
+ _, _ = w.Write(dataVer210HealthzOk)
208
+ default:
209
+ w.WriteHeader(http.StatusNotFound)
210
+ }
211
+ }))
212
+ collr := New()
213
+ collr.URL = srv.URL
214
+ require.NoError(t, collr.Init(context.Background()))
215
+
216
+ return collr, srv.Close
217
+}
218
+
219
+func caseUnexpectedJsonResponse(t *testing.T) (*Collector, func()) {
220
+ t.Helper()
221
+ resp := `
222
+{
223
+ "elephant": {
224
+ "burn": false,
225
+ "mountain": true,
226
+ "fog": false,
227
+ "skin": -1561907625,
228
+ "burst": "anyway",
229
+ "shadow": 1558616893
230
+ },
231
+ "start": "ever",
232
+ "base": 2093056027,
233
+ "mission": -2007590351,
234
+ "victory": 999053756,
235
+ "die": false
236
+}
237
+`
238
+ srv := httptest.NewServer(http.HandlerFunc(
239
+ func(w http.ResponseWriter, r *http.Request) {
240
+ _, _ = w.Write([]byte(resp))
241
+ }))
242
+ collr := New()
243
+ collr.URL = srv.URL
244
+ require.NoError(t, collr.Init(context.Background()))
245
+
246
+ return collr, srv.Close
247
+}
248
+
249
+func caseInvalidDataResponse(t *testing.T) (*Collector, func()) {
250
+ t.Helper()
251
+ srv := httptest.NewServer(http.HandlerFunc(
252
+ func(w http.ResponseWriter, r *http.Request) {
253
+ _, _ = w.Write([]byte("hello and\n goodbye"))
254
+ }))
255
+ collr := New()
256
+ collr.URL = srv.URL
257
+ require.NoError(t, collr.Init(context.Background()))
258
+
259
+ return collr, srv.Close
260
+}
261
+
262
+func caseConnectionRefused(t *testing.T) (*Collector, func()) {
263
+ t.Helper()
264
+ collr := New()
265
+ collr.URL = "http://127.0.0.1:65001"
266
+ require.NoError(t, collr.Init(context.Background()))
267
+
268
+ return collr, func() {}
269
+}
270
+
271
+func case404(t *testing.T) (*Collector, func()) {
272
+ t.Helper()
273
+ srv := httptest.NewServer(http.HandlerFunc(
274
+ func(w http.ResponseWriter, r *http.Request) {
275
+ w.WriteHeader(http.StatusNotFound)
276
+ }))
277
+ collr := New()
278
+ collr.URL = srv.URL
279
+ require.NoError(t, collr.Init(context.Background()))
280
+
281
+ return collr, srv.Close
282
+}
src/go/plugin/go.d/collector/nats/config_schema.json
new
+191
@@ -0,0 +1,191 @@
1
+{
2
+ "jsonSchema": {
3
+ "$schema": "http://json-schema.org/draft-07/schema#",
4
+ "title": "NATS collector configuration.",
5
+ "type": "object",
6
+ "properties": {
7
+ "update_every": {
8
+ "title": "Update every",
9
+ "description": "Data collection interval, measured in seconds.",
10
+ "type": "integer",
11
+ "minimum": 1,
12
+ "default": 1
13
+ },
14
+ "url": {
15
+ "title": "URL",
16
+ "description": "The URL of the NATS [monitoring endpoint](https://docs.nats.io/running-a-nats-service/nats_admin/monitoring#enabling-monitoring).",
17
+ "type": "string",
18
+ "default": "http://127.0.0.1:8222",
19
+ "format": "uri"
20
+ },
21
+ "timeout": {
22
+ "title": "Timeout",
23
+ "description": "The timeout in seconds for the HTTP request.",
24
+ "type": "number",
25
+ "minimum": 0.5,
26
+ "default": 1
27
+ },
28
+ "not_follow_redirects": {
29
+ "title": "Not follow redirects",
30
+ "description": "If set, the client will not follow HTTP redirects automatically.",
31
+ "type": "boolean"
32
+ },
33
+ "vnode": {
34
+ "title": "Vnode",
35
+ "description": "Associates this data collection job with a [Virtual Node](https://learn.netdata.cloud/docs/netdata-agent/configuration/organize-systems-metrics-and-alerts#virtual-nodes).",
36
+ "type": "string"
37
+ },
38
+ "username": {
39
+ "title": "Username",
40
+ "description": "The username for basic authentication.",
41
+ "type": "string",
42
+ "sensitive": true
43
+ },
44
+ "password": {
45
+ "title": "Password",
46
+ "description": "The password for basic authentication.",
47
+ "type": "string",
48
+ "sensitive": true
49
+ },
50
+ "proxy_url": {
51
+ "title": "Proxy URL",
52
+ "description": "The URL of the proxy server.",
53
+ "type": "string"
54
+ },
55
+ "proxy_username": {
56
+ "title": "Proxy username",
57
+ "description": "The username for proxy authentication.",
58
+ "type": "string",
59
+ "sensitive": true
60
+ },
61
+ "proxy_password": {
62
+ "title": "Proxy password",
63
+ "description": "The password for proxy authentication.",
64
+ "type": "string",
65
+ "sensitive": true
66
+ },
67
+ "headers": {
68
+ "title": "Headers",
69
+ "description": "Additional HTTP headers to include in the request.",
70
+ "type": [
71
+ "object",
72
+ "null"
73
+ ],
74
+ "additionalProperties": {
75
+ "type": "string"
76
+ }
77
+ },
78
+ "tls_skip_verify": {
79
+ "title": "Skip TLS verification",
80
+ "description": "If set, TLS certificate verification will be skipped.",
81
+ "type": "boolean"
82
+ },
83
+ "tls_ca": {
84
+ "title": "TLS CA",
85
+ "description": "The path to the CA certificate file for TLS verification.",
86
+ "type": "string",
87
+ "pattern": "^$|^/"
88
+ },
89
+ "tls_cert": {
90
+ "title": "TLS certificate",
91
+ "description": "The path to the client certificate file for TLS authentication.",
92
+ "type": "string",
93
+ "pattern": "^$|^/"
94
+ },
95
+ "tls_key": {
96
+ "title": "TLS key",
97
+ "description": "The path to the client key file for TLS authentication.",
98
+ "type": "string",
99
+ "pattern": "^$|^/"
100
+ },
101
+ "body": {
102
+ "title": "Body",
103
+ "type": "string"
104
+ },
105
+ "method": {
106
+ "title": "Method",
107
+ "type": "string"
108
+ }
109
+ },
110
+ "required": [
111
+ "url"
112
+ ],
113
+ "patternProperties": {
114
+ "^name$": {}
115
+ }
116
+ },
117
+ "uiSchema": {
118
+ "uiOptions": {
119
+ "fullPage": true
120
+ },
121
+ "body": {
122
+ "ui:widget": "hidden"
123
+ },
124
+ "method": {
125
+ "ui:widget": "hidden"
126
+ },
127
+ "vnode": {
128
+ "ui:placeholder": "To use this option, first create a Virtual Node and then reference its name here."
129
+ },
130
+ "timeout": {
131
+ "ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)."
132
+ },
133
+ "username": {
134
+ "ui:widget": "password"
135
+ },
136
+ "proxy_username": {
137
+ "ui:widget": "password"
138
+ },
139
+ "password": {
140
+ "ui:widget": "password"
141
+ },
142
+ "proxy_password": {
143
+ "ui:widget": "password"
144
+ },
145
+ "ui:flavour": "tabs",
146
+ "ui:options": {
147
+ "tabs": [
148
+ {
149
+ "title": "Base",
150
+ "fields": [
151
+ "update_every",
152
+ "url",
153
+ "timeout",
154
+ "not_follow_redirects",
155
+ "vnode"
156
+ ]
157
+ },
158
+ {
159
+ "title": "Auth",
160
+ "fields": [
161
+ "username",
162
+ "password"
163
+ ]
164
+ },
165
+ {
166
+ "title": "TLS",
167
+ "fields": [
168
+ "tls_skip_verify",
169
+ "tls_ca",
170
+ "tls_cert",
171
+ "tls_key"
172
+ ]
173
+ },
174
+ {
175
+ "title": "Proxy",
176
+ "fields": [
177
+ "proxy_url",
178
+ "proxy_username",
179
+ "proxy_password"
180
+ ]
181
+ },
182
+ {
183
+ "title": "Headers",
184
+ "fields": [
185
+ "headers"
186
+ ]
187
+ }
188
+ ]
189
+ }
190
+ }
191
+}
src/go/plugin/go.d/collector/nats/metadata.yml
new
+248
@@ -0,0 +1,248 @@
1
+plugin_name: go.d.plugin
2
+modules:
3
+ - meta:
4
+ id: collector-go.d.plugin-nats
5
+ plugin_name: go.d.plugin
6
+ module_name: nats
7
+ monitored_instance:
8
+ name: NATS
9
+ link: https://nats.io/
10
+ categories:
11
+ - data-collection.message-brokers
12
+ icon_filename: nats.svg
13
+ related_resources:
14
+ integrations:
15
+ list: []
16
+ alternative_monitored_instances: []
17
+ info_provided_to_referring_integrations:
18
+ description: ""
19
+ keywords:
20
+ - nats
21
+ - messaging
22
+ - broker
23
+ most_popular: false
24
+ overview:
25
+ data_collection:
26
+ metrics_description: |
27
+ This collector monitors the activity and performance of NATS servers.
28
+ method_description: |
29
+ It sends HTTP requests to the NATS HTTP server's dedicated [monitoring port](https://docs.nats.io/running-a-nats-service/nats_admin/monitoring#monitoring-nats).
30
+ default_behavior:
31
+ auto_detection:
32
+ description: |
33
+ The collector can automatically detect NATS instances running on:
34
+
35
+ - localhost that are listening on port 8222
36
+ - within Docker containers
37
+ limits:
38
+ description: ""
39
+ performance_impact:
40
+ description: ""
41
+ additional_permissions:
42
+ description: ""
43
+ multi_instance: true
44
+ supported_platforms:
45
+ include: []
46
+ exclude: []
47
+ setup:
48
+ prerequisites:
49
+ list:
50
+ - title: Enable NATS monitoring
51
+ description: |
52
+ See [Enable monitoring](https://docs.nats.io/running-a-nats-service/nats_admin/monitoring#enabling-monitoring).
53
+ configuration:
54
+ file:
55
+ name: go.d/nats.conf
56
+ options:
57
+ description: |
58
+ The following options can be defined globally: update_every, autodetection_retry.
59
+ folding:
60
+ title: Config options
61
+ enabled: true
62
+ list:
63
+ - name: update_every
64
+ description: Data collection frequency.
65
+ default_value: 1
66
+ required: false
67
+ - name: autodetection_retry
68
+ description: Recheck interval in seconds. Zero means no recheck will be scheduled.
69
+ default_value: 0
70
+ required: false
71
+ - name: url
72
+ description: Server URL.
73
+ default_value: http://127.0.0.1:8222
74
+ required: true
75
+ - name: timeout
76
+ description: HTTP request timeout.
77
+ default_value: 1
78
+ required: false
79
+ - name: username
80
+ description: Username for basic HTTP authentication.
81
+ default_value: ""
82
+ required: false
83
+ - name: password
84
+ description: Password for basic HTTP authentication.
85
+ default_value: ""
86
+ required: false
87
+ - name: proxy_url
88
+ description: Proxy URL.
89
+ default_value: ""
90
+ required: false
91
+ - name: proxy_username
92
+ description: Username for proxy basic HTTP authentication.
93
+ default_value: ""
94
+ required: false
95
+ - name: proxy_password
96
+ description: Password for proxy basic HTTP authentication.
97
+ default_value: ""
98
+ required: false
99
+ - name: method
100
+ description: HTTP request method.
101
+ default_value: GET
102
+ required: false
103
+ - name: body
104
+ description: HTTP request body.
105
+ default_value: ""
106
+ required: false
107
+ - name: headers
108
+ description: HTTP request headers.
109
+ default_value: ""
110
+ required: false
111
+ - name: not_follow_redirects
112
+ description: Redirect handling policy. Controls whether the client follows redirects.
113
+ default_value: false
114
+ required: false
115
+ - name: tls_skip_verify
116
+ description: Server certificate chain and hostname validation policy. Controls whether the client performs this check.
117
+ default_value: false
118
+ required: false
119
+ - name: tls_ca
120
+ description: Certification authority that the client uses when verifying the server's certificates.
121
+ default_value: ""
122
+ required: false
123
+ - name: tls_cert
124
+ description: Client TLS certificate.
125
+ default_value: ""
126
+ required: false
127
+ - name: tls_key
128
+ description: Client TLS key.
129
+ default_value: ""
130
+ required: false
131
+ examples:
132
+ folding:
133
+ title: Config
134
+ enabled: true
135
+ list:
136
+ - name: Basic
137
+ description: A basic example configuration.
138
+ folding:
139
+ enabled: false
140
+ config: |
141
+ jobs:
142
+ - name: local
143
+ url: http://127.0.0.1:8222
144
+ - name: HTTP authentication
145
+ description: Basic HTTP authentication.
146
+ config: |
147
+ jobs:
148
+ - name: local
149
+ url: http://127.0.0.1:8222
150
+ username: username
151
+ password: password
152
+ - name: HTTPS with self-signed certificate
153
+ description: NATS with enabled HTTPS and self-signed certificate.
154
+ config: |
155
+ jobs:
156
+ - name: local
157
+ url: http://127.0.0.1:8222
158
+ tls_skip_verify: yes
159
+ - name: Multi-instance
160
+ description: |
161
+ > **Note**: When you define multiple jobs, their names must be unique.
162
+
163
+ Collecting metrics from local and remote instances.
164
+ config: |
165
+ jobs:
166
+ - name: local
167
+ url: http://127.0.0.1:8222
168
+
169
+ - name: remote
170
+ url: http://192.0.2.1:8222
171
+ troubleshooting:
172
+ problems:
173
+ list: []
174
+ alerts: []
175
+ metrics:
176
+ folding:
177
+ title: Metrics
178
+ enabled: false
179
+ description: ""
180
+ availability: []
181
+ scopes:
182
+ - name: server
183
+ description: These metrics refer to NATS servers.
184
+ labels: []
185
+ metrics:
186
+ - name: nats.server_traffic
187
+ description: Server Traffic
188
+ unit: bytes/s
189
+ chart_type: area
190
+ dimensions:
191
+ - name: in
192
+ - name: out
193
+ - name: nats.server_messages
194
+ description: Server Messages
195
+ unit: messages/s
196
+ chart_type: line
197
+ dimensions:
198
+ - name: in
199
+ - name: out
200
+ - name: nats.server_connections_current
201
+ description: Server Current Connections
202
+ unit: connections
203
+ chart_type: line
204
+ dimensions:
205
+ - name: active
206
+ - name: nats.server_connections_rate
207
+ description: Server Connections
208
+ unit: connections/s
209
+ chart_type: line
210
+ dimensions:
211
+ - name: connections
212
+ - name: nats.server_health_probe_status
213
+ description: Server Health Probe Status
214
+ unit: status
215
+ chart_type: line
216
+ dimensions:
217
+ - name: ok
218
+ - name: error
219
+ - name: nats.server_cpu_usage
220
+ description: Server CPU Usage
221
+ unit: percent
222
+ chart_type: area
223
+ dimensions:
224
+ - name: used
225
+ - name: nats.server_mem_usage
226
+ description: Server Memory Usage
227
+ unit: bytes
228
+ chart_type: area
229
+ dimensions:
230
+ - name: used
231
+ - name: nats.server_uptime
232
+ description: Server Uptime
233
+ unit: seconds
234
+ chart_type: line
235
+ dimensions:
236
+ - name: uptime
237
+ - name: http endpoint
238
+ description: These metrics refer to HTTP endpoints.
239
+ labels:
240
+ - name: http_endpoint
241
+ description: "HTTP endpoint path."
242
+ metrics:
243
+ - name: nats.http_endpoint_requests
244
+ description: HTTP Endpoint Requests
245
+ unit: requests/s
246
+ chart_type: line
247
+ dimensions:
248
+ - name: requests
src/go/plugin/go.d/collector/nats/restapi.go
new
+76
@@ -0,0 +1,76 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package nats
4
+
5
+import (
6
+ "time"
7
+)
8
+
9
+// https://docs.nats.io/running-a-nats-service/nats_admin/monitoring
10
+
11
+// https://github.com/nats-io/nats-server/blob/v2.10.24/server/monitor.go#L1164
12
+type varzResponse struct {
13
+ ID string `json:"server_id"`
14
+ Name string `json:"server_name"`
15
+ Version string `json:"version"`
16
+ Proto int `json:"proto"`
17
+ Host string `json:"host"`
18
+ Port int `json:"port"`
19
+ IP string `json:"ip,omitempty"`
20
+ MaxConn int `json:"max_connections"`
21
+ MaxSubs int `json:"max_subscriptions,omitempty"`
22
+ PingInterval time.Duration `json:"ping_interval"`
23
+ MaxPingsOut int `json:"ping_max"`
24
+ HTTPHost string `json:"http_host"`
25
+ HTTPPort int `json:"http_port"`
26
+ HTTPBasePath string `json:"http_base_path"`
27
+ HTTPSPort int `json:"https_port"`
28
+ AuthTimeout float64 `json:"auth_timeout"`
29
+ MaxControlLine int32 `json:"max_control_line"`
30
+ MaxPayload int `json:"max_payload"`
31
+ MaxPending int64 `json:"max_pending"`
32
+ TLSTimeout float64 `json:"tls_timeout"`
33
+ WriteDeadline time.Duration `json:"write_deadline"`
34
+ Start time.Time `json:"start"`
35
+ Now time.Time `json:"now"`
36
+ Uptime string `json:"uptime"`
37
+ Mem int64 `json:"mem"`
38
+ Cores int `json:"cores"`
39
+ MaxProcs int `json:"gomaxprocs"`
40
+ CPU float64 `json:"cpu"`
41
+ Connections int `json:"connections"`
42
+ TotalConnections uint64 `json:"total_connections"`
43
+ Routes int `json:"routes"`
44
+ Remotes int `json:"remotes"`
45
+ Leafs int `json:"leafnodes"`
46
+ InMsgs int64 `json:"in_msgs"`
47
+ OutMsgs int64 `json:"out_msgs"`
48
+ InBytes int64 `json:"in_bytes"`
49
+ OutBytes int64 `json:"out_bytes"`
50
+ SlowConsumers int64 `json:"slow_consumers"`
51
+ Subscriptions uint32 `json:"subscriptions"`
52
+ HTTPReqStats map[string]uint64 `json:"http_req_stats"`
53
+}
54
+
55
+// //https://github.com/nats-io/nats-server/blob/v2.10.24/server/server.go#L2851
56
+var httpEndpoints = []string{
57
+ "/",
58
+ "/varz",
59
+ "/connz",
60
+ "/routez",
61
+ "/gatewayz",
62
+ "/leafz",
63
+ "/subsz",
64
+ "/stacksz",
65
+ "/accountz",
66
+ "/accstatz",
67
+ "/jsz",
68
+ "/healthz",
69
+ "/ipqueuesz",
70
+ "/raftz",
71
+}
72
+
73
+// https://github.com/nats-io/nats-server/blob/v2.10.24/server/monitor.go#L3125
74
+type healthzResponse struct {
75
+ Status *string `json:"status"`
76
+}
src/go/plugin/go.d/collector/nats/testdata/config.json
new
+22
@@ -0,0 +1,22 @@
1
+{
2
+ "vnode": "ok",
3
+ "update_every": 123,
4
+ "url": "ok",
5
+ "body": "ok",
6
+ "method": "ok",
7
+ "headers": {
8
+ "ok": "ok"
9
+ },
10
+ "username": "ok",
11
+ "password": "ok",
12
+ "proxy_url": "ok",
13
+ "proxy_username": "ok",
14
+ "proxy_password": "ok",
15
+ "timeout": 123.123,
16
+ "not_follow_redirects": true,
17
+ "tls_ca": "ok",
18
+ "tls_cert": "ok",
19
+ "tls_key": "ok",
20
+ "tls_skip_verify": true,
21
+ "force_http2": true
22
+}
src/go/plugin/go.d/collector/nats/testdata/config.yaml
new
+19
@@ -0,0 +1,19 @@
1
+vnode: "ok"
2
+update_every: 123
3
+url: "ok"
4
+body: "ok"
5
+method: "ok"
6
+headers:
7
+ ok: "ok"
8
+username: "ok"
9
+password: "ok"
10
+proxy_url: "ok"
11
+proxy_username: "ok"
12
+proxy_password: "ok"
13
+timeout: 123.123
14
+not_follow_redirects: yes
15
+tls_ca: "ok"
16
+tls_cert: "ok"
17
+tls_key: "ok"
18
+tls_skip_verify: yes
19
+force_http2: yes
src/go/plugin/go.d/collector/nats/testdata/v2.10.24/healthz-ok.json
new
+3
@@ -0,0 +1,3 @@
1
+{
2
+ "status": "ok"
3
+}
src/go/plugin/go.d/collector/nats/testdata/v2.10.24/varz.json
new
+75
@@ -0,0 +1,75 @@
1
+{
2
+ "server_id": "NASZPQXJ3BIJOGQHV5ZEWGI6EH3YRQPI2Z5GJRA4AZ47TC4PX4OJGY63",
3
+ "server_name": "NASZPQXJ3BIJOGQHV5ZEWGI6EH3YRQPI2Z5GJRA4AZ47TC4PX4OJGY63",
4
+ "version": "2.10.24",
5
+ "proto": 1,
6
+ "git_commit": "1d6f7ea",
7
+ "go": "go1.23.4",
8
+ "host": "0.0.0.0",
9
+ "port": 4222,
10
+ "max_connections": 65536,
11
+ "ping_interval": 120000000000,
12
+ "ping_max": 2,
13
+ "http_host": "0.0.0.0",
14
+ "http_port": 8222,
15
+ "http_base_path": "",
16
+ "https_port": 0,
17
+ "auth_timeout": 2,
18
+ "max_control_line": 4096,
19
+ "max_payload": 1048576,
20
+ "max_pending": 67108864,
21
+ "cluster": {
22
+ "name": "my_cluster",
23
+ "addr": "0.0.0.0",
24
+ "cluster_port": 6222,
25
+ "auth_timeout": 2,
26
+ "tls_timeout": 2,
27
+ "pool_size": 3
28
+ },
29
+ "gateway": {},
30
+ "leaf": {},
31
+ "mqtt": {},
32
+ "websocket": {},
33
+ "jetstream": {},
34
+ "tls_timeout": 2,
35
+ "write_deadline": 10000000000,
36
+ "start": "2024-12-19T11:51:48.038140697Z",
37
+ "now": "2024-12-19T19:30:21.110744698Z",
38
+ "uptime": "7h38m33s",
39
+ "mem": 21725184,
40
+ "cores": 16,
41
+ "gomaxprocs": 16,
42
+ "cpu": 0,
43
+ "connections": 0,
44
+ "total_connections": 0,
45
+ "routes": 0,
46
+ "remotes": 0,
47
+ "leafnodes": 0,
48
+ "in_msgs": 0,
49
+ "out_msgs": 0,
50
+ "in_bytes": 0,
51
+ "out_bytes": 0,
52
+ "slow_consumers": 0,
53
+ "subscriptions": 57,
54
+ "http_req_stats": {
55
+ "/": 3,
56
+ "/accountz": 2,
57
+ "/accstatz": 2,
58
+ "/connz": 2,
59
+ "/gatewayz": 2,
60
+ "/healthz": 2017,
61
+ "/jsz": 3,
62
+ "/leafz": 2,
63
+ "/routez": 2,
64
+ "/subsz": 1,
65
+ "/varz": 3750
66
+ },
67
+ "config_load_time": "2024-12-19T11:51:48.038140697Z",
68
+ "system_account": "$SYS",
69
+ "slow_consumer_stats": {
70
+ "clients": 0,
71
+ "routes": 0,
72
+ "gateways": 0,
73
+ "leafs": 0
74
+ }
75
+}
src/go/plugin/go.d/config/go.d.conf
+1
@@ -70,6 +70,7 @@ modules:
70
# mongodb: yes
71
# monit: yes
72
# mysql: yes
73
+# nats: yes
74
# nginx: yes
75
# nginxplus: yes
76
# nginxunit: yes
src/go/plugin/go.d/config/go.d/nats.conf
new
+6
@@ -0,0 +1,6 @@
1
+## All available configuration options, their descriptions and default values:
2
+## https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/collector/nats#readme
3
+
4
+#jobs:
5
+# - name: local
6
+# url: http://127.0.0.1:8222
src/go/plugin/go.d/config/go.d/sd/docker.conf
+7
@@ -58,6 +58,8 @@ classify:
58
expr: '{{ or (eq .PrivatePort "27017") (match "sp" .Image "mongo mongo:* */mongodb */mongodb:* */mongodb-community-server */mongodb-community-server:*") }}'
59
- tags: "mysql"
60
expr: '{{ or (eq .PrivatePort "3306") (match "sp" .Image "mysql mysql:* */mysql */mysql:* mariadb mariadb:* */mariadb */mariadb:* percona percona:* */percona-mysql */percona-mysql:*") }}'
61
+ - tags: "nats"
62
+ expr: '{{ and (eq .PrivatePort "8222") (match "sp" .Image "nats nats:*") }}'
63
- tags: "nginx"
64
expr: '{{ match "sp" .Image "nginx nginx:*" }}'
65
- tags: "nginxunit"
@@ -197,6 +199,11 @@ compose:
199
module: mysql
200
name: docker_{{.Name}}
201
dsn: netdata@tcp({{.Address}})/
202
+ - selector: "nats"
203
+ template: |
204
+ - module: nats
205
+ name: docker_{{.Name}}
206
+ url: http://{{.Address}}
207
- selector: "nginx"
208
template: |
209
- module: nginx
src/go/plugin/go.d/config/go.d/sd/net_listeners.conf
+7
@@ -92,6 +92,8 @@ classify:
92
expr: '{{ or (eq .Port "2812") (eq .Comm "monit") }}'
93
- tags: "mysql"
94
expr: '{{ or (eq .Port "3306") (eq .Comm "mysqld" "mariadbd") }}'
95
+ - tags: "nats"
96
+ expr: '{{ and (eq .Port "8222") (eq .Comm "nats-server") }}'
97
- tags: "nginx"
98
expr: '{{ and (eq .Port "80" "8080") (eq .Comm "nginx") }}'
99
- tags: "nginxunit"
@@ -393,6 +395,11 @@ compose:
395
- module: mysql
396
name: local
397
dsn: netdata@tcp({{.Address}})/
398
+ - selector: "nats"
399
+ template: |
400
+ - module: nats
401
+ name: local
402
+ url: http://{{.Address}}
403
- selector: "nginx"
404
template: |
405
- module: nginx