]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: contrib/prometheus-exporter: export base stick table stats
authorWilliam Dauchy <wdauchy@gmail.com>
Sun, 7 Feb 2021 19:42:38 +0000 (20:42 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Mon, 8 Feb 2021 09:49:08 +0000 (10:49 +0100)
I saw some people falling back to unix socket to collect some data they
could not find in prometheus exporter. One of them is base info from
stick tables (used/size).
I do not plan to extend it more for now; keys are quite a mess to
handle.

This should resolve github issue #1008.

Signed-off-by: William Dauchy <wdauchy@gmail.com>
contrib/prometheus-exporter/README
contrib/prometheus-exporter/service-prometheus.c
reg-tests/contrib/prometheus.vtc

index a8598159709724e2a79488d54b2d98c551473c13..d882b092f48ad98d2efdd8e47ba6ed04b2d5525b 100644 (file)
@@ -72,6 +72,7 @@ exported. Here are examples:
   /metrics?scope=frontend&scope=backend # ==> Frontend and backend metrics will be exported
   /metrics?scope=*&scope=               # ==> no metrics will be exported
   /metrics?scope=&scope=global          # ==> global metrics will be exported
+  /metrics?scope=sticktable             # ==> stick tables metrics will be exported
 
 * How do I prevent my prometheus instance to explode?
 
@@ -320,3 +321,12 @@ See prometheus export for the description of each field.
 | haproxy_server_need_connections_current            |
 | haproxy_server_uweight                             |
 +----------------------------------------------------+
+
+* Stick table metrics
+
++----------------------------------------------------+
+|    Metric name                                     |
++----------------------------------------------------+
+| haproxy_sticktable_size                            |
+| haproxy_sticktable_used                            |
++----------------------------------------------------+
index 769389735ac632e658dfa4e195cc15c3cce4461e..521fe105680a30664a296a4ef73fdef0a4a6f891 100644 (file)
@@ -47,28 +47,33 @@ enum {
 
 /* Prometheus exporter dumper states (appctx->st1) */
 enum {
-        PROMEX_DUMPER_INIT = 0, /* initialized */
-        PROMEX_DUMPER_GLOBAL,   /* dump metrics of globals */
-        PROMEX_DUMPER_FRONT,    /* dump metrics of frontend proxies */
-        PROMEX_DUMPER_BACK,     /* dump metrics of backend proxies */
-        PROMEX_DUMPER_LI,       /* dump metrics of listeners */
-        PROMEX_DUMPER_SRV,      /* dump metrics of servers */
-       PROMEX_DUMPER_DONE,     /* finished */
+       PROMEX_DUMPER_INIT = 0,   /* initialized */
+       PROMEX_DUMPER_GLOBAL,     /* dump metrics of globals */
+       PROMEX_DUMPER_FRONT,      /* dump metrics of frontend proxies */
+       PROMEX_DUMPER_BACK,       /* dump metrics of backend proxies */
+       PROMEX_DUMPER_LI,         /* dump metrics of listeners */
+       PROMEX_DUMPER_SRV,        /* dump metrics of servers */
+       PROMEX_DUMPER_STICKTABLE, /* dump metrics of stick tables */
+       PROMEX_DUMPER_DONE,       /* finished */
 };
 
 /* Prometheus exporter flags (appctx->ctx.stats.flags) */
-#define PROMEX_FL_METRIC_HDR    0x00000001
-#define PROMEX_FL_INFO_METRIC   0x00000002
-#define PROMEX_FL_FRONT_METRIC  0x00000004
-#define PROMEX_FL_BACK_METRIC   0x00000008
-#define PROMEX_FL_SRV_METRIC    0x00000010
-#define PROMEX_FL_SCOPE_GLOBAL  0x00000020
-#define PROMEX_FL_SCOPE_FRONT   0x00000040
-#define PROMEX_FL_SCOPE_BACK    0x00000080
-#define PROMEX_FL_SCOPE_SERVER  0x00000100
-#define PROMEX_FL_NO_MAINT_SRV  0x00000200
-
-#define PROMEX_FL_SCOPE_ALL (PROMEX_FL_SCOPE_GLOBAL|PROMEX_FL_SCOPE_FRONT|PROMEX_FL_SCOPE_BACK|PROMEX_FL_SCOPE_SERVER)
+#define PROMEX_FL_METRIC_HDR        0x00000001
+#define PROMEX_FL_INFO_METRIC       0x00000002
+#define PROMEX_FL_FRONT_METRIC      0x00000004
+#define PROMEX_FL_BACK_METRIC       0x00000008
+#define PROMEX_FL_SRV_METRIC        0x00000010
+#define PROMEX_FL_SCOPE_GLOBAL      0x00000020
+#define PROMEX_FL_SCOPE_FRONT       0x00000040
+#define PROMEX_FL_SCOPE_BACK        0x00000080
+#define PROMEX_FL_SCOPE_SERVER      0x00000100
+#define PROMEX_FL_NO_MAINT_SRV      0x00000200
+#define PROMEX_FL_STICKTABLE_METRIC 0x00000400
+#define PROMEX_FL_SCOPE_STICKTABLE  0x00000800
+
+#define PROMEX_FL_SCOPE_ALL (PROMEX_FL_SCOPE_GLOBAL | PROMEX_FL_SCOPE_FRONT | \
+                            PROMEX_FL_SCOPE_BACK | PROMEX_FL_SCOPE_SERVER | \
+                            PROMEX_FL_SCOPE_STICKTABLE)
 
 /* Promtheus metric type (gauge or counter) */
 enum promex_mt_type {
@@ -298,6 +303,25 @@ const struct ist promex_st_metric_desc[ST_F_TOTAL_FIELDS] = {
        [ST_F_TT_MAX]         = IST("Maximum observed total request+response time (request+queue+connect+response+processing)"),
 };
 
+/* stick table base fields */
+enum sticktable_field {
+       STICKTABLE_SIZE = 0,
+       STICKTABLE_USED,
+       /* must always be the last one */
+       STICKTABLE_TOTAL_FIELDS
+};
+
+const struct promex_metric promex_sticktable_metrics[STICKTABLE_TOTAL_FIELDS] = {
+       [STICKTABLE_SIZE] = { .n = IST("size"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_STICKTABLE_METRIC },
+       [STICKTABLE_USED] = { .n = IST("used"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_STICKTABLE_METRIC },
+};
+
+/* stick table base description */
+const struct ist promex_sticktable_metric_desc[STICKTABLE_TOTAL_FIELDS] = {
+       [STICKTABLE_SIZE] = IST("Stick table size."),
+       [STICKTABLE_USED] = IST("Number of entries used in this stick table."),
+};
+
 /* Specific labels for all ST_F_HRSP_* fields */
 const struct ist promex_hrsp_code[1 + ST_F_HRSP_OTHER - ST_F_HRSP_1XX] = {
        [ST_F_HRSP_1XX - ST_F_HRSP_1XX]   = IST("1xx"),
@@ -419,6 +443,8 @@ static int promex_dump_metric_header(struct appctx *appctx, struct htx *htx,
 
        if (metric->flags & PROMEX_FL_INFO_METRIC)
                desc = ist(info_fields[appctx->st2].desc);
+       else if (metric->flags & PROMEX_FL_STICKTABLE_METRIC)
+               desc = promex_sticktable_metric_desc[appctx->st2];
        else if (!isttest(promex_st_metric_desc[appctx->st2]))
                desc = ist(stat_fields[appctx->st2].desc);
        else
@@ -958,6 +984,68 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
        goto end;
 }
 
+/* Dump stick table metrics (prefixed by "haproxy_sticktable_"). It returns 1 on success,
+ * 0 if <htx> is full and -1 in case of any error. */
+static int promex_dump_sticktable_metrics(struct appctx *appctx, struct htx *htx)
+{
+       static struct ist prefix = IST("haproxy_sticktable_");
+       struct field val;
+       struct channel *chn = si_ic(appctx->owner);
+       struct ist out = ist2(trash.area, 0);
+       size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
+       int ret = 1;
+       struct stktable *t;
+
+       for (; appctx->st2 < STICKTABLE_TOTAL_FIELDS; appctx->st2++) {
+               if (!(promex_sticktable_metrics[appctx->st2].flags & appctx->ctx.stats.flags))
+                       continue;
+
+               while (appctx->ctx.stats.obj1) {
+                       struct promex_label labels[PROMEX_MAX_LABELS - 1] = {};
+
+                       t = appctx->ctx.stats.obj1;
+                       if (!t->size)
+                               goto next_px;
+
+                       labels[0].name  = ist("name");
+                       labels[0].value = ist2(t->id, strlen(t->id));
+                       labels[1].name  = ist("type");
+                       labels[1].value = ist2(stktable_types[t->type].kw, strlen(stktable_types[t->type].kw));
+                       switch (appctx->st2) {
+                               case STICKTABLE_SIZE:
+                                       val = mkf_u32(FN_GAUGE, t->size);
+                                       break;
+                               case STICKTABLE_USED:
+                                       val = mkf_u32(FN_GAUGE, t->current);
+                                       break;
+                               default:
+                                       goto next_px;
+                       }
+
+                       if (!promex_dump_metric(appctx, htx, prefix,
+                                               &promex_sticktable_metrics[appctx->st2],
+                                               &val, labels, &out, max))
+                               goto full;
+
+                 next_px:
+                       appctx->ctx.stats.obj1 = t->next;
+               }
+               appctx->ctx.stats.flags |= PROMEX_FL_METRIC_HDR;
+               appctx->ctx.stats.obj1 = stktables_list;
+       }
+
+  end:
+       if (out.len) {
+               if (!htx_add_data_atonce(htx, out))
+                       return -1; /* Unexpected and unrecoverable error */
+               channel_add_input(chn, out.len);
+       }
+       return ret;
+  full:
+       ret = 0;
+       goto end;
+}
+
 /* Dump all metrics (global, frontends, backends and servers) depending on the
  * dumper state (appctx->st1). It returns 1 on success, 0 if <htx> is full and
  * -1 in case of any error.
@@ -1044,9 +1132,27 @@ static int promex_dump_metrics(struct appctx *appctx, struct stream_interface *s
                                }
                        }
 
-                       appctx->ctx.stats.obj1 = NULL;
+                       appctx->ctx.stats.obj1 = stktables_list;
                        appctx->ctx.stats.obj2 = NULL;
                        appctx->ctx.stats.flags &= ~(PROMEX_FL_METRIC_HDR|PROMEX_FL_SRV_METRIC);
+                       appctx->ctx.stats.flags |= (PROMEX_FL_METRIC_HDR|PROMEX_FL_STICKTABLE_METRIC);
+                       appctx->st2 = STICKTABLE_SIZE;
+                       appctx->st1 = PROMEX_DUMPER_STICKTABLE;
+                       /* fall through */
+
+               case PROMEX_DUMPER_STICKTABLE:
+                       if (appctx->ctx.stats.flags & PROMEX_FL_SCOPE_STICKTABLE) {
+                               ret = promex_dump_sticktable_metrics(appctx, htx);
+                               if (ret <= 0) {
+                                       if (ret == -1)
+                                               goto error;
+                                       goto full;
+                               }
+                       }
+
+                       appctx->ctx.stats.obj1 = NULL;
+                       appctx->ctx.stats.obj2 = NULL;
+                       appctx->ctx.stats.flags &= ~(PROMEX_FL_METRIC_HDR|PROMEX_FL_STICKTABLE_METRIC);
                        appctx->st2 = 0;
                        appctx->st1 = PROMEX_DUMPER_DONE;
                        /* fall through */
@@ -1155,6 +1261,8 @@ static int promex_parse_uri(struct appctx *appctx, struct stream_interface *si)
                                appctx->ctx.stats.flags |= PROMEX_FL_SCOPE_BACK;
                        else if (strcmp(value, "frontend") == 0)
                                appctx->ctx.stats.flags |= PROMEX_FL_SCOPE_FRONT;
+                       else if (strcmp(value, "sticktable") == 0)
+                               appctx->ctx.stats.flags |= PROMEX_FL_SCOPE_STICKTABLE;
                        else
                                goto error;
                }
index 59ff82d5b9e7e0a60bf2bf13f7df4e65f1b8368c..bf3d4caec2f7ff39a84e43667d666cdc32d08ab0 100644 (file)
@@ -1,6 +1,6 @@
 varnishtest "prometheus exporter test"
 
-#REQUIRE_VERSION=2.0
+#REQUIRE_VERSION=2.4
 #REQUIRE_SERVICES=prometheus-exporter
 
 feature ignore_unknown_macro
@@ -26,6 +26,7 @@ haproxy h1 -conf {
        default_backend be
 
     backend be
+       stick-table type ip size 1m expire 10s store http_req_rate(10s)
        server s1 ${s1_addr}:${s1_port}
 } -start
 
@@ -37,6 +38,7 @@ client c1 -connect ${h1_stats_sock} {
        expect resp.body ~ ".*haproxy_frontend.*"
        expect resp.body ~ ".*haproxy_backend.*"
        expect resp.body ~ ".*haproxy_server.*"
+       expect resp.body ~ ".*haproxy_sticktable.*"
 
        txreq -url "/metrics?scope="
        rxresp
@@ -50,6 +52,7 @@ client c1 -connect ${h1_stats_sock} {
        expect resp.body !~ ".*haproxy_frontend.*"
        expect resp.body !~ ".*haproxy_backend.*"
        expect resp.body ~ ".*haproxy_server.*"
+       expect resp.body !~ ".*haproxy_sticktable.*"
 
        txreq -url "/metrics?scope=frontend&scope=backend"
        rxresp
@@ -58,6 +61,7 @@ client c1 -connect ${h1_stats_sock} {
        expect resp.body ~ ".*haproxy_frontend.*"
        expect resp.body ~ ".*haproxy_backend.*"
        expect resp.body !~ ".*haproxy_server.*"
+       expect resp.body !~ ".*haproxy_sticktable.*"
 
        txreq -url "/metrics?scope"
        rxresp