]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: promex: Add support for filters on metric names
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 31 Jan 2024 16:15:49 +0000 (17:15 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 2 Feb 2024 08:11:34 +0000 (09:11 +0100)
It is now possible to filter the metrics on their name, by listing
explicitly metrics to dump or on the opposite to exclude only some metrics
from the dump. To do so, a comma-separated list of metrics must be
specified. If a name is preceded by a minus (-), the metric is excluded from
the dump. If at least one metric is specified to be explicitly dumped, all
metrics are no longer dumped, but only those explicitly listed.

The list is specified via one or more "metrics" parameters in the uri
query-string. For insance:

  # Dumped all metrics, except "haproxy_server_check_status"
  /metrics?metrics=-haproxy_server_check_status

  # Only dump frontends, backends and servers status
  /metrics?metrics=haproxy_frontend_status,haproxy_backend_status,haproxy_server_status

Included and Excluded metrics can be mixed. Only the intersection will be
dumped.

This patch should fix the issue #770.

addons/promex/README
addons/promex/include/promex/promex.h
addons/promex/service-prometheus.c

index c4fbc65160d4e9f5fb5589c70a231ef1cb0caefd..7f638a5db1cbef328e9b7f607a0b8acf09a03a2d 100644 (file)
@@ -75,6 +75,23 @@ exported. Here are examples:
   /metrics?scope=&scope=global          # ==> global metrics will be exported
   /metrics?scope=sticktable             # ==> stick tables metrics will be exported
 
+* Filtering on metrics name
+
+It is possible to filter metrics dumped by the exporter. To to so, multiple
+"metrics" parameters may be passed to specify all metrics to include or exclude,
+as a comma-separated list of filter. By default, there is no filter and all
+metrics are dumped. By specifying at least one metric to be included in the
+dump, this disables the default behavior and only explicitly mentionned metrics
+are dumped. To include a metric, its name must be specified. To exclude it, its
+name must be preceeded by a minus character ('-'). Here are examples:
+
+  # Dumped all metrics, except "haproxy_server_check_status"
+  /metrics?metrics=-haproxy_server_check_status
+
+  # Only dump frontends, backends and servers status
+  /metrics?metrics=haproxy_frontend_status,haproxy_backend_status,haproxy_server_status
+
+
 * Dump extra counters
 
 Internally, some modules can register to frontends, backends, servers or
index dff9dd291d4007df1ead5c1e595484ba3ce67c4a..c4712bc21638b914eee3717bfd96501c6649545a 100644 (file)
@@ -46,6 +46,7 @@
 #define PROMEX_FL_SCOPE_MODULE      0x00001000
 #define PROMEX_FL_NO_MAINT_SRV      0x00002000
 #define PROMEX_FL_EXTRA_COUNTERS    0x00004000
+#define PROMEX_FL_INC_METRIC_BY_DEFAULT 0x00008000
 
 #define PROMEX_FL_SCOPE_ALL (PROMEX_FL_SCOPE_GLOBAL | PROMEX_FL_SCOPE_FRONT | \
                             PROMEX_FL_SCOPE_LI | PROMEX_FL_SCOPE_BACK | \
index aeec42aa5851a9dd793174f635a49cbaa1622bfd..4fc418ab29f7ae8203f6476bea15308e0010b84f 100644 (file)
@@ -39,6 +39,7 @@
 #include <haproxy/task.h>
 #include <haproxy/tools.h>
 #include <haproxy/version.h>
+#include <haproxy/xxhash.h>
 
 #include <promex/promex.h>
 
@@ -68,6 +69,12 @@ struct promex_module_ref {
        struct list list;
 };
 
+/* An entry in a headers map */
+struct promex_metric_filter  {
+       int exclude;
+        struct eb32_node node;
+};
+
 /* the context of the applet */
 struct promex_ctx {
        void *p[4];                /* generic pointers used to save context  */
@@ -76,6 +83,7 @@ struct promex_ctx {
        unsigned mod_field_num;    /* first field number of the current module (ST_F_* etc) */
        int obj_state;             /* current state among PROMEX_{FRONT|BACK|SRV|LI}_STATE_* */
        struct list modules;       /* list of promex modules to export */
+       struct eb_root filters;    /* list of filters to apply on metrics name */
 };
 
 /* The max length for metrics name. It is a hard limit but it should be
@@ -365,8 +373,9 @@ void promex_register_module(struct promex_module *m)
        LIST_APPEND(&promex_module_list, &m->list);
 }
 
-/* Pools used to allocate ref on Promex modules */
-DECLARE_STATIC_POOL(pool_head_promex_mod_ref,   "promex_module_ref",  sizeof(struct promex_module_ref));
+/* Pools used to allocate ref on Promex modules and filters */
+DECLARE_STATIC_POOL(pool_head_promex_mod_ref,    "promex_module_ref",  sizeof(struct promex_module_ref));
+DECLARE_STATIC_POOL(pool_head_promex_metric_flt, "promex_metric_filter", sizeof(struct promex_metric_filter));
 
 /* Return the server status. */
 enum promex_srv_state promex_srv_status(struct server *sv)
@@ -521,6 +530,32 @@ static int promex_dump_ts(struct appctx *appctx, struct ist prefix,
 
 }
 
+static int promex_filter_metric(struct appctx *appctx, struct ist prefix, struct ist name)
+{
+       struct promex_ctx *ctx = appctx->svcctx;
+       struct eb32_node *node;
+       struct promex_metric_filter *flt;
+       unsigned int hash;
+       XXH32_state_t state;
+
+       if (!eb_is_empty(&ctx->filters)) {
+               XXH32_reset(&state, 0);
+               XXH32_update(&state, istptr(prefix), istlen(prefix));
+               XXH32_update(&state, istptr(name), istlen(name));
+               hash = XXH32_digest(&state);
+
+               node = eb32_lookup(&ctx->filters, hash);
+               if (node) {
+                       flt = container_of(node, typeof(*flt), node);
+                       if (flt->exclude)
+                               return 1;
+               }
+               else if (!(ctx->flags & PROMEX_FL_INC_METRIC_BY_DEFAULT))
+                       return 1;
+       }
+
+       return 0;
+}
 
 /* Dump global metrics (prefixed by "haproxy_process_"). It returns 1 on success,
  * 0 if <htx> is full and -1 in case of any error. */
@@ -546,6 +581,9 @@ static int promex_dump_global_metrics(struct appctx *appctx, struct htx *htx)
                name = promex_global_metrics[ctx->field_num].n;
                desc = ist(info_fields[ctx->field_num].desc);
 
+               if (promex_filter_metric(appctx, prefix, name))
+                       continue;
+
                switch (ctx->field_num) {
                        case INF_BUILD_INFO:
                                labels[0].name  = ist("version");
@@ -605,6 +643,9 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
                if (!isttest(desc))
                        desc = ist(stat_fields[ctx->field_num].desc);
 
+               if (promex_filter_metric(appctx, prefix, name))
+                       continue;
+
                if (!px)
                        px = proxies_list;
 
@@ -700,6 +741,9 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
                        name = ist2(mod->stats[ctx->mod_field_num].name, strlen(mod->stats[ctx->mod_field_num].name));
                        desc = ist2(mod->stats[ctx->mod_field_num].desc, strlen(mod->stats[ctx->mod_field_num].desc));
 
+                       if (promex_filter_metric(appctx, prefix, name))
+                               continue;
+
                        if (!px)
                                px = proxies_list;
 
@@ -786,6 +830,9 @@ static int promex_dump_listener_metrics(struct appctx *appctx, struct htx *htx)
                if (!isttest(desc))
                        desc = ist(stat_fields[ctx->field_num].desc);
 
+               if (promex_filter_metric(appctx, prefix, name))
+                       continue;
+
                if (!px)
                        px = proxies_list;
 
@@ -866,6 +913,9 @@ static int promex_dump_listener_metrics(struct appctx *appctx, struct htx *htx)
                        name = ist2(mod->stats[ctx->mod_field_num].name, strlen(mod->stats[ctx->mod_field_num].name));
                        desc = ist2(mod->stats[ctx->mod_field_num].desc, strlen(mod->stats[ctx->mod_field_num].desc));
 
+                       if (promex_filter_metric(appctx, prefix, name))
+                               continue;
+
                        if (!px)
                                px = proxies_list;
 
@@ -968,6 +1018,9 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
                if (!isttest(desc))
                        desc = ist(stat_fields[ctx->field_num].desc);
 
+               if (promex_filter_metric(appctx, prefix, name))
+                       continue;
+
                if (!px)
                        px = proxies_list;
 
@@ -1141,6 +1194,9 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
                        name = ist2(mod->stats[ctx->mod_field_num].name, strlen(mod->stats[ctx->mod_field_num].name));
                        desc = ist2(mod->stats[ctx->mod_field_num].desc, strlen(mod->stats[ctx->mod_field_num].desc));
 
+                       if (promex_filter_metric(appctx, prefix, name))
+                               continue;
+
                        if (!px)
                                px = proxies_list;
 
@@ -1228,6 +1284,9 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
                if (!isttest(desc))
                        desc = ist(stat_fields[ctx->field_num].desc);
 
+               if (promex_filter_metric(appctx, prefix, name))
+                       continue;
+
                if (!px)
                        px = proxies_list;
 
@@ -1393,6 +1452,9 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
                        name = ist2(mod->stats[ctx->mod_field_num].name, strlen(mod->stats[ctx->mod_field_num].name));
                        desc = ist2(mod->stats[ctx->mod_field_num].desc, strlen(mod->stats[ctx->mod_field_num].desc));
 
+                       if (promex_filter_metric(appctx, prefix, name))
+                               continue;
+
                        if (!px)
                                px = proxies_list;
 
@@ -1489,12 +1551,16 @@ static int promex_dump_module_metrics(struct appctx *appctx, struct promex_modul
                struct promex_metric metric;
                struct ist desc;
 
+
                ret = mod->metric_info(ctx->mod_field_num, &metric, &desc);
                if (!ret)
                        continue;
                if (ret < 0)
                        goto error;
 
+               if (promex_filter_metric(appctx, prefix, metric.n))
+                       continue;
+
                if (!ctx->p[2])
                        ctx->p[2] = mod->start_ts(ctx->p[1], ctx->mod_field_num);
 
@@ -1775,6 +1841,7 @@ static int promex_parse_uri(struct appctx *appctx, struct stconn *sc)
        const char *end;
        struct buffer *err;
        int default_scopes = PROMEX_FL_SCOPE_ALL;
+       int default_metrics_filter = PROMEX_FL_INC_METRIC_BY_DEFAULT;
        int len;
 
        /* Get the query-string */
@@ -1837,7 +1904,7 @@ static int promex_parse_uri(struct appctx *appctx, struct stconn *sc)
                                goto error;
                        else if (*value == 0)
                                ctx->flags &= ~PROMEX_FL_SCOPE_ALL;
-                       else if (*value == '*')
+                       else if (*value == '*' && *(value+1) == 0)
                                ctx->flags |= PROMEX_FL_SCOPE_ALL;
                        else if (strcmp(value, "global") == 0)
                                ctx->flags |= PROMEX_FL_SCOPE_GLOBAL;
@@ -1868,6 +1935,48 @@ static int promex_parse_uri(struct appctx *appctx, struct stconn *sc)
                                        goto error;
                        }
                }
+               else if (strcmp(key, "metrics") == 0) {
+                       struct ist args;
+
+                       if (!value)
+                               goto error;
+
+                       for (args = ist(value); istlen(args); args = istadv(istfind(args, ','), 1)) {
+                               struct eb32_node *node;
+                               struct promex_metric_filter *flt;
+                               struct ist m = iststop(args, ',');
+                               unsigned int hash;
+                               int exclude = 0;
+
+                               if (!istlen(m))
+                                       continue;
+
+                               if (*istptr(m) == '-') {
+                                       m = istnext(m);
+                                       if (!istlen(m))
+                                               continue;
+                                       exclude = 1;
+                               }
+                               else
+                                       default_metrics_filter &= ~PROMEX_FL_INC_METRIC_BY_DEFAULT;
+
+
+                               hash = XXH32(istptr(m), istlen(m), 0);
+                               node = eb32_lookup(&ctx->filters, hash);
+                               if (node) {
+                                       flt = container_of(node, typeof(*flt), node);
+                                       flt->exclude = exclude;
+                                       continue;
+                               }
+
+                               flt = pool_alloc(pool_head_promex_metric_flt);
+                               if (!flt)
+                                       goto internal_error;
+                               flt->node.key = hash;
+                               flt->exclude = exclude;
+                               eb32_insert(&ctx->filters, &flt->node);
+                       }
+               }
                else if (strcmp(key, "extra-counters") == 0) {
                        ctx->flags |= PROMEX_FL_EXTRA_COUNTERS;
                }
@@ -1876,7 +1985,7 @@ static int promex_parse_uri(struct appctx *appctx, struct stconn *sc)
        }
 
   end:
-       ctx->flags |= default_scopes;
+       ctx->flags |= (default_scopes | default_metrics_filter);
        return 1;
 
   error:
@@ -1937,6 +2046,7 @@ static int promex_appctx_init(struct appctx *appctx)
        ctx = appctx->svcctx;
        memset(ctx->p, 0, sizeof(ctx->p));
        LIST_INIT(&ctx->modules);
+       ctx->filters = EB_ROOT;
        appctx->st0 = PROMEX_ST_INIT;
        return 0;
 }
@@ -1948,11 +2058,22 @@ static void promex_appctx_release(struct appctx *appctx)
 {
        struct promex_ctx *ctx = appctx->svcctx;
        struct promex_module_ref *ref, *back;
+       struct promex_metric_filter *flt;
+        struct eb32_node *node, *next;
 
        list_for_each_entry_safe(ref, back, &ctx->modules, list) {
                LIST_DELETE(&ref->list);
                pool_free(pool_head_promex_mod_ref, ref);
        }
+
+       node = eb32_first(&ctx->filters);
+       while (node) {
+               next = eb32_next(node);
+               eb32_delete(node);
+               flt = container_of(node, typeof(*flt), node);
+               pool_free(pool_head_promex_metric_flt, flt);
+               node = next;
+       }
 }
 
 /* The main I/O handler for the promex applet. */