diff options
Diffstat (limited to 'src/vpp/app/vpp_prometheus_export.c')
-rw-r--r-- | src/vpp/app/vpp_prometheus_export.c | 334 |
1 files changed, 268 insertions, 66 deletions
diff --git a/src/vpp/app/vpp_prometheus_export.c b/src/vpp/app/vpp_prometheus_export.c index 99944917766..c6000a8a008 100644 --- a/src/vpp/app/vpp_prometheus_export.c +++ b/src/vpp/app/vpp_prometheus_export.c @@ -27,6 +27,11 @@ #include <errno.h> #include <unistd.h> #include <netdb.h> +#ifdef __FreeBSD__ +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif /* __FreeBSD__ */ #include <sys/socket.h> #include <vpp-api/client/stat_client.h> #include <vlib/vlib.h> @@ -35,6 +40,8 @@ /* https://github.com/prometheus/prometheus/wiki/Default-port-allocations */ #define SERVER_PORT 9482 +#define MAX_TOKENS 10 + static char * prom_string (char *s) { @@ -49,79 +56,269 @@ prom_string (char *s) } static void -dump_metrics (FILE * stream, u8 ** patterns) +print_metric_v1 (FILE *stream, stat_segment_data_t *res) { - stat_segment_data_t *res; - int i, j, k; - static u32 *stats = 0; + int j, k; -retry: - res = stat_segment_dump (stats); - if (res == 0) - { /* Memory layout has changed */ - if (stats) - vec_free (stats); - stats = stat_segment_ls (patterns); - goto retry; + switch (res->type) + { + case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE: + fformat (stream, "# TYPE %s counter\n", prom_string (res->name)); + for (k = 0; k < vec_len (res->simple_counter_vec); k++) + for (j = 0; j < vec_len (res->simple_counter_vec[k]); j++) + fformat (stream, "%s{thread=\"%d\",interface=\"%d\"} %lld\n", + prom_string (res->name), k, j, + res->simple_counter_vec[k][j]); + break; + + case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED: + fformat (stream, "# TYPE %s_packets counter\n", prom_string (res->name)); + fformat (stream, "# TYPE %s_bytes counter\n", prom_string (res->name)); + for (k = 0; k < vec_len (res->simple_counter_vec); k++) + for (j = 0; j < vec_len (res->combined_counter_vec[k]); j++) + { + fformat (stream, + "%s_packets{thread=\"%d\",interface=\"%d\"} %lld\n", + prom_string (res->name), k, j, + res->combined_counter_vec[k][j].packets); + fformat (stream, "%s_bytes{thread=\"%d\",interface=\"%d\"} %lld\n", + prom_string (res->name), k, j, + res->combined_counter_vec[k][j].bytes); + } + break; + case STAT_DIR_TYPE_SCALAR_INDEX: + fformat (stream, "# TYPE %s counter\n", prom_string (res->name)); + fformat (stream, "%s %.2f\n", prom_string (res->name), + res->scalar_value); + break; + + case STAT_DIR_TYPE_NAME_VECTOR: + fformat (stream, "# TYPE %s_info gauge\n", prom_string (res->name)); + for (k = 0; k < vec_len (res->name_vector); k++) + if (res->name_vector[k]) + fformat (stream, "%s_info{index=\"%d\",name=\"%s\"} 1\n", + prom_string (res->name), k, res->name_vector[k]); + break; + + case STAT_DIR_TYPE_EMPTY: + break; + + default: + fformat (stderr, "Unknown value %d\n", res->type); + ; } +} - for (i = 0; i < vec_len (res); i++) +static void +sanitize (char *str, int len) +{ + for (int i = 0; i < len; i++) { - switch (res[i].type) + if (!isalnum (str[i])) + str[i] = '_'; + } +} + +static int +tokenize (const char *name, char **tokens, int *lengths, int max_tokens) +{ + char *p = (char *) name; + char *savep = p; + + int i = 0; + while (*p && i < max_tokens - 1) + { + if (*p == '/') { - case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE: - fformat (stream, "# TYPE %s counter\n", prom_string (res[i].name)); - for (k = 0; k < vec_len (res[i].simple_counter_vec); k++) - for (j = 0; j < vec_len (res[i].simple_counter_vec[k]); j++) - fformat (stream, "%s{thread=\"%d\",interface=\"%d\"} %lld\n", - prom_string (res[i].name), k, j, - res[i].simple_counter_vec[k][j]); - break; + tokens[i] = (char *) savep; + lengths[i] = (int) (p - savep); + i++; + p++; + savep = p; + } + else + { + p++; + } + } + tokens[i] = (char *) savep; + lengths[i] = (int) (p - savep); + + i++; + return i; +} - case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED: - fformat (stream, "# TYPE %s_packets counter\n", - prom_string (res[i].name)); - fformat (stream, "# TYPE %s_bytes counter\n", - prom_string (res[i].name)); - for (k = 0; k < vec_len (res[i].simple_counter_vec); k++) - for (j = 0; j < vec_len (res[i].combined_counter_vec[k]); j++) +static void +print_metric_v2 (FILE *stream, stat_segment_data_t *res) +{ + int num_tokens = 0; + char *tokens[MAX_TOKENS]; + int lengths[MAX_TOKENS]; + int j, k; + + num_tokens = tokenize (res->name, tokens, lengths, MAX_TOKENS); + switch (res->type) + { + case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE: + if (res->simple_counter_vec == 0) + return; + for (k = 0; k < vec_len (res->simple_counter_vec); k++) + for (j = 0; j < vec_len (res->simple_counter_vec[k]); j++) + { + if ((num_tokens == 4) && + (!strncmp (tokens[1], "nodes", lengths[1]) || + !strncmp (tokens[1], "interfaces", lengths[1]))) + { + sanitize (tokens[1], lengths[1]); + sanitize (tokens[3], lengths[3]); + fformat ( + stream, + "%.*s_%.*s{%.*s=\"%.*s\",index=\"%d\",thread=\"%d\"} %lu\n", + lengths[1], tokens[1], lengths[3], tokens[3], lengths[1] - 1, + tokens[1], lengths[2], tokens[2], j, k, + res->simple_counter_vec[k][j]); + } + else if ((num_tokens == 3) && + !strncmp (tokens[1], "sys", lengths[1])) + { + sanitize (tokens[1], lengths[1]); + fformat (stream, "%.*s_%.*s{index=\"%d\",thread=\"%d\"} %lu\n", + lengths[1], tokens[1], lengths[2], tokens[2], j, k, + res->simple_counter_vec[k][j]); + } + else if (!strncmp (tokens[1], "mem", lengths[1])) + { + if (num_tokens == 3) + { + fformat ( + stream, + "%.*s{heap=\"%.*s\",index=\"%d\",thread=\"%d\"} %lu\n", + lengths[1], tokens[1], lengths[2], tokens[2], j, k, + res->simple_counter_vec[k][j]); + } + else if (num_tokens == 4) + { + fformat (stream, + "%.*s_%.*s{heap=\"%.*s\",index=\"%d\",thread=\"%" + "d\"} %lu\n", + lengths[1], tokens[1], lengths[3], tokens[3], + lengths[2], tokens[2], j, k, + res->simple_counter_vec[k][j]); + } + else + { + print_metric_v1 (stream, res); + } + } + else if (!strncmp (tokens[1], "err", lengths[1])) { + // NOTE: the error is in token3, but it may contain '/'. + // Considering this is the last token, it is safe to print + // token3 until the end of res->name fformat (stream, - "%s_packets{thread=\"%d\",interface=\"%d\"} %lld\n", - prom_string (res[i].name), k, j, - res[i].combined_counter_vec[k][j].packets); + "%.*s{node=\"%.*s\",error=\"%s\",index=\"%d\",thread=" + "\"%d\"} %lu\n", + lengths[1], tokens[1], lengths[2], tokens[2], + tokens[3], j, k, res->simple_counter_vec[k][j]); + } + else + { + print_metric_v1 (stream, res); + } + } + break; + + case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED: + if (res->combined_counter_vec == 0) + return; + for (k = 0; k < vec_len (res->combined_counter_vec); k++) + for (j = 0; j < vec_len (res->combined_counter_vec[k]); j++) + { + if ((num_tokens == 4) && + !strncmp (tokens[1], "interfaces", lengths[1])) + { + sanitize (tokens[1], lengths[1]); + sanitize (tokens[3], lengths[3]); + fformat (stream, + "%.*s_%.*s_packets{interface=\"%.*s\",index=\"%d\"," + "thread=\"%d\"} %lu\n", + lengths[1], tokens[1], lengths[3], tokens[3], + lengths[2], tokens[2], j, k, + res->combined_counter_vec[k][j].packets); fformat (stream, - "%s_bytes{thread=\"%d\",interface=\"%d\"} %lld\n", - prom_string (res[i].name), k, j, - res[i].combined_counter_vec[k][j].bytes); + "%.*s_%.*s_bytes{interface=\"%.*s\",index=\"%d\"," + "thread=\"%d\"} %lu\n", + lengths[1], tokens[1], lengths[3], tokens[3], + lengths[2], tokens[2], j, k, + res->combined_counter_vec[k][j].bytes); } - break; - case STAT_DIR_TYPE_ERROR_INDEX: - for (j = 0; j < vec_len (res[i].error_vector); j++) + else + { + print_metric_v1 (stream, res); + } + } + break; + + case STAT_DIR_TYPE_SCALAR_INDEX: + if ((num_tokens == 4) && + !strncmp (tokens[1], "buffer-pools", lengths[1])) + { + sanitize (tokens[1], lengths[1]); + sanitize (tokens[3], lengths[3]); + fformat (stream, "%.*s_%.*s{pool=\"%.*s\"} %.2f\n", lengths[1], + tokens[1], lengths[3], tokens[3], lengths[2], tokens[2], + res->scalar_value); + } + else if ((num_tokens == 3) && !strncmp (tokens[1], "sys", lengths[1])) + { + sanitize (tokens[1], lengths[1]); + sanitize (tokens[2], lengths[2]); + fformat (stream, "%.*s_%.*s %.2f\n", lengths[1], tokens[1], + lengths[2], tokens[2], res->scalar_value); + if (!strncmp (tokens[2], "boottime", lengths[2])) { - fformat (stream, "# TYPE %s counter\n", - prom_string (res[i].name)); - fformat (stream, "%s{thread=\"%d\"} %lld\n", - prom_string (res[i].name), j, res[i].error_vector[j]); + struct timeval tv; + gettimeofday (&tv, NULL); + fformat (stream, "sys_uptime %.2f\n", + tv.tv_sec - res->scalar_value); } - break; + } + else + { + print_metric_v1 (stream, res); + } + break; - case STAT_DIR_TYPE_SCALAR_INDEX: - fformat (stream, "# TYPE %s counter\n", prom_string (res[i].name)); - fformat (stream, "%s %.2f\n", prom_string (res[i].name), - res[i].scalar_value); - break; + default:; + fformat (stderr, "Unhandled type %d name %s\n", res->type, res->name); + } +} - case STAT_DIR_TYPE_EMPTY: - break; +static void +dump_metrics (FILE *stream, u8 **patterns, u8 v2) +{ + stat_segment_data_t *res; + int i; + static u32 *stats = 0; - default: - fformat (stderr, "Unknown value %d\n", res[i].type); - ; - } +retry: + res = stat_segment_dump (stats); + if (res == 0) + { /* Memory layout has changed */ + if (stats) + vec_free (stats); + stats = stat_segment_ls (patterns); + goto retry; } - stat_segment_data_free (res); + for (i = 0; i < vec_len (res); i++) + { + if (v2) + print_metric_v2 (stream, &res[i]); + else + print_metric_v1 (stream, &res[i]); + } + stat_segment_data_free (res); } @@ -129,7 +326,7 @@ retry: #define NOT_FOUND_ERROR "<html><head><title>Document not found</title></head><body><h1>404 - Document not found</h1></body></html>" static void -http_handler (FILE * stream, u8 ** patterns) +http_handler (FILE *stream, u8 **patterns, u8 v2) { char status[80] = { 0 }; if (fgets (status, sizeof (status) - 1, stream) == 0) @@ -181,7 +378,7 @@ http_handler (FILE * stream, u8 ** patterns) return; } fputs ("HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n", stream); - dump_metrics (stream, patterns); + dump_metrics (stream, patterns, v2); } static int @@ -243,10 +440,14 @@ main (int argc, char **argv) { unformat_input_t _argv, *a = &_argv; u8 *stat_segment_name, *pattern = 0, **patterns = 0; + u16 port = SERVER_PORT; + char *usage = + "%s: usage [socket-name <name>] [port <0 - 65535>] [v2] <patterns> ...\n"; int rv; + u8 v2 = 0; - /* Allocating 32MB heap */ - clib_mem_init (0, 32 << 20); + /* Allocating 256MB heap */ + clib_mem_init (0, 256 << 20); unformat_init_command_line (a, argv); @@ -256,23 +457,24 @@ main (int argc, char **argv) { if (unformat (a, "socket-name %s", &stat_segment_name)) ; + if (unformat (a, "v2")) + v2 = 1; + else if (unformat (a, "port %d", &port)) + ; else if (unformat (a, "%s", &pattern)) { vec_add1 (patterns, pattern); } else { - fformat (stderr, - "%s: usage [socket-name <name>] <patterns> ...\n", - argv[0]); + fformat (stderr, usage, argv[0]); exit (1); } } if (vec_len (patterns) == 0) { - fformat (stderr, - "%s: usage [socket-name <name>] <patterns> ...\n", argv[0]); + fformat (stderr, usage, argv[0]); exit (1); } @@ -284,7 +486,7 @@ main (int argc, char **argv) exit (1); } - int fd = start_listen (SERVER_PORT); + int fd = start_listen (port); if (fd < 0) { exit (1); @@ -319,7 +521,7 @@ main (int argc, char **argv) continue; } /* Single reader at the moment */ - http_handler (stream, patterns); + http_handler (stream, patterns, v2); fclose (stream); } |