diff --git a/src/stats.c b/src/stats.c
index 2042687f0..ae2f58d2b 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -370,6 +370,20 @@ int stats_emit_raw_data_field(struct buffer *out, const struct field *f)
}
}
+const char *field_to_html_str(const struct field *f)
+{
+ switch (field_format(f, 0)) {
+ case FF_S32: return U2H(f->u.s32);
+ case FF_S64: return U2H(f->u.s64);
+ case FF_U64: return U2H(f->u.u64);
+ case FF_U32: return U2H(f->u.u32);
+ case FF_STR: return field_str(f, 0);
+ case FF_EMPTY:
+ default:
+ return "";
+ }
+}
+
/* Emits a stats field prefixed with its type. No CSV encoding is prepared, the
* output is supposed to be used on its own line. Returns non-zero on success, 0
* if the buffer is full.
@@ -757,6 +771,8 @@ static int stats_dump_fields_html(struct buffer *out,
unsigned int flags)
{
struct buffer src;
+ struct stats_module *mod;
+ int i = 0, j = 0;
if (stats[ST_F_TYPE].u.u32 == STATS_TYPE_FE) {
chunk_appendf(out,
@@ -900,11 +916,32 @@ static int stats_dump_fields_html(struct buffer *out,
/* server status : reflect frontend status */
"
%s | "
/* rest of server: nothing */
- " | "
+ " | "
"",
U2H(stats[ST_F_DREQ].u.u64), U2H(stats[ST_F_DRESP].u.u64),
U2H(stats[ST_F_EREQ].u.u64),
field_str(stats, ST_F_STATUS));
+
+ list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+ chunk_appendf(out,
+ "%s",
+ mod->name);
+ if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_FE) {
+ for (j = 0; j < mod->stats_count; ++j) {
+ chunk_appendf(out,
+ "%s | %s | ",
+ mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
+ ++i;
+ }
+ } else {
+ i += mod->stats_count;
+ }
+
+ chunk_appendf(out,
+ " | ");
+ }
+
+ chunk_appendf(out, "");
}
else if (stats[ST_F_TYPE].u.u32 == STATS_TYPE_SO) {
chunk_appendf(out, "");
@@ -961,11 +998,32 @@ static int stats_dump_fields_html(struct buffer *out,
/* server status: reflect listener status */
"%s | "
/* rest of server: nothing */
- " |
"
+ " | "
"",
U2H(stats[ST_F_DREQ].u.u64), U2H(stats[ST_F_DRESP].u.u64),
U2H(stats[ST_F_EREQ].u.u64),
field_str(stats, ST_F_STATUS));
+
+ list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+ chunk_appendf(out,
+ "%s",
+ mod->name);
+ if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_LI) {
+ for (j = 0; j < mod->stats_count; ++j) {
+ chunk_appendf(out,
+ "%s | %s | ",
+ mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
+ ++i;
+ }
+ } else {
+ i += mod->stats_count;
+ }
+
+ chunk_appendf(out,
+ " | ");
+ }
+
+ chunk_appendf(out, "");
}
else if (stats[ST_F_TYPE].u.u32 == STATS_TYPE_SV) {
const char *style;
@@ -1278,9 +1336,30 @@ static int stats_dump_fields_html(struct buffer *out,
/* throttle */
if (stats[ST_F_THROTTLE].type)
- chunk_appendf(out, "%d %% | \n", stats[ST_F_THROTTLE].u.u32);
+ chunk_appendf(out, "%d %% | \n", stats[ST_F_THROTTLE].u.u32);
else
- chunk_appendf(out, "- | \n");
+ chunk_appendf(out, "- | ");
+
+ list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+ chunk_appendf(out,
+ "%s",
+ mod->name);
+ if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_SRV) {
+ for (j = 0; j < mod->stats_count; ++j) {
+ chunk_appendf(out,
+ "%s | %s | ",
+ mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
+ ++i;
+ }
+ } else {
+ i += mod->stats_count;
+ }
+
+ chunk_appendf(out,
+ " | ");
+ }
+
+ chunk_appendf(out, "\n");
}
else if (stats[ST_F_TYPE].u.u32 == STATS_TYPE_BE) {
chunk_appendf(out, "");
@@ -1446,11 +1525,30 @@ static int stats_dump_fields_html(struct buffer *out,
/* rest of backend: nothing, down transitions, total downtime, throttle */
" | %d | "
"%s | "
- " | "
- "
",
+ " | ",
stats[ST_F_CHKDOWN].u.u32,
stats[ST_F_DOWNTIME].type ? human_time(stats[ST_F_DOWNTIME].u.u32, 1) : " ");
+
+ list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+ chunk_appendf(out,
+ "%s",
+ mod->name);
+ if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_BE) {
+ for (j = 0; j < mod->stats_count; ++j) {
+ chunk_appendf(out,
+ "%s | %s | ",
+ mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
+ ++i;
+ }
+ } else {
+ i += mod->stats_count;
+ }
+ chunk_appendf(out,
+ " | ");
+ }
+ chunk_appendf(out, "");
}
+
return 1;
}
@@ -2154,6 +2252,8 @@ static void stats_dump_html_px_hdr(struct stream_interface *si, struct proxy *px
{
struct appctx *appctx = __objt_appctx(si->end);
char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
+ struct stats_module *mod;
+ int stats_module_len = 0;
if (px->cap & PR_CAP_BE && px->srv && (appctx->ctx.stats.flags & STAT_ADMIN)) {
/* A form to enable/disable this proxy servers */
@@ -2216,6 +2316,11 @@ static void stats_dump_html_px_hdr(struct stream_interface *si, struct proxy *px
chunk_appendf(&trash, " | ");
}
+ // calculate the count of module for colspan attribute
+ list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+ ++stats_module_len;
+ }
+
chunk_appendf(&trash,
" | "
"Queue | "
@@ -2223,6 +2328,7 @@ static void stats_dump_html_px_hdr(struct stream_interface *si, struct proxy *px
"Bytes | Denied | "
"Errors | Warnings | "
"Server | "
+ "Extra modules | "
"\n"
""
"Cur | Max | Limit | "
@@ -2232,8 +2338,13 @@ static void stats_dump_html_px_hdr(struct stream_interface *si, struct proxy *px
"Resp | Retr | Redis | "
"Status | LastChk | Wght | Act | "
"Bck | Chk | Dwn | Dwntme | "
- "Thrtle | \n"
- "
");
+ "Thrtle | \n", stats_module_len);
+
+ list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+ chunk_appendf(&trash, "%s | ", mod->name);
+ }
+
+ chunk_appendf(&trash, "");
}
/* Dumps the HTML table trailer for proxy to the trash for and uses the state from