From: Aki Tuomi Date: Sun, 6 Nov 2016 00:50:52 +0000 (+0200) Subject: stats: Add support for carbon-server X-Git-Tag: 2.2.27~170 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=03cb9435d73d12638b43c57c5b782a92c40c7f36;p=thirdparty%2Fdovecot%2Fcore.git stats: Add support for carbon-server Allows admin to specify stats_carbon_server=ip:port. Stats are delivered in "key value" format to specified port. Can be received with e.g. metronome. --- diff --git a/src/stats/Makefile.am b/src/stats/Makefile.am index ec5ae7482d..7d9e68ecfd 100644 --- a/src/stats/Makefile.am +++ b/src/stats/Makefile.am @@ -26,6 +26,7 @@ stats_SOURCES = \ mail-stats.c \ mail-user.c \ main.c \ + stats-carbon.c \ stats-settings.c noinst_HEADERS = \ @@ -40,4 +41,5 @@ noinst_HEADERS = \ mail-session.h \ mail-stats.h \ mail-user.h \ + stats-carbon.h \ stats-settings.h diff --git a/src/stats/mail-stats.c b/src/stats/mail-stats.c index e297082950..0d5d6a4cd4 100644 --- a/src/stats/mail-stats.c +++ b/src/stats/mail-stats.c @@ -2,19 +2,67 @@ #include "lib.h" #include "ioloop.h" -#include "time-util.h" #include "mail-stats.h" +#include "stats-carbon.h" +#include "stats-settings.h" +#include "str.h" struct mail_global mail_global_stats; +static void +mail_global_stats_sent(void *ctx) +{ + struct mail_global *stats = ctx; + stats_carbon_destroy(&stats->stats_send_ctx); +} + +static void +mail_global_stats_send(void *u0 ATTR_UNUSED) +{ + unsigned long ts = (unsigned long)ioloop_time; + if (*stats_settings->carbon_name != '\0' && + *stats_settings->carbon_server != '\0') { + string_t *str = t_str_new(256); + const char *prefix = t_strdup_printf("dovecot.%s.global", + stats_settings->carbon_name); + str_printfa(str, "%s.logins %u %lu\r\n", prefix, + mail_global_stats.num_logins, ts); + str_printfa(str, "%s.cmds %u %lu\r\n", prefix, + mail_global_stats.num_cmds, ts); + str_printfa(str, "%s.connected_sessions %u %lu\r\n", prefix, + mail_global_stats.num_connected_sessions, + ts); + str_printfa(str, "%s.last_reset %lu %lu\r\n", prefix, + mail_global_stats.reset_timestamp, ts); + /* then export rest of the stats */ + for(size_t i = 0; i < stats_field_count(); i++) { + str_printfa(str, "%s.%s ", prefix, + stats_field_name(i)); + stats_field_value(str, mail_global_stats.stats, i); + str_printfa(str, " %lu\r\n", ts); + } + + /* and send them along */ + (void)stats_carbon_send(stats_settings->carbon_server, str_c(str), + mail_global_stats_sent, &mail_global_stats, + &mail_global_stats.stats_send_ctx); + } +} + void mail_global_init(void) { mail_global_stats.reset_timestamp = ioloop_time; mail_global_stats.stats = stats_alloc(default_pool); + mail_global_stats.to_stats_send = timeout_add(stats_settings->carbon_interval*1000, + mail_global_stats_send, + NULL); } void mail_global_deinit(void) { + if (mail_global_stats.stats_send_ctx != NULL) + stats_carbon_destroy(&mail_global_stats.stats_send_ctx); + timeout_remove(&mail_global_stats.to_stats_send); i_free(mail_global_stats.stats); } diff --git a/src/stats/mail-stats.h b/src/stats/mail-stats.h index 045dd64f33..efd08244e7 100644 --- a/src/stats/mail-stats.h +++ b/src/stats/mail-stats.h @@ -7,6 +7,8 @@ #include "guid.h" #include "stats.h" +struct stats_send_ctx; + struct mail_command { struct mail_command *stable_prev, *stable_next; struct mail_command *session_prev, *session_next; @@ -104,6 +106,9 @@ struct mail_global { unsigned int num_logins; unsigned int num_cmds; unsigned int num_connected_sessions; + + struct timeout *to_stats_send; + struct stats_send_ctx *stats_send_ctx; }; extern struct mail_global mail_global_stats; diff --git a/src/stats/stats-carbon.c b/src/stats/stats-carbon.c new file mode 100644 index 0000000000..9dadfbcb99 --- /dev/null +++ b/src/stats/stats-carbon.c @@ -0,0 +1,128 @@ +/* Copyright (c) 2011-2016 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "time-util.h" +#include "stats-settings.h" +#include "mail-stats.h" +#include "istream.h" +#include "ostream.h" +#include "net.h" +#include "str.h" +#include "write-full.h" +#include "stats-carbon.h" + +#define CARBON_SERVER_DEFAULT_PORT 2003 + +struct stats_send_ctx { + pool_t pool; + int fd; + unsigned long to_msecs; + const char *endpoint; + const char *str; + struct io *io; + struct timeout *to; + + void (*callback)(void *); + void *ctx; +}; + +void +stats_carbon_destroy(struct stats_send_ctx **_ctx) +{ + struct stats_send_ctx *ctx = *_ctx; + *_ctx = NULL; + + if (ctx->io != NULL) + io_remove(&ctx->io); + if (ctx->to != NULL) + timeout_remove(&ctx->to); + if (ctx->fd != -1) + i_close_fd(&ctx->fd); + pool_unref(&ctx->pool); +} + +static void +stats_carbon_callback(struct stats_send_ctx *ctx) +{ + i_assert(ctx->callback != NULL); + void (*callback)(void *) = ctx->callback; + ctx->callback = NULL; + callback(ctx->ctx); +} + +static void +stats_carbon_timeout(struct stats_send_ctx *ctx) +{ + i_error("Stats submit(%s) failed: endpoint timeout after %lu msecs", + ctx->endpoint, ctx->to_msecs); + stats_carbon_callback(ctx); +} + +static void +stats_carbon_connected(struct stats_send_ctx *ctx) +{ + io_remove(&ctx->io); + if ((errno = net_geterror(ctx->fd)) != 0) { + i_error("connect(%s) failed: %m", + ctx->endpoint); + stats_carbon_callback(ctx); + return; + } + if (write_full(ctx->fd, ctx->str, strlen(ctx->str)) < 0) + i_error("write(%s) failed: %m", + ctx->endpoint); + stats_carbon_callback(ctx); +} + +int +stats_carbon_send(const char *endpoint, const char *data, + void (*callback)(void *), void *cb_ctx, + struct stats_send_ctx **ctx_r) +{ + const char *host; + in_port_t port; + struct ip_addr ip; + + if (net_str2hostport(endpoint, CARBON_SERVER_DEFAULT_PORT, + &host, &port) < 0 || + net_addr2ip(host, &ip) < 0) { + i_error("stats_submit: Cannot parse endpoint '%s'", + endpoint); + return -1; + } + + pool_t pool = pool_alloconly_create("stats carbon send", 1024); + struct stats_send_ctx *ctx = p_new(pool, + struct stats_send_ctx, 1); + ctx->pool = pool; + ctx->str = p_strdup(ctx->pool, data); + + ctx->fd = net_connect_ip(&ip, port, NULL); + if (ctx->fd < 0) { + i_error("connect(%s) failed: %m", endpoint); + stats_carbon_callback(ctx); + return -1; + } + ctx->io = io_add(ctx->fd, IO_WRITE, + stats_carbon_connected, + ctx); + + /* give time for almost until next update + this is to ensure we leave a little pause between + attempts. Multiplier 800 gives us 20% window, and + ensures the number stays positive. */ + ctx->to_msecs = stats_settings->carbon_interval*800; + ctx->to = timeout_add(ctx->to_msecs, + stats_carbon_timeout, + ctx); + if (net_ipport2str(&ip, port, &host) < 0) + i_unreached(); + ctx->endpoint = p_strdup(ctx->pool, host); + ctx->callback = callback; + ctx->ctx = cb_ctx; + + *ctx_r = ctx; + + return 0; +} diff --git a/src/stats/stats-carbon.h b/src/stats/stats-carbon.h new file mode 100644 index 0000000000..07859761d3 --- /dev/null +++ b/src/stats/stats-carbon.h @@ -0,0 +1,13 @@ +#ifndef STATS_CARBON +#define STATS_CARBON 1 + +struct stats_send_ctx; + +int +stats_carbon_send(const char *endpoint, const char *data, + void (*callback)(void *), void *cb_ctx, + struct stats_send_ctx **ctx_r); +void +stats_carbon_destroy(struct stats_send_ctx **ctx); + +#endif diff --git a/src/stats/stats-settings.c b/src/stats/stats-settings.c index b274300a51..347a4827d5 100644 --- a/src/stats/stats-settings.c +++ b/src/stats/stats-settings.c @@ -71,7 +71,9 @@ static const struct setting_define stats_setting_defines[] = { DEF(SET_TIME, user_min_time), DEF(SET_TIME, domain_min_time), DEF(SET_TIME, ip_min_time), - + DEF(SET_STR, carbon_server), + DEF(SET_TIME, carbon_interval), + DEF(SET_STR, carbon_name), SETTING_DEFINE_LIST_END }; @@ -82,7 +84,11 @@ const struct stats_settings stats_default_settings = { .session_min_time = 60*15, .user_min_time = 60*60, .domain_min_time = 60*60*12, - .ip_min_time = 60*60*12 + .ip_min_time = 60*60*12, + + .carbon_interval = 30, + .carbon_server = "", + .carbon_name = "" }; const struct setting_parser_info stats_setting_parser_info = { diff --git a/src/stats/stats-settings.h b/src/stats/stats-settings.h index b8b3e6ee18..f762b63a02 100644 --- a/src/stats/stats-settings.h +++ b/src/stats/stats-settings.h @@ -9,6 +9,10 @@ struct stats_settings { unsigned int user_min_time; unsigned int domain_min_time; unsigned int ip_min_time; + + unsigned int carbon_interval; + const char *carbon_server; + const char *carbon_name; }; extern const struct setting_parser_info stats_setting_parser_info;