Add per queue charts in rabbitmq.chart.py (#10064)
zarak committed
Oct 20, 2020 at 10:58 UTC
52db45930be11709df9ce945dd7940c99121ab5a
4 files changed
+153
collectors/python.d.plugin/rabbitmq/README.md
+21
@@ -69,6 +69,27 @@ Per Vhost charts:
69
- redeliver
70
- return_unroutable
71
72
+2. Per Queue charts:
73
+
74
+ 1. **Queued Messages**
75
+
76
+ - messages
77
+ - paged_out
78
+ - persistent
79
+ - ready
80
+ - unacknowledged
81
+
82
+ 2. **Queue Messages stats**
83
+
84
+ - ack
85
+ - confirm
86
+ - deliver
87
+ - get
88
+ - get_no_ack
89
+ - publish
90
+ - redeliver
91
+ - return_unroutable
92
+
93
## Configuration
94
95
Edit the `python.d/rabbitmq.conf` configuration file using `edit-config` from the your agent's [config
collectors/python.d.plugin/rabbitmq/rabbitmq.chart.py
+116
@@ -9,6 +9,7 @@ from bases.FrameworkServices.UrlService import UrlService
9
10
API_NODE = 'api/nodes'
11
API_OVERVIEW = 'api/overview'
12
+API_QUEUES = 'api/queues'
13
API_VHOSTS = 'api/vhosts'
14
15
NODE_STATS = [
@@ -41,6 +42,22 @@ OVERVIEW_STATS = [
42
'churn_rates.queue_deleted_details.rate'
43
]
44
45
+QUEUE_STATS = [
46
+ 'messages',
47
+ 'messages_paged_out',
48
+ 'messages_persistent',
49
+ 'messages_ready',
50
+ 'messages_unacknowledged',
51
+ 'message_stats.ack',
52
+ 'message_stats.confirm',
53
+ 'message_stats.deliver',
54
+ 'message_stats.get',
55
+ 'message_stats.get_no_ack',
56
+ 'message_stats.publish',
57
+ 'message_stats.redeliver',
58
+ 'message_stats.return_unroutable',
59
+]
60
+
61
VHOST_MESSAGE_STATS = [
62
'message_stats.ack',
63
'message_stats.confirm',
@@ -180,6 +197,44 @@ def vhost_chart_template(name):
197
198
return order, charts
199
200
+def queue_chart_template(queue_id):
201
+ vhost, name = queue_id
202
+ order = [
203
+ 'vhost_{0}_queue_{1}_queued_message'.format(vhost, name),
204
+ 'vhost_{0}_queue_{1}_messages_stats'.format(vhost, name),
205
+ ]
206
+ family = 'vhost {0}'.format(vhost)
207
+
208
+ charts = {
209
+ order[0]: {
210
+ 'options': [
211
+ None, 'Queue "{0}" in "{1}" queued messages'.format(name, vhost), 'messages', family, 'rabbitmq.queue_messages', 'line'],
212
+ 'lines': [
213
+ ['vhost_{0}_queue_{1}_messages'.format(vhost, name), 'messages', 'absolute'],
214
+ ['vhost_{0}_queue_{1}_messages_paged_out'.format(vhost, name), 'paged_out', 'absolute'],
215
+ ['vhost_{0}_queue_{1}_messages_persistent'.format(vhost, name), 'persistent', 'absolute'],
216
+ ['vhost_{0}_queue_{1}_messages_ready'.format(vhost, name), 'ready', 'absolute'],
217
+ ['vhost_{0}_queue_{1}_messages_unacknowledged'.format(vhost, name), 'unack', 'absolute'],
218
+ ]
219
+ },
220
+ order[1]: {
221
+ 'options': [
222
+ None, 'Queue "{0}" in "{1}" messages stats'.format(name, vhost), 'messages/s', family, 'rabbitmq.queue_messages_stats', 'line'],
223
+ 'lines': [
224
+ ['vhost_{0}_queue_{1}_message_stats_ack'.format(vhost, name), 'ack', 'incremental'],
225
+ ['vhost_{0}_queue_{1}_message_stats_confirm'.format(vhost, name), 'confirm', 'incremental'],
226
+ ['vhost_{0}_queue_{1}_message_stats_deliver'.format(vhost, name), 'deliver', 'incremental'],
227
+ ['vhost_{0}_queue_{1}_message_stats_get'.format(vhost, name), 'get', 'incremental'],
228
+ ['vhost_{0}_queue_{1}_message_stats_get_no_ack'.format(vhost, name), 'get_no_ack', 'incremental'],
229
+ ['vhost_{0}_queue_{1}_message_stats_publish'.format(vhost, name), 'publish', 'incremental'],
230
+ ['vhost_{0}_queue_{1}_message_stats_redeliver'.format(vhost, name), 'redeliver', 'incremental'],
231
+ ['vhost_{0}_queue_{1}_message_stats_return_unroutable'.format(vhost, name), 'return_unroutable', 'incremental'],
232
+ ]
233
+ },
234
+ }
235
+
236
+ return order, charts
237
+
238
239
class VhostStatsBuilder:
240
def __init__(self):
@@ -199,6 +254,21 @@ class VhostStatsBuilder:
254
stats = fetch_data(raw_data=self.stats, metrics=VHOST_MESSAGE_STATS)
255
return dict(('vhost_{0}_{1}'.format(name, k), v) for k, v in stats.items())
256
257
+class QueueStatsBuilder:
258
+ def __init__(self):
259
+ self.stats = None
260
+
261
+ def set(self, raw_stats):
262
+ self.stats = raw_stats
263
+
264
+ def id(self):
265
+ return self.stats['vhost'], self.stats['name']
266
+
267
+ def queue_stats(self):
268
+ vhost, name = self.id()
269
+ stats = fetch_data(raw_data=self.stats, metrics=QUEUE_STATS)
270
+ return dict(('vhost_{0}_queue_{1}_{2}'.format(vhost, name, k), v) for k, v in stats.items())
271
+
272
273
class Service(UrlService):
274
def __init__(self, configuration=None, name=None):
@@ -213,6 +283,11 @@ class Service(UrlService):
283
self.node_name = str()
284
self.vhost = VhostStatsBuilder()
285
self.collected_vhosts = set()
286
+ self.collect_queues_metrics = configuration.get('collect_queues_metrics', False)
287
+ self.debug("collect_queues_metrics is {0}".format("enabled" if self.collect_queues_metrics else "disabled"))
288
+ if self.collect_queues_metrics:
289
+ self.queue = QueueStatsBuilder()
290
+ self.collected_queues = set()
291
292
def _get_data(self):
293
data = dict()
@@ -233,6 +308,11 @@ class Service(UrlService):
308
if stats:
309
data.update(stats)
310
311
+ if self.collect_queues_metrics:
312
+ stats = self.get_queues_stats()
313
+ if stats:
314
+ data.update(stats)
315
+
316
return data or None
317
318
def get_overview_stats(self):
@@ -292,6 +372,31 @@ class Service(UrlService):
372
self.debug("number of vhosts: {0}, metrics: {1}".format(len(vhosts), len(data)))
373
return data
374
375
+ def get_queues_stats(self):
376
+ url = '{0}/{1}'.format(self.url, API_QUEUES)
377
+ self.debug("doing http request to '{0}'".format(url))
378
+ raw = self._get_raw_data(url)
379
+ if not raw:
380
+ return None
381
+
382
+ data = dict()
383
+ queues = loads(raw)
384
+ charts_initialized = len(self.charts) > 0
385
+
386
+ for queue in queues:
387
+ self.queue.set(queue)
388
+ if self.queue.id()[0] not in self.collected_vhosts:
389
+ continue
390
+
391
+ if charts_initialized and self.queue.id() not in self.collected_queues:
392
+ self.collected_queues.add(self.queue.id())
393
+ self.add_queue_charts(self.queue.id())
394
+
395
+ data.update(self.queue.queue_stats())
396
+
397
+ self.debug("number of queues: {0}, metrics: {1}".format(len(queues), len(data)))
398
+ return data
399
+
400
def add_vhost_charts(self, vhost_name):
401
order, charts = vhost_chart_template(vhost_name)
402
@@ -303,6 +408,17 @@ class Service(UrlService):
408
for dimension in dimensions:
409
new_chart.add_dimension(dimension)
410
411
+ def add_queue_charts(self, queue_id):
412
+ order, charts = queue_chart_template(queue_id)
413
+
414
+ for chart_name in order:
415
+ params = [chart_name] + charts[chart_name]['options']
416
+ dimensions = charts[chart_name]['lines']
417
+
418
+ new_chart = self.charts.add_chart(params)
419
+ for dimension in dimensions:
420
+ new_chart.add_dimension(dimension)
421
+
422
423
def fetch_data(raw_data, metrics):
424
data = dict()
collectors/python.d.plugin/rabbitmq/rabbitmq.conf
+6
@@ -70,6 +70,12 @@
70
# user: 'username'
71
# pass: 'password'
72
#
73
+# Rabbitmq plugin can also collect stats per vhost per queues, which is disabled
74
+# by default. Please note that enabling this can induced a serious overhead on
75
+# both netdata and rabbitmq if a look of queues are configured and used.
76
+#
77
+# collect_queues_metrics: 'yes/no'
78
+#
79
# ----------------------------------------------------------------------
80
# AUTO-DETECTION JOBS
81
# only one of them will run (they have the same name)
web/gui/dashboard_info.js
+10
@@ -2539,6 +2539,16 @@ netdataDashboard.context = {
2539
colors: NETDATA.colors[3]
2540
},
2541
2542
+ 'rabbitmq.queue_messages': {
2543
+ info: 'Total amount of messages and their states in this queue.',
2544
+ colors: NETDATA.colors[3]
2545
+ },
2546
+
2547
+ 'rabbitmq.queue_messages_stats': {
2548
+ info: 'Overall messaging rates including acknowledgements, delieveries, redeliveries, and publishes.',
2549
+ colors: NETDATA.colors[3]
2550
+ },
2551
+
2552
// ------------------------------------------------------------------------
2553
// ntpd
2554