netdata couchdb plugin with conf file and app group support
Joan Touzet committed
Sep 12, 2017 at 17:13 UTC
95433c572acef91820e827bf7f7d3031068f34a7
11 files changed
+593
-2
README.md
+3
@@ -210,6 +210,9 @@ This is a list of what it currently monitors:
210
- **Redis databases**<br/>
211
multiple servers, each showing: operations, hit rate, memory, keys, clients, slaves
212
213
+- **couchdb**<br/>
214
+ reads/writes, request methods, status codes, tasks, replication, per-db, etc
215
+
216
- **mongodb**<br/>
217
operations, clients, transactions, cursors, connections, asserts, locks, etc
218
conf.d/Makefile.am
+2
@@ -30,6 +30,7 @@ dist_pythonconfig_DATA = \
30
python.d/apache_cache.conf \
31
python.d/bind_rndc.conf \
32
python.d/chrony.conf \
33
+ python.d/couchdb.conf \
34
python.d/cpufreq.conf \
35
python.d/dns_query_time.conf \
36
python.d/dovecot.conf \
@@ -73,6 +74,7 @@ dist_healthconfig_DATA = \
74
health.d/backend.conf \
75
health.d/bind_rndc.conf \
76
health.d/cpu.conf \
77
+ health.d/couchdb.conf \
78
health.d/disks.conf \
79
health.d/elasticsearch.conf \
80
health.d/entropy.conf \
conf.d/apps_groups.conf
+1
-1
@@ -101,7 +101,7 @@ puma: *puma*
101
# database servers
102
103
sql: mysqld* mariad* postgres* oracle_* ora_*
104
-nosql: mongod redis* memcached
104
+nosql: mongod redis* memcached *couchdb*
105
timedb: prometheus *carbon-cache.py* *carbon-aggregator.py* *graphite/manage.py* *net.opentsdb.tools.TSDMain*
106
107
# -----------------------------------------------------------------------------
conf.d/health.d/couchdb.conf
new
+13
@@ -0,0 +1,13 @@
1
+
2
+# make sure couchdb is running
3
+
4
+template: couchdb_last_collected_secs
5
+ on: couchdb.request_methods
6
+ calc: $now - $last_collected_t
7
+ units: seconds ago
8
+ every: 10s
9
+ warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every))
10
+ crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every))
11
+ delay: down 5m multiplier 1.5 max 1h
12
+ info: number of seconds since the last successful data collection
13
+ to: dba
conf.d/python.d.conf
+1
@@ -31,6 +31,7 @@ apache_cache: no
31
# apache: yes
32
# bind_rndc: yes
33
# chrony: yes
34
+# couchdb: yes
35
# cpufreq: yes
36
# cpuidle: yes
37
# dns_query_time: yes
conf.d/python.d/couchdb.conf
new
+84
@@ -0,0 +1,84 @@
1
+# netdata python.d.plugin configuration for couchdb
2
+#
3
+# This file is in YaML format. Generally the format is:
4
+#
5
+# name: value
6
+#
7
+# There are 2 sections:
8
+# - global variables
9
+# - one or more JOBS
10
+#
11
+# JOBS allow you to collect values from multiple sources.
12
+# Each source will have its own set of charts.
13
+#
14
+# JOB parameters have to be indented (using spaces only, example below).
15
+
16
+# ----------------------------------------------------------------------
17
+# Global Variables
18
+# These variables set the defaults for all JOBs, however each JOB
19
+# may define its own, overriding the defaults.
20
+
21
+# update_every sets the default data collection frequency.
22
+# If unset, the python.d.plugin default is used.
23
+# By default, CouchDB only updates its stats every 10 seconds.
24
+update_every: 10
25
+
26
+# priority controls the order of charts at the netdata dashboard.
27
+# Lower numbers move the charts towards the top of the page.
28
+# If unset, the default for python.d.plugin is used.
29
+# priority: 60000
30
+
31
+# retries sets the number of retries to be made in case of failures.
32
+# If unset, the default for python.d.plugin is used.
33
+# Attempts to restore the service are made once every update_every
34
+# and only if the module has collected values in the past.
35
+# retries: 5
36
+
37
+# ----------------------------------------------------------------------
38
+# JOBS (data collection sources)
39
+#
40
+# The default JOBS share the same *name*. JOBS with the same name
41
+# are mutually exclusive. Only one of them will be allowed running at
42
+# any time. This allows autodetection to try several alternatives and
43
+# pick the one that works.
44
+#
45
+# Any number of jobs is supported.
46
+#
47
+# All python.d.plugin JOBS (for all its modules) support a set of
48
+# predefined parameters. These are:
49
+#
50
+# job_name:
51
+# name: myname # the JOB's name as it will appear at the
52
+# # dashboard (by default is the job_name)
53
+# # JOBs sharing a name are mutually exclusive
54
+# update_every: 1 # the JOB's data collection frequency
55
+# priority: 60000 # the JOB's order on the dashboard
56
+# retries: 5 # the JOB's number of restoration attempts
57
+#
58
+# Additionally to the above, the couchdb plugin also supports the following:
59
+#
60
+# host: 'ipaddress' # Server ip address or hostname. Default: 127.0.0.1
61
+# port: 'port' # Rabbitmq port. Default: 15672
62
+# scheme: 'scheme' # http or https. Default: http
63
+# node: 'couchdb@127.0.0.1' # CouchDB node name. Same as -name vm.args argument.
64
+#
65
+# if the URL is password protected, the following are supported:
66
+#
67
+# user: 'username'
68
+# pass: 'password'
69
+#
70
+# if db-specific stats are desired, place their names in databases:
71
+# databases: 'npm-registry animaldb'
72
+#
73
+# ----------------------------------------------------------------------
74
+# AUTO-DETECTION JOBS
75
+# only one of them will run (they have the same name)
76
+#
77
+localhost:
78
+ name: 'local'
79
+ host: '127.0.0.1'
80
+ port: '5984'
81
+ node: 'couchdb@127.0.0.1'
82
+ scheme: 'http'
83
+# user: 'admin'
84
+# pass: 'password'
diagrams/netdata-overview.xml
+1
-1
@@ -1 +1 @@
1
-<mxfile userAgent="Mozilla/5.0 (X11; Linux x86_64; rv:55.0) Gecko/20100101 Firefox/55.0" version="7.4.7" editor="www.draw.io" type="github"><diagram name="Page-1" id="6533187f-1b6b-8515-a257-c05e29b4d991">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</diagram></mxfile>
\ No newline at end of file
1
+<mxfile userAgent="Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0" version="7.4.9" editor="www.draw.io" type="github"><diagram name="Page-1" id="6533187f-1b6b-8515-a257-c05e29b4d991">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</diagram></mxfile>
python.d/Makefile.am
+1
@@ -17,6 +17,7 @@ dist_python_DATA = \
17
apache_cache.chart.py \
18
bind_rndc.chart.py \
19
chrony.chart.py \
20
+ couchdb.chart.py \
21
cpufreq.chart.py \
22
cpuidle.chart.py \
23
dns_query_time.chart.py \
python.d/README.md
+36
@@ -218,6 +218,42 @@ local:
218
219
---
220
221
+# couchdb
222
+
223
+This module monitors vital statistics of a local Apache CouchDB 2.x server, including:
224
+
225
+* Overall server reads/writes
226
+* HTTP traffic breakdown
227
+ * Request methods (`GET`, `PUT`, `POST`, etc.)
228
+ * Response status codes (`200`, `201`, `4xx`, etc.)
229
+* Active server tasks
230
+* Replication status (CouchDB 2.1 and up only)
231
+* Erlang VM stats
232
+* Optional per-database statistics: sizes, # of docs, # of deleted docs
233
+
234
+### Configuration
235
+
236
+Sample for a local server running on port 5984:
237
+```yaml
238
+local:
239
+ user: 'admin'
240
+ pass: 'password'
241
+ node: 'couchdb@127.0.0.1'
242
+```
243
+
244
+Be sure to specify a correct admin-level username and password.
245
+
246
+You may also need to change the `node` name; this should match the value of `-name NODENAME` in your CouchDB's `etc/vm.args` file. Typically this is of the form `couchdb@fully.qualified.domain.name` in a cluster, or `couchdb@127.0.0.1` / `couchdb@localhost` for a single-node server.
247
+
248
+If you want per-database statistics, these need to be added to the configuration, separated by spaces:
249
+```yaml
250
+local:
251
+ ...
252
+ databases: 'db1 db2 db3 ...'
253
+```
254
+
255
+---
256
+
257
# cpufreq
258
259
This module shows the current CPU frequency as set by the cpufreq kernel
python.d/couchdb.chart.py
new
+410
@@ -0,0 +1,410 @@
1
+# -*- coding: utf-8 -*-
2
+# Description: couchdb netdata python.d module
3
+# Author: wohali <wohali@apache.org>
4
+# Thanks to l2isbad for good examples :)
5
+
6
+from collections import namedtuple, defaultdict
7
+from json import loads
8
+from threading import Thread
9
+from socket import gethostbyname, gaierror
10
+try:
11
+ from queue import Queue
12
+except ImportError:
13
+ from Queue import Queue
14
+
15
+from base import UrlService
16
+
17
+# default module values (can be overridden per job in `config`)
18
+update_every = 1
19
+priority = 60000
20
+retries = 60
21
+
22
+METHODS = namedtuple('METHODS', ['get_data', 'url', 'stats'])
23
+
24
+OVERVIEW_STATS = [
25
+ 'couchdb.database_reads.value',
26
+ 'couchdb.database_writes.value',
27
+ 'couchdb.httpd.view_reads.value'
28
+ 'couchdb.httpd_request_methods.COPY.value',
29
+ 'couchdb.httpd_request_methods.DELETE.value',
30
+ 'couchdb.httpd_request_methods.GET.value',
31
+ 'couchdb.httpd_request_methods.HEAD.value',
32
+ 'couchdb.httpd_request_methods.OPTIONS.value',
33
+ 'couchdb.httpd_request_methods.POST.value',
34
+ 'couchdb.httpd_request_methods.PUT.value',
35
+ 'couchdb.httpd_status_codes.200.value',
36
+ 'couchdb.httpd_status_codes.201.value',
37
+ 'couchdb.httpd_status_codes.202.value',
38
+ 'couchdb.httpd_status_codes.204.value',
39
+ 'couchdb.httpd_status_codes.206.value',
40
+ 'couchdb.httpd_status_codes.301.value',
41
+ 'couchdb.httpd_status_codes.302.value',
42
+ 'couchdb.httpd_status_codes.304.value',
43
+ 'couchdb.httpd_status_codes.400.value',
44
+ 'couchdb.httpd_status_codes.401.value',
45
+ 'couchdb.httpd_status_codes.403.value',
46
+ 'couchdb.httpd_status_codes.404.value',
47
+ 'couchdb.httpd_status_codes.405.value',
48
+ 'couchdb.httpd_status_codes.406.value',
49
+ 'couchdb.httpd_status_codes.409.value',
50
+ 'couchdb.httpd_status_codes.412.value',
51
+ 'couchdb.httpd_status_codes.413.value',
52
+ 'couchdb.httpd_status_codes.414.value',
53
+ 'couchdb.httpd_status_codes.415.value',
54
+ 'couchdb.httpd_status_codes.416.value',
55
+ 'couchdb.httpd_status_codes.417.value',
56
+ 'couchdb.httpd_status_codes.500.value',
57
+ 'couchdb.httpd_status_codes.501.value',
58
+ 'couchdb.open_os_files.value',
59
+ 'couch_replicator.jobs.running.value',
60
+ 'couch_replicator.jobs.pending.value',
61
+ 'couch_replicator.jobs.crashed.value',
62
+]
63
+
64
+SYSTEM_STATS = [
65
+ 'context_switches',
66
+ 'run_queue',
67
+ 'ets_table_count',
68
+ 'reductions',
69
+ 'memory.atom',
70
+ 'memory.atom_used',
71
+ 'memory.binary',
72
+ 'memory.code',
73
+ 'memory.ets',
74
+ 'memory.other',
75
+ 'memory.processes',
76
+ 'io_input',
77
+ 'io_output',
78
+ 'os_proc_count',
79
+ 'process_count',
80
+ 'internal_replication_jobs'
81
+]
82
+
83
+DB_STATS = [
84
+ 'doc_count',
85
+ 'doc_del_count',
86
+ 'sizes.file',
87
+ 'sizes.external',
88
+ 'sizes.active'
89
+]
90
+
91
+ORDER = [
92
+ 'activity',
93
+ 'request_methods',
94
+ 'response_codes',
95
+ 'active_tasks',
96
+ 'replicator_jobs',
97
+ 'open_files',
98
+ 'db_sizes_file',
99
+ 'db_sizes_external',
100
+ 'db_sizes_active',
101
+ 'db_doc_counts',
102
+ 'db_doc_del_counts',
103
+ 'erlang_memory',
104
+ 'erlang_proc_counts',
105
+ 'erlang_peak_msg_queue',
106
+ 'erlang_reductions'
107
+]
108
+
109
+CHARTS = {
110
+ 'activity': {
111
+ 'options': [None, 'Overall Activity', 'req/s',
112
+ 'dbactivity', 'couchdb.activity', 'stacked'],
113
+ 'lines': [
114
+ ['couchdb_database_reads', 'DB reads', 'incremental'],
115
+ ['couchdb_database_writes', 'DB writes', 'incremental'],
116
+ ['couchdb_httpd_view_reads', 'View reads', 'incremental']
117
+ ]
118
+ },
119
+ 'request_methods': {
120
+ 'options': [None, 'HTTP request methods', 'req/s',
121
+ 'httptraffic', 'couchdb.request_methods',
122
+ 'stacked'],
123
+ 'lines': [
124
+ ['couchdb_httpd_request_methods_COPY', 'COPY', 'incremental'],
125
+ ['couchdb_httpd_request_methods_DELETE', 'DELETE', 'incremental'],
126
+ ['couchdb_httpd_request_methods_GET', 'GET', 'incremental'],
127
+ ['couchdb_httpd_request_methods_HEAD', 'HEAD', 'incremental'],
128
+ ['couchdb_httpd_request_methods_OPTIONS', 'OPTIONS',
129
+ 'incremental'],
130
+ ['couchdb_httpd_request_methods_POST', 'POST', 'incremental'],
131
+ ['couchdb_httpd_request_methods_PUT', 'PUT', 'incremental']
132
+ ]
133
+ },
134
+ 'response_codes': {
135
+ 'options': [None, 'HTTP response status codes', 'resp/s',
136
+ 'httptraffic', 'couchdb.response_codes',
137
+ 'stacked'],
138
+ 'lines': [
139
+ ['couchdb_httpd_status_codes_200', '200 OK', 'incremental'],
140
+ ['couchdb_httpd_status_codes_201', '201 Created', 'incremental'],
141
+ ['couchdb_httpd_status_codes_202', '202 Accepted', 'incremental'],
142
+ ['couchdb_httpd_status_codes_2xx', 'Other 2xx Success',
143
+ 'incremental'],
144
+ ['couchdb_httpd_status_codes_3xx', '3xx Redirection',
145
+ 'incremental'],
146
+ ['couchdb_httpd_status_codes_4xx', '4xx Client error',
147
+ 'incremental'],
148
+ ['couchdb_httpd_status_codes_5xx', '5xx Server error',
149
+ 'incremental']
150
+ ]
151
+ },
152
+ 'open_files': {
153
+ 'options': [None, 'Open files', 'files',
154
+ 'ops', 'couchdb.open_files', 'line'],
155
+ 'lines': [
156
+ ['couchdb_open_os_files', '# files', 'absolute']
157
+ ]
158
+ },
159
+ 'active_tasks': {
160
+ 'options': [None, 'Active task breakdown', 'tasks',
161
+ 'ops', 'couchdb.active_tasks', 'stacked'],
162
+ 'lines': [
163
+ ['activetasks_indexer', 'Indexer', 'absolute'],
164
+ ['activetasks_database_compaction', 'DB Compaction', 'absolute'],
165
+ ['activetasks_replication', 'Replication', 'absolute'],
166
+ ['activetasks_view_compaction', 'View Compaction', 'absolute']
167
+ ]
168
+ },
169
+ 'replicator_jobs': {
170
+ 'options': [None, 'Replicator job breakdown', 'jobs',
171
+ 'ops', 'couchdb.replicator_jobs', 'stacked'],
172
+ 'lines': [
173
+ ['couch_replicator_jobs_running', 'Running', 'absolute'],
174
+ ['couch_replicator_jobs_pending', 'Pending', 'absolute'],
175
+ ['couch_replicator_jobs_crashed', 'Crashed', 'absolute'],
176
+ ['internal_replication_jobs', 'Internal replication jobs',
177
+ 'absolute']
178
+ ]
179
+ },
180
+ 'erlang_memory': {
181
+ 'options': [None, 'Erlang VM memory usage', 'bytes',
182
+ 'erlang', 'couchdb.erlang_vm_memory', 'stacked'],
183
+ 'lines': [
184
+ ['memory_atom', 'atom', 'absolute'],
185
+ ['memory_binary', 'binaries', 'absolute'],
186
+ ['memory_code', 'code', 'absolute'],
187
+ ['memory_ets', 'ets', 'absolute'],
188
+ ['memory_processes', 'procs', 'absolute'],
189
+ ['memory_other', 'other', 'absolute']
190
+ ]
191
+ },
192
+ 'erlang_reductions': {
193
+ 'options': [None, 'Erlang reductions', 'count',
194
+ 'erlang', 'couchdb.reductions', 'line'],
195
+ 'lines': [
196
+ ['reductions', 'reductions', 'incremental']
197
+ ]
198
+ },
199
+ 'erlang_proc_counts': {
200
+ 'options': [None, 'Process counts', 'count',
201
+ 'erlang', 'couchdb.proccounts', 'line'],
202
+ 'lines': [
203
+ ['os_proc_count', 'OS procs', 'absolute'],
204
+ ['process_count', 'erl procs', 'absolute']
205
+ ]
206
+ },
207
+ 'erlang_peak_msg_queue': {
208
+ 'options': [None, 'Peak message queue size', 'count',
209
+ 'erlang', 'couchdb.peakmsgqueue',
210
+ 'line'],
211
+ 'lines': [
212
+ ['peak_msg_queue', 'peak size', 'absolute']
213
+ ]
214
+ },
215
+ # Lines for the following are added as part of check()
216
+ 'db_sizes_file': {
217
+ 'options': [None, 'Database sizes (file)', 'KB',
218
+ 'perdbstats', 'couchdb.db_sizes_file', 'line'],
219
+ 'lines': []
220
+ },
221
+ 'db_sizes_external': {
222
+ 'options': [None, 'Database sizes (external)', 'KB',
223
+ 'perdbstats', 'couchdb.db_sizes_external', 'line'],
224
+ 'lines': []
225
+ },
226
+ 'db_sizes_active': {
227
+ 'options': [None, 'Database sizes (active)', 'KB',
228
+ 'perdbstats', 'couchdb.db_sizes_active', 'line'],
229
+ 'lines': []
230
+ },
231
+ 'db_doc_counts': {
232
+ 'options': [None, 'Database # of docs', 'docs',
233
+ 'perdbstats', 'couchdb_db_doc_count', 'line'],
234
+ 'lines': []
235
+ },
236
+ 'db_doc_del_counts': {
237
+ 'options': [None, 'Database # of deleted docs', 'docs',
238
+ 'perdbstats', 'couchdb_db_doc_del_count', 'line'],
239
+ 'lines': []
240
+ }
241
+}
242
+
243
+
244
+class Service(UrlService):
245
+ def __init__(self, configuration=None, name=None):
246
+ UrlService.__init__(self, configuration=configuration, name=name)
247
+ self.order = ORDER
248
+ self.definitions = CHARTS
249
+ self.host = self.configuration.get('host', '127.0.0.1')
250
+ self.port = self.configuration.get('port', 5984)
251
+ self.node = self.configuration.get('node', 'couchdb@127.0.0.1')
252
+ self.scheme = self.configuration.get('scheme', 'http')
253
+ self.user = self.configuration.get('user')
254
+ self.password = self.configuration.get('pass')
255
+ try:
256
+ self.dbs = self.configuration.get('databases').split(' ')
257
+ except (KeyError, AttributeError):
258
+ self.dbs = []
259
+
260
+ def check(self):
261
+ if not (self.host and self.port):
262
+ self.error('Host is not defined in the module configuration file')
263
+ return False
264
+ try:
265
+ self.host = gethostbyname(self.host)
266
+ except gaierror as error:
267
+ self.error(str(error))
268
+ return False
269
+ self.url = '{scheme}://{host}:{port}'.format(scheme=self.scheme,
270
+ host=self.host,
271
+ port=self.port)
272
+ stats = self.url + '/_node/{node}/_stats'.format(node=self.node)
273
+ active_tasks = self.url + '/_active_tasks'
274
+ system = self.url + '/_node/{node}/_system'.format(node=self.node)
275
+ self.methods = [METHODS(get_data=self._get_overview_stats,
276
+ url=stats,
277
+ stats=OVERVIEW_STATS),
278
+ METHODS(get_data=self._get_active_tasks_stats,
279
+ url=active_tasks,
280
+ stats=None),
281
+ METHODS(get_data=self._get_overview_stats,
282
+ url=system,
283
+ stats=SYSTEM_STATS),
284
+ METHODS(get_data=self._get_dbs_stats,
285
+ url=self.url,
286
+ stats=DB_STATS)]
287
+ # must initialise manager before using _get_raw_data
288
+ self._manager = self._build_manager()
289
+ self.dbs = [db for db in self.dbs
290
+ if self._get_raw_data(self.url + '/' + db)]
291
+ for db in self.dbs:
292
+ self.definitions['db_sizes_file']['lines'].append(
293
+ ['db_'+db+'_sizes_file', db, 'absolute', 1, 1000]
294
+ )
295
+ self.definitions['db_sizes_external']['lines'].append(
296
+ ['db_'+db+'_sizes_external', db, 'absolute', 1, 1000]
297
+ )
298
+ self.definitions['db_sizes_active']['lines'].append(
299
+ ['db_'+db+'_sizes_active', db, 'absolute', 1, 1000]
300
+ )
301
+ self.definitions['db_doc_counts']['lines'].append(
302
+ ['db_'+db+'_doc_count', db, 'absolute']
303
+ )
304
+ self.definitions['db_doc_del_counts']['lines'].append(
305
+ ['db_'+db+'_doc_del_count', db, 'absolute']
306
+ )
307
+ return UrlService.check(self)
308
+
309
+ def _get_data(self):
310
+ threads = list()
311
+ queue = Queue()
312
+ result = dict()
313
+
314
+ for method in self.methods:
315
+ th = Thread(target=method.get_data,
316
+ args=(queue, method.url, method.stats))
317
+ th.start()
318
+ threads.append(th)
319
+
320
+ for thread in threads:
321
+ thread.join()
322
+ result.update(queue.get())
323
+
324
+ # self.info('couchdb result = ' + str(result))
325
+ return result or None
326
+
327
+ def _get_overview_stats(self, queue, url, stats):
328
+ raw_data = self._get_raw_data(url)
329
+ if not raw_data:
330
+ return queue.put(dict())
331
+ data = loads(raw_data)
332
+ to_netdata = self._fetch_data(raw_data=data, metrics=stats)
333
+ if 'message_queues' in data:
334
+ to_netdata['peak_msg_queue'] = get_peak_msg_queue(data)
335
+ return queue.put(to_netdata)
336
+
337
+ def _get_active_tasks_stats(self, queue, url, _):
338
+ taskdict = defaultdict(int)
339
+ taskdict["activetasks_indexer"] = 0
340
+ taskdict["activetasks_database_compaction"] = 0
341
+ taskdict["activetasks_replication"] = 0
342
+ taskdict["activetasks_view_compaction"] = 0
343
+ raw_data = self._get_raw_data(url)
344
+ if not raw_data:
345
+ return queue.put(dict())
346
+ data = loads(raw_data)
347
+ for task in data:
348
+ taskdict["activetasks_" + task["type"]] += 1
349
+ return queue.put(dict(taskdict))
350
+
351
+ def _get_dbs_stats(self, queue, url, stats):
352
+ to_netdata = {}
353
+ for db in self.dbs:
354
+ raw_data = self._get_raw_data(url + '/' + db)
355
+ if not raw_data:
356
+ continue
357
+ data = loads(raw_data)
358
+ for metric in stats:
359
+ value = data
360
+ metrics_list = metric.split('.')
361
+ try:
362
+ for m in metrics_list:
363
+ value = value[m]
364
+ except KeyError as e:
365
+ self.debug('cannot process ' + metric + ' for ' + db
366
+ + ": " + str(e))
367
+ continue
368
+ metric_name = 'db_{0}_{1}'.format(db, '_'.join(metrics_list))
369
+ to_netdata[metric_name] = value
370
+ return queue.put(to_netdata)
371
+
372
+ def _fetch_data(self, raw_data, metrics):
373
+ data = dict()
374
+ for metric in metrics:
375
+ value = raw_data
376
+ metrics_list = metric.split('.')
377
+ try:
378
+ for m in metrics_list:
379
+ value = value[m]
380
+ except KeyError as e:
381
+ self.debug('cannot process ' + metric + ': ' + str(e))
382
+ continue
383
+ # strip off .value from end of stat
384
+ if metrics_list[-1] == 'value':
385
+ metrics_list = metrics_list[:-1]
386
+ # sum up 3xx/4xx/5xx
387
+ if metrics_list[0:2] == ['couchdb', 'httpd_status_codes'] and \
388
+ int(metrics_list[2]) > 202:
389
+ metrics_list[2] = '{0}xx'.format(int(metrics_list[2]) // 100)
390
+ if '_'.join(metrics_list) in data:
391
+ data['_'.join(metrics_list)] += value
392
+ else:
393
+ data['_'.join(metrics_list)] = value
394
+ else:
395
+ data['_'.join(metrics_list)] = value
396
+ return data
397
+
398
+
399
+def get_peak_msg_queue(data):
400
+ maxsize = 0
401
+ queues = data['message_queues']
402
+ for queue in iter(queues.values()):
403
+ if isinstance(queue, dict) and 'count' in queue:
404
+ value = queue['count']
405
+ elif isinstance(queue, int):
406
+ value = queue
407
+ else:
408
+ continue
409
+ maxsize = max(maxsize, value)
410
+ return maxsize
web/dashboard_info.js
+41
@@ -312,6 +312,11 @@ netdataDashboard.menu = {
312
'chrony': {
313
icon: '<i class="fa fa-clock-o" aria-hidden="true"></i>',
314
info: 'chronyd parameters about the system’s clock performance.'
315
+ },
316
+
317
+ 'couchdb': {
318
+ icon: '<i class="fa fa-database" aria-hidden="true"></i>',
319
+ info: 'Performance metrics for <b><a href="https://couchdb.apache.org/">CouchDB</a></b>, the open-source, JSON document-based database with an HTTP API and multi-master replication.'
320
}
321
};
322
@@ -434,6 +439,30 @@ netdataDashboard.submenu = {
439
'go_expvar.memstats': {
440
title: 'Memory statistics',
441
info: 'Go runtime memory statistics. See <a href="https://golang.org/pkg/runtime/#MemStats" target="_blank">runtime.MemStats</a> documentation for more info about each chart and the values.'
442
+ },
443
+
444
+ 'couchdb.dbactivity': {
445
+ title: 'DB activity',
446
+ info: 'Overall database reads and writes for the entire server. This includes any external HTTP traffic, as well as internal replication traffic performed in a cluster to ensure node consistency.'
447
+ },
448
+
449
+ 'couchdb.httptraffic': {
450
+ title: 'HTTP traffic breakdown',
451
+ info: 'All HTTP traffic, broken down by type of request (<tt>GET</tt>, <tt>PUT</tt>, <tt>POST</tt>, etc.) and response status code (<tt>200</tt>, <tt>201</tt>, <tt>4xx</tt>, etc.)<br/><br/>Any <tt>5xx</tt> errors here indicate a likely CouchDB bug; check the logfile for further information.'
452
+ },
453
+
454
+ 'couchdb.ops': {
455
+ title: 'Server operations'
456
+ },
457
+
458
+ 'couchdb.perdbstats': {
459
+ title: 'Per-DB statistics',
460
+ info: 'Statistics per database. This includes <a href="http://docs.couchdb.org/en/latest/api/database/common.html#get--db">3 size graphs per database</a>: active (the size of live data in the database), external (the uncompressed size of the database contents), and file (the size of the file on disk, exclusive of any views and indexes). It also includes the number of documents and number of deleted documents per database.'
461
+ },
462
+
463
+ 'couchdb.erlang': {
464
+ title: 'Erlang statistics',
465
+ info: 'Detailed information about the status of the Erlang VM that hosts CouchDB. These are intended for advanced users only. High values of the peak message queue (>10e6) generally indicate an overload condition.'
466
}
467
};
468
@@ -1534,5 +1563,17 @@ netdataDashboard.context = {
1563
info: 'The estimated error bound on the frequency.',
1564
height: 0.5,
1565
colors: NETDATA.colors[5]
1566
+ },
1567
+
1568
+ 'couchdb.active_tasks': {
1569
+ info: 'Active tasks running on this CouchDB <b>cluster</b>. Four types of tasks currently exist: indexer (view building), replication, database compaction and view compaction.'
1570
+ },
1571
+
1572
+ 'couchdb.replicator_jobs': {
1573
+ info: 'Detailed breakdown of any replication jobs in progress on this node. For more information, see the <a href="http://docs.couchdb.org/en/latest/replication/replicator.html">replicator documentation</a>.'
1574
+ },
1575
+
1576
+ 'couchdb.open_files': {
1577
+ info: 'Count of all files held open by CouchDB. If this value seems pegged at 1024 or 4096, your server process is probably hitting the open file handle limit and <a href="http://docs.couchdb.org/en/latest/maintenance/performance.html#pam-and-ulimit">needs to be increased.</a>'
1578
}
1579
};