@cryptotaxi247 / netdata-1 / commits / 085487659

improvement(go.d/nats): add routez metrics (#19264)

Ilya Mashchenko committed Dec 21, 2024 at 22:32 UTC 08548765920be03daa23126a492ba66f898dfd62
7 files changed +199 -4
src/go/plugin/go.d/collector/nats/charts.go
+78 -1
@@ -4,6 +4,7 @@ package nats
4
5 import (
6 "fmt"
7 + "strconv"
8 "strings"
9
10 "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
@@ -27,6 +28,10 @@ const (
28 prioAccountSubscriptions
29 prioAccountSlowConsumers
30 prioAccountLeafNodes
31 +
32 + prioRouteTraffic
33 + prioRouteMessages
34 + prioRouteSubscriptions
35 )
36
37 var serverCharts = func() module.Charts {
@@ -290,7 +295,79 @@ func (c *Collector) addAccountCharts(acc string) {
295 }
296
297 func (c *Collector) removeAccountCharts(acc string) {
293 - px := fmt.Sprintf("accstatz_acc_%s_", acc)
298 + px := fmt.Sprintf("account_%s_", acc)
299 + c.removeCharts(px)
300 +}
301 +
302 +var routeChartsTmpl = module.Charts{
303 + routeTrafficTmpl.Copy(),
304 + routeMessagesTmpl.Copy(),
305 + routeSubscriptionsTmpl.Copy(),
306 +}
307 +
308 +var (
309 + routeTrafficTmpl = module.Chart{
310 + ID: "route_%d_traffic",
311 + Title: "Route Traffic",
312 + Units: "bytes/s",
313 + Fam: "route traffic",
314 + Ctx: "nats.route_traffic",
315 + Priority: prioRouteTraffic,
316 + Type: module.Area,
317 + Dims: module.Dims{
318 + {ID: "routez_route_id_%d_in_bytes", Name: "in", Algo: module.Incremental},
319 + {ID: "routez_route_id_%d_out_bytes", Name: "out", Mul: -1, Algo: module.Incremental},
320 + },
321 + }
322 + routeMessagesTmpl = module.Chart{
323 + ID: "route_%d_messages",
324 + Title: "Route Messages",
325 + Units: "messages/s",
326 + Fam: "route traffic",
327 + Ctx: "nats.route_messages",
328 + Priority: prioRouteMessages,
329 + Type: module.Line,
330 + Dims: module.Dims{
331 + {ID: "routez_route_id_%d_in_msgs", Name: "in", Algo: module.Incremental},
332 + {ID: "routez_route_id_%d_out_msgs", Name: "out", Mul: -1, Algo: module.Incremental},
333 + },
334 + }
335 + routeSubscriptionsTmpl = module.Chart{
336 + ID: "route_%d_subscriptions",
337 + Title: "Route Active Subscriptions",
338 + Units: "subscriptions",
339 + Fam: "route subscriptions",
340 + Ctx: "nats.route_subscriptions",
341 + Priority: prioRouteSubscriptions,
342 + Type: module.Line,
343 + Dims: module.Dims{
344 + {ID: "routez_route_id_%d_num_subs", Name: "active"},
345 + },
346 + }
347 +)
348 +
349 +func (c *Collector) addRouteCharts(rid uint64, remoteId string) {
350 + charts := routeChartsTmpl.Copy()
351 +
352 + for _, chart := range *charts {
353 + chart.ID = fmt.Sprintf(chart.ID, rid)
354 + chart.Labels = []module.Label{
355 + {Key: "route_id", Value: strconv.FormatUint(rid, 10)},
356 + {Key: "remote_id", Value: remoteId},
357 + }
358 + for _, dim := range chart.Dims {
359 + dim.ID = fmt.Sprintf(dim.ID, rid)
360 + }
361 + }
362 +
363 + if err := c.Charts().Add(*charts...); err != nil {
364 + c.Warningf("failed to add charts for route id %d: %s", rid, err)
365 + }
366 +
367 +}
368 +
369 +func (c *Collector) removeRouteCharts(rid uint64) {
370 + px := fmt.Sprintf("route_%d_", rid)
371 c.removeCharts(px)
372 }
373
src/go/plugin/go.d/collector/nats/collect.go
+42
@@ -22,6 +22,9 @@ func (c *Collector) collect() (map[string]int64, error) {
22 if err := c.collectAccstatz(mx); err != nil {
23 return mx, err
24 }
25 + if err := c.collectRoutez(mx); err != nil {
26 + return mx, err
27 + }
28
29 return mx, nil
30 }
@@ -137,3 +140,42 @@ func (c *Collector) collectAccstatz(mx map[string]int64) error {
140
141 return nil
142 }
143 +
144 +func (c *Collector) collectRoutez(mx map[string]int64) error {
145 + req, err := web.NewHTTPRequestWithPath(c.RequestConfig, urlPathRoutez)
146 + if err != nil {
147 + return err
148 + }
149 +
150 + var resp routezResponse
151 + if err := web.DoHTTP(c.httpClient).RequestJSON(req, &resp); err != nil {
152 + return err
153 + }
154 +
155 + seen := make(map[uint64]bool)
156 +
157 + for _, route := range resp.Routes {
158 + seen[route.Rid] = true
159 + if !c.seenRoutes[route.Rid] {
160 + c.seenRoutes[route.Rid] = true
161 + c.addRouteCharts(route.Rid, route.RemoteID)
162 + }
163 +
164 + px := fmt.Sprintf("routez_route_id_%d_", route.Rid)
165 +
166 + mx[px+"in_bytes"] = route.InBytes
167 + mx[px+"out_bytes"] = route.OutBytes
168 + mx[px+"in_msgs"] = route.InMsgs
169 + mx[px+"out_msgs"] = route.OutMsgs
170 + mx[px+"num_subs"] = int64(route.NumSubs)
171 + }
172 +
173 + for rid := range c.seenRoutes {
174 + if !seen[rid] {
175 + delete(c.seenRoutes, rid)
176 + c.removeRouteCharts(rid)
177 + }
178 + }
179 +
180 + return nil
181 +}
src/go/plugin/go.d/collector/nats/collector.go
+2
@@ -41,6 +41,7 @@ func New() *Collector {
41 },
42 charts: serverCharts.Copy(),
43 seenAccounts: make(map[string]bool),
44 + seenRoutes: make(map[uint64]bool),
45 }
46 }
47
@@ -60,6 +61,7 @@ type Collector struct {
61 httpClient *http.Client
62
63 seenAccounts map[string]bool
64 + seenRoutes map[uint64]bool
65 }
66
67 func (c *Collector) Configuration() any {
src/go/plugin/go.d/collector/nats/collector_test.go
+13 -2
@@ -23,6 +23,7 @@ var (
23 dataVer210HealthzOk, _ = os.ReadFile("testdata/v2.10.24/healthz-ok.json")
24 dataVer210Varz, _ = os.ReadFile("testdata/v2.10.24/varz.json")
25 dataVer210Accstatz, _ = os.ReadFile("testdata/v2.10.24/accstatz.json")
26 + dataVer210Routez, _ = os.ReadFile("testdata/v2.10.24/routez.json")
27 )
28
29 func Test_testDataIsValid(t *testing.T) {
@@ -32,6 +33,7 @@ func Test_testDataIsValid(t *testing.T) {
33 "dataVer210HealthzOk": dataVer210HealthzOk,
34 "dataVer210Varz": dataVer210Varz,
35 "dataVer210Accstatz": dataVer210Accstatz,
36 + "dataVer210Routez": dataVer210Routez,
37 } {
38 require.NotNil(t, data, name)
39 }
@@ -126,8 +128,10 @@ func TestCollector_Collect(t *testing.T) {
128 wantMetrics map[string]int64
129 }{
130 "success on valid response": {
129 - prepare: caseOk,
130 - wantNumOfCharts: len(serverCharts) + len(accountChartsTmpl)*3,
131 + prepare: caseOk,
132 + wantNumOfCharts: len(serverCharts) +
133 + len(accountChartsTmpl)*3 +
134 + len(routeChartsTmpl)*1,
135 wantMetrics: map[string]int64{
136 "accstatz_acc_$G_conns": 0,
137 "accstatz_acc_$G_leaf_nodes": 0,
@@ -156,6 +160,11 @@ func TestCollector_Collect(t *testing.T) {
160 "accstatz_acc_default_sent_msgs": 2546732,
161 "accstatz_acc_default_slow_consumers": 1,
162 "accstatz_acc_default_total_conns": 44,
163 + "routez_route_id_1_in_bytes": 4,
164 + "routez_route_id_1_in_msgs": 1,
165 + "routez_route_id_1_num_subs": 1,
166 + "routez_route_id_1_out_bytes": 4,
167 + "routez_route_id_1_out_msgs": 1,
168 "varz_http_endpoint_/_req": 5710,
169 "varz_http_endpoint_/accountz_req": 2201,
170 "varz_http_endpoint_/accstatz_req": 6,
@@ -240,6 +249,8 @@ func caseOk(t *testing.T) (*Collector, func()) {
249 return
250 }
251 _, _ = w.Write(dataVer210Accstatz)
252 + case urlPathRoutez:
253 + _, _ = w.Write(dataVer210Routez)
254 default:
255 w.WriteHeader(http.StatusNotFound)
256 }
src/go/plugin/go.d/collector/nats/metadata.yaml
+29 -1
@@ -239,7 +239,7 @@ modules:
239 dimensions:
240 - name: uptime
241 - name: account
242 - description: These metrics refer to [accounts](https://docs.nats.io/running-a-nats-service/nats_admin/monitoring#account-statistics).
242 + description: These metrics refer to [Accounts](https://docs.nats.io/running-a-nats-service/nats_admin/monitoring#account-statistics).
243 labels:
244 - name: account
245 description: "Account name."
@@ -288,6 +288,34 @@ modules:
288 chart_type: line
289 dimensions:
290 - name: leafnode
291 + - name: route
292 + description: These metrics refer to [Routes](https://docs.nats.io/running-a-nats-service/nats_admin/monitoring#route-information).
293 + labels:
294 + - name: route_id
295 + description: "A unique identifier for a route within the NATS cluster."
296 + - name: remote_id
297 + description: "he unique identifier of the remote server connected via the route."
298 + metrics:
299 + - name: nats.route_traffic
300 + description: Route Traffic
301 + unit: bytes/s
302 + chart_type: area
303 + dimensions:
304 + - name: in
305 + - name: out
306 + - name: nats.route_messages
307 + description: Route Messages
308 + unit: messages/s
309 + chart_type: line
310 + dimensions:
311 + - name: in
312 + - name: out
313 + - name: nats.route_subscriptions
314 + description: Route Active Subscriptions
315 + unit: subscriptions
316 + chart_type: line
317 + dimensions:
318 + - name: active
319 - name: http endpoint
320 description: These metrics refer to HTTP endpoints.
321 labels:
src/go/plugin/go.d/collector/nats/restapi.go
+15
@@ -17,6 +17,8 @@ const (
17 urlPathHealthz = "/healthz"
18 // https://docs.nats.io/running-a-nats-service/nats_admin/monitoring#account-statistics
19 urlPathAccstatz = "/accstatz"
20 + // https://docs.nats.io/running-a-nats-service/nats_admin/monitoring#route-information
21 + urlPathRoutez = "/routez"
22 )
23
24 var (
@@ -96,3 +98,16 @@ type accstatzResponse struct {
98 SlowConsumers int64 `json:"slow_consumers"`
99 } `json:"account_statz"`
100 }
101 +
102 +// https://github.com/nats-io/nats-server/blob/v2.10.24/server/monitor.go#L752
103 +type routezResponse struct {
104 + Routes []struct {
105 + Rid uint64 `json:"rid"`
106 + RemoteID string `json:"remote_id"`
107 + InMsgs int64 `json:"in_msgs"`
108 + OutMsgs int64 `json:"out_msgs"`
109 + InBytes int64 `json:"in_bytes"`
110 + OutBytes int64 `json:"out_bytes"`
111 + NumSubs uint32 `json:"subscriptions"`
112 + } `json:"routes"`
113 +}
src/go/plugin/go.d/collector/nats/testdata/v2.10.24/routez.json new
+20
@@ -0,0 +1,20 @@
1 +{
2 + "server_id": "NACDVKFBUW4C4XA24OOT6L4MDP56MW76J5RJDFXG7HLABSB46DCMWCOW",
3 + "now": "2019-06-24T14:29:16.046656-07:00",
4 + "num_routes": 1,
5 + "routes": [
6 + {
7 + "rid": 1,
8 + "remote_id": "de475c0041418afc799bccf0fdd61b47",
9 + "did_solicit": true,
10 + "ip": "127.0.0.1",
11 + "port": 61791,
12 + "pending_size": 0,
13 + "in_msgs": 1,
14 + "out_msgs": 1,
15 + "in_bytes": 4,
16 + "out_bytes": 4,
17 + "subscriptions": 1
18 + }
19 + ]
20 +}