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.
This commit is contained in:
Amaury Denoyelle 2024-04-16 18:17:48 +02:00
parent 6615252656
commit 83281303f6
5 changed files with 43 additions and 25 deletions

View File

@ -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) {

View File

@ -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

View File

@ -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);

View File

@ -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<ST_I_PX_MAX; i++) {
lua_pushstring(L, stat_cols_px[i].name);

View File

@ -693,26 +693,34 @@ static int stcol_hide(enum stat_idx_px idx, enum obj_type *objt)
/* Generate if possible a metric value from <col>. <cap> 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 <counters>.
* type. <stat_file> must be set when dumping stats-file. Metric value will be
* extracted from <counters>.
*
* 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))
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 };
/* Check if metric should be hidden in output. */
if (stcol_hide(idx, objt))
}
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);