MINOR: stats: introduce a more expressive stat definition method

Previously, statistics were simply defined as a list of name_desc, as
for example "stat_cols_px" for proxy stats. No notion of type was fixed
for each stat definition. This correspondance was done individually
inside stats_fill_*_line() functions. This renders the process to
define new statistics tedious.

Implement a more expressive stat definition method via a new API. A new
type "struct stat_col" for stat column to replace name_desc usage is
defined. It contains a field to store the stat nature and format. A
<cap> field is also defined to be able to define a proxy stat only for
certain type of objects.

This new type is also further extended to include counter offsets. This
allows to define a method to automatically generate a stat value field
from a "struct stat_col". This will be the subject of a future commit.

New type "struct stat_col" is fully compatible full name_desc. This
allows to gradually convert stats definition. The focus will be first
for proxies counters to implement statistics preservation on reload.
This commit is contained in:
Amaury Denoyelle 2024-03-28 14:53:39 +01:00
parent 861370a6d4
commit 65624876f2
9 changed files with 66 additions and 21 deletions

View File

@ -337,6 +337,20 @@ enum stat_idx_info {
ST_I_INF_MAX
};
/* Represent an exposed statistic. */
struct stat_col {
const char *name; /* short name, used notably in CSV headers */
const char *desc; /* user-friendly description */
uint32_t type; /* combination of field_nature and field_format */
uint8_t cap; /* mask of stats_domain_px_cap to restrain metrics to an object types subset */
/* used only for generic metrics */
struct {
int offset[2]; /* offset in counters */
} metric;
};
/* Stats columns for CSV output. For any column added here, please add the text
* representation in the metrics_px array. Please only append at the end,
@ -494,7 +508,7 @@ struct stats_module {
/* functor used to generate the stats module using counters provided through data parameter */
int (*fill_stats)(void *data, struct field *, unsigned int *);
struct name_desc *stats; /* name/description of stats provided by the module */
struct stat_col *stats; /* statistics provided by the module */
void *counters; /* initial values of allocated counters */
size_t counters_off[COUNTERS_OFF_END]; /* list of offsets of allocated counters in various objects */
size_t stats_count; /* count of stats provided */

View File

@ -38,7 +38,7 @@ struct stconn;
/* These two structs contains all column names and descriptions according to
* the the number of entries in "enum stat_idx_px" and "enum stat_idx_info"
*/
extern const struct name_desc stat_cols_px[];
extern const struct stat_col stat_cols_px[];
extern const struct name_desc stat_cols_info[];
extern const char *stat_status_codes[];
extern struct applet http_stats_applet;

View File

@ -35,7 +35,7 @@ enum {
H3_STATS_COUNT /* must be the last */
};
static struct name_desc h3_stats[] = {
static struct stat_col h3_stats[] = {
/* h3 frame type counters */
[H3_ST_DATA] = { .name = "h3_data",
.desc = "Total number of DATA frames received" },

View File

@ -227,7 +227,7 @@ enum {
};
static struct name_desc h1_stats[] = {
static struct stat_col h1_stats[] = {
[H1_ST_OPEN_CONN] = { .name = "h1_open_connections",
.desc = "Count of currently open connections" },
[H1_ST_OPEN_STREAM] = { .name = "h1_open_streams",

View File

@ -306,7 +306,7 @@ enum {
H2_STATS_COUNT /* must be the last member of the enum */
};
static struct name_desc h2_stats[] = {
static struct stat_col h2_stats[] = {
[H2_ST_HEADERS_RCVD] = { .name = "h2_headers_rcvd",
.desc = "Total number of received HEADERS frames" },
[H2_ST_DATA_RCVD] = { .name = "h2_data_rcvd",

View File

@ -2,7 +2,7 @@
#include <haproxy/quic_stats-t.h>
#include <haproxy/stats.h>
static struct name_desc quic_stats[] = {
static struct stat_col quic_stats[] = {
[QUIC_ST_RXBUF_FULL] = { .name = "quic_rxbuf_full",
.desc = "Total number of cancelled reception due to full receiver buffer" },
[QUIC_ST_DROPPED_PACKET] = { .name = "quic_dropped_pkt",

View File

@ -96,7 +96,7 @@ enum {
RSLV_STAT_END,
};
static struct name_desc resolv_stats[] = {
static struct stat_col resolv_stats[] = {
[RSLV_STAT_ID] = { .name = "id", .desc = "ID" },
[RSLV_STAT_PID] = { .name = "pid", .desc = "Parent ID" },
[RSLV_STAT_SENT] = { .name = "sent", .desc = "Sent" },

View File

@ -159,7 +159,7 @@ enum {
SSL_ST_STATS_COUNT /* must be the last member of the enum */
};
static struct name_desc ssl_stats[] = {
static struct stat_col ssl_stats[] = {
[SSL_ST_SESS] = { .name = "ssl_sess",
.desc = "Total number of ssl sessions established" },
[SSL_ST_REUSED_SESS] = { .name = "ssl_reused_sess",

View File

@ -68,6 +68,35 @@
#include <haproxy/uri_auth-t.h>
#include <haproxy/version.h>
/* Define a new metric for both frontend and backend sides. */
#define ME_NEW_PX(name_f, nature, format, offset_f, cap_f, desc_f) \
{ .name = (name_f), .desc = (desc_f), .type = (nature)|(format), \
.metric.offset[0] = offsetof(struct fe_counters, offset_f), \
.metric.offset[1] = offsetof(struct be_counters, offset_f), \
.cap = (cap_f), \
}
/* Define a new metric for frontend side only. */
#define ME_NEW_FE(name_f, nature, format, offset_f, cap_f, desc_f) \
{ .name = (name_f), .desc = (desc_f), .type = (nature)|(format), \
.metric.offset[0] = offsetof(struct fe_counters, offset_f), \
.cap = (cap_f), \
}
/* Define a new metric for backend side only. */
#define ME_NEW_BE(name_f, nature, format, offset_f, cap_f, desc_f) \
{ .name = (name_f), .desc = (desc_f), .type = (nature)|(format), \
.metric.offset[1] = offsetof(struct be_counters, offset_f), \
.cap = (cap_f), \
}
/* Convert stat_col <col> to old-style <name> as name_desc. */
static void stcol2ndesc(struct name_desc *name, const struct stat_col *col)
{
name->name = col->name;
name->desc = col->desc;
}
/* status codes available for the stats admin page (strictly 4 chars length) */
const char *stat_status_codes[STAT_STATUS_SIZE] = {
@ -167,7 +196,7 @@ const struct name_desc stat_cols_info[ST_I_INF_MAX] = {
/* one line of info */
THREAD_LOCAL struct field stat_line_info[ST_I_INF_MAX];
const struct name_desc stat_cols_px[ST_I_PX_MAX] = {
const struct stat_col stat_cols_px[ST_I_PX_MAX] = {
[ST_I_PX_PXNAME] = { .name = "pxname", .desc = "Proxy name" },
[ST_I_PX_SVNAME] = { .name = "svname", .desc = "Server name" },
[ST_I_PX_QCUR] = { .name = "qcur", .desc = "Number of current queued connections" },
@ -2823,7 +2852,7 @@ void stats_register_module(struct stats_module *m)
static int allocate_stats_px_postcheck(void)
{
struct stats_module *mod;
size_t i = ST_I_PX_MAX;
size_t i = ST_I_PX_MAX, offset;
int err_code = 0;
struct proxy *px;
@ -2836,14 +2865,15 @@ static int allocate_stats_px_postcheck(void)
return err_code;
}
memcpy(stat_cols[STATS_DOMAIN_PROXY], stat_cols_px,
ST_I_PX_MAX * sizeof(struct name_desc));
for (i = 0; i < ST_I_PX_MAX; ++i)
stcol2ndesc(&stat_cols[STATS_DOMAIN_PROXY][i], &stat_cols_px[i]);
list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
memcpy(stat_cols[STATS_DOMAIN_PROXY] + i,
mod->stats,
mod->stats_count * sizeof(struct name_desc));
i += mod->stats_count;
for (offset = i, i = 0; i < mod->stats_count; ++i) {
stcol2ndesc(&stat_cols[STATS_DOMAIN_PROXY][offset + i],
&mod->stats[i]);
}
i += offset;
}
for (px = proxies_list; px; px = px->next) {
@ -2864,7 +2894,7 @@ REGISTER_CONFIG_POSTPARSER("allocate-stats-px", allocate_stats_px_postcheck);
static int allocate_stats_rslv_postcheck(void)
{
struct stats_module *mod;
size_t i = 0;
size_t i = 0, offset;
int err_code = 0;
stat_cols[STATS_DOMAIN_RESOLVERS] = malloc(stat_cols_len[STATS_DOMAIN_RESOLVERS] * sizeof(struct name_desc));
@ -2875,10 +2905,11 @@ static int allocate_stats_rslv_postcheck(void)
}
list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_RESOLVERS], list) {
memcpy(stat_cols[STATS_DOMAIN_RESOLVERS] + i,
mod->stats,
mod->stats_count * sizeof(struct name_desc));
i += mod->stats_count;
for (offset = i, i = 0; i < mod->stats_count; ++i) {
stcol2ndesc(&stat_cols[STATS_DOMAIN_RESOLVERS][offset + i],
&mod->stats[i]);
}
i += offset;
}
if (!resolv_allocate_counters(&stats_module_list[STATS_DOMAIN_RESOLVERS])) {