rabbitmq: collect vhosts msg metrics from `/api/vhosts` (#6976)
* rabbitmq: collect vhosts msg metrics from `api/vhosts` * rabbitmq: update readme
Ilya Mashchenko committed
Oct 3, 2019 at 19:41 UTC
2701913e93424d58fb4ec3d07af37b5d39fe66f2
2 files changed
+114
-11
collectors/python.d.plugin/rabbitmq/README.md
+15
-1
@@ -1,6 +1,6 @@
1
# rabbitmq
2
3
-Module monitor rabbitmq performance and health metrics.
3
+This module monitors [RabbitMQ](https://www.rabbitmq.com/) performance and health metrics.
4
5
Following charts are drawn:
6
@@ -48,6 +48,20 @@ Following charts are drawn:
48
49
- free disk space in gigabytes
50
51
+
52
+Per Vhost charts:
53
+
54
+1. **Vhost Messages**
55
+
56
+ - ack
57
+ - confirm
58
+ - deliver
59
+ - get
60
+ - get_no_ack
61
+ - publish
62
+ - redeliver
63
+ - return_unroutable
64
+
65
## configuration
66
67
```yaml
collectors/python.d.plugin/rabbitmq/rabbitmq.chart.py
+99
-10
@@ -9,6 +9,7 @@ from bases.FrameworkServices.UrlService import UrlService
9
10
API_NODE = 'api/nodes'
11
API_OVERVIEW = 'api/overview'
12
+API_VHOSTS = 'api/vhosts'
13
14
NODE_STATS = [
15
'fd_used',
@@ -33,6 +34,17 @@ OVERVIEW_STATS = [
34
'message_stats.publish'
35
]
36
37
+VHOST_MESSAGE_STATS = [
38
+ 'message_stats.ack',
39
+ 'message_stats.confirm',
40
+ 'message_stats.deliver',
41
+ 'message_stats.get',
42
+ 'message_stats.get_no_ack',
43
+ 'message_stats.publish',
44
+ 'message_stats.redeliver',
45
+ 'message_stats.return_unroutable',
46
+]
47
+
48
ORDER = [
49
'queued_messages',
50
'message_rates',
@@ -111,6 +123,51 @@ CHARTS = {
123
}
124
125
126
+def vhost_chart_template(name):
127
+ order = [
128
+ 'vhost_{0}_message_stats'.format(name),
129
+ ]
130
+ family = 'vhost {0}'.format(name)
131
+
132
+ charts = {
133
+ order[0]: {
134
+ 'options': [
135
+ None, 'Vhost "{0}" Messages'.format(name), 'messages/s', family, 'rabbitmq.vhost_messages', 'stacked'],
136
+ 'lines': [
137
+ ['vhost_{0}_message_stats_ack'.format(name), 'ack', 'incremental'],
138
+ ['vhost_{0}_message_stats_confirm'.format(name), 'confirm', 'incremental'],
139
+ ['vhost_{0}_message_stats_deliver'.format(name), 'deliver', 'incremental'],
140
+ ['vhost_{0}_message_stats_get'.format(name), 'get', 'incremental'],
141
+ ['vhost_{0}_message_stats_get_no_ack'.format(name), 'get_no_ack', 'incremental'],
142
+ ['vhost_{0}_message_stats_publish'.format(name), 'publish', 'incremental'],
143
+ ['vhost_{0}_message_stats_redeliver'.format(name), 'redeliver', 'incremental'],
144
+ ['vhost_{0}_message_stats_return_unroutable'.format(name), 'return_unroutable', 'incremental'],
145
+ ]
146
+ },
147
+ }
148
+
149
+ return order, charts
150
+
151
+
152
+class VhostStatsBuilder:
153
+ def __init__(self):
154
+ self.stats = None
155
+
156
+ def set(self, raw_stats):
157
+ self.stats = raw_stats
158
+
159
+ def name(self):
160
+ return self.stats['name']
161
+
162
+ def has_msg_stats(self):
163
+ return bool(self.stats.get('message_stats'))
164
+
165
+ def msg_stats(self):
166
+ name = self.name()
167
+ stats = fetch_data(raw_data=self.stats, metrics=VHOST_MESSAGE_STATS)
168
+ return dict(('vhost_{0}_{1}'.format(name, k), v) for k, v in stats.items())
169
+
170
+
171
class Service(UrlService):
172
def __init__(self, configuration=None, name=None):
173
UrlService.__init__(self, configuration=configuration, name=name)
@@ -122,19 +179,25 @@ class Service(UrlService):
179
configuration.get('port', 15672),
180
)
181
self.node_name = str()
182
+ self.vhost = VhostStatsBuilder()
183
+ self.collected_vhosts = set()
184
185
def _get_data(self):
186
data = dict()
187
188
stats = self.get_overview_stats()
130
-
189
if not stats:
190
return None
191
192
data.update(stats)
193
194
stats = self.get_nodes_stats()
195
+ if not stats:
196
+ return None
197
198
+ data.update(stats)
199
+
200
+ stats = self.get_vhosts_stats()
201
if not stats:
202
return None
203
@@ -144,41 +207,67 @@ class Service(UrlService):
207
208
def get_overview_stats(self):
209
url = '{0}/{1}'.format(self.url, API_OVERVIEW)
147
-
210
raw = self._get_raw_data(url)
149
-
211
if not raw:
212
return None
213
214
data = loads(raw)
154
-
215
self.node_name = data['node']
156
-
216
return fetch_data(raw_data=data, metrics=OVERVIEW_STATS)
217
218
def get_nodes_stats(self):
219
url = '{0}/{1}/{2}'.format(self.url, API_NODE, self.node_name)
161
-
220
raw = self._get_raw_data(url)
163
-
221
if not raw:
222
return None
223
224
data = loads(raw)
168
-
225
return fetch_data(raw_data=data, metrics=NODE_STATS)
226
227
+ def get_vhosts_stats(self):
228
+ url = '{0}/{1}'.format(self.url, API_VHOSTS)
229
+ raw = self._get_raw_data(url)
230
+ if not raw:
231
+ return None
232
+
233
+ data = dict()
234
+ vhosts = loads(raw)
235
+ charts_initialized = len(self.charts) > 0
236
+
237
+ for vhost in vhosts:
238
+ self.vhost.set(vhost)
239
+ if not self.vhost.has_msg_stats():
240
+ continue
241
+
242
+ if charts_initialized and self.vhost.name() not in self.collected_vhosts:
243
+ self.collected_vhosts.add(self.vhost.name())
244
+ self.add_vhost_charts(self.vhost.name())
245
+
246
+ data.update(self.vhost.msg_stats())
247
+
248
+ return data
249
+
250
+ def add_vhost_charts(self, vhost_name):
251
+ order, charts = vhost_chart_template(vhost_name)
252
+
253
+ for chart_name in order:
254
+ params = [chart_name] + charts[chart_name]['options']
255
+ dimensions = charts[chart_name]['lines']
256
+
257
+ new_chart = self.charts.add_chart(params)
258
+ for dimension in dimensions:
259
+ new_chart.add_dimension(dimension)
260
+
261
262
def fetch_data(raw_data, metrics):
263
data = dict()
174
-
264
for metric in metrics:
265
value = raw_data
266
metrics_list = metric.split('.')
267
try:
268
for m in metrics_list:
269
value = value[m]
181
- except KeyError:
270
+ except (KeyError, TypeError):
271
continue
272
data['_'.join(metrics_list)] = value
273