diff --git a/plugins/stats_pusher_file/plugin.c b/plugins/stats_pusher_file/plugin.c new file mode 100644 index 00000000..34207687 --- /dev/null +++ b/plugins/stats_pusher_file/plugin.c @@ -0,0 +1,57 @@ +#include + +struct uwsgi_stats_pusher_file_conf { + char *path; + char *freq; + char *separator; +}; + +static void stats_pusher_file(struct uwsgi_stats_pusher_instance *uspi, time_t now, char *json, size_t json_len) { + struct uwsgi_stats_pusher_file_conf *uspic = (struct uwsgi_stats_pusher_file_conf *) uspi->data; + if (!uspi->configured) { + uspic = uwsgi_calloc(sizeof(struct uwsgi_stats_pusher_file_conf)); + if (uspi->arg) { + if (uwsgi_kvlist_parse(uspi->arg, strlen(uspi->arg), ',', '=', "path", &uspic->path, "separator", &uspic->separator, "freq", &uspic->freq, NULL)) { + free(uspi); + return; + } + } + if (!uspic->path) + uspic->path = "uwsgi.stats"; + if (!uspic->separator) + uspic->separator = "\n\n"; + if (uspic->freq) + uspi->freq = atoi(uspic->freq); + uspi->configured = 1; + uspi->data = uspic; + } + + int fd = open(uspic->path, O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP); + if (fd < 0) { + uwsgi_error_open(uspic->path); + return; + } + ssize_t rlen = write(fd, json, json_len); + if (rlen != (ssize_t) json_len) { + uwsgi_error("uwsgi_stats_pusher_file() -> write()\n"); + } + + rlen = write(fd, uspic->separator, strlen(uspic->separator)); + if (rlen != (ssize_t) strlen(uspic->separator)) { + uwsgi_error("uwsgi_stats_pusher_file() -> write()\n"); + } + + close(fd); +} + + +static void stats_pusher_file_init(void) { + uwsgi_register_stats_pusher("file", stats_pusher_file); +} + +struct uwsgi_plugin stats_pusher_file_plugin = { + + .name = "stats_pusher_file", + .on_load = stats_pusher_file_init, +}; + diff --git a/plugins/stats_pusher_file/uwsgiplugin.py b/plugins/stats_pusher_file/uwsgiplugin.py new file mode 100644 index 00000000..643f13a1 --- /dev/null +++ b/plugins/stats_pusher_file/uwsgiplugin.py @@ -0,0 +1,7 @@ +NAME='stats_pusher_file' + +CFLAGS = [] +LDFLAGS = [] +LIBS = [] + +GCC_LIST = ['plugin'] diff --git a/plugins/stats_pusher_socket/plugin.c b/plugins/stats_pusher_socket/plugin.c new file mode 100644 index 00000000..5c947d98 --- /dev/null +++ b/plugins/stats_pusher_socket/plugin.c @@ -0,0 +1,111 @@ +#include + +/* + +this is a stats pusher plugin for sendign metrics over a udp socket + +--stats-push socket:address[,prefix] + +example: + +--stats-push socket:127.0.0.1:8125,myinstance + +it exports values exposed by the metric subsystem + +(it is based on the statsd plugin) + +*/ + +extern struct uwsgi_server uwsgi; + +// configuration of a socket node +struct socket_node { + int fd; + union uwsgi_sockaddr addr; + socklen_t addr_len; + char *prefix; + uint16_t prefix_len; +}; + +static int socket_send_metric(struct uwsgi_buffer *ub, struct uwsgi_stats_pusher_instance *uspi, struct uwsgi_metric *um) { + struct socket_node *sn = (struct socket_node *) uspi->data; + // reset the buffer + ub->pos = 0; + if (uwsgi_buffer_append(ub, sn->prefix, sn->prefix_len)) return -1; + if (uwsgi_buffer_append(ub, ".", 1)) return -1; + if (uwsgi_buffer_append(ub, um->name, um->name_len)) return -1; + if (uwsgi_buffer_append(ub, " ", 1)) return -1; + if (uwsgi_buffer_num64(ub, (int64_t) um->type)) return -1; + if (uwsgi_buffer_append(ub, " ", 1)) return -1; + if (uwsgi_buffer_num64(ub, *um->value)) return -1; + + if (sendto(sn->fd, ub->buf, ub->pos, 0, (struct sockaddr *) &sn->addr.sa_in, sn->addr_len) < 0) { + uwsgi_error("socket_send_metric()/sendto()"); + } + + return 0; + +} + + +static void stats_pusher_socket(struct uwsgi_stats_pusher_instance *uspi, time_t now, char *json, size_t json_len) { + + if (!uspi->configured) { + struct socket_node *sn = uwsgi_calloc(sizeof(struct socket_node)); + char *comma = strchr(uspi->arg, ','); + if (comma) { + sn->prefix = comma+1; + sn->prefix_len = strlen(sn->prefix); + *comma = 0; + } + else { + sn->prefix = "uwsgi"; + sn->prefix_len = 5; + } + + char *colon = strchr(uspi->arg, ':'); + if (!colon) { + uwsgi_log("invalid socket address %s\n", uspi->arg); + if (comma) *comma = ','; + free(sn); + return; + } + sn->addr_len = socket_to_in_addr(uspi->arg, colon, 0, &sn->addr.sa_in); + + sn->fd = socket(AF_INET, SOCK_DGRAM, 0); + if (sn->fd < 0) { + uwsgi_error("stats_pusher_socket()/socket()"); + if (comma) *comma = ','; + free(sn); + return; + } + uwsgi_socket_nb(sn->fd); + if (comma) *comma = ','; + uspi->data = sn; + uspi->configured = 1; + } + + // we use the same buffer for all of the packets + struct uwsgi_buffer *ub = uwsgi_buffer_new(uwsgi.page_size); + struct uwsgi_metric *um = uwsgi.metrics; + while(um) { + uwsgi_rlock(uwsgi.metrics_lock); + socket_send_metric(ub, uspi, um); + uwsgi_rwunlock(uwsgi.metrics_lock); + um = um->next; + } + uwsgi_buffer_destroy(ub); +} + +static void stats_pusher_socket_init(void) { + struct uwsgi_stats_pusher *usp = uwsgi_register_stats_pusher("socket", stats_pusher_socket); + // we use a custom format not the JSON one + usp->raw = 1; +} + +struct uwsgi_plugin stats_pusher_socket_plugin = { + + .name = "stats_pusher_socket", + .on_load = stats_pusher_socket_init, +}; + diff --git a/plugins/stats_pusher_socket/uwsgiplugin.py b/plugins/stats_pusher_socket/uwsgiplugin.py new file mode 100644 index 00000000..5c4d7fbb --- /dev/null +++ b/plugins/stats_pusher_socket/uwsgiplugin.py @@ -0,0 +1,7 @@ +NAME='stats_pusher_socket' + +CFLAGS = [] +LDFLAGS = [] +LIBS = [] + +GCC_LIST = ['plugin']