mongodb plugin: Fix build issues with version 0.4 of libmongoc.
[collectd.git] / src / mongodb.c
1 /**
2  * collectd - src/mongo.c
3  * Copyright (C) 2010 Ryan Cox
4  * Copyright (C) 2012 Florian Forster
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; only version 2 of the License is applicable.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Ryan Cox <ryan.a.cox at gmail.com>
21  *   Florian Forster <octo at collectd.org>
22  **/
23
24 #include "collectd.h"
25 #include "common.h"
26 #include "plugin.h"
27
28 #if HAVE_STDINT_H
29 # define MONGO_HAVE_STDINT 1
30 #else
31 # define MONGO_USE_LONG_LONG_INT 1
32 #endif
33 #include <mongo.h>
34
35 #define MC_MONGO_DEF_HOST "127.0.0.1"
36 #define MC_MONGO_DEF_DB "admin"
37
38 static char *mc_user     = NULL;
39 static char *mc_password = NULL;
40 static char *mc_db       = NULL;
41 static char *mc_host     = NULL;
42 static int   mc_port     = 0;
43
44 static mongo mc_connection;
45 static _Bool mc_have_connection = 0;
46
47 static const char *config_keys[] = {
48     "User",
49     "Password",
50     "Database",
51     "Host",
52     "Port"
53 };
54 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
55
56 static void submit (const char *type, const char *instance, /* {{{ */
57         value_t *values, size_t values_len)
58 {
59     value_list_t v = VALUE_LIST_INIT;
60
61     v.values = values;
62     v.values_len = values_len;
63
64     sstrncpy (v.host, hostname_g, sizeof(v.host));
65     sstrncpy (v.plugin, "mongodb", sizeof(v.plugin));
66     ssnprintf (v.plugin_instance, sizeof (v.plugin_instance), "%i", mc_port);
67     sstrncpy (v.type, type, sizeof(v.type));
68
69     if (instance != NULL)
70         sstrncpy (v.type_instance, instance, sizeof (v.type_instance));
71
72     plugin_dispatch_values (&v);
73 } /* }}} void submit */
74
75 static void submit_gauge (const char *type, const char *instance, /* {{{ */
76         gauge_t gauge)
77 {
78     value_t v;
79
80     v.gauge = gauge;
81     submit(type, instance, &v, /* values_len = */ 1);
82 } /* }}} void submit_gauge */
83
84 static void submit_derive (const char *type, const char *instance, /* {{{ */
85         derive_t derive)
86 {
87     value_t v;
88
89     v.derive = derive;
90     submit(type, instance, &v, /* values_len = */ 1);
91 } /* }}} void submit_derive */
92
93 static int handle_field (bson *obj, const char *field, /* {{{ */
94         int (*func) (bson_iterator *))
95 {
96     bson_type type;
97     bson_iterator iter;
98     bson_iterator subiter;
99     int status = 0;
100
101     type = bson_find (&iter, obj, field);
102     if (type != BSON_OBJECT)
103         return (EINVAL);
104
105     bson_iterator_subiterator (&iter, &subiter);
106     while (bson_iterator_more (&subiter))
107     {
108         (void) bson_iterator_next (&subiter);
109         status = (*func) (&subiter);
110         if (status != 0)
111             break;
112     }
113
114     return (status);
115 } /* }}} int handle_field */
116
117 static int handle_opcounters (bson_iterator *iter) /* {{{ */
118 {
119     bson_type type;
120     const char *key;
121     derive_t value;
122
123     type = bson_iterator_type (iter);
124     if ((type != BSON_INT) && (type != BSON_LONG))
125         return (0);
126
127     key = bson_iterator_key (iter);
128     if (key == NULL)
129         return (0);
130
131     value = (derive_t) bson_iterator_long (iter);
132
133     submit_derive ("total_operations", key, value);
134     return (0);
135 } /* }}} int handle_opcounters */
136
137 static int handle_mem (bson_iterator *iter) /* {{{ */
138 {
139     bson_type type;
140     const char *key;
141     gauge_t value;
142
143     type = bson_iterator_type (iter);
144     if ((type != BSON_DOUBLE) && (type != BSON_LONG) && (type != BSON_INT))
145         return (0);
146
147     key = bson_iterator_key (iter);
148     if (key == NULL)
149         return (0);
150
151     /* Is "virtual" really interesting?
152      * What exactly does "mapped" mean? */
153     if ((strcasecmp ("mapped", key) != 0)
154             && (strcasecmp ("resident", key) != 0)
155             && (strcasecmp ("virtual", key) != 0))
156         return (0);
157
158     value = (gauge_t) bson_iterator_double (iter);
159     /* All values are in MByte */
160     value *= 1048576.0;
161
162     submit_gauge ("memory", key, value);
163     return (0);
164 } /* }}} int handle_mem */
165
166 static int handle_connections (bson_iterator *iter) /* {{{ */
167 {
168     bson_type type;
169     const char *key;
170     gauge_t value;
171
172     type = bson_iterator_type (iter);
173     if ((type != BSON_DOUBLE) && (type != BSON_LONG) && (type != BSON_INT))
174         return (0);
175
176     key = bson_iterator_key (iter);
177     if (key == NULL)
178         return (0);
179
180     if (strcmp ("current", key) != 0)
181         return (0);
182
183     value = (gauge_t) bson_iterator_double (iter);
184
185     submit_gauge ("current_connections", NULL, value);
186     return (0);
187 } /* }}} int handle_connections */
188
189 static int handle_lock (bson_iterator *iter) /* {{{ */
190 {
191     bson_type type;
192     const char *key;
193     derive_t value;
194
195     type = bson_iterator_type (iter);
196     if ((type != BSON_DOUBLE) && (type != BSON_LONG) && (type != BSON_INT))
197         return (0);
198
199     key = bson_iterator_key (iter);
200     if (key == NULL)
201         return (0);
202
203     if (strcmp ("lockTime", key) != 0)
204         return (0);
205
206     value = (derive_t) bson_iterator_long (iter);
207     /* The time is measured in microseconds (us). We convert it to
208      * milliseconds (ms). */
209     value = value / 1000;
210
211     submit_derive ("total_time_in_ms", "lock_held", value);
212     return (0);
213 } /* }}} int handle_lock */
214
215 static int handle_btree (const bson *obj) /* {{{ */
216 {
217     bson_iterator i;
218
219     bson_iterator_init (&i, obj);
220     while (bson_iterator_next (&i))
221     {
222         bson_type type;
223         const char *key;
224         gauge_t value;
225
226         type = bson_iterator_type (&i);
227         if ((type != BSON_DOUBLE) && (type != BSON_LONG) && (type != BSON_INT))
228             continue;
229
230         key = bson_iterator_key (&i);
231         if (key == NULL)
232             continue;
233
234         value = (gauge_t) bson_iterator_double (&i);
235
236         if (strcmp ("hits", key) == 0)
237             submit_gauge ("cache_result", "hit", value);
238         else if (strcmp ("misses", key) != 0)
239             submit_gauge ("cache_result", "miss", value);
240     }
241
242     return (0);
243 } /* }}} int handle_btree */
244
245 static int handle_index_counters (bson_iterator *iter) /* {{{ */
246 {
247     bson_type type;
248     const char *key;
249     bson subobj;
250     int status;
251
252     type = bson_iterator_type (iter);
253     if (type != BSON_OBJECT)
254         return (0);
255
256     key = bson_iterator_key (iter);
257     if (key == NULL)
258         return (0);
259
260     if (strcmp ("btree", key) != 0)
261         return (0);
262
263     bson_iterator_subobject (iter, &subobj);
264     status = handle_btree (&subobj);
265     bson_destroy (&subobj);
266
267     return (status);
268 } /* }}} int handle_index_counters */
269
270 static int query_server_status (void) /* {{{ */
271 {
272     bson result;
273     int status;
274
275     status = mongo_simple_int_command (&mc_connection,
276             (mc_db != NULL) ? mc_db : MC_MONGO_DEF_DB,
277             /* cmd = */ "serverStatus", /* arg = */ 1,
278             &result);
279     if (status != MONGO_OK)
280     {
281         ERROR ("mongodb plugin: Calling {\"serverStatus\": 1} failed: %i",
282                 (int) mc_connection.err);
283         return (-1);
284     }
285
286     handle_field (&result, "opcounters",    handle_opcounters);
287     handle_field (&result, "mem",           handle_mem);
288     handle_field (&result, "connections",   handle_connections);
289     handle_field (&result, "globalLock",    handle_lock);
290     handle_field (&result, "indexCounters", handle_index_counters);
291
292     bson_destroy(&result);
293     return (0);
294 } /* }}} int query_server_status */
295
296 static int handle_dbstats (const bson *obj) /* {{{ */
297 {
298     bson_iterator i;
299
300     bson_iterator_init (&i, obj);
301     while (bson_iterator_next (&i))
302     {
303         bson_type type;
304         const char *key;
305         gauge_t value;
306
307         type = bson_iterator_type (&i);
308         if ((type != BSON_DOUBLE) && (type != BSON_LONG) && (type != BSON_INT))
309             return (0);
310
311         key = bson_iterator_key (&i);
312         if (key == NULL)
313             return (0);
314
315         value = (gauge_t) bson_iterator_double (&i);
316
317         /* counts */
318         if (strcmp ("collections", key) == 0)
319             submit_gauge ("gauge", "collections", value);
320         else if (strcmp ("objects", key) == 0)
321             submit_gauge ("gauge", "objects", value);
322         else if (strcmp ("numExtents", key) == 0)
323             submit_gauge ("gauge", "num_extents", value);
324         else if (strcmp ("indexes", key) == 0)
325             submit_gauge ("gauge", "indexes", value);
326         /* sizes */
327         else if (strcmp ("dataSize", key) == 0)
328             submit_gauge ("bytes", "data", value);
329         else if (strcmp ("storageSize", key) == 0)
330             submit_gauge ("bytes", "storage", value);
331         else if (strcmp ("indexSize", key) == 0)
332             submit_gauge ("bytes", "index", value);
333     }
334
335     return (0);
336 } /* }}} int handle_dbstats */
337
338 static int query_dbstats (void) /* {{{ */
339 {
340     bson result;
341     int status;
342
343     /* TODO:
344      *
345      *  change this to raw runCommand
346      *       db.runCommand( { dbstats : 1 } );
347      *       succeeds but is getting back all zeros !?!
348      *  modify bson_print to print type 18
349      *  repro problem w/o db name - show dbs doesn't work again
350      *  why does db.admin.dbstats() work fine in shell?
351      *  implement retries ? noticed that if db is unavailable, collectd dies
352      */
353
354     memset (&result, 0, sizeof (result));
355     status = mongo_simple_int_command (&mc_connection,
356             (mc_db != NULL) ? mc_db : MC_MONGO_DEF_DB,
357             /* cmd = */ "dbstats", /* arg = */ 1,
358             &result);
359     if (status != MONGO_OK)
360     {
361         ERROR ("mongodb plugin: Calling {\"dbstats\": 1} failed: %i",
362                 (int) mc_connection.err);
363         return (-1);
364     }
365
366     handle_dbstats (&result);
367
368     bson_destroy (&result);
369     return (0);
370 } /* }}} int query_dbstats */
371
372 static int mc_read(void) /* {{{ */
373 {
374     if (query_server_status () != 0)
375         return (-1);
376
377     if (query_dbstats () != 0)
378         return (-1);
379
380     return (0);
381 } /* }}} int mc_read */
382
383 static void mc_config_set (char **dest, const char *src ) /* {{{ */
384 {
385     sfree(*dest);
386     *dest = strdup (src);
387 } /* }}} void mc_config_set */
388
389 static int mc_config (const char *key, const char *value) /* {{{ */
390 {
391     if (strcasecmp("Host", key) == 0)
392         mc_config_set(&mc_host,value);
393     else if (strcasecmp("Port", key) == 0)
394     {
395         int tmp;
396
397         tmp = service_name_to_port_number (value);
398         if (tmp > 0)
399             mc_port = tmp;
400         else
401         {
402             ERROR("mongodb plugin: failed to parse Port value: %s", value);
403             return (-1);
404         }
405     }
406     else if(strcasecmp("User", key) == 0)
407         mc_config_set(&mc_user,value);
408     else if(strcasecmp("Password", key) == 0)
409         mc_config_set(&mc_password,value);
410     else if(strcasecmp("Database", key) == 0)
411         mc_config_set(&mc_db,value);
412     else
413     {
414         ERROR ("mongodb plugin: Unknown config option: %s", key);
415         return (-1);
416     }
417
418     return (0);
419 } /* }}} int mc_config */
420
421 static int mc_init (void) /* {{{ */
422 {
423     int status;
424
425     if (mc_have_connection)
426         return (0);
427
428     mongo_init (&mc_connection);
429
430     status = mongo_connect (&mc_connection,
431             (mc_host != NULL) ? mc_host : MC_MONGO_DEF_HOST,
432             (mc_port > 0) ? mc_port : MONGO_DEFAULT_PORT);
433     if (status != MONGO_OK)
434     {
435         ERROR ("mongo plugin: Connecting to %s:%i failed with status %i.",
436                 (mc_host != NULL) ? mc_host : MC_MONGO_DEF_HOST,
437                 (mc_port > 0) ? mc_port : MONGO_DEFAULT_PORT,
438                 (int) mc_connection.err);
439         return (-1);
440     }
441
442     mc_have_connection = 1;
443     return (0);
444 } /* }}} int mc_init */
445
446 static int mc_shutdown(void) /* {{{ */
447 {
448     if (mc_have_connection) {
449         mongo_disconnect (&mc_connection);
450         mongo_destroy (&mc_connection);
451         mc_have_connection = 0;
452     }
453
454     sfree (mc_user);
455     sfree (mc_password);
456     sfree (mc_db);
457     sfree (mc_host);
458
459     return (0);
460 } /* }}} int mc_shutdown */
461
462 void module_register(void)
463 {
464     plugin_register_config ("mongodb", mc_config,
465             config_keys, config_keys_num);
466     plugin_register_read ("mongodb", mc_read);
467     plugin_register_init ("mongodb", mc_init);
468     plugin_register_shutdown ("mongodb", mc_shutdown);
469 }
470
471 /* vim: set sw=4 sts=4 et fdm=marker : */