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.
This commit is contained in:
Amaury Denoyelle 2020-10-05 11:49:42 +02:00 committed by Christopher Faulet
parent 730c727ea3
commit ee63d4bd67
6 changed files with 242 additions and 24 deletions

View 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

View 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 {

View 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);
};

View 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 */

View File

@ -119,6 +119,7 @@
#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);
}

View 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);
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, "-,");
/* 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 },