From 83281303f69291d4df9b3e40ee8d098b6b3a3dd9 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Tue, 16 Apr 2024 18:17:48 +0200 Subject: [PATCH] MINOR: stats: define stats-file output format support Prepare stats function to handle a new format labelled "stats-file". Its purpose is to generate a statistics dump with a format closed from the CSV output. Such output will be then used to preload haproxy internal counters on process startup. stats-file output differs from a standard CSV on several points. First, only an excerpt of all statistics is outputted. All values that does not make sense to preload are excluded. For the moment, stats-file only list stats fully defined via "struct stat_col" method. Contrary to a CSV, sll columns of a stats-file will be filled. As such, empty field value is used to mark stats which should not be outputted. Some adaptation specifics to stats-file are necessary into me_generate_field(). First, stats-file will output separatedly values from frontend and backend sides with their own respective set of columns. As such, an empty field value is returned if stat is not defined for either frontend/listener, or backend/server when outputting the other side. Also, as stats-file does not support empty column, stcol_hide() is not used for it. A minor adjustement was necessary for stats_fill_fe_line() to pass context flags. This is necessary to detect stat output format. All other listener/server/backend corresponding functions already have it. --- addons/promex/service-prometheus.c | 2 +- include/haproxy/stats-t.h | 5 +-- include/haproxy/stats.h | 2 +- src/hlua_fcn.c | 2 +- src/stats.c | 57 +++++++++++++++++++----------- 5 files changed, 43 insertions(+), 25 deletions(-) diff --git a/addons/promex/service-prometheus.c b/addons/promex/service-prometheus.c index 82427e2dce..4e0bc68e10 100644 --- a/addons/promex/service-prometheus.c +++ b/addons/promex/service-prometheus.c @@ -659,7 +659,7 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx) if ((px->flags & PR_FL_DISABLED) || px->uuid <= 0 || !(px->cap & PR_CAP_FE)) goto next_px; - if (!stats_fill_fe_line(px, stats, ST_I_PX_MAX, &(ctx->field_num))) + if (!stats_fill_fe_line(px, 0, stats, ST_I_PX_MAX, &(ctx->field_num))) return -1; switch (ctx->field_num) { diff --git a/include/haproxy/stats-t.h b/include/haproxy/stats-t.h index 61ac86d4c8..320dfb1ef2 100644 --- a/include/haproxy/stats-t.h +++ b/include/haproxy/stats-t.h @@ -29,7 +29,7 @@ #define STAT_F_FMT_HTML 0x00000001 /* dump the stats in HTML format */ #define STAT_F_FMT_TYPED 0x00000002 /* use the typed output format */ #define STAT_F_FMT_JSON 0x00000004 /* dump the stats in JSON format */ -#define STAT_F_HIDE_DOWN 0x00000008 /* hide 'down' servers in the stats page */ +#define STAT_F_FMT_FILE 0x00000008 /* dump stats-file */ #define STAT_F_NO_REFRESH 0x00000010 /* do not automatically refresh the stats page */ #define STAT_F_ADMIN 0x00000020 /* indicate a stats admin level */ #define STAT_F_CHUNKED 0x00000040 /* use chunked encoding (HTTP/1.1) */ @@ -44,11 +44,12 @@ #define STAT_F_HIDE_MAINT 0x00004000 /* hide maint/disabled servers */ #define STAT_F_CONVDONE 0x00008000 /* conf: rules conversion done */ #define STAT_F_USE_FLOAT 0x00010000 /* use floats where possible in the outputs */ +#define STAT_F_HIDE_DOWN 0x00020000 /* hide 'down' servers in the stats page */ #define STAT_F_BOUND 0x00800000 /* bound statistics to selected proxies/types/services */ #define STAT_F_STARTED 0x01000000 /* some output has occurred */ -#define STAT_F_FMT_MASK 0x00000007 +#define STAT_F_FMT_MASK 0x0000000f #define STATS_TYPE_FE 0 #define STATS_TYPE_BE 1 diff --git a/include/haproxy/stats.h b/include/haproxy/stats.h index e364824db9..d1ab6b3a70 100644 --- a/include/haproxy/stats.h +++ b/include/haproxy/stats.h @@ -55,7 +55,7 @@ const char *stats_scope_ptr(struct appctx *appctx); int stats_dump_one_line(const struct field *line, size_t stats_count, struct appctx *appctx); int stats_fill_info(struct field *info, int len, uint flags); -int stats_fill_fe_line(struct proxy *px, struct field *line, int len, +int stats_fill_fe_line(struct proxy *px, int flags, struct field *line, int len, enum stat_idx_px *index); int stats_fill_li_line(struct proxy *px, struct listener *l, int flags, struct field *line, int len, enum stat_idx_px *index); diff --git a/src/hlua_fcn.c b/src/hlua_fcn.c index 52db1af5ec..0340ce1f3c 100644 --- a/src/hlua_fcn.c +++ b/src/hlua_fcn.c @@ -2054,7 +2054,7 @@ int hlua_proxy_get_stats(lua_State *L) if (px->cap & PR_CAP_BE) stats_fill_be_line(px, STAT_F_SHLGNDS, stats, STATS_LEN, NULL); else - stats_fill_fe_line(px, stats, STATS_LEN, NULL); + stats_fill_fe_line(px, 0, stats, STATS_LEN, NULL); lua_newtable(L); for (i=0; i. must be set to one of * STATS_PX_CAP_* values to check if the metric is available for this object - * type. Metric value will be extracted from . + * type. must be set when dumping stats-file. Metric value will be + * extracted from . * - * Returns a field value or an empty one if cap not compatible. + * Returns a field metric. */ static struct field me_generate_field(const struct stat_col *col, enum stat_idx_px idx, enum obj_type *objt, - const void *counters, uint8_t cap) + const void *counters, uint8_t cap, + int stat_file) { struct field value; void *counter = NULL; + int wrong_side = 0; + + /* Only generic stat column must be used as input. */ + BUG_ON(!stcol_is_generic(col)); switch (cap) { case STATS_PX_CAP_FE: case STATS_PX_CAP_LI: counter = (char *)counters + col->metric.offset[0]; + wrong_side = !(col->cap & (STATS_PX_CAP_FE|STATS_PX_CAP_LI)); break; case STATS_PX_CAP_BE: case STATS_PX_CAP_SRV: counter = (char *)counters + col->metric.offset[1]; + wrong_side = !(col->cap & (STATS_PX_CAP_BE|STATS_PX_CAP_SRV)); break; default: @@ -720,13 +728,18 @@ static struct field me_generate_field(const struct stat_col *col, ABORT_NOW(); } - /* Check if metric is defined for this side. */ - if (!(col->cap & cap)) - return (struct field){ .type = FF_EMPTY }; - - /* Check if metric should be hidden in output. */ - if (stcol_hide(idx, objt)) - return (struct field){ .type = FF_EMPTY }; + if (stat_file) { + /* stats-file emits separately frontend and backend stats. + * Skip metric if not defined for any object on the cap side. + */ + if (wrong_side) + return (struct field){ .type = FF_EMPTY }; + } + else { + /* Ensure metric is defined for the current cap. */ + if (!(col->cap & cap) || stcol_hide(idx, objt)) + return (struct field){ .type = FF_EMPTY }; + } switch (stcol_format(col)) { case FF_U64: @@ -746,7 +759,7 @@ static struct field me_generate_field(const struct stat_col *col, * this value, or if the selected field is not implemented for frontends, the * function returns 0, otherwise, it returns 1. */ -int stats_fill_fe_line(struct proxy *px, struct field *line, int len, +int stats_fill_fe_line(struct proxy *px, int flags, struct field *line, int len, enum stat_idx_px *index) { enum stat_idx_px i = index ? *index : 0; @@ -760,9 +773,10 @@ int stats_fill_fe_line(struct proxy *px, struct field *line, int len, if (stcol_is_generic(col)) { field = me_generate_field(col, i, &px->obj_type, - &px->fe_counters, STATS_PX_CAP_FE); + &px->fe_counters, STATS_PX_CAP_FE, + flags & STAT_F_FMT_FILE); } - else { + else if (!(flags & STAT_F_FMT_FILE)) { switch (i) { case ST_I_PX_PXNAME: field = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, px->id); @@ -889,7 +903,7 @@ static int stats_dump_fe_line(struct stconn *sc, struct proxy *px) memset(line, 0, sizeof(struct field) * stat_cols_len[STATS_DOMAIN_PROXY]); - if (!stats_fill_fe_line(px, line, ST_I_PX_MAX, NULL)) + if (!stats_fill_fe_line(px, ctx->flags, line, ST_I_PX_MAX, NULL)) return 0; list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) { @@ -935,9 +949,10 @@ int stats_fill_li_line(struct proxy *px, struct listener *l, int flags, if (stcol_is_generic(col)) { field = me_generate_field(col, i, &l->obj_type, - l->counters, STATS_PX_CAP_LI); + l->counters, STATS_PX_CAP_LI, + flags & STAT_F_FMT_FILE); } - else { + else if (!(flags & STAT_F_FMT_FILE)) { switch (i) { case ST_I_PX_PXNAME: field = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, px->id); @@ -1184,9 +1199,10 @@ int stats_fill_sv_line(struct proxy *px, struct server *sv, int flags, if (stcol_is_generic(col)) { field = me_generate_field(col, i, &sv->obj_type, - &sv->counters, STATS_PX_CAP_SRV); + &sv->counters, STATS_PX_CAP_SRV, + flags & STAT_F_FMT_FILE); } - else { + else if (!(flags & STAT_F_FMT_FILE)) { switch (i) { case ST_I_PX_PXNAME: field = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, px->id); @@ -1570,9 +1586,10 @@ int stats_fill_be_line(struct proxy *px, int flags, struct field *line, int len, if (stcol_is_generic(col)) { field = me_generate_field(col, i, &px->obj_type, - &px->be_counters, STATS_PX_CAP_BE); + &px->be_counters, STATS_PX_CAP_BE, + flags & STAT_F_FMT_FILE); } - else { + else if (!(flags & STAT_F_FMT_FILE)) { switch (i) { case ST_I_PX_PXNAME: field = mkf_str(FO_KEY|FN_NAME|FS_SERVICE, px->id);