]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: stats: integrate static proxies stats in new stats
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 5 Oct 2020 09:49:42 +0000 (11:49 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Mon, 5 Oct 2020 10:02:14 +0000 (12:02 +0200)
This is executed on startup with the registered statistics module. The
existing statistics have been merged in a list containing all
statistics for each domain. This is useful to print all available
statistics in a generic way.

Allocate extra counters for all proxies/servers/listeners instances.
These counters are allocated with the counters from the stats modules
registered on startup.

include/haproxy/listener-t.h
include/haproxy/proxy-t.h
include/haproxy/server-t.h
include/haproxy/stats.h
src/haproxy.c
src/stats.c

index cc5546c503b2113a482931a85ee2de21c72d839d..0af6353a8fab7d05c5989ff05def40b5a8ffd683 100644 (file)
@@ -30,7 +30,8 @@
 #include <haproxy/api-t.h>
 #include <haproxy/obj_type-t.h>
 #include <haproxy/receiver-t.h>
-#include <haproxy/thread-t.h>
+#include <haproxy/stats-t.h>
+#include <haproxy/thread.h>
 
 #ifdef USE_OPENSSL
 #include <haproxy/openssl-compat.h>
@@ -221,6 +222,8 @@ struct listener {
        struct {
                struct eb32_node id;    /* place in the tree of used IDs */
        } conf;                         /* config information */
+
+       EXTRA_COUNTERS(extra_counters);
 };
 
 /* Descriptor for a "bind" keyword. The ->parse() function returns 0 in case of
index baffcc0deb986b22dcb250d1e0d889fb9f0fe17a..2680fbffacf3ea5d75369936c95ccadb91ba875d 100644 (file)
@@ -37,6 +37,7 @@
 #include <haproxy/freq_ctr-t.h>
 #include <haproxy/obj_type-t.h>
 #include <haproxy/server-t.h>
+#include <haproxy/stats-t.h>
 #include <haproxy/tcpcheck-t.h>
 #include <haproxy/thread-t.h>
 #include <haproxy/uri_auth-t.h>
@@ -451,6 +452,9 @@ struct proxy {
                                                 */
        struct list filter_configs;             /* list of the filters that are declared on this proxy */
        __decl_thread(HA_SPINLOCK_T lock);   /* may be taken under the server's lock */
+
+       EXTRA_COUNTERS(extra_counters_fe);
+       EXTRA_COUNTERS(extra_counters_be);
 };
 
 struct switching_rule {
index decc5e292daa82e070c61b5bd21f4f75cb98358d..6ef7e43a5daa97964f6b1a6857d319b05de632b2 100644 (file)
@@ -38,6 +38,7 @@
 #include <haproxy/obj_type-t.h>
 #include <haproxy/openssl-compat.h>
 #include <haproxy/ssl_sock-t.h>
+#include <haproxy/stats-t.h>
 #include <haproxy/task-t.h>
 #include <haproxy/thread-t.h>
 
@@ -352,6 +353,8 @@ struct server {
        char adm_st_chg_cause[48];              /* administrative status change's cause */
 
        struct sockaddr_storage socks4_addr;    /* the address of the SOCKS4 Proxy, including the port */
+
+       EXTRA_COUNTERS(extra_counters);
 };
 
 
index 272274610faee4238d71469f795305a7b2506d9c..6246b0405c0a01247598abda7efbde3dca5e327c 100644 (file)
@@ -123,6 +123,8 @@ static inline struct field mkf_flt(uint32_t type, double value)
 #define MK_STATS_PROXY_DOMAIN(px_cap) \
        ((px_cap) << STATS_PX_CAP | STATS_DOMAIN_PROXY)
 
+int stats_allocate_proxy_counters(struct proxy *px);
+
 void stats_register_module(struct stats_module *m);
 
 #endif /* _HAPROXY_STATS_H */
index 0c1dd5a97e9738b75dca141c34594cafc4783b4b..49f3fbd89d3b1454e36dcc19d5da25e98d1e6017 100644 (file)
 #include <haproxy/sock.h>
 #include <haproxy/sock_inet.h>
 #include <haproxy/ssl_sock.h>
+#include <haproxy/stats-t.h>
 #include <haproxy/stream.h>
 #include <haproxy/task.h>
 #include <haproxy/thread.h>
@@ -2451,6 +2452,9 @@ void deinit(void)
                        free(cond);
                }
 
+               EXTRA_COUNTERS_FREE(p->extra_counters_fe);
+               EXTRA_COUNTERS_FREE(p->extra_counters_be);
+
                /* build a list of unique uri_auths */
                if (!ua)
                        ua = p->uri_auth;
@@ -2593,6 +2597,7 @@ void deinit(void)
                        list_for_each_entry(srvdf, &server_deinit_list, list)
                                srvdf->fct(s);
 
+                       EXTRA_COUNTERS_FREE(s->extra_counters);
                        free(s);
                        s = s_next;
                }/* end while(s) */
@@ -2614,6 +2619,8 @@ void deinit(void)
                        LIST_DEL(&l->by_bind);
                        free(l->name);
                        free(l->counters);
+
+                       EXTRA_COUNTERS_FREE(l->extra_counters);
                        free(l);
                }
 
index 83c0e664532e156b6e0583ce6adaa09f385515c5..12b5945084a9378946950c96d3689541af0e2ead 100644 (file)
@@ -254,8 +254,13 @@ const struct name_desc stat_fields[ST_F_TOTAL_FIELDS] = {
 
 /* one line of info */
 static THREAD_LOCAL struct field info[INF_TOTAL_FIELDS];
-/* one line of stats */
-static THREAD_LOCAL struct field stats[ST_F_TOTAL_FIELDS];
+
+/* description of statistics (static and dynamic) */
+static struct name_desc *stat_f[STATS_DOMAIN_COUNT];
+static size_t stat_count[STATS_DOMAIN_COUNT];
+
+/* one line for stats */
+static struct field *stat_l[STATS_DOMAIN_COUNT];
 
 /* list of all registered stats module */
 static struct list stats_module_list[STATS_DOMAIN_COUNT] = {
@@ -334,20 +339,20 @@ static void stats_dump_csv_header(enum stats_domain domain)
        int field;
 
        chunk_appendf(&trash, "# ");
-       for (field = 0; field < ST_F_TOTAL_FIELDS; field++) {
-               chunk_appendf(&trash, "%s,", stat_fields[field].name);
-
-               /* print special delimiter on proxy stats to mark end of
-                  static fields */
-               if (domain == STATS_DOMAIN_PROXY && field + 1 == ST_F_TOTAL_FIELDS) {
-                       chunk_appendf(&trash, "-,");
+       if (stat_f[domain]) {
+               for (field = 0; field < stat_count[domain]; ++field) {
+                       chunk_appendf(&trash, "%s,", stat_f[domain][field].name);
+
+                       /* print special delimiter on proxy stats to mark end of
+                          static fields */
+                       if (domain == STATS_DOMAIN_PROXY && field + 1 == ST_F_TOTAL_FIELDS)
+                               chunk_appendf(&trash, "-,");
                }
        }
 
        chunk_appendf(&trash, "\n");
 }
 
-
 /* Emits a stats field without any surrounding element and properly encoded to
  * resist CSV output. Returns non-zero on success, 0 if the buffer is full.
  */
@@ -581,7 +586,7 @@ static int stats_dump_fields_typed(struct buffer *out,
                                      '?',
                                      stats[ST_F_IID].u.u32, stats[ST_F_SID].u.u32,
                                      field,
-                                     stat_fields[field].name,
+                                     stat_f[domain][field].name,
                                      stats[ST_F_PID].u.u32);
                        break;
 
@@ -593,8 +598,12 @@ static int stats_dump_fields_typed(struct buffer *out,
                        return 0;
                if (!stats_emit_typed_data_field(out, &stats[field]))
                        return 0;
-               if ((flags & STAT_SHOW_FDESC) && !chunk_appendf(out, ":\"%s\"", stat_fields[field].desc))
+
+               if (flags & STAT_SHOW_FDESC
+                   && !chunk_appendf(out, ":\"%s\"", stat_f[domain][field].name)) {
                        return 0;
+               }
+
                if (!chunk_strcat(out, "\n"))
                        return 0;
        }
@@ -705,7 +714,8 @@ static int stats_dump_fields_json(struct buffer *out,
                old_len = out->data;
                if (domain == STATS_DOMAIN_PROXY) {
                        stats_print_proxy_field_json(out, &stats[field],
-                                                    stat_fields[field].name, field,
+                                                    stat_f[domain][field].name,
+                                                    field,
                                                     stats[ST_F_TYPE].u.u32,
                                                     stats[ST_F_IID].u.u32,
                                                     stats[ST_F_SID].u.u32,
@@ -1474,8 +1484,6 @@ int stats_fill_fe_stats(struct proxy *px, struct field *stats, int len)
        if (len < ST_F_TOTAL_FIELDS)
                return 0;
 
-       memset(stats, 0, sizeof(*stats) * len);
-
        stats[ST_F_PXNAME]   = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, px->id);
        stats[ST_F_SVNAME]   = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, "FRONTEND");
        stats[ST_F_MODE]     = mkf_str(FO_CONFIG|FS_SERVICE, proxy_mode_str(px->mode));
@@ -1540,6 +1548,9 @@ int stats_fill_fe_stats(struct proxy *px, struct field *stats, int len)
 static int stats_dump_fe_stats(struct stream_interface *si, struct proxy *px)
 {
        struct appctx *appctx = __objt_appctx(si->end);
+       struct field *stats = stat_l[STATS_DOMAIN_PROXY];
+       struct stats_module *mod;
+       size_t stats_count = ST_F_TOTAL_FIELDS;
 
        if (!(px->cap & PR_CAP_FE))
                return 0;
@@ -1547,10 +1558,25 @@ static int stats_dump_fe_stats(struct stream_interface *si, struct proxy *px)
        if ((appctx->ctx.stats.flags & STAT_BOUND) && !(appctx->ctx.stats.type & (1 << STATS_TYPE_FE)))
                return 0;
 
+       memset(stats, 0, sizeof(struct field) * stat_count[STATS_DOMAIN_PROXY]);
+
        if (!stats_fill_fe_stats(px, stats, ST_F_TOTAL_FIELDS))
                return 0;
 
-       return stats_dump_one_line(stats, ST_F_TOTAL_FIELDS, appctx);
+       list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+               void *counters;
+
+               if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_FE)) {
+                       stats_count += mod->stats_count;
+                       continue;
+               }
+
+               counters = EXTRA_COUNTERS_GET(px->extra_counters_fe, mod);
+               mod->fill_stats(counters, stats + stats_count);
+               stats_count += mod->stats_count;
+       }
+
+       return stats_dump_one_line(stats, stats_count, appctx);
 }
 
 /* Fill <stats> with the listener statistics. <stats> is
@@ -1571,7 +1597,6 @@ int stats_fill_li_stats(struct proxy *px, struct listener *l, int flags,
                return 0;
 
        chunk_reset(out);
-       memset(stats, 0, sizeof(*stats) * len);
 
        stats[ST_F_PXNAME]   = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, px->id);
        stats[ST_F_SVNAME]   = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, l->name);
@@ -1631,11 +1656,29 @@ int stats_fill_li_stats(struct proxy *px, struct listener *l, int flags,
 static int stats_dump_li_stats(struct stream_interface *si, struct proxy *px, struct listener *l)
 {
        struct appctx *appctx = __objt_appctx(si->end);
+       struct field *stats = stat_l[STATS_DOMAIN_PROXY];
+       struct stats_module *mod;
+       size_t stats_count = ST_F_TOTAL_FIELDS;
+
+       memset(stats, 0, sizeof(struct field) * stat_count[STATS_DOMAIN_PROXY]);
 
        if (!stats_fill_li_stats(px, l, appctx->ctx.stats.flags, stats, ST_F_TOTAL_FIELDS))
                return 0;
 
-       return stats_dump_one_line(stats, ST_F_TOTAL_FIELDS, appctx);
+       list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+               void *counters;
+
+               if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_LI)) {
+                       stats_count += mod->stats_count;
+                       continue;
+               }
+
+               counters = EXTRA_COUNTERS_GET(l->extra_counters, mod);
+               mod->fill_stats(counters, stats + stats_count);
+               stats_count += mod->stats_count;
+       }
+
+       return stats_dump_one_line(stats, stats_count, appctx);
 }
 
 enum srv_stats_state {
@@ -1688,8 +1731,6 @@ int stats_fill_sv_stats(struct proxy *px, struct server *sv, int flags,
        if (len < ST_F_TOTAL_FIELDS)
                return 0;
 
-       memset(stats, 0, sizeof(*stats) * len);
-
        /* we have "via" which is the tracked server as described in the configuration,
         * and "ref" which is the checked server and the end of the chain.
         */
@@ -1938,11 +1979,32 @@ int stats_fill_sv_stats(struct proxy *px, struct server *sv, int flags,
 static int stats_dump_sv_stats(struct stream_interface *si, struct proxy *px, struct server *sv)
 {
        struct appctx *appctx = __objt_appctx(si->end);
+       struct stats_module *mod;
+       struct field *stats = stat_l[STATS_DOMAIN_PROXY];
+       size_t stats_count = ST_F_TOTAL_FIELDS;
+
+       memset(stats, 0, sizeof(struct field) * stat_count[STATS_DOMAIN_PROXY]);
 
        if (!stats_fill_sv_stats(px, sv, appctx->ctx.stats.flags, stats, ST_F_TOTAL_FIELDS))
                return 0;
 
-       return stats_dump_one_line(stats, ST_F_TOTAL_FIELDS, appctx);
+       list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+               void *counters;
+
+               if (stats_get_domain(mod->domain_flags) != STATS_DOMAIN_PROXY)
+                       continue;
+
+               if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_SRV)) {
+                       stats_count += mod->stats_count;
+                       continue;
+               }
+
+               counters = EXTRA_COUNTERS_GET(sv->extra_counters, mod);
+               mod->fill_stats(counters, stats + stats_count);
+               stats_count += mod->stats_count;
+       }
+
+       return stats_dump_one_line(stats, stats_count, appctx);
 }
 
 /* Fill <stats> with the backend statistics. <stats> is
@@ -1959,8 +2021,6 @@ int stats_fill_be_stats(struct proxy *px, int flags, struct field *stats, int le
        if (len < ST_F_TOTAL_FIELDS)
                return 0;
 
-       memset(stats, 0, sizeof(*stats) * len);
-
        stats[ST_F_PXNAME]   = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, px->id);
        stats[ST_F_SVNAME]   = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, "BACKEND");
        stats[ST_F_MODE]     = mkf_str(FO_CONFIG|FS_SERVICE, proxy_mode_str(px->mode));
@@ -2052,6 +2112,9 @@ int stats_fill_be_stats(struct proxy *px, int flags, struct field *stats, int le
 static int stats_dump_be_stats(struct stream_interface *si, struct proxy *px)
 {
        struct appctx *appctx = __objt_appctx(si->end);
+       struct field *stats = stat_l[STATS_DOMAIN_PROXY];
+       struct stats_module *mod;
+       size_t stats_count = ST_F_TOTAL_FIELDS;
 
        if (!(px->cap & PR_CAP_BE))
                return 0;
@@ -2059,10 +2122,28 @@ static int stats_dump_be_stats(struct stream_interface *si, struct proxy *px)
        if ((appctx->ctx.stats.flags & STAT_BOUND) && !(appctx->ctx.stats.type & (1 << STATS_TYPE_BE)))
                return 0;
 
+       memset(stats, 0, sizeof(struct field) * stat_count[STATS_DOMAIN_PROXY]);
+
        if (!stats_fill_be_stats(px, appctx->ctx.stats.flags, stats, ST_F_TOTAL_FIELDS))
                return 0;
 
-       return stats_dump_one_line(stats, ST_F_TOTAL_FIELDS, appctx);
+       list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+               struct extra_counters *counters;
+
+               if (stats_get_domain(mod->domain_flags) != STATS_DOMAIN_PROXY)
+                       continue;
+
+               if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_BE)) {
+                       stats_count += mod->stats_count;
+                       continue;
+               }
+
+               counters = EXTRA_COUNTERS_GET(px->extra_counters_be, mod);
+               mod->fill_stats(counters, stats + stats_count);
+               stats_count += mod->stats_count;
+       }
+
+       return stats_dump_one_line(stats, stats_count, appctx);
 }
 
 /* Dumps the HTML table header for proxy <px> to the trash for and uses the state from
@@ -4040,13 +4121,131 @@ static int cli_io_handler_dump_json_schema(struct appctx *appctx)
        return stats_dump_json_schema_to_buffer(appctx->owner);
 }
 
+static int stats_allocate_proxy_counters_internal(struct extra_counters **counters,
+                                                  int type, int px_cap)
+{
+       struct stats_module *mod;
+
+       EXTRA_COUNTERS_REGISTER(counters, type, alloc_failed);
+
+       list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+               if (!(stats_px_get_cap(mod->domain_flags) & px_cap))
+                       continue;
+
+               EXTRA_COUNTERS_ADD(mod, *counters, mod->counters, mod->counters_size);
+       }
+
+       EXTRA_COUNTERS_ALLOC(*counters, alloc_failed);
+
+       list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+               if (!(stats_px_get_cap(mod->domain_flags) & px_cap))
+                       continue;
+
+               EXTRA_COUNTERS_INIT(*counters, mod, mod->counters, mod->counters_size);
+       }
+
+       return 1;
+
+  alloc_failed:
+       return 0;
+}
+
+/* Initialize and allocate all extra counters for a proxy and its attached
+ * servers/listeners with all already registered stats module
+ */
+int stats_allocate_proxy_counters(struct proxy *px)
+{
+       struct server *sv;
+       struct listener *li;
+
+       if (px->cap & PR_CAP_FE) {
+               if (!stats_allocate_proxy_counters_internal(&px->extra_counters_fe,
+                                                           COUNTERS_FE,
+                                                           STATS_PX_CAP_FE)) {
+                       return 0;
+               }
+       }
+
+       if (px->cap & PR_CAP_BE) {
+               if (!stats_allocate_proxy_counters_internal(&px->extra_counters_be,
+                                                           COUNTERS_BE,
+                                                           STATS_PX_CAP_BE)) {
+                       return 0;
+               }
+       }
+
+       for (sv = px->srv; sv; sv = sv->next) {
+               if (!stats_allocate_proxy_counters_internal(&sv->extra_counters,
+                                                           COUNTERS_SV,
+                                                           STATS_PX_CAP_SRV)) {
+                       return 0;
+               }
+       }
+
+       list_for_each_entry(li, &px->conf.listeners, by_fe) {
+               if (!stats_allocate_proxy_counters_internal(&li->extra_counters,
+                                                           COUNTERS_LI,
+                                                           STATS_PX_CAP_LI)) {
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
 void stats_register_module(struct stats_module *m)
 {
        const uint8_t domain = stats_get_domain(m->domain_flags);
 
        LIST_ADDQ(&stats_module_list[domain], &m->list);
+       stat_count[domain] += m->stats_count;
 }
 
+static int allocate_stats_px_postcheck(void)
+{
+       struct stats_module *mod;
+       size_t i = ST_F_TOTAL_FIELDS;
+       int err_code = 0;
+
+       stat_count[STATS_DOMAIN_PROXY] += ST_F_TOTAL_FIELDS;
+
+       stat_f[STATS_DOMAIN_PROXY] = malloc(stat_count[STATS_DOMAIN_PROXY] * sizeof(struct name_desc));
+       if (!stat_f[STATS_DOMAIN_PROXY]) {
+               ha_alert("stats: cannot allocate all fields for proxy statistics\n");
+               err_code |= ERR_ALERT | ERR_FATAL;
+               return err_code;
+       }
+
+       memcpy(stat_f[STATS_DOMAIN_PROXY], stat_fields,
+              ST_F_TOTAL_FIELDS * sizeof(struct name_desc));
+
+       list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+               memcpy(stat_f[STATS_DOMAIN_PROXY] + i,
+                      mod->stats,
+                      mod->stats_count * sizeof(struct name_desc));
+               i += mod->stats_count;
+       }
+
+       for (struct proxy *px = proxies_list; px; px = px->next) {
+               if (!stats_allocate_proxy_counters(px)) {
+                       ha_alert("stats: cannot allocate all counters for proxy statistics\n");
+                       err_code |= ERR_ALERT | ERR_FATAL;
+                       return err_code;
+               }
+       }
+
+       stat_l[STATS_DOMAIN_PROXY] = malloc(stat_count[STATS_DOMAIN_PROXY] * sizeof(struct field));
+       if (!stat_l[STATS_DOMAIN_PROXY]) {
+               ha_alert("stats: cannot allocate a line for proxy statistics\n");
+               err_code |= ERR_ALERT | ERR_FATAL;
+               return err_code;
+       }
+
+       return err_code;
+}
+
+REGISTER_CONFIG_POSTPARSER("allocate-stats-px", allocate_stats_px_postcheck);
+
 /* register cli keywords */
 static struct cli_kw_list cli_kws = {{ },{
        { { "clear", "counters",  NULL }, "clear counters : clear max statistics counters (add 'all' for all counters)", cli_parse_clear_counters, NULL, NULL },