aboutsummaryrefslogtreecommitdiffstats
path: root/src/vpp/app/vpp_prometheus_export.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vpp/app/vpp_prometheus_export.c')
-rw-r--r--src/vpp/app/vpp_prometheus_export.c334
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);
}