From e7dbfc663417c24260e1668259ec5d30bd24df88 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 23 Dec 2012 01:59:23 +0100 Subject: [PATCH] MEDIUM: stats: use hover boxes instead of title to report details Using titles to report detailed information is not convenient. The browser decides to wrap the line where it wants, generally the box quickly fades away, and it's not possible to copy-paste the text from the box. By using two levels, we can make a block appear/disappear depending on whether its parent it being hovered or not, for example : .tip { display: none; } u:hover .tip { display: block; } or better: .tip { display: block; visibility: hidden; } u:hover .tip { visibility: visible; } Toggling visibility ensures that the place required to display the block is always reserved. This is important to display boxes that are close to the edges. Then using this is a box will make the text appear only when the upper is hovered. But this still adds much text. So instead we use a generic
tag which we don't use anywhere else. That way we don't have to specify a class : div { display: block; visibility: hidden; } u:hover div { visibility: hidden; } This works pretty well, even in old browsers from 2005. This commit does not change the display format, it only replaces the title attribute with the div tag. Later commits will adjust the layout. --- src/dumpstats.c | 222 ++++++++++++++++++++++++++---------------------- 1 file changed, 119 insertions(+), 103 deletions(-) diff --git a/src/dumpstats.c b/src/dumpstats.c index 9109a276dd..4bd05fce37 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -1836,13 +1836,13 @@ static int stats_dump_fe_stats(struct stream_interface *si, struct proxy *px) if (px->mode == PR_MODE_HTTP) chunk_appendf(&trash, /* sessions rate : current, max, limit */ - "%s%s%s" + "%s
Cur: %s req/s
%s
Max: %s req/s
%s" "", - read_freq_ctr(&px->fe_req_per_sec), U2H0(read_freq_ctr(&px->fe_sess_per_sec)), - px->fe_counters.p.http.rps_max, - U2H1(px->fe_counters.sps_max), - LIM2A2(px->fe_sps_lim, "-")); + U2H1(read_freq_ctr(&px->fe_req_per_sec)), + U2H2(px->fe_counters.p.http.rps_max), + U2H3(px->fe_counters.sps_max), + LIM2A4(px->fe_sps_lim, "-")); else chunk_appendf(&trash, /* sessions rate : current, max, limit */ @@ -1852,15 +1852,16 @@ static int stats_dump_fe_stats(struct stream_interface *si, struct proxy *px) U2H1(px->fe_counters.sps_max), LIM2A2(px->fe_sps_lim, "-")); chunk_appendf(&trash, - /* sessions: current, max, limit */ + /* sessions: current, max, limit, total */ "%s%s%s" - "%s%s" "", - U2H3(px->feconn), U2H4(px->fe_counters.conn_max), U2H5(px->maxconn)); + U2H3(px->feconn), U2H4(px->fe_counters.conn_max), U2H5(px->maxconn), + (px->mode == PR_MODE_HTTP)?"":"", U2H6(px->fe_counters.cum_sess)); /* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */ if (px->mode == PR_MODE_HTTP) { - chunk_appendf(&trash, " title=\"%lld requests:", px->fe_counters.p.http.cum_req); + chunk_appendf(&trash, "
%lld requests:", px->fe_counters.p.http.cum_req); for (i = 1; i < 6; i++) chunk_appendf(&trash, " %dxx=%lld,", i, px->fe_counters.p.http.rsp[i]); @@ -1870,32 +1871,25 @@ static int stats_dump_fe_stats(struct stream_interface *si, struct proxy *px) px->fe_counters.p.http.comp_rsp, px->fe_counters.p.http.rsp[2] ? (int)(100*px->fe_counters.p.http.comp_rsp/px->fe_counters.p.http.rsp[2]) : 0); - chunk_appendf(&trash, " intercepted=%lld\"", px->fe_counters.intercepted_req); + chunk_appendf(&trash, " intercepted=%lld
", px->fe_counters.intercepted_req); } chunk_appendf(&trash, - /* sessions: total, lbtot */ - ">%s%s%s" + /* sessions: lbtot */ + "" /* bytes : in */ - "%s%s" "", - (px->mode == PR_MODE_HTTP)?"":"", - U2H6(px->fe_counters.cum_sess), - (px->mode == PR_MODE_HTTP)?"":"", U2H7(px->fe_counters.bytes_in)); - /* compression stats (via td title): comp_in, comp_out, comp_byp */ - chunk_appendf(&trash, " title=\"compression: in=%lld out=%lld bypassed=%lld savings=%d%%\"", - px->fe_counters.comp_in, px->fe_counters.comp_out, px->fe_counters.comp_byp, - px->fe_counters.comp_in ? - (int)((px->fe_counters.comp_in - px->fe_counters.comp_out)*100/px->fe_counters.comp_in) : 0); - chunk_appendf(&trash, - /* bytes: out */ - ">%s%s%s" - "", + /* bytes:out + compression stats (via hover): comp_in, comp_out, comp_byp */ + "%s%s
compression: in=%lld out=%lld bypassed=%lld savings=%d%%
%s", (px->fe_counters.comp_in || px->fe_counters.comp_byp) ? "":"", U2H0(px->fe_counters.bytes_out), + px->fe_counters.comp_in, px->fe_counters.comp_out, px->fe_counters.comp_byp, + px->fe_counters.comp_in ? + (int)((px->fe_counters.comp_in - px->fe_counters.comp_out)*100/px->fe_counters.comp_in) : 0, (px->fe_counters.comp_in || px->fe_counters.comp_byp) ? "":""); chunk_appendf(&trash, @@ -1997,13 +1991,20 @@ static int stats_dump_li_stats(struct stream_interface *si, struct proxy *px, st /* Column sub-heading for Enable or Disable server */ chunk_appendf(&trash, ""); } - chunk_appendf(&trash, "%s" + "%s" + "", + px->id, l->name, + (flags & ST_SHLGNDS)?"":"", + px->id, l->name, l->name); if (flags & ST_SHLGNDS) { char str[INET6_ADDRSTRLEN]; int port; - chunk_appendf(&trash, " title=\""); + chunk_appendf(&trash, "
"); port = get_host_port(&l->addr); switch (addr_to_str(&l->addr, str, sizeof(str))) { @@ -2022,13 +2023,12 @@ static int stats_dump_li_stats(struct stream_interface *si, struct proxy *px, st } /* id */ - chunk_appendf(&trash, "id: %d\"", l->luid); + chunk_appendf(&trash, "id: %d
", l->luid); } chunk_appendf(&trash, - /* name, queue */ - ">%s" - "%s%s" + /* queue */ + "%s" /* sessions rate: current, max, limit */ " " /* sessions: current, max, limit, total, lbtot */ @@ -2037,8 +2037,6 @@ static int stats_dump_li_stats(struct stream_interface *si, struct proxy *px, st /* bytes: in, out */ "%s%s" "", - (flags & ST_SHLGNDS)?"":"", - px->id, l->name, px->id, l->name, l->name, (flags & ST_SHLGNDS)?"":"", U2H3(l->nbconn), U2H4(l->counters->conn_max), U2H5(l->maxconn), U2H6(l->counters->cum_conn), U2H7(l->counters->bytes_in), U2H8(l->counters->bytes_out)); @@ -2142,10 +2140,16 @@ static int stats_dump_sv_stats(struct stream_interface *si, struct proxy *px, in "", sv->id); - chunk_appendf(&trash, "%s" + "%s" + "", + px->id, sv->id, + (flags & ST_SHLGNDS) ? "" : "", + px->id, sv->id, sv->id); if (flags & ST_SHLGNDS) { - chunk_appendf(&trash, " title=\""); + chunk_appendf(&trash, "
"); switch (addr_to_str(&sv->addr, str, sizeof(str))) { case AF_INET: @@ -2177,43 +2181,39 @@ static int stats_dump_sv_stats(struct stream_interface *si, struct proxy *px, in chunk_appendf(&trash, "'"); } - chunk_appendf(&trash, "\""); + chunk_appendf(&trash, "
"); } chunk_appendf(&trash, - ">%s" - "%s%s" /* queue : current, max, limit */ - "%s%s%s" + "%s%s%s%s" /* sessions rate : current, max, limit */ "%s%s" /* sessions: current, max, limit */ "%s%s%s" - "%s%s" "", - (flags & ST_SHLGNDS) ? "" : "", - px->id, sv->id, px->id, sv->id, sv->id, (flags & ST_SHLGNDS) ? "" : "", U2H0(sv->nbpend), U2H1(sv->counters.nbpend_max), LIM2A2(sv->maxqueue, "-"), U2H3(read_freq_ctr(&sv->sess_per_sec)), U2H4(sv->counters.sps_max), - U2H5(sv->cur_sess), U2H6(sv->counters.cur_sess_max), LIM2A7(sv->maxconn, "-")); + U2H5(sv->cur_sess), U2H6(sv->counters.cur_sess_max), LIM2A7(sv->maxconn, "-"), + (px->mode == PR_MODE_HTTP) ? "" : "", U2H8(sv->counters.cum_sess)); /* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */ if (px->mode == PR_MODE_HTTP) { - chunk_appendf(&trash, " title=\"rsp codes:"); + chunk_appendf(&trash, "
rsp codes:"); for (i = 1; i < 6; i++) chunk_appendf(&trash, " %dxx=%lld,", i, sv->counters.p.http.rsp[i]); - chunk_appendf(&trash, " other=%lld\"", sv->counters.p.http.rsp[0]); + chunk_appendf(&trash, " other=%lld
", sv->counters.p.http.rsp[0]); } chunk_appendf(&trash, - /* sessions: total, lbtot */ - ">%s%s%s%s", - (px->mode == PR_MODE_HTTP)?"":"", - U2H0(sv->counters.cum_sess), - (px->mode == PR_MODE_HTTP)?"":"", + /* sessions: lbtot */ + "%s%s", + (px->mode == PR_MODE_HTTP) ? "
" : "", U2H1(sv->counters.cum_lbconn)); chunk_appendf(&trash, @@ -2224,16 +2224,16 @@ static int stats_dump_sv_stats(struct stream_interface *si, struct proxy *px, in /* errors : request, connect */ "%s" /* errors : response */ - "%s" + "%s
Connection resets during transfers: %lld client, %lld server
" /* warnings: retries, redispatches */ "%lld%lld" "", U2H0(sv->counters.bytes_in), U2H1(sv->counters.bytes_out), U2H2(sv->counters.failed_secu), U2H3(sv->counters.failed_conns), + U2H6(sv->counters.failed_resp), sv->counters.cli_aborts, sv->counters.srv_aborts, - U2H6(sv->counters.failed_resp), sv->counters.retries, sv->counters.redispatches); /* status, lest check */ @@ -2256,16 +2256,8 @@ static int stats_dump_sv_stats(struct stream_interface *si, struct proxy *px, in } if (sv->state & SRV_CHECKED) { - chunk_appendf(&trash, "check.status)); - - if (*sv->check.desc) { - chunk_appendf(&trash, ": "); - chunk_initlen(&src, sv->check.desc, 0, strlen(sv->check.desc)); - chunk_htmlencode(&trash, &src); - } - - chunk_appendf(&trash, "\"> %s%s", + chunk_appendf(&trash, + " %s%s", (sv->state & SRV_CHK_RUNNING) ? "* " : "", get_check_status_info(sv->check.status)); @@ -2273,7 +2265,16 @@ static int stats_dump_sv_stats(struct stream_interface *si, struct proxy *px, in chunk_appendf(&trash, "/%d", sv->check.code); if (sv->check.status >= HCHK_STATUS_CHECKED && sv->check.duration >= 0) - chunk_appendf(&trash, " in %lums", sv->check.duration); + chunk_appendf(&trash, " in %lums", sv->check.duration); + + chunk_appendf(&trash, "
%s", + get_check_status_description(sv->check.status)); + if (*sv->check.desc) { + chunk_appendf(&trash, ": "); + chunk_initlen(&src, sv->check.desc, 0, strlen(sv->check.desc)); + chunk_htmlencode(&trash, &src); + } + chunk_appendf(&trash, "
"); } else chunk_appendf(&trash, ""); @@ -2290,16 +2291,16 @@ static int stats_dump_sv_stats(struct stream_interface *si, struct proxy *px, in /* check failures: unique, fatal, down time */ if (sv->state & SRV_CHECKED) { - chunk_appendf(&trash, "%lld", - ref->observe?"/Health Analyses":"", ref->counters.failed_checks); + chunk_appendf(&trash, "%lld", ref->counters.failed_checks); if (ref->observe) chunk_appendf(&trash, "/%lld", ref->counters.failed_hana); chunk_appendf(&trash, - "" + "
Failed Health Checks%s
" "%lld%s" "", + ref->observe ? "/Health Analyses" : "", ref->counters.down_trans, human_time(srv_downtime(sv), 1)); } else if (sv != ref) @@ -2483,11 +2484,18 @@ static int stats_dump_be_stats(struct stream_interface *si, struct proxy *px, in /* Column sub-heading for Enable or Disable server */ chunk_appendf(&trash, ""); } - chunk_appendf(&trash, "" + /* name */ + "%s" + "Backend" + "", + (flags & ST_SHLGNDS)?"":"", + px->id, px->id); if (flags & ST_SHLGNDS) { /* balancing */ - chunk_appendf(&trash, " title=\"balancing: %s", + chunk_appendf(&trash, "
balancing: %s", backend_lb_algo_str(px->lbprm.algo & BE_LB_ALGO)); /* cookie */ @@ -2497,69 +2505,59 @@ static int stats_dump_be_stats(struct stream_interface *si, struct proxy *px, in chunk_htmlencode(&trash, &src); chunk_appendf(&trash, "'"); } - chunk_appendf(&trash, "\""); + chunk_appendf(&trash, "
"); } chunk_appendf(&trash, - /* name */ - ">%s" - "Backend%s" + "%s" /* queue : current, max */ "%s%s" /* sessions rate : current, max, limit */ "%s%s" "", - (flags & ST_SHLGNDS)?"":"", - px->id, px->id, (flags & ST_SHLGNDS)?"":"", U2H0(px->nbpend) /* or px->totpend ? */, U2H1(px->be_counters.nbpend_max), U2H2(read_freq_ctr(&px->be_sess_per_sec)), U2H3(px->be_counters.sps_max)); chunk_appendf(&trash, - /* sessions: current, max, limit */ + /* sessions: current, max, limit, total */ "%s%s%s" - "%s%s" "", - U2H2(px->beconn), U2H3(px->be_counters.conn_max), U2H4(px->fullconn)); + U2H2(px->beconn), U2H3(px->be_counters.conn_max), U2H4(px->fullconn), + (px->mode == PR_MODE_HTTP)?"":"", U2H6(px->be_counters.cum_conn)); /* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */ if (px->mode == PR_MODE_HTTP) { - chunk_appendf(&trash, " title=\"%lld requests:", px->be_counters.p.http.cum_req); + chunk_appendf(&trash, "
%lld requests:", px->be_counters.p.http.cum_req); for (i = 1; i < 6; i++) chunk_appendf(&trash, " %dxx=%lld", i, px->be_counters.p.http.rsp[i]); chunk_appendf(&trash, " other=%lld ", px->be_counters.p.http.rsp[0]); - chunk_appendf(&trash, " compressed=%lld (%d%%)\"", + chunk_appendf(&trash, " compressed=%lld (%d%%)
", px->be_counters.p.http.comp_rsp, px->be_counters.p.http.rsp[2] ? (int)(100*px->be_counters.p.http.comp_rsp/px->be_counters.p.http.rsp[2]) : 0); } chunk_appendf(&trash, - /* sessions: total, lbtot */ - ">%s%s%s%s" + /* sessions: lbtot */ + "%s" /* bytes: in */ - "%s%s" "", - (px->mode == PR_MODE_HTTP)?"":"", - U2H6(px->be_counters.cum_conn), - (px->mode == PR_MODE_HTTP)?"":"", U2H7(px->be_counters.cum_lbconn), U2H8(px->be_counters.bytes_in)); - /* compression stats (via td title): comp_in, comp_out, comp_byp */ - chunk_appendf(&trash, " title=\"compression: in=%lld out=%lld bypassed=%lld savings=%d%%\"", - px->be_counters.comp_in, px->be_counters.comp_out, px->be_counters.comp_byp, - px->be_counters.comp_in ? - (int)((px->be_counters.comp_in - px->be_counters.comp_out)*100/px->be_counters.comp_in) : 0); - chunk_appendf(&trash, - /* bytes: out */ - ">%s%s%s" - "", + /* bytes:out + compression stats (via hover): comp_in, comp_out, comp_byp */ + "%s%s
compression: in=%lld out=%lld bypassed=%lld savings=%d%%
%s", (px->be_counters.comp_in || px->be_counters.comp_byp) ? "":"", U2H0(px->be_counters.bytes_out), + px->be_counters.comp_in, px->be_counters.comp_out, px->be_counters.comp_byp, + px->be_counters.comp_in ? + (int)((px->be_counters.comp_in - px->be_counters.comp_out)*100/px->be_counters.comp_in) : 0, (px->be_counters.comp_in || px->be_counters.comp_byp) ? "":""); chunk_appendf(&trash, @@ -2568,7 +2566,7 @@ static int stats_dump_be_stats(struct stream_interface *si, struct proxy *px, in /* errors : request, connect */ "%s" /* errors : response */ - "%s" + "%s
Connection resets during transfers: %lld client, %lld server
" /* warnings: retries, redispatches */ "%lld%lld" /* backend status: reflect backend status (up/down): we display UP @@ -2580,9 +2578,9 @@ static int stats_dump_be_stats(struct stream_interface *si, struct proxy *px, in "", U2H0(px->be_counters.denied_req), U2H1(px->be_counters.denied_resp), U2H2(px->be_counters.failed_conns), + U2H5(px->be_counters.failed_resp), px->be_counters.cli_aborts, px->be_counters.srv_aborts, - U2H5(px->be_counters.failed_resp), px->be_counters.retries, px->be_counters.redispatches, human_time(now.tv_sec - px->last_change, 1), (px->lbprm.tot_weight > 0 || !px->srv) ? "UP" : @@ -2695,26 +2693,30 @@ static void stats_dump_html_px_hdr(struct stream_interface *si, struct proxy *px chunk_appendf(&trash, "\n" "" - "" + "%s" "" "\n" "
"); + + chunk_appendf(&trash, + "%s" + "%s", + px->id, + (uri->flags & ST_SHLGNDS) ? "":"", + px->id, px->id); if (uri->flags & ST_SHLGNDS) { /* cap, mode, id */ - chunk_appendf(&trash, " title=\"cap: %s, mode: %s, id: %d", + chunk_appendf(&trash, "
cap: %s, mode: %s, id: %d", proxy_cap_str(px->cap), proxy_mode_str(px->mode), px->uuid); - chunk_appendf(&trash, "\""); + chunk_appendf(&trash, "
"); } chunk_appendf(&trash, - ">%s" - "%s%s
%s
\n" "\n" "", - (uri->flags & ST_SHLGNDS) ? "":"", - px->id, px->id, px->id, (uri->flags & ST_SHLGNDS) ? "":"", px->desc ? "desc" : "empty", px->desc ? px->desc : ""); @@ -3046,6 +3048,20 @@ static void stats_dump_html_head(struct uri_auth *uri) "table.lgd td { border-width: 1px; border-style: solid solid solid solid; border-color: gray; padding: 2px;}\n" "table.lgd td.noborder { border-style: none; padding: 2px; white-space: nowrap;}\n" "u {text-decoration:none; border-bottom: 1px dotted black;}\n" + "div {\n" + " display:block;\n" + " visibility:hidden;\n" + " z-index:2147483647;\n" + " position:absolute;\n" + " padding:2px 4px 3px;\n" + " background:#f0f060; color:#000000;\n" + " border:1px solid #7040c0;\n" + " white-space:nowrap;\n" + " font-style:normal;font-size:11px;font-weight:normal;\n" + " -moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;\n" + " -moz-box-shadow:gray 2px 2px 3px;-webkit-box-shadow:gray 2px 2px 3px;box-shadow:gray 2px 2px 3px;\n" + "}\n" + "u:hover div {visibility:visible;}\n" "-->\n" "\n", (uri->flags & ST_SHNODE) ? " on " : "", -- 2.39.5