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>
26 #include <mongo/mongo.h>
31 #define MC_PLUGIN_NAME "mongo"
32 #define MC_MONGO_DEF_HOST "127.0.0.1"
33 #define MC_MONGO_DEF_PORT 27017
34 #define MC_MONGO_DEF_DB "admin"
36 #define MC_MAX_PORT 65535
40 static char *mc_user = NULL;
41 static char *mc_password = NULL;
42 static char *mc_db = NULL;
43 static char *mc_host = NULL;
44 static int mc_port = MC_MONGO_DEF_PORT;
46 static mongo_connection conn[1];
48 static const char *config_keys[] = {
56 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
58 static void submit(const char *type, const char *instance, value_t *values, size_t values_len) {
59 INFO("Submiting [%d] for type [%s]",values_len,type);
60 value_list_t v = VALUE_LIST_INIT;
63 v.values_len = values_len;
65 if( instance != NULL ) {
66 sstrncpy(v.type_instance,instance,sizeof(v.type_instance));
69 sstrncpy(v.host, hostname_g, sizeof(v.host));
70 sstrncpy(v.plugin, MC_PLUGIN_NAME, sizeof(v.plugin));
72 sprintf(port, "%d",mc_port);
73 sstrncpy(v.plugin_instance, port, sizeof(v.plugin_instance));
74 sstrncpy(v.type, type, sizeof(v.type));
76 plugin_dispatch_values(&v);
79 static void submit_counter(const char *type, const char *instance, int64_t counter ) {
81 v[0].counter = counter;
82 submit(type, instance, v, STATIC_ARRAY_SIZE (v));
85 static void submit_gauge(const char *type, const char *instance, gauge_t gauge ) {
88 submit(type, instance, v, STATIC_ARRAY_SIZE (v));
92 static void handle_opcounters(bson* obj) {
93 INFO("handle op counters");
95 if(bson_find(&it, obj, "opcounters")) {
97 bson_iterator_subobject(&it, &subobj);
99 bson_iterator_init(&it2, subobj.data);
107 while(bson_iterator_next(&it2)){
108 if(strcmp(bson_iterator_key(&it2),"insert") == 0) {
109 insert = bson_iterator_long(&it2);
111 if(strcmp(bson_iterator_key(&it2),"query") == 0) {
112 query = bson_iterator_long(&it2);
114 if(strcmp(bson_iterator_key(&it2),"delete") == 0) {
115 delete = bson_iterator_long(&it2);
117 if(strcmp(bson_iterator_key(&it2),"getmore") == 0) {
118 getmore = bson_iterator_long(&it2);
120 if(strcmp(bson_iterator_key(&it2),"command") == 0) {
121 command = bson_iterator_long(&it2);
125 bson_destroy(&subobj);
127 submit_gauge("mongo_counter", "indert", insert );
128 submit_gauge("mongo_counter", "query", query );
129 submit_gauge("mongo_counter", "delete", delete );
130 submit_gauge("mongo_counter", "getmore", getmore );
131 submit_gauge("mongo_counter", "command", command );
132 submit_gauge("mongo_counter", "insert", insert );
136 static void handle_mem(bson* obj) {
139 if(bson_find(&it, obj, "mem")) {
141 bson_iterator_subobject(&it, &subobj);
143 bson_iterator_init(&it2, subobj.data);
144 int64_t resident = 0;
147 while(bson_iterator_next(&it2)) {
148 if(strcmp(bson_iterator_key(&it2),"resident") == 0) {
149 resident = bson_iterator_long(&it2);
151 if(strcmp(bson_iterator_key(&it2),"virtual") == 0) {
152 virtual = bson_iterator_long(&it2);
154 if(strcmp(bson_iterator_key(&it2),"mapped") == 0) {
155 mapped = bson_iterator_long(&it2);
159 values[0].gauge = resident;
160 values[1].gauge = virtual;
161 values[2].gauge = mapped;
162 submit_gauge("memory", "resident", resident );
163 submit_gauge("memory", "virtual", virtual );
164 submit_gauge("memory", "mapped", mapped );
165 bson_destroy(&subobj);
169 static void handle_connections(bson* obj) {
170 INFO("handle connections");
172 if(bson_find(&it, obj, "connections")) {
174 bson_iterator_subobject(&it, &subobj);
176 bson_iterator_init(&it2, subobj.data);
177 while(bson_iterator_next(&it2)) {
178 if(strcmp(bson_iterator_key(&it2),"current") == 0) {
179 submit_gauge("connections", "connections", bson_iterator_int(&it2));
183 bson_destroy(&subobj);
187 static void handle_lock(bson* obj) {
190 if(bson_find(&it, obj, "globalLock")) {
192 bson_iterator_subobject(&it, &subobj);
194 bson_iterator_init(&it2, subobj.data);
195 while(bson_iterator_next(&it2)) {
196 if(strcmp(bson_iterator_key(&it2),"ratio") == 0) {
197 submit_gauge("percent", "lock_ratio", bson_iterator_double(&it2));
200 bson_destroy(&subobj);
204 static void handle_index_counters(bson* obj) {
205 INFO("handle index counters");
207 if(bson_find(&icit, obj, "indexCounters")) {
209 bson_iterator_subobject(&icit, &oic);
211 if(bson_find(&icit, &oic, "btree")) {
213 bson_iterator_subobject(&icit, &obt);
215 bson_iterator_init(&bit, oic.data);
218 while(bson_iterator_next(&bit)) {
219 if(strcmp(bson_iterator_key(&bit),"accesses") == 0) {
220 accesses = bson_iterator_int(&bit);
222 if(strcmp(bson_iterator_key(&bit),"misses") == 0) {
223 misses = bson_iterator_int(&bit);
229 double ratio = accesses/misses;
231 submit_gauge("cache_ratio", "cache_misses", ratio );
238 static void handle_stats_counts(bson* obj) {
240 INFO("handle stats counts");
242 bson_iterator_init(&it, obj->data);
243 int64_t collections = 0;
245 int64_t numExtents = 0;
248 while(bson_iterator_next(&it)) {
249 if(strcmp(bson_iterator_key(&it),"collections") == 0) {
250 collections = bson_iterator_long(&it);
252 if(strcmp(bson_iterator_key(&it),"objects") == 0) {
253 objects = bson_iterator_long(&it);
255 if(strcmp(bson_iterator_key(&it),"numExtents") == 0) {
256 numExtents = bson_iterator_long(&it);
258 if(strcmp(bson_iterator_key(&it),"indexes") == 0) {
259 indexes = bson_iterator_long(&it);
263 submit_gauge("counter","object_count",objects);
265 submit_gauge("counter", "collections",collections);
266 submit_gauge("counter", "num_extents",numExtents);
267 submit_gauge("counter", "indexes",indexes);
270 static void handle_stats_sizes(bson* obj) {
272 bson_iterator_init(&it, obj->data);
273 int64_t storageSize = 0;
274 int64_t dataSize = 0;
275 int64_t indexSize = 0;
276 while(bson_iterator_next(&it)) {
277 if(strcmp(bson_iterator_key(&it),"storageSize") == 0) {
278 storageSize = bson_iterator_long(&it);
280 if(strcmp(bson_iterator_key(&it),"dataSize") == 0) {
281 dataSize = bson_iterator_long(&it);
283 if(strcmp(bson_iterator_key(&it),"indexSize") == 0) {
284 indexSize = bson_iterator_long(&it);
289 submit_gauge("file_size", "storage",storageSize );
290 submit_gauge("file_size", "index",indexSize );
291 submit_gauge("file_size", "data",dataSize );
294 static int do_stats(void) {
299 change this to raw runCommand
300 db.runCommand( { dbstats : 1 } );
301 succeeds but is getting back all zeros !?!
302 modify bson_print to print type 18
303 repro problem w/o db name - show dbs doesn't work again
304 why does db.admin.dbstats() work fine in shell?
305 implement retries ? noticed that if db is unavailable, collectd dies
308 if( !mongo_simple_int_command(conn, mc_db, "dbstats", 1, &obj) ) {
309 ERROR("Mongo: failed to call stats Host [%s] Port [%d] User [%s]",
310 mc_host, mc_port, mc_user);
316 handle_stats_sizes(&obj);
317 handle_stats_counts(&obj);
326 static int do_server_status(void) {
329 if( !mongo_simple_int_command(conn, mc_db, "serverStatus", 1, &obj) ) {
330 ERROR("Mongo: failed to call serverStatus Host [%s] Port [%d] User [%s]",
331 mc_host, mc_port, mc_user);
337 handle_opcounters(&obj);
339 handle_connections(&obj);
340 handle_index_counters(&obj);
347 static int mc_read(void) {
348 DEBUG("Mongo: mongo driver read");
350 if(do_server_status() != SUCCESS) {
351 ERROR("Mongo: do server status failed");
355 if(do_stats() != SUCCESS) {
356 ERROR("Mongo: do stats status failed");
364 static void config_set(char** dest, const char* src ) {
368 *dest= malloc(strlen(src)+1);
369 sstrncpy(*dest,src,strlen(src)+1);
372 static int mc_config(const char *key, const char *value) {
374 DEBUG("Mongo: config key [%s] value [%s]", key, value);
375 if(strcasecmp("Host", key) == 0) {
376 config_set(&mc_host,value);
380 if(strcasecmp("Port", key) == 0) {
381 long l = strtol(value,NULL,10);
383 if((errno == ERANGE && (l == LONG_MAX || l == LONG_MIN))
384 || (errno != 0 && l == 0)) {
385 ERROR("Mongo: failed to parse Port value [%s]", value);
389 if( l < MC_MIN_PORT || l > MC_MAX_PORT ) {
390 ERROR("Mongo: failed Port value [%s] outside range [1..65535]", value);
398 if(strcasecmp("User", key) == 0) {
399 config_set(&mc_user,value);
403 if(strcasecmp("Password", key) == 0) {
404 config_set(&mc_password,value);
408 if(strcasecmp("Database", key) == 0) {
409 config_set(&mc_db,value);
416 static int mc_init(void) {
418 DEBUG("mongo driver initializing");
420 DEBUG("Mongo: Host not specified. Using default [%s]",MC_MONGO_DEF_HOST);
421 config_set(&mc_host, MC_MONGO_DEF_HOST);
425 DEBUG("Mongo: Database not specified. Using default [%s]",MC_MONGO_DEF_DB);
426 config_set(&mc_db, MC_MONGO_DEF_DB);
429 mongo_connection_options opts;
430 sstrncpy(opts.host, mc_host, sizeof(opts.host));
433 if(mongo_connect( conn , &opts )){
434 ERROR("Mongo: driver failed to connect. Host [%s] Port [%d] User [%s]",
435 mc_host, mc_port, mc_user);
442 static int mc_shutdown(void) {
443 DEBUG("Mongo: driver shutting down");
446 mongo_disconnect(conn);
467 void module_register(void) {
468 plugin_register_config(MC_PLUGIN_NAME, mc_config,config_keys,config_keys_num);
469 plugin_register_read(MC_PLUGIN_NAME, mc_read);
470 plugin_register_init(MC_PLUGIN_NAME, mc_init);
471 plugin_register_shutdown(MC_PLUGIN_NAME, mc_shutdown);
472 DEBUG("Mongo: driver registered");