X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fredis.c;h=36f57b0972749117893661e482561feecd7e9735;hb=9ea20c99eadea54a7cb4efb51e2f6e06e4187915;hp=2a355045e6281d02a886152de29c5d299f15fd1b;hpb=215bd828c6943381d2332284a8950c76138e50da;p=collectd.git diff --git a/src/redis.c b/src/redis.c index 2a355045..36f57b09 100644 --- a/src/redis.c +++ b/src/redis.c @@ -67,6 +67,8 @@ struct redis_node_s { char *passwd; int port; struct timeval timeout; + bool report_command_stats; + bool report_cpu_usage; redisContext *redisContext; redis_query_t *queries; @@ -176,6 +178,7 @@ static int redis_config_node(oconfig_item_t *ci) /* {{{ */ rn->port = REDIS_DEF_PORT; rn->timeout.tv_sec = REDIS_DEF_TIMEOUT_SEC; + rn->report_cpu_usage = true; rn->host = strdup(REDIS_DEF_HOST); if (rn->host == NULL) { @@ -220,6 +223,10 @@ static int redis_config_node(oconfig_item_t *ci) /* {{{ */ } } else if (strcasecmp("Password", option->key) == 0) status = cf_util_get_string(option, &rn->passwd); + else if (strcasecmp("ReportCommandStats", option->key) == 0) + status = cf_util_get_boolean(option, &rn->report_command_stats); + else if (strcasecmp("ReportCpuUsage", option->key) == 0) + status = cf_util_get_boolean(option, &rn->report_cpu_usage); else WARNING("redis plugin: Option `%s' not allowed inside a `Node' " "block. I'll ignore this option.", @@ -254,8 +261,8 @@ static int redis_config(oconfig_item_t *ci) /* {{{ */ } /* }}} */ __attribute__((nonnull(2))) static void -redis_submit(char *plugin_instance, const char *type, const char *type_instance, - value_t value) /* {{{ */ +redis_submit(const char *plugin_instance, const char *type, + const char *type_instance, value_t value) /* {{{ */ { value_list_t vl = VALUE_LIST_INIT; @@ -272,7 +279,7 @@ redis_submit(char *plugin_instance, const char *type, const char *type_instance, } /* }}} */ __attribute__((nonnull(2))) static void -redis_submit2(char *plugin_instance, const char *type, +redis_submit2(const char *plugin_instance, const char *type, const char *type_instance, value_t value0, value_t value1) /* {{{ */ { @@ -309,8 +316,12 @@ static int redis_init(void) /* {{{ */ rn->name = strdup("default"); rn->host = strdup(REDIS_DEF_HOST); - if (rn->name == NULL || rn->host == NULL) + if (rn->name == NULL || rn->host == NULL) { + sfree(rn->name); + sfree(rn->host); + sfree(rn); return ENOMEM; + } return redis_node_add(rn); } /* }}} int redis_init */ @@ -447,7 +458,7 @@ static int redis_handle_query(redis_node_t *rn, redis_query_t *rq) /* {{{ */ return 0; } /* }}} int redis_handle_query */ -static int redis_db_stats(char *node, char const *info_line) /* {{{ */ +static int redis_db_stats(const char *node, char const *info_line) /* {{{ */ { /* redis_db_stats parses and dispatches Redis database statistics, * currently the number of keys for each database. @@ -486,6 +497,44 @@ static int redis_db_stats(char *node, char const *info_line) /* {{{ */ } /* }}} int redis_db_stats */ +static void redis_cpu_usage(const char *node, char const *info_line) { + while (42) { + value_t rusage_user; + value_t rusage_syst; + + if (redis_get_info_value(info_line, "used_cpu_user", DS_TYPE_GAUGE, + &rusage_user) != 0) + break; + + if (redis_get_info_value(info_line, "used_cpu_sys", DS_TYPE_GAUGE, + &rusage_syst) != 0) + break; + + redis_submit2(node, "ps_cputime", "daemon", + (value_t){.derive = rusage_user.gauge * 1000000}, + (value_t){.derive = rusage_syst.gauge * 1000000}); + break; + } + + while (42) { + value_t rusage_user; + value_t rusage_syst; + + if (redis_get_info_value(info_line, "used_cpu_user_children", DS_TYPE_GAUGE, + &rusage_user) != 0) + break; + + if (redis_get_info_value(info_line, "used_cpu_sys_children", DS_TYPE_GAUGE, + &rusage_syst) != 0) + break; + + redis_submit2(node, "ps_cputime", "children", + (value_t){.derive = rusage_user.gauge * 1000000}, + (value_t){.derive = rusage_syst.gauge * 1000000}); + break; + } +} /* void redis_cpu_usage */ + static void redis_check_connection(redis_node_t *rn) { if (rn->redisContext) return; @@ -575,46 +624,89 @@ static void redis_read_server_info(redis_node_t *rn) { redis_handle_info(rn->name, rr->str, "total_bytes", "output", "total_net_output_bytes", DS_TYPE_DERIVE); - while (42) { - value_t rusage_user; - value_t rusage_syst; + redis_db_stats(rn->name, rr->str); - if (redis_get_info_value(rr->str, "used_cpu_user", DS_TYPE_GAUGE, - &rusage_user) != 0) - break; + if (rn->report_cpu_usage) + redis_cpu_usage(rn->name, rr->str); - if (redis_get_info_value(rr->str, "used_cpu_sys", DS_TYPE_GAUGE, - &rusage_syst) != 0) - break; + freeReplyObject(rr); +} /* void redis_read_server_info */ - redis_submit2(rn->name, "ps_cputime", "daemon", - (value_t){.derive = rusage_user.gauge * 1000000}, - (value_t){.derive = rusage_syst.gauge * 1000000}); - break; +static void redis_read_command_stats(redis_node_t *rn) { + redisReply *rr; + + if ((rr = c_redisCommand(rn, "INFO commandstats")) == NULL) { + WARNING("redis plugin: node `%s': unable to get `INFO commandstats'.", + rn->name); + return; } - while (42) { - value_t rusage_user; - value_t rusage_syst; + if (rr->type != REDIS_REPLY_STRING) { + WARNING("redis plugin: node `%s' `INFO commandstats' returned unsupported " + "redis type %i.", + rn->name, rr->type); + freeReplyObject(rr); + return; + } - if (redis_get_info_value(rr->str, "used_cpu_user_children", DS_TYPE_GAUGE, - &rusage_user) != 0) - break; + char *command; + char *line; + char *ptr = rr->str; + char *saveptr = NULL; + while ((line = strtok_r(ptr, "\n\r", &saveptr)) != NULL) { + ptr = NULL; - if (redis_get_info_value(rr->str, "used_cpu_sys_children", DS_TYPE_GAUGE, - &rusage_syst) != 0) - break; + if (line[0] == '#') + continue; - redis_submit2(rn->name, "ps_cputime", "children", - (value_t){.derive = rusage_user.gauge * 1000000}, - (value_t){.derive = rusage_syst.gauge * 1000000}); - break; - } + /* command name */ + if (strstr(line, "cmdstat_") != line) { + ERROR("redis plugin: not found 'cmdstat_' prefix in line '%s'", line); + continue; + } - redis_db_stats(rn->name, rr->str); + char *values = strstr(line, ":"); + if (values == NULL) { + ERROR("redis plugin: not found ':' separator in line '%s'", line); + continue; + } + /* Null-terminate command token */ + values[0] = '\0'; + command = line + strlen("cmdstat_"); + values++; + + /* parse values */ + /* cmdstat_publish:calls=20795774,usec=111039258,usec_per_call=5.34 */ + char *field; + char *saveptr_field = NULL; + while ((field = strtok_r(values, "=", &saveptr_field)) != NULL) { + values = NULL; + + const char *type; + /* only these are supported */ + if (strcmp(field, "calls") == 0) + type = "commands"; + else if (strcmp(field, "usec") == 0) + type = "redis_command_cputime"; + else + continue; + + if ((field = strtok_r(NULL, ",", &saveptr_field)) == NULL) + continue; + + char *endptr = NULL; + errno = 0; + derive_t value = strtoll(field, &endptr, 0); + + if ((endptr == field) || (errno != 0)) + continue; + + redis_submit(rn->name, type, command, (value_t){.derive = value}); + } + } freeReplyObject(rr); -} /* void redis_read_server_info */ +} /* void redis_read_command_stats */ static int redis_read(user_data_t *user_data) /* {{{ */ { @@ -633,6 +725,13 @@ static int redis_read(user_data_t *user_data) /* {{{ */ if (!rn->redisContext) /* connection lost */ return -1; + if (rn->report_command_stats) { + redis_read_command_stats(rn); + + if (!rn->redisContext) /* connection lost */ + return -1; + } + for (redis_query_t *rq = rn->queries; rq != NULL; rq = rq->next) { redis_handle_query(rn, rq); if (!rn->redisContext) /* connection lost */