From ee63d4bd6743fee650c4483e3de6daa2c589822e Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Mon, 5 Oct 2020 11:49:42 +0200 Subject: [PATCH] MEDIUM: stats: integrate static proxies stats in new stats 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 | 5 +- include/haproxy/proxy-t.h | 4 + include/haproxy/server-t.h | 3 + include/haproxy/stats.h | 2 + src/haproxy.c | 7 + src/stats.c | 247 +++++++++++++++++++++++++++++++---- 6 files changed, 243 insertions(+), 25 deletions(-) diff --git a/include/haproxy/listener-t.h b/include/haproxy/listener-t.h index cc5546c503..0af6353a8f 100644 --- a/include/haproxy/listener-t.h +++ b/include/haproxy/listener-t.h @@ -30,7 +30,8 @@ #include #include #include -#include +#include +#include #ifdef USE_OPENSSL #include @@ -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 diff --git a/include/haproxy/proxy-t.h b/include/haproxy/proxy-t.h index baffcc0deb..2680fbffac 100644 --- a/include/haproxy/proxy-t.h +++ b/include/haproxy/proxy-t.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -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 { diff --git a/include/haproxy/server-t.h b/include/haproxy/server-t.h index decc5e292d..6ef7e43a5d 100644 --- a/include/haproxy/server-t.h +++ b/include/haproxy/server-t.h @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -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); }; diff --git a/include/haproxy/stats.h b/include/haproxy/stats.h index 272274610f..6246b0405c 100644 --- a/include/haproxy/stats.h +++ b/include/haproxy/stats.h @@ -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 */ diff --git a/src/haproxy.c b/src/haproxy.c index 0c1dd5a97e..49f3fbd89d 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -119,6 +119,7 @@ #include #include #include +#include #include #include #include @@ -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); } diff --git a/src/stats.c b/src/stats.c index 83c0e66453..12b5945084 100644 --- a/src/stats.c +++ b/src/stats.c @@ -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 with the listener statistics. 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 with the backend statistics. 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 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 }, -- 2.39.5