2 * collectd - src/mongo.c
3 * Copyright (C) 2010 Ryan Cox
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; only version 2 of the License is applicable.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 * Ryan Cox <ryan.a.cox@gmail.com>
32 # define MONGO_HAVE_STDINT 1
34 # define MONGO_USE_LONG_LONG_INT 1
38 #define MC_PLUGIN_NAME "mongo"
39 #define MC_MONGO_DEF_HOST "127.0.0.1"
40 #define MC_MONGO_DEF_PORT 27017
41 #define MC_MONGO_DEF_DB "admin"
43 #define MC_MAX_PORT 65535
47 static char *mc_user = NULL;
48 static char *mc_password = NULL;
49 static char *mc_db = NULL;
50 static char *mc_host = NULL;
51 static int mc_port = MC_MONGO_DEF_PORT;
53 static mongo_connection mc_connection;
54 static _Bool mc_have_connection = 0;
56 static const char *config_keys[] = {
63 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
65 static void submit (const char *type, const char *instance,
66 value_t *values, size_t values_len)
68 value_list_t v = VALUE_LIST_INIT;
71 v.values_len = values_len;
73 sstrncpy (v.host, hostname_g, sizeof(v.host));
74 sstrncpy (v.plugin, MC_PLUGIN_NAME, sizeof(v.plugin));
75 ssnprintf (v.plugin_instance, sizeof (v.plugin_instance), "%i", mc_port);
76 sstrncpy (v.type, type, sizeof(v.type));
79 sstrncpy (v.type_instance, instance, sizeof (v.type_instance));
81 plugin_dispatch_values (&v);
84 static void submit_gauge (const char *type, const char *instance, /* {{{ */
90 submit(type, instance, &v, /* values_len = */ 1);
91 } /* }}} void submit_gauge */
93 static void submit_derive (const char *type, const char *instance, /* {{{ */
99 submit(type, instance, &v, /* values_len = */ 1);
100 } /* }}} void submit_derive */
102 static int handle_field (bson *obj, const char *field, /* {{{ */
103 int (*func) (bson_iterator *))
111 type = bson_find (&i, obj, field);
112 if (type != bson_object)
115 bson_iterator_subobject (&i, &subobj);
116 bson_iterator_init (&j, subobj.data);
117 while (bson_iterator_next (&j))
119 status = (*func) (&j);
123 bson_destroy (&subobj);
126 } /* }}} int handle_field */
128 static int handle_opcounters (bson_iterator *iter) /* {{{ */
134 type = bson_iterator_type (iter);
135 if ((type != bson_long) && (type != bson_int))
138 key = bson_iterator_key (iter);
142 value = (derive_t) bson_iterator_long (iter);
144 submit_derive ("total_operations", key, value);
146 } /* }}} int handle_opcounters */
148 static int handle_mem (bson_iterator *iter) /* {{{ */
154 type = bson_iterator_type (iter);
155 if ((type != bson_double) && (type != bson_long) && (type != bson_int))
158 key = bson_iterator_key (iter);
162 /* Is "virtual" really interesting?
163 * What exactly does "mapped" mean? */
164 if ((strcasecmp ("mapped", key) != 0)
165 && (strcasecmp ("resident", key) != 0)
166 && (strcasecmp ("virtual", key) != 0))
169 value = (gauge_t) bson_iterator_double (iter);
170 /* All values are in MByte */
173 submit_gauge ("memory", key, value);
175 } /* }}} int handle_mem */
177 static int handle_connections (bson_iterator *iter) /* {{{ */
183 type = bson_iterator_type (iter);
184 if ((type != bson_double) && (type != bson_long) && (type != bson_int))
187 key = bson_iterator_key (iter);
191 if (strcmp ("current", key) != 0)
194 value = (gauge_t) bson_iterator_double (iter);
196 submit_gauge ("current_connections", NULL, value);
198 } /* }}} int handle_connections */
200 static int handle_lock (bson_iterator *iter) /* {{{ */
206 type = bson_iterator_type (iter);
207 if ((type != bson_double) && (type != bson_long) && (type != bson_int))
210 key = bson_iterator_key (iter);
214 if (strcmp ("lockTime", key) != 0)
217 value = (derive_t) bson_iterator_long (iter);
218 /* The time is measured in microseconds (us). We convert it to
219 * milliseconds (ms). */
220 value = value / 1000;
222 submit_derive ("total_time_in_ms", "lock_held", value);
224 } /* }}} int handle_lock */
226 static int handle_btree (bson *obj) /* {{{ */
230 bson_iterator_init (&i, obj->data);
231 while (bson_iterator_next (&i))
237 type = bson_iterator_type (&i);
238 if ((type != bson_double) && (type != bson_long) && (type != bson_int))
241 key = bson_iterator_key (&i);
245 value = (gauge_t) bson_iterator_double (&i);
247 if (strcmp ("hits", key) == 0)
248 submit_gauge ("cache_result", "hit", value);
249 else if (strcmp ("misses", key) != 0)
250 submit_gauge ("cache_result", "miss", value);
254 } /* }}} int handle_btree */
256 static int handle_index_counters (bson_iterator *iter) /* {{{ */
263 type = bson_iterator_type (iter);
264 if (type != bson_object)
267 key = bson_iterator_key (iter);
271 if (strcmp ("btree", key) != 0)
274 bson_iterator_subobject (iter, &subobj);
275 status = handle_btree (&subobj);
276 bson_destroy (&subobj);
279 } /* }}} int handle_index_counters */
281 static int handle_dbstats (bson *obj)
285 bson_iterator_init (&i, obj->data);
286 while (bson_iterator_next (&i))
292 type = bson_iterator_type (&i);
293 if ((type != bson_double) && (type != bson_long) && (type != bson_int))
296 key = bson_iterator_key (&i);
300 value = (gauge_t) bson_iterator_double (&i);
303 if (strcmp ("collections", key) == 0)
304 submit_gauge ("gauge", "collections", value);
305 else if (strcmp ("objects", key) == 0)
306 submit_gauge ("gauge", "objects", value);
307 else if (strcmp ("numExtents", key) == 0)
308 submit_gauge ("gauge", "num_extents", value);
309 else if (strcmp ("indexes", key) == 0)
310 submit_gauge ("gauge", "indexes", value);
312 else if (strcmp ("dataSize", key) == 0)
313 submit_gauge ("bytes", "data", value);
314 else if (strcmp ("storageSize", key) == 0)
315 submit_gauge ("bytes", "storage", value);
316 else if (strcmp ("indexSize", key) == 0)
317 submit_gauge ("bytes", "index", value);
321 } /* }}} int handle_dbstats */
323 static int do_stats (void) /* {{{ */
329 change this to raw runCommand
330 db.runCommand( { dbstats : 1 } );
331 succeeds but is getting back all zeros !?!
332 modify bson_print to print type 18
333 repro problem w/o db name - show dbs doesn't work again
334 why does db.admin.dbstats() work fine in shell?
335 implement retries ? noticed that if db is unavailable, collectd dies
338 if( !mongo_simple_int_command(&mc_connection, mc_db, "dbstats", 1, &obj) ) {
339 ERROR("Mongo: failed to call stats Host [%s] Port [%d] User [%s]",
340 mc_host, mc_port, mc_user);
344 handle_dbstats (&obj);
349 } /* }}} int do_stats */
351 static int do_server_status (void) /* {{{ */
356 status = mongo_simple_int_command (&mc_connection, /* db = */ mc_db,
357 /* command = */ "serverStatus", /* arg = */ 1, /* out = */ &obj);
360 ERROR("mongodb plugin: mongo_simple_int_command (%s:%i, serverStatus) "
361 "failed.", mc_host, mc_port);
365 handle_field (&obj, "opcounters", handle_opcounters);
366 handle_field (&obj, "mem", handle_mem);
367 handle_field (&obj, "connections", handle_connections);
368 handle_field (&obj, "globalLock", handle_lock);
369 handle_field (&obj, "indexCounters", handle_index_counters);
373 } /* }}} int do_server_status */
375 static int mc_read(void) {
376 DEBUG("Mongo: mongo driver read");
378 if(do_server_status() != SUCCESS) {
379 ERROR("Mongo: do server status failed");
383 if(do_stats() != SUCCESS) {
384 ERROR("Mongo: do stats status failed");
392 static void config_set(char** dest, const char* src ) {
396 *dest= malloc(strlen(src)+1);
397 sstrncpy(*dest,src,strlen(src)+1);
400 static int mc_config(const char *key, const char *value)
402 DEBUG("Mongo: config key [%s] value [%s]", key, value);
404 if(strcasecmp("Host", key) == 0) {
405 config_set(&mc_host,value);
407 else if(strcasecmp("Port", key) == 0)
411 tmp = service_name_to_port_number (value);
416 ERROR("mongodb plugin: failed to parse Port value: %s", value);
420 else if(strcasecmp("User", key) == 0) {
421 config_set(&mc_user,value);
423 else if(strcasecmp("Password", key) == 0) {
424 config_set(&mc_password,value);
426 else if(strcasecmp("Database", key) == 0) {
427 config_set(&mc_db,value);
431 ERROR ("mongodb plugin: Unknown config option: %s", key);
438 static int mc_init(void)
440 if (mc_have_connection)
443 DEBUG("mongo driver initializing");
445 DEBUG("Mongo: Host not specified. Using default [%s]",MC_MONGO_DEF_HOST);
446 config_set(&mc_host, MC_MONGO_DEF_HOST);
450 DEBUG("Mongo: Database not specified. Using default [%s]",MC_MONGO_DEF_DB);
451 config_set(&mc_db, MC_MONGO_DEF_DB);
454 mongo_connection_options opts;
455 sstrncpy(opts.host, mc_host, sizeof(opts.host));
458 if(mongo_connect(&mc_connection, &opts )){
459 ERROR("Mongo: driver failed to connect. Host [%s] Port [%d] User [%s]",
460 mc_host, mc_port, mc_user);
464 mc_have_connection = 1;
468 static int mc_shutdown(void)
470 DEBUG("Mongo: driver shutting down");
472 if (mc_have_connection) {
473 mongo_disconnect (&mc_connection);
474 mongo_destroy (&mc_connection);
475 mc_have_connection = 0;
487 void module_register(void) {
488 plugin_register_config (MC_PLUGIN_NAME, mc_config,
489 config_keys, config_keys_num);
490 plugin_register_read (MC_PLUGIN_NAME, mc_read);
491 plugin_register_init (MC_PLUGIN_NAME, mc_init);
492 plugin_register_shutdown (MC_PLUGIN_NAME, mc_shutdown);
495 /* vim: set sw=4 sts=4 et fdm=marker : */