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_opcounters (bson *obj) /* {{{ */
109 type = bson_find (&i, obj, "opcounters");
110 if ((type != bson_object) && (type != bson_array))
113 bson_iterator_subobject (&i, &subobj);
115 bson_iterator_init (&j, subobj.data);
116 while (bson_iterator_next (&j))
121 type = bson_iterator_type (&j);
122 if ((type != bson_long) && (type != bson_int))
125 key = bson_iterator_key (&j);
129 value = (derive_t) bson_iterator_long (&j);
131 submit_derive ("total_operations", key, value);
133 bson_destroy (&subobj);
136 } /* }}} int handle_opcounters */
138 static void handle_mem(bson* obj) {
141 if(bson_find(&it, obj, "mem")) {
143 bson_iterator_subobject(&it, &subobj);
145 bson_iterator_init(&it2, subobj.data);
146 int64_t resident = 0;
149 while(bson_iterator_next(&it2)) {
150 if(strcmp(bson_iterator_key(&it2),"resident") == 0) {
151 resident = bson_iterator_long(&it2);
153 if(strcmp(bson_iterator_key(&it2),"virtual") == 0) {
154 virtual = bson_iterator_long(&it2);
156 if(strcmp(bson_iterator_key(&it2),"mapped") == 0) {
157 mapped = bson_iterator_long(&it2);
161 values[0].gauge = resident;
162 values[1].gauge = virtual;
163 values[2].gauge = mapped;
164 submit_gauge("memory", "resident", resident );
165 submit_gauge("memory", "virtual", virtual );
166 submit_gauge("memory", "mapped", mapped );
167 bson_destroy(&subobj);
171 static void handle_connections(bson* obj) {
172 INFO("handle connections");
174 if(bson_find(&it, obj, "connections")) {
176 bson_iterator_subobject(&it, &subobj);
178 bson_iterator_init(&it2, subobj.data);
179 while(bson_iterator_next(&it2)) {
180 if(strcmp(bson_iterator_key(&it2),"current") == 0) {
181 submit_gauge("connections", "connections", bson_iterator_int(&it2));
185 bson_destroy(&subobj);
189 static void handle_lock(bson* obj) {
192 if(bson_find(&it, obj, "globalLock")) {
194 bson_iterator_subobject(&it, &subobj);
196 bson_iterator_init(&it2, subobj.data);
197 while(bson_iterator_next(&it2)) {
198 if(strcmp(bson_iterator_key(&it2),"ratio") == 0) {
199 submit_gauge("percent", "lock_ratio", bson_iterator_double(&it2));
202 bson_destroy(&subobj);
206 static void handle_index_counters(bson* obj) {
207 INFO("handle index counters");
209 if(bson_find(&icit, obj, "indexCounters")) {
211 bson_iterator_subobject(&icit, &oic);
212 if(bson_find(&icit, &oic, "btree")) {
214 bson_iterator_subobject(&icit, &obt);
216 bson_iterator_init(&bit, oic.data);
221 while(bson_iterator_next(&bit)) {
222 if(strcmp(bson_iterator_key(&bit),"accesses") == 0) {
223 accesses = bson_iterator_int(&bit);
225 if(strcmp(bson_iterator_key(&bit),"misses") == 0) {
226 misses = bson_iterator_int(&bit);
231 if (misses <= accesses)
232 ratio = ((double) misses) / ((double) accesses);
233 submit_gauge("cache_ratio", "cache_misses", ratio );
241 static void handle_stats_counts(bson* obj) {
243 INFO("handle stats counts");
245 bson_iterator_init(&it, obj->data);
246 int64_t collections = 0;
248 int64_t numExtents = 0;
251 while(bson_iterator_next(&it)) {
252 if(strcmp(bson_iterator_key(&it),"collections") == 0) {
253 collections = bson_iterator_long(&it);
255 if(strcmp(bson_iterator_key(&it),"objects") == 0) {
256 objects = bson_iterator_long(&it);
258 if(strcmp(bson_iterator_key(&it),"numExtents") == 0) {
259 numExtents = bson_iterator_long(&it);
261 if(strcmp(bson_iterator_key(&it),"indexes") == 0) {
262 indexes = bson_iterator_long(&it);
266 submit_gauge("counter","object_count",objects);
268 submit_gauge("counter", "collections",collections);
269 submit_gauge("counter", "num_extents",numExtents);
270 submit_gauge("counter", "indexes",indexes);
273 static void handle_stats_sizes(bson* obj) {
275 bson_iterator_init(&it, obj->data);
276 int64_t storageSize = 0;
277 int64_t dataSize = 0;
278 int64_t indexSize = 0;
279 while(bson_iterator_next(&it)) {
280 if(strcmp(bson_iterator_key(&it),"storageSize") == 0) {
281 storageSize = bson_iterator_long(&it);
283 if(strcmp(bson_iterator_key(&it),"dataSize") == 0) {
284 dataSize = bson_iterator_long(&it);
286 if(strcmp(bson_iterator_key(&it),"indexSize") == 0) {
287 indexSize = bson_iterator_long(&it);
291 submit_gauge ("file_size", "storage", storageSize);
292 submit_gauge ("file_size", "index", indexSize);
293 submit_gauge ("file_size", "data", dataSize);
296 static int do_stats(void) {
301 change this to raw runCommand
302 db.runCommand( { dbstats : 1 } );
303 succeeds but is getting back all zeros !?!
304 modify bson_print to print type 18
305 repro problem w/o db name - show dbs doesn't work again
306 why does db.admin.dbstats() work fine in shell?
307 implement retries ? noticed that if db is unavailable, collectd dies
310 if( !mongo_simple_int_command(&mc_connection, mc_db, "dbstats", 1, &obj) ) {
311 ERROR("Mongo: failed to call stats Host [%s] Port [%d] User [%s]",
312 mc_host, mc_port, mc_user);
318 handle_stats_sizes(&obj);
319 handle_stats_counts(&obj);
328 static int do_server_status(void) {
331 if( !mongo_simple_int_command(&mc_connection, mc_db, "serverStatus", 1, &obj) ) {
332 ERROR("Mongo: failed to call serverStatus Host [%s] Port [%d] User [%s]",
333 mc_host, mc_port, mc_user);
339 handle_opcounters(&obj);
341 handle_connections(&obj);
342 handle_index_counters(&obj);
349 static int mc_read(void) {
350 DEBUG("Mongo: mongo driver read");
352 if(do_server_status() != SUCCESS) {
353 ERROR("Mongo: do server status failed");
357 if(do_stats() != SUCCESS) {
358 ERROR("Mongo: do stats status failed");
366 static void config_set(char** dest, const char* src ) {
370 *dest= malloc(strlen(src)+1);
371 sstrncpy(*dest,src,strlen(src)+1);
374 static int mc_config(const char *key, const char *value)
376 DEBUG("Mongo: config key [%s] value [%s]", key, value);
378 if(strcasecmp("Host", key) == 0) {
379 config_set(&mc_host,value);
381 else if(strcasecmp("Port", key) == 0)
385 tmp = service_name_to_port_number (value);
390 ERROR("mongodb plugin: failed to parse Port value: %s", value);
394 else if(strcasecmp("User", key) == 0) {
395 config_set(&mc_user,value);
397 else if(strcasecmp("Password", key) == 0) {
398 config_set(&mc_password,value);
400 else if(strcasecmp("Database", key) == 0) {
401 config_set(&mc_db,value);
405 ERROR ("mongodb plugin: Unknown config option: %s", key);
412 static int mc_init(void)
414 if (mc_have_connection)
417 DEBUG("mongo driver initializing");
419 DEBUG("Mongo: Host not specified. Using default [%s]",MC_MONGO_DEF_HOST);
420 config_set(&mc_host, MC_MONGO_DEF_HOST);
424 DEBUG("Mongo: Database not specified. Using default [%s]",MC_MONGO_DEF_DB);
425 config_set(&mc_db, MC_MONGO_DEF_DB);
428 mongo_connection_options opts;
429 sstrncpy(opts.host, mc_host, sizeof(opts.host));
432 if(mongo_connect(&mc_connection, &opts )){
433 ERROR("Mongo: driver failed to connect. Host [%s] Port [%d] User [%s]",
434 mc_host, mc_port, mc_user);
438 mc_have_connection = 1;
442 static int mc_shutdown(void)
444 DEBUG("Mongo: driver shutting down");
446 if (mc_have_connection) {
447 mongo_disconnect (&mc_connection);
448 mongo_destroy (&mc_connection);
449 mc_have_connection = 0;
461 void module_register(void) {
462 plugin_register_config (MC_PLUGIN_NAME, mc_config,
463 config_keys, config_keys_num);
464 plugin_register_read (MC_PLUGIN_NAME, mc_read);
465 plugin_register_init (MC_PLUGIN_NAME, mc_init);
466 plugin_register_shutdown (MC_PLUGIN_NAME, mc_shutdown);
469 /* vim: set sw=4 sts=4 et fdm=marker : */