/*
* Functions dedicated to statistics output and the stats socket
*
- * Copyright 2000-2010 Willy Tarreau <w@1wt.eu>
+ * Copyright 2000-2012 Willy Tarreau <w@1wt.eu>
* Copyright 2007-2009 Krzysztof Piotr Oledzki <ole@ans.pl>
*
* This program is free software; you can redistribute it and/or
#include <proto/ssl_sock.h>
#endif
-static int stats_dump_raw_to_buffer(struct stream_interface *si);
+static int stats_dump_raw_info_to_buffer(struct stream_interface *si);
+static int stats_dump_raw_stat_to_buffer(struct stream_interface *si);
static int stats_dump_full_sess_to_buffer(struct stream_interface *si, struct session *sess);
static int stats_dump_sess_to_buffer(struct stream_interface *si);
static int stats_dump_errors_to_buffer(struct stream_interface *si);
static int stats_dump_proxy(struct stream_interface *si, struct proxy *px, struct uri_auth *uri);
static int stats_dump_http(struct stream_interface *si, struct uri_auth *uri);
+/*
+ cli_io_handler()
+ -> stats_dump_sess_to_buffer() // "show sess"
+ -> stats_dump_errors_to_buffer() // "show errors"
+ -> stats_dump_raw_info_to_buffer() // "show info"
+ -> stats_dump_raw_info()
+ -> stats_dump_raw_stat_to_buffer() // "show stat"
+ -> stats_dump_csv_header()
+ -> stats_dump_proxy()
+ -> stats_dump_px_hdr()
+ -> stats_dump_fe_stats()
+ -> stats_dump_li_stats()
+ -> stats_dump_sv_stats()
+ -> stats_dump_be_stats()
+ -> stats_dump_px_end()
+
+ http_stats_io_handler()
+ -> stats_http_redir()
+ -> stats_dump_http() // also emits the HTTP headers
+ -> stats_dump_html_head() // emits the HTML headers
+ -> stats_dump_csv_header() // emits the CSV headers (same as above)
+ -> stats_dump_http_info() // note: ignores non-HTML output
+ -> stats_dump_proxy() // same as above
+ -> stats_dump_http_end() // emits HTML trailer
+ */
+
static struct si_applet cli_applet;
static const char stats_sock_usage_msg[] =
return 0;
}
-static int print_csv_header(struct chunk *msg)
+/* Dumps the stats CSV header to the trash buffer which. The caller is responsible
+ * for clearing it if needed.
+ */
+static void stats_dump_csv_header()
{
- return chunk_appendf(msg,
- "# pxname,svname,"
- "qcur,qmax,"
- "scur,smax,slim,stot,"
- "bin,bout,"
- "dreq,dresp,"
- "ereq,econ,eresp,"
- "wretr,wredis,"
- "status,weight,act,bck,"
- "chkfail,chkdown,lastchg,downtime,qlimit,"
- "pid,iid,sid,throttle,lbtot,tracked,type,"
- "rate,rate_lim,rate_max,"
- "check_status,check_code,check_duration,"
- "hrsp_1xx,hrsp_2xx,hrsp_3xx,hrsp_4xx,hrsp_5xx,hrsp_other,hanafail,"
- "req_rate,req_rate_max,req_tot,"
- "cli_abrt,srv_abrt,"
- "comp_in,comp_out,comp_byp,comp_rsp,"
- "\n");
+ chunk_appendf(&trash,
+ "# pxname,svname,"
+ "qcur,qmax,"
+ "scur,smax,slim,stot,"
+ "bin,bout,"
+ "dreq,dresp,"
+ "ereq,econ,eresp,"
+ "wretr,wredis,"
+ "status,weight,act,bck,"
+ "chkfail,chkdown,lastchg,downtime,qlimit,"
+ "pid,iid,sid,throttle,lbtot,tracked,type,"
+ "rate,rate_lim,rate_max,"
+ "check_status,check_code,check_duration,"
+ "hrsp_1xx,hrsp_2xx,hrsp_3xx,hrsp_4xx,hrsp_5xx,hrsp_other,hanafail,"
+ "req_rate,req_rate_max,req_tot,"
+ "cli_abrt,srv_abrt,"
+ "comp_in,comp_out,comp_byp,comp_rsp,"
+ "\n");
}
/* print a string of text buffer to <out>. The format is :
si->applet.ctx.stats.sid = atoi(args[4]);
}
- si->applet.ctx.stats.flags |= STAT_SHOW_STAT;
si->applet.ctx.stats.flags |= STAT_FMT_CSV;
si->conn->xprt_st = STAT_ST_INIT;
- si->applet.st0 = STAT_CLI_O_INFO; // stats_dump_raw_to_buffer
+ si->applet.st0 = STAT_CLI_O_STAT; // stats_dump_raw_stat_to_buffer
}
else if (strcmp(args[1], "info") == 0) {
- si->applet.ctx.stats.flags |= STAT_SHOW_INFO;
si->applet.ctx.stats.flags |= STAT_FMT_CSV;
si->conn->xprt_st = STAT_ST_INIT;
- si->applet.st0 = STAT_CLI_O_INFO; // stats_dump_raw_to_buffer
+ si->applet.st0 = STAT_CLI_O_INFO; // stats_dump_raw_info_to_buffer
}
else if (strcmp(args[1], "sess") == 0) {
si->conn->xprt_st = STAT_ST_INIT;
si->applet.st0 = STAT_CLI_PROMPT;
break;
case STAT_CLI_O_INFO:
- if (stats_dump_raw_to_buffer(si))
+ if (stats_dump_raw_info_to_buffer(si))
+ si->applet.st0 = STAT_CLI_PROMPT;
+ break;
+ case STAT_CLI_O_STAT:
+ if (stats_dump_raw_stat_to_buffer(si))
si->applet.st0 = STAT_CLI_PROMPT;
break;
case STAT_CLI_O_SESS:
}
}
+/* Dumps the raw stats information block to the trash for and uses the state from
+ * stream interface <si>. The caller is responsible for clearing the trash if needed.
+ */
+static void stats_dump_raw_info(struct stream_interface *si)
+{
+ unsigned int up = (now.tv_sec - start_date.tv_sec);
+
+ chunk_appendf(&trash,
+ "Name: " PRODUCT_NAME "\n"
+ "Version: " HAPROXY_VERSION "\n"
+ "Release_date: " HAPROXY_DATE "\n"
+ "Nbproc: %d\n"
+ "Process_num: %d\n"
+ "Pid: %d\n"
+ "Uptime: %dd %dh%02dm%02ds\n"
+ "Uptime_sec: %d\n"
+ "Memmax_MB: %d\n"
+ "Ulimit-n: %d\n"
+ "Maxsock: %d\n"
+ "Maxconn: %d\n"
+ "Hard_maxconn: %d\n"
+ "Maxpipes: %d\n"
+ "CurrConns: %d\n"
+ "PipesUsed: %d\n"
+ "PipesFree: %d\n"
+ "ConnRate: %d\n"
+ "ConnRateLimit: %d\n"
+ "MaxConnRate: %d\n"
+ "CompressBpsIn: %u\n"
+ "CompressBpsOut: %u\n"
+ "CompressBpsRateLim: %u\n"
+#ifdef USE_ZLIB
+ "ZlibMemUsage: %ld\n"
+ "MaxZlibMemUsage: %ld\n"
+#endif
+ "Tasks: %d\n"
+ "Run_queue: %d\n"
+ "Idle_pct: %d\n"
+ "node: %s\n"
+ "description: %s\n"
+ "",
+ global.nbproc,
+ relative_pid,
+ pid,
+ up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60),
+ up,
+ global.rlimit_memmax,
+ global.rlimit_nofile,
+ global.maxsock, global.maxconn, global.hardmaxconn, global.maxpipes,
+ actconn, pipes_used, pipes_free,
+ read_freq_ctr(&global.conn_per_sec), global.cps_lim, global.cps_max,
+ read_freq_ctr(&global.comp_bps_in), read_freq_ctr(&global.comp_bps_out),
+ global.comp_rate_lim,
+#ifdef USE_ZLIB
+ zlib_used_memory, global.maxzlibmem,
+#endif
+ nb_tasks_cur, run_queue_cur, idle_pct,
+ global.node, global.desc ? global.desc : ""
+ );
+}
+
+/* This function dumps information onto the stream interface's read buffer.
+ * It returns 0 as long as it does not complete, non-zero upon completion.
+ * No state is used.
+ */
+static int stats_dump_raw_info_to_buffer(struct stream_interface *si)
+{
+ chunk_reset(&trash);
+ stats_dump_raw_info(si);
+
+ if (bi_putchk(si->ib, &trash) == -1)
+ return 0;
+
+ return 1;
+}
+
/* This function dumps statistics onto the stream interface's read buffer.
* The xprt_ctx must have been zeroed first, and the flags properly set.
* It returns 0 as long as it does not complete, non-zero upon completion.
- * Some states are not used but it makes the code more similar to other
- * functions which handle stats too.
+ * Only states STAT_ST_INIT and STAT_ST_LIST are used.
*/
-static int stats_dump_raw_to_buffer(struct stream_interface *si)
+static int stats_dump_raw_stat_to_buffer(struct stream_interface *si)
{
struct proxy *px;
- unsigned int up;
chunk_reset(&trash);
switch (si->conn->xprt_st) {
case STAT_ST_INIT:
- /* the function had not been called yet */
- si->conn->xprt_st = STAT_ST_HEAD;
- /* fall through */
-
- case STAT_ST_HEAD:
- if (si->applet.ctx.stats.flags & STAT_SHOW_STAT) {
- print_csv_header(&trash);
- if (bi_putchk(si->ib, &trash) == -1)
- return 0;
- }
-
- si->conn->xprt_st = STAT_ST_INFO;
- /* fall through */
-
- case STAT_ST_INFO:
- up = (now.tv_sec - start_date.tv_sec);
- if (si->applet.ctx.stats.flags & STAT_SHOW_INFO) {
- chunk_appendf(&trash,
- "Name: " PRODUCT_NAME "\n"
- "Version: " HAPROXY_VERSION "\n"
- "Release_date: " HAPROXY_DATE "\n"
- "Nbproc: %d\n"
- "Process_num: %d\n"
- "Pid: %d\n"
- "Uptime: %dd %dh%02dm%02ds\n"
- "Uptime_sec: %d\n"
- "Memmax_MB: %d\n"
- "Ulimit-n: %d\n"
- "Maxsock: %d\n"
- "Maxconn: %d\n"
- "Hard_maxconn: %d\n"
- "Maxpipes: %d\n"
- "CurrConns: %d\n"
- "PipesUsed: %d\n"
- "PipesFree: %d\n"
- "ConnRate: %d\n"
- "ConnRateLimit: %d\n"
- "MaxConnRate: %d\n"
- "CompressBpsIn: %u\n"
- "CompressBpsOut: %u\n"
- "CompressBpsRateLim: %u\n"
-#ifdef USE_ZLIB
- "ZlibMemUsage: %ld\n"
- "MaxZlibMemUsage: %ld\n"
-#endif
- "Tasks: %d\n"
- "Run_queue: %d\n"
- "Idle_pct: %d\n"
- "node: %s\n"
- "description: %s\n"
- "",
- global.nbproc,
- relative_pid,
- pid,
- up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60),
- up,
- global.rlimit_memmax,
- global.rlimit_nofile,
- global.maxsock, global.maxconn, global.hardmaxconn, global.maxpipes,
- actconn, pipes_used, pipes_free,
- read_freq_ctr(&global.conn_per_sec), global.cps_lim, global.cps_max,
- read_freq_ctr(&global.comp_bps_in), read_freq_ctr(&global.comp_bps_out),
- global.comp_rate_lim,
-#ifdef USE_ZLIB
- zlib_used_memory, global.maxzlibmem,
-#endif
- nb_tasks_cur, run_queue_cur, idle_pct,
- global.node, global.desc?global.desc:""
- );
- if (bi_putchk(si->ib, &trash) == -1)
- return 0;
- }
+ stats_dump_csv_header();
+ if (bi_putchk(si->ib, &trash) == -1)
+ return 0;
si->applet.ctx.stats.px = proxy;
si->applet.ctx.stats.px_st = STAT_PX_ST_INIT;
si->conn->xprt_st = STAT_ST_LIST;
/* fall through */
- case STAT_ST_LIST:
+ default:
/* dump proxies */
- if (si->applet.ctx.stats.flags & STAT_SHOW_STAT) {
- while (si->applet.ctx.stats.px) {
- px = si->applet.ctx.stats.px;
- /* skip the disabled proxies, global frontend and non-networked ones */
- if (px->state != PR_STSTOPPED && px->uuid > 0 &&
- (px->cap & (PR_CAP_FE | PR_CAP_BE))) {
- if (stats_dump_proxy(si, px, NULL) == 0)
- return 0;
- }
-
- si->applet.ctx.stats.px = px->next;
- si->applet.ctx.stats.px_st = STAT_PX_ST_INIT;
+ while (si->applet.ctx.stats.px) {
+ px = si->applet.ctx.stats.px;
+ /* skip the disabled proxies, global frontend and non-networked ones */
+ if (px->state != PR_STSTOPPED && px->uuid > 0 &&
+ (px->cap & (PR_CAP_FE | PR_CAP_BE))) {
+ if (stats_dump_proxy(si, px, NULL) == 0)
+ return 0;
}
- /* here, we just have reached the last proxy */
+ si->applet.ctx.stats.px = px->next;
+ si->applet.ctx.stats.px_st = STAT_PX_ST_INIT;
}
-
- si->conn->xprt_st = STAT_ST_END;
- /* fall through */
-
- case STAT_ST_END:
- si->conn->xprt_st = STAT_ST_FIN;
- /* fall through */
-
- case STAT_ST_FIN:
- return 1;
-
- default:
- /* unknown state ! */
- si->conn->xprt_st = STAT_ST_FIN;
+ /* here, we just have reached the last proxy */
return 1;
}
}
-
-/* We don't want to land on the posted stats page because a refresh will
- * repost the data. We don't want this to happen on accident so we redirect
- * the browse to the stats page with a GET.
+/* Dumps a frontend's line to the trash for the current proxy <px> and uses
+ * the state from stream interface <si>. The caller is responsible for clearing
+ * the trash if needed. Returns non-zero if it emits anything, zero otherwise.
*/
-static int stats_http_redir(struct stream_interface *si, struct uri_auth *uri)
+static int stats_dump_fe_stats(struct stream_interface *si, struct proxy *px)
{
- struct session *s = si->conn->xprt_ctx;
+ int i;
- chunk_reset(&trash);
+ if (!(px->cap & PR_CAP_FE))
+ return 0;
- switch (si->conn->xprt_st) {
- case STAT_ST_INIT:
+ if ((si->applet.ctx.stats.flags & STAT_BOUND) && !(si->applet.ctx.stats.type & (1 << STATS_TYPE_FE)))
+ return 0;
+
+ if (!(si->applet.ctx.stats.flags & STAT_FMT_CSV)) {
chunk_appendf(&trash,
- "HTTP/1.0 303 See Other\r\n"
- "Cache-Control: no-cache\r\n"
- "Content-Type: text/plain\r\n"
- "Connection: close\r\n"
- "Location: %s;st=%s",
- uri->uri_prefix,
- ((si->applet.ctx.stats.st_code > STAT_STATUS_INIT) &&
- (si->applet.ctx.stats.st_code < STAT_STATUS_SIZE) &&
- stat_status_codes[si->applet.ctx.stats.st_code]) ?
- stat_status_codes[si->applet.ctx.stats.st_code] :
- stat_status_codes[STAT_STATUS_UNKN]);
- chunk_appendf(&trash, "\r\n\r\n");
+ /* name, queue */
+ "<tr class=\"frontend\">");
- if (bi_putchk(si->ib, &trash) == -1)
- return 0;
+ if (px->cap & PR_CAP_BE && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) {
+ /* Column sub-heading for Enable or Disable server */
+ chunk_appendf(&trash, "<td></td>");
+ }
- s->txn.status = 303;
+ chunk_appendf(&trash,
+ "<td class=ac>"
+ "<a name=\"%s/Frontend\"></a>"
+ "<a class=lfsb href=\"#%s/Frontend\">Frontend</a></td>"
+ "<td colspan=3></td>"
+ "",
+ px->id, px->id);
+
+ if (px->mode == PR_MODE_HTTP)
+ chunk_appendf(&trash,
+ /* sessions rate : current, max, limit */
+ "<td title=\"Cur: %u req/s\"><u>%s</u></td><td title=\"Max: %u req/s\"><u>%s</u></td><td>%s</td>"
+ "",
+ 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, "-"));
+ else
+ chunk_appendf(&trash,
+ /* sessions rate : current, max, limit */
+ "<td>%s</td><td>%s</td><td>%s</td>"
+ "",
+ U2H0(read_freq_ctr(&px->fe_sess_per_sec)),
+ U2H1(px->fe_counters.sps_max), LIM2A2(px->fe_sps_lim, "-"));
- if (!(s->flags & SN_ERR_MASK)) // this is not really an error but it is
- s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy
- if (!(s->flags & SN_FINST_MASK))
- s->flags |= SN_FINST_R;
+ chunk_appendf(&trash,
+ /* sessions: current, max, limit */
+ "<td>%s</td><td>%s</td><td>%s</td>"
+ "<td"
+ "",
+ U2H3(px->feconn), U2H4(px->fe_counters.conn_max), U2H5(px->maxconn));
+
+ /* 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);
+
+ for (i = 1; i < 6; i++)
+ chunk_appendf(&trash, " %dxx=%lld,", i, px->fe_counters.p.http.rsp[i]);
+
+ chunk_appendf(&trash, " other=%lld,", px->fe_counters.p.http.rsp[0]);
+ chunk_appendf(&trash, " compressed=%lld (%d%%)",
+ 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);
+ }
- si->conn->xprt_st = STAT_ST_FIN;
- return 1;
+ chunk_appendf(&trash,
+ /* sessions: total, lbtot */
+ ">%s%s%s</td><td></td>"
+ /* bytes : in */
+ "<td>%s</td><td"
+ "",
+ (px->mode == PR_MODE_HTTP)?"<u>":"",
+ U2H6(px->fe_counters.cum_sess),
+ (px->mode == PR_MODE_HTTP)?"</u>":"",
+ 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</td>"
+ "",
+ (px->fe_counters.comp_in || px->fe_counters.comp_byp) ? "<u>":"",
+ U2H0(px->fe_counters.bytes_out),
+ (px->fe_counters.comp_in || px->fe_counters.comp_byp) ? "</u>":"");
+
+ chunk_appendf(&trash,
+ /* denied: req, resp */
+ "<td>%s</td><td>%s</td>"
+ /* errors : request, connect, response */
+ "<td>%s</td><td></td><td></td>"
+ /* warnings: retries, redispatches */
+ "<td></td><td></td>"
+ /* server status : reflect frontend status */
+ "<td class=ac>%s</td>"
+ /* rest of server: nothing */
+ "<td class=ac colspan=8></td></tr>"
+ "",
+ U2H0(px->fe_counters.denied_req), U2H1(px->fe_counters.denied_resp),
+ U2H2(px->fe_counters.failed_req),
+ px->state == PR_STREADY ? "OPEN" :
+ px->state == PR_STFULL ? "FULL" : "STOP");
+ }
+ else { /* CSV mode */
+ chunk_appendf(&trash,
+ /* pxid, name, queue cur, queue max, */
+ "%s,FRONTEND,,,"
+ /* sessions : current, max, limit, total */
+ "%d,%d,%d,%lld,"
+ /* bytes : in, out */
+ "%lld,%lld,"
+ /* denied: req, resp */
+ "%lld,%lld,"
+ /* errors : request, connect, response */
+ "%lld,,,"
+ /* warnings: retries, redispatches */
+ ",,"
+ /* server status : reflect frontend status */
+ "%s,"
+ /* rest of server: nothing */
+ ",,,,,,,,"
+ /* pid, iid, sid, throttle, lbtot, tracked, type */
+ "%d,%d,0,,,,%d,"
+ /* rate, rate_lim, rate_max */
+ "%u,%u,%u,"
+ /* check_status, check_code, check_duration */
+ ",,,",
+ px->id,
+ px->feconn, px->fe_counters.conn_max, px->maxconn, px->fe_counters.cum_sess,
+ px->fe_counters.bytes_in, px->fe_counters.bytes_out,
+ px->fe_counters.denied_req, px->fe_counters.denied_resp,
+ px->fe_counters.failed_req,
+ px->state == PR_STREADY ? "OPEN" :
+ px->state == PR_STFULL ? "FULL" : "STOP",
+ relative_pid, px->uuid, STATS_TYPE_FE,
+ read_freq_ctr(&px->fe_sess_per_sec),
+ px->fe_sps_lim, px->fe_counters.sps_max);
+
+ /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
+ if (px->mode == PR_MODE_HTTP) {
+ for (i=1; i<6; i++)
+ chunk_appendf(&trash, "%lld,", px->fe_counters.p.http.rsp[i]);
+ chunk_appendf(&trash, "%lld,", px->fe_counters.p.http.rsp[0]);
+ }
+ else
+ chunk_appendf(&trash, ",,,,,,");
+
+ /* failed health analyses */
+ chunk_appendf(&trash, ",");
+
+ /* requests : req_rate, req_rate_max, req_tot, */
+ chunk_appendf(&trash, "%u,%u,%lld,",
+ read_freq_ctr(&px->fe_req_per_sec),
+ px->fe_counters.p.http.rps_max, px->fe_counters.p.http.cum_req);
+
+ /* errors: cli_aborts, srv_aborts */
+ chunk_appendf(&trash, ",,");
+
+ /* compression: in, out, bypassed */
+ chunk_appendf(&trash, "%lld,%lld,%lld,",
+ px->fe_counters.comp_in, px->fe_counters.comp_out, px->fe_counters.comp_byp);
+
+ /* compression: comp_rsp */
+ chunk_appendf(&trash, "%lld,",
+ px->fe_counters.p.http.comp_rsp);
+
+ /* finish with EOL */
+ chunk_appendf(&trash, "\n");
}
return 1;
}
-
-/* This I/O handler runs as an applet embedded in a stream interface. It is
- * used to send HTTP stats over a TCP socket. The mechanism is very simple.
- * si->applet.st0 becomes non-zero once the transfer is finished. The handler
- * automatically unregisters itself once transfer is complete.
+/* Dumps a line for listener <l> and proxy <px> to the trash and uses the state
+ * from stream interface <si>, and stats flags <flags>. The caller is responsible
+ * for clearing the trash if needed. Returns non-zero if it emits anything, zero
+ * otherwise.
*/
-static void http_stats_io_handler(struct stream_interface *si)
+static int stats_dump_li_stats(struct stream_interface *si, struct proxy *px, struct listener *l, int flags)
{
- struct session *s = si->conn->xprt_ctx;
- struct channel *req = si->ob;
- struct channel *res = si->ib;
+ if (!(si->applet.ctx.stats.flags & STAT_FMT_CSV)) {
+ chunk_appendf(&trash, "<tr class=socket>");
+ if (px->cap & PR_CAP_BE && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) {
+ /* Column sub-heading for Enable or Disable server */
+ chunk_appendf(&trash, "<td></td>");
+ }
+ chunk_appendf(&trash, "<td class=ac");
- if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
- goto out;
+ if (flags & ST_SHLGNDS) {
+ char str[INET6_ADDRSTRLEN];
+ int port;
- /* check that the output is not closed */
- if (res->flags & (CF_SHUTW|CF_SHUTW_NOW))
- si->applet.st0 = 1;
+ chunk_appendf(&trash, " title=\"");
- if (!si->applet.st0) {
- if (s->txn.meth == HTTP_METH_POST) {
- if (stats_http_redir(si, s->be->uri_auth)) {
- si->applet.st0 = 1;
- si_shutw(si);
- }
- } else {
- if (stats_dump_http(si, s->be->uri_auth)) {
- si->applet.st0 = 1;
- si_shutw(si);
+ port = get_host_port(&l->addr);
+ switch (addr_to_str(&l->addr, str, sizeof(str))) {
+ case AF_INET:
+ chunk_appendf(&trash, "IPv4: %s:%d, ", str, port);
+ break;
+ case AF_INET6:
+ chunk_appendf(&trash, "IPv6: [%s]:%d, ", str, port);
+ break;
+ case AF_UNIX:
+ chunk_appendf(&trash, "unix, ");
+ break;
+ case -1:
+ chunk_appendf(&trash, "(%s), ", strerror(errno));
+ break;
}
+
+ /* id */
+ chunk_appendf(&trash, "id: %d\"", l->luid);
}
- }
- if ((res->flags & CF_SHUTR) && (si->state == SI_ST_EST))
- si_shutw(si);
+ chunk_appendf(&trash,
+ /* name, queue */
+ ">%s<a name=\"%s/+%s\"></a>"
+ "<a class=lfsb href=\"#%s/+%s\">%s</a></td><td colspan=3>%s</td>"
+ /* sessions rate: current, max, limit */
+ "<td colspan=3> </td>"
+ /* sessions: current, max, limit, total, lbtot */
+ "<td>%s</td><td>%s</td><td>%s</td>"
+ "<td>%s</td><td> </td>"
+ /* bytes: in, out */
+ "<td>%s</td><td>%s</td>"
+ "",
+ (flags & ST_SHLGNDS)?"<u>":"",
+ px->id, l->name, px->id, l->name, l->name,
+ (flags & ST_SHLGNDS)?"</u>":"",
+ 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));
- if ((req->flags & CF_SHUTW) && (si->state == SI_ST_EST) && si->applet.st0) {
- si_shutr(si);
- res->flags |= CF_READ_NULL;
+ chunk_appendf(&trash,
+ /* denied: req, resp */
+ "<td>%s</td><td>%s</td>"
+ /* errors: request, connect, response */
+ "<td>%s</td><td></td><td></td>"
+ /* warnings: retries, redispatches */
+ "<td></td><td></td>"
+ /* server status: reflect listener status */
+ "<td class=ac>%s</td>"
+ /* rest of server: nothing */
+ "<td class=ac colspan=8></td></tr>"
+ "",
+ U2H0(l->counters->denied_req), U2H1(l->counters->denied_resp),
+ U2H2(l->counters->failed_req),
+ (l->nbconn < l->maxconn) ? (l->state == LI_LIMITED) ? "WAITING" : "OPEN" : "FULL");
}
-
- /* update all other flags and resync with the other side */
- si_update(si);
-
- /* we don't want to expire timeouts while we're processing requests */
- si->ib->rex = TICK_ETERNITY;
- si->ob->wex = TICK_ETERNITY;
-
- out:
- if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO)) {
- /* check that we have released everything then unregister */
- stream_int_unregister_handler(si);
+ else { /* CSV mode */
+ chunk_appendf(&trash,
+ /* pxid, name, queue cur, queue max, */
+ "%s,%s,,,"
+ /* sessions: current, max, limit, total */
+ "%d,%d,%d,%lld,"
+ /* bytes: in, out */
+ "%lld,%lld,"
+ /* denied: req, resp */
+ "%lld,%lld,"
+ /* errors: request, connect, response */
+ "%lld,,,"
+ /* warnings: retries, redispatches */
+ ",,"
+ /* server status: reflect listener status */
+ "%s,"
+ /* rest of server: nothing */
+ ",,,,,,,,"
+ /* pid, iid, sid, throttle, lbtot, tracked, type */
+ "%d,%d,%d,,,,%d,"
+ /* rate, rate_lim, rate_max */
+ ",,,"
+ /* check_status, check_code, check_duration */
+ ",,,"
+ /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
+ ",,,,,,"
+ /* failed health analyses */
+ ","
+ /* requests : req_rate, req_rate_max, req_tot, */
+ ",,,"
+ /* errors: cli_aborts, srv_aborts */
+ ",,"
+ /* compression: in, out, bypassed, comp_rsp */
+ ",,,,"
+ "\n",
+ px->id, l->name,
+ l->nbconn, l->counters->conn_max,
+ l->maxconn, l->counters->cum_conn,
+ l->counters->bytes_in, l->counters->bytes_out,
+ l->counters->denied_req, l->counters->denied_resp,
+ l->counters->failed_req,
+ (l->nbconn < l->maxconn) ? "OPEN" : "FULL",
+ relative_pid, px->uuid, l->luid, STATS_TYPE_SO);
}
+ return 1;
}
-
-/* This function dumps statistics in HTTP format onto the stream interface's
- * read buffer. The xprt_ctx must have been zeroed first, and the flags
- * properly set. It returns 0 if it had to stop writing data and an I/O is
- * needed, 1 if the dump is finished and the session must be closed, or -1
- * in case of any error.
+/* Dumps a line for server <sv> and proxy <px> to the trash and uses the state
+ * from stream interface <si>, stats flags <flags>, and server state <state>.
+ * The caller is responsible for clearing the trash if needed. Returns non-zero
+ * if it emits anything, zero otherwise. The <state> parameter can take the
+ * following values : 0=DOWN, 1=going up, 2=going down, 3=UP, 4,5=NOLB, 6=unchecked.
*/
-static int stats_dump_http(struct stream_interface *si, struct uri_auth *uri)
+static int stats_dump_sv_stats(struct stream_interface *si, struct proxy *px, int flags, struct server *sv, int state)
{
- struct session *s = si->conn->xprt_ctx;
- struct channel *rep = si->ib;
- struct proxy *px;
- unsigned int up;
+ struct server *ref = sv->track ? sv->track : sv;
+ char str[INET6_ADDRSTRLEN];
+ struct chunk src;
+ int i;
+
+ if (!(si->applet.ctx.stats.flags & STAT_FMT_CSV)) { /* HTML mode */
+ static char *srv_hlt_st[7] = {
+ "DOWN",
+ "DN %d/%d ↑",
+ "UP %d/%d ↓",
+ "UP",
+ "NOLB %d/%d ↓",
+ "NOLB",
+ "<i>no check</i>"
+ };
- chunk_reset(&trash);
+ if ((sv->state & SRV_MAINTAIN) || (ref->state & SRV_MAINTAIN))
+ chunk_appendf(&trash, "<tr class=\"maintain\">");
+ else
+ chunk_appendf(&trash,
+ "<tr class=\"%s%d\">",
+ (sv->state & SRV_BACKUP) ? "backup" : "active", state);
- switch (si->conn->xprt_st) {
- case STAT_ST_INIT:
- chunk_appendf(&trash,
- "HTTP/1.0 200 OK\r\n"
- "Cache-Control: no-cache\r\n"
- "Connection: close\r\n"
- "Content-Type: %s\r\n",
- (si->applet.ctx.stats.flags & STAT_FMT_CSV) ? "text/plain" : "text/html");
+ if ((px->cap & PR_CAP_BE) && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN))
+ chunk_appendf(&trash,
+ "<td><input type=\"checkbox\" name=\"s\" value=\"%s\"></td>",
+ sv->id);
- if (uri->refresh > 0 && !(si->applet.ctx.stats.flags & STAT_NO_REFRESH))
- chunk_appendf(&trash, "Refresh: %d\r\n",
- uri->refresh);
+ chunk_appendf(&trash, "<td class=ac");
- chunk_appendf(&trash, "\r\n");
+ if (flags & ST_SHLGNDS) {
+ chunk_appendf(&trash, " title=\"");
- s->txn.status = 200;
- if (bi_putchk(rep, &trash) == -1)
- return 0;
+ switch (addr_to_str(&sv->addr, str, sizeof(str))) {
+ case AF_INET:
+ chunk_appendf(&trash, "IPv4: %s:%d, ", str, get_host_port(&sv->addr));
+ break;
+ case AF_INET6:
+ chunk_appendf(&trash, "IPv6: [%s]:%d, ", str, get_host_port(&sv->addr));
+ break;
+ case AF_UNIX:
+ chunk_appendf(&trash, "unix, ");
+ break;
+ case -1:
+ chunk_appendf(&trash, "(%s), ", strerror(errno));
+ break;
+ default: /* address family not supported */
+ break;
+ }
- if (!(s->flags & SN_ERR_MASK)) // this is not really an error but it is
- s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy
- if (!(s->flags & SN_FINST_MASK))
- s->flags |= SN_FINST_R;
+ /* id */
+ chunk_appendf(&trash, "id: %d", sv->puid);
- if (s->txn.meth == HTTP_METH_HEAD) {
- /* that's all we return in case of HEAD request */
- si->conn->xprt_st = STAT_ST_FIN;
- return 1;
- }
+ /* cookie */
+ if (sv->cookie) {
+ chunk_appendf(&trash, ", cookie: '");
- si->conn->xprt_st = STAT_ST_HEAD; /* let's start producing data */
- /* fall through */
+ chunk_initlen(&src, sv->cookie, 0, strlen(sv->cookie));
+ chunk_htmlencode(&trash, &src);
- case STAT_ST_HEAD:
- if (!(si->applet.ctx.stats.flags & STAT_FMT_CSV)) {
- /* WARNING! This must fit in the first buffer !!! */
- chunk_appendf(&trash,
- "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"
- "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
- "<html><head><title>Statistics Report for " PRODUCT_NAME "%s%s</title>\n"
- "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
- "<style type=\"text/css\"><!--\n"
- "body {"
- " font-family: arial, helvetica, sans-serif;"
- " font-size: 12px;"
- " font-weight: normal;"
- " color: black;"
- " background: white;"
- "}\n"
- "th,td {"
- " font-size: 10px;"
- "}\n"
- "h1 {"
- " font-size: x-large;"
- " margin-bottom: 0.5em;"
- "}\n"
- "h2 {"
- " font-family: helvetica, arial;"
- " font-size: x-large;"
- " font-weight: bold;"
- " font-style: italic;"
- " color: #6020a0;"
- " margin-top: 0em;"
- " margin-bottom: 0em;"
- "}\n"
- "h3 {"
- " font-family: helvetica, arial;"
- " font-size: 16px;"
- " font-weight: bold;"
- " color: #b00040;"
- " background: #e8e8d0;"
- " margin-top: 0em;"
- " margin-bottom: 0em;"
- "}\n"
- "li {"
- " margin-top: 0.25em;"
- " margin-right: 2em;"
- "}\n"
- ".hr {margin-top: 0.25em;"
- " border-color: black;"
- " border-bottom-style: solid;"
- "}\n"
- ".titre {background: #20D0D0;color: #000000; font-weight: bold; text-align: center;}\n"
- ".total {background: #20D0D0;color: #ffff80;}\n"
- ".frontend {background: #e8e8d0;}\n"
- ".socket {background: #d0d0d0;}\n"
- ".backend {background: #e8e8d0;}\n"
- ".active0 {background: #ff9090;}\n"
- ".active1 {background: #ffd020;}\n"
- ".active2 {background: #ffffa0;}\n"
- ".active3 {background: #c0ffc0;}\n"
- ".active4 {background: #ffffa0;}\n" /* NOLB state shows same as going down */
- ".active5 {background: #a0e0a0;}\n" /* NOLB state shows darker than up */
- ".active6 {background: #e0e0e0;}\n"
- ".backup0 {background: #ff9090;}\n"
- ".backup1 {background: #ff80ff;}\n"
- ".backup2 {background: #c060ff;}\n"
- ".backup3 {background: #b0d0ff;}\n"
- ".backup4 {background: #c060ff;}\n" /* NOLB state shows same as going down */
- ".backup5 {background: #90b0e0;}\n" /* NOLB state shows same as going down */
- ".backup6 {background: #e0e0e0;}\n"
- ".maintain {background: #c07820;}\n"
- ".rls {letter-spacing: 0.2em; margin-right: 1px;}\n" /* right letter spacing (used for grouping digits) */
- "\n"
- "a.px:link {color: #ffff40; text-decoration: none;}"
- "a.px:visited {color: #ffff40; text-decoration: none;}"
- "a.px:hover {color: #ffffff; text-decoration: none;}"
- "a.lfsb:link {color: #000000; text-decoration: none;}"
- "a.lfsb:visited {color: #000000; text-decoration: none;}"
- "a.lfsb:hover {color: #505050; text-decoration: none;}"
- "\n"
- "table.tbl { border-collapse: collapse; border-style: none;}\n"
- "table.tbl td { text-align: right; border-width: 1px 1px 1px 1px; border-style: solid solid solid solid; padding: 2px 3px; border-color: gray; white-space: nowrap;}\n"
- "table.tbl td.ac { text-align: center;}\n"
- "table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray;}\n"
- "table.tbl th.pxname { background: #b00040; color: #ffff40; font-weight: bold; border-style: solid solid none solid; padding: 2px 3px; white-space: nowrap;}\n"
- "table.tbl th.empty { border-style: none; empty-cells: hide; background: white;}\n"
- "table.tbl th.desc { background: white; border-style: solid solid none solid; text-align: left; padding: 2px 3px;}\n"
- "\n"
- "table.lgd { border-collapse: collapse; border-width: 1px; border-style: none none none solid; border-color: black;}\n"
- "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"
- "-->\n"
- "</style></head>\n",
- (uri->flags&ST_SHNODE) ? " on " : "",
- (uri->flags&ST_SHNODE) ? (uri->node ? uri->node : global.node) : ""
- );
- } else {
- print_csv_header(&trash);
+ chunk_appendf(&trash, "'");
+ }
+
+ chunk_appendf(&trash, "\"");
}
- if (bi_putchk(rep, &trash) == -1)
- return 0;
- si->conn->xprt_st = STAT_ST_INFO;
- /* fall through */
+ chunk_appendf(&trash,
+ ">%s<a name=\"%s/%s\"></a>"
+ "<a class=lfsb href=\"#%s/%s\">%s</a>%s</td>"
+ /* queue : current, max, limit */
+ "<td>%s</td><td>%s</td><td>%s</td>"
+ /* sessions rate : current, max, limit */
+ "<td>%s</td><td>%s</td><td></td>"
+ /* sessions: current, max, limit */
+ "<td>%s</td><td>%s</td><td>%s</td>"
+ "<td"
+ "",
+ (flags & ST_SHLGNDS) ? "<u>" : "",
+ px->id, sv->id, px->id, sv->id, sv->id,
+ (flags & ST_SHLGNDS) ? "</u>" : "",
+ 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, "-"));
+
+ /* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */
+ if (px->mode == PR_MODE_HTTP) {
+ chunk_appendf(&trash, " title=\"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]);
+ }
- case STAT_ST_INFO:
- up = (now.tv_sec - start_date.tv_sec);
+ chunk_appendf(&trash,
+ /* sessions: total, lbtot */
+ ">%s%s%s</td><td>%s</td>",
+ (px->mode == PR_MODE_HTTP)?"<u>":"",
+ U2H0(sv->counters.cum_sess),
+ (px->mode == PR_MODE_HTTP)?"</u>":"",
+ U2H1(sv->counters.cum_lbconn));
- /* WARNING! this has to fit the first packet too.
- * We are around 3.5 kB, add adding entries will
- * become tricky if we want to support 4kB buffers !
- */
- if (!(si->applet.ctx.stats.flags & STAT_FMT_CSV)) {
+ chunk_appendf(&trash,
+ /* bytes : in, out */
+ "<td>%s</td><td>%s</td>"
+ /* denied: req, resp */
+ "<td></td><td>%s</td>"
+ /* errors : request, connect */
+ "<td></td><td>%s</td>"
+ /* errors : response */
+ "<td title=\"Connection resets during transfers: %lld client, %lld server\"><u>%s</u></td>"
+ /* warnings: retries, redispatches */
+ "<td>%lld</td><td>%lld</td>"
+ "",
+ U2H0(sv->counters.bytes_in), U2H1(sv->counters.bytes_out),
+ U2H2(sv->counters.failed_secu),
+ U2H3(sv->counters.failed_conns),
+ sv->counters.cli_aborts,
+ sv->counters.srv_aborts,
+ U2H6(sv->counters.failed_resp),
+ sv->counters.retries, sv->counters.redispatches);
+
+ /* status, lest check */
+ chunk_appendf(&trash, "<td class=ac>");
+
+ if (sv->state & SRV_MAINTAIN) {
+ chunk_appendf(&trash, "%s ", human_time(now.tv_sec - sv->last_change, 1));
+ chunk_appendf(&trash, "MAINT");
+ }
+ else if (ref != sv && ref->state & SRV_MAINTAIN) {
+ chunk_appendf(&trash, "%s ", human_time(now.tv_sec - ref->last_change, 1));
+ chunk_appendf(&trash, "MAINT(via)");
+ }
+ else if (ref->state & SRV_CHECKED) {
+ chunk_appendf(&trash, "%s ", human_time(now.tv_sec - ref->last_change, 1));
chunk_appendf(&trash,
- "<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
- PRODUCT_NAME "%s</a></h1>\n"
- "<h2>Statistics Report for pid %d%s%s%s%s</h2>\n"
- "<hr width=\"100%%\" class=\"hr\">\n"
- "<h3>> General process information</h3>\n"
- "<table border=0><tr><td align=\"left\" nowrap width=\"1%%\">\n"
- "<p><b>pid = </b> %d (process #%d, nbproc = %d)<br>\n"
- "<b>uptime = </b> %dd %dh%02dm%02ds<br>\n"
- "<b>system limits:</b> memmax = %s%s; ulimit-n = %d<br>\n"
- "<b>maxsock = </b> %d; <b>maxconn = </b> %d; <b>maxpipes = </b> %d<br>\n"
- "current conns = %d; current pipes = %d/%d; conn rate = %d/sec<br>\n"
- "Running tasks: %d/%d; idle = %d %%<br>\n"
- "</td><td align=\"center\" nowrap>\n"
- "<table class=\"lgd\"><tr>\n"
- "<td class=\"active3\"> </td><td class=\"noborder\">active UP </td>"
- "<td class=\"backup3\"> </td><td class=\"noborder\">backup UP </td>"
- "</tr><tr>\n"
- "<td class=\"active2\"></td><td class=\"noborder\">active UP, going down </td>"
- "<td class=\"backup2\"></td><td class=\"noborder\">backup UP, going down </td>"
- "</tr><tr>\n"
- "<td class=\"active1\"></td><td class=\"noborder\">active DOWN, going up </td>"
- "<td class=\"backup1\"></td><td class=\"noborder\">backup DOWN, going up </td>"
- "</tr><tr>\n"
- "<td class=\"active0\"></td><td class=\"noborder\">active or backup DOWN </td>"
- "<td class=\"active6\"></td><td class=\"noborder\">not checked </td>"
- "</tr><tr>\n"
- "<td class=\"maintain\"></td><td class=\"noborder\" colspan=\"3\">active or backup DOWN for maintenance (MAINT) </td>"
- "</tr></table>\n"
- "Note: UP with load-balancing disabled is reported as \"NOLB\"."
- "</td>"
- "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
- "<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
- "",
- (uri->flags&ST_HIDEVER)?"":(STATS_VERSION_STRING),
- pid, (uri->flags&ST_SHNODE) ? " on " : "", (uri->flags&ST_SHNODE) ? (uri->node ? uri->node : global.node) : "",
- (uri->flags&ST_SHDESC)? ": " : "", (uri->flags&ST_SHDESC) ? (uri->desc ? uri->desc : global.desc) : "",
- pid, relative_pid, global.nbproc,
- up / 86400, (up % 86400) / 3600,
- (up % 3600) / 60, (up % 60),
- global.rlimit_memmax ? ultoa(global.rlimit_memmax) : "unlimited",
- global.rlimit_memmax ? " MB" : "",
- global.rlimit_nofile,
- global.maxsock, global.maxconn, global.maxpipes,
- actconn, pipes_used, pipes_used+pipes_free, read_freq_ctr(&global.conn_per_sec),
- run_queue_cur, nb_tasks_cur, idle_pct
- );
-
- if (si->applet.ctx.stats.flags & STAT_HIDE_DOWN)
- chunk_appendf(&trash,
- "<li><a href=\"%s%s%s\">Show all servers</a><br>\n",
- uri->uri_prefix,
- "",
- (si->applet.ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
- else
- chunk_appendf(&trash,
- "<li><a href=\"%s%s%s\">Hide 'DOWN' servers</a><br>\n",
- uri->uri_prefix,
- ";up",
- (si->applet.ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
-
- if (uri->refresh > 0) {
- if (si->applet.ctx.stats.flags & STAT_NO_REFRESH)
- chunk_appendf(&trash,
- "<li><a href=\"%s%s%s\">Enable refresh</a><br>\n",
- uri->uri_prefix,
- (si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
- "");
- else
- chunk_appendf(&trash,
- "<li><a href=\"%s%s%s\">Disable refresh</a><br>\n",
- uri->uri_prefix,
- (si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
- ";norefresh");
+ srv_hlt_st[state],
+ (ref->state & SRV_RUNNING) ? (ref->health - ref->rise + 1) : (ref->health),
+ (ref->state & SRV_RUNNING) ? (ref->fall) : (ref->rise));
+ }
+
+ if (sv->state & SRV_CHECKED) {
+ chunk_appendf(&trash, "</td><td class=ac title=\"%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,
- "<li><a href=\"%s%s%s\">Refresh now</a><br>\n",
- uri->uri_prefix,
- (si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
- (si->applet.ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
+ chunk_appendf(&trash, "\"><u> %s%s",
+ (sv->state & SRV_CHK_RUNNING) ? "* " : "",
+ get_check_status_info(sv->check.status));
+ if (sv->check.status >= HCHK_STATUS_L57DATA)
+ chunk_appendf(&trash, "/%d", sv->check.code);
+
+ if (sv->check.status >= HCHK_STATUS_CHECKED && sv->check.duration >= 0)
+ chunk_appendf(&trash, " in %lums</u>", sv->check.duration);
+ }
+ else
+ chunk_appendf(&trash, "</td><td>");
+
+ chunk_appendf(&trash,
+ /* weight */
+ "</td><td class=ac>%d</td>"
+ /* act, bck */
+ "<td class=ac>%s</td><td class=ac>%s</td>"
+ "",
+ (sv->eweight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
+ (sv->state & SRV_BACKUP) ? "-" : "Y",
+ (sv->state & SRV_BACKUP) ? "Y" : "-");
+
+ /* check failures: unique, fatal, down time */
+ if (sv->state & SRV_CHECKED) {
+ chunk_appendf(&trash, "<td title=\"Failed Health Checks%s\"><u>%lld",
+ ref->observe?"/Health Analyses":"", ref->counters.failed_checks);
+
+ if (ref->observe)
+ chunk_appendf(&trash, "/%lld", ref->counters.failed_hana);
+
+ chunk_appendf(&trash,
+ "</u></td>"
+ "<td>%lld</td><td>%s</td>"
+ "",
+ ref->counters.down_trans, human_time(srv_downtime(sv), 1));
+ }
+ else if (sv != ref)
chunk_appendf(&trash,
- "<li><a href=\"%s;csv%s\">CSV export</a><br>\n",
- uri->uri_prefix,
- (uri->refresh > 0) ? ";norefresh" : "");
+ "<td class=ac colspan=3><a class=lfsb href=\"#%s/%s\">via %s/%s<a></td>",
+ ref->proxy->id, ref->id, ref->proxy->id, ref->id);
+ else
+ chunk_appendf(&trash, "<td colspan=3></td>");
+
+ /* throttle */
+ if ((sv->state & SRV_WARMINGUP) &&
+ now.tv_sec < sv->last_change + sv->slowstart &&
+ now.tv_sec >= sv->last_change) {
+ chunk_appendf(&trash, "<td class=ac>%d %%</td></tr>\n",
+ (int)MAX(1, 100 * (now.tv_sec - sv->last_change) / sv->slowstart));
+ }
+ else
+ chunk_appendf(&trash, "<td class=ac>-</td></tr>\n");
+ }
+ else { /* CSV mode */
+ static char *srv_hlt_st[7] = {
+ "DOWN,",
+ "DOWN %d/%d,",
+ "UP %d/%d,",
+ "UP,",
+ "NOLB %d/%d,",
+ "NOLB,",
+ "no check,"
+ };
+ chunk_appendf(&trash,
+ /* pxid, name */
+ "%s,%s,"
+ /* queue : current, max */
+ "%d,%d,"
+ /* sessions : current, max, limit, total */
+ "%d,%d,%s,%lld,"
+ /* bytes : in, out */
+ "%lld,%lld,"
+ /* denied: req, resp */
+ ",%lld,"
+ /* errors : request, connect, response */
+ ",%lld,%lld,"
+ /* warnings: retries, redispatches */
+ "%lld,%lld,"
+ "",
+ px->id, sv->id,
+ sv->nbpend, sv->counters.nbpend_max,
+ sv->cur_sess, sv->counters.cur_sess_max, LIM2A0(sv->maxconn, ""), sv->counters.cum_sess,
+ sv->counters.bytes_in, sv->counters.bytes_out,
+ sv->counters.failed_secu,
+ sv->counters.failed_conns, sv->counters.failed_resp,
+ sv->counters.retries, sv->counters.redispatches);
+
+ /* status */
+ if (sv->state & SRV_MAINTAIN)
+ chunk_appendf(&trash, "MAINT,");
+ else if (ref != sv && ref->state & SRV_MAINTAIN)
+ chunk_appendf(&trash, "MAINT(via),");
+ else
chunk_appendf(&trash,
- "</ul></td>"
- "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
- "<b>External resources:</b><ul style=\"margin-top: 0.25em;\">\n"
- "<li><a href=\"" PRODUCT_URL "\">Primary site</a><br>\n"
- "<li><a href=\"" PRODUCT_URL_UPD "\">Updates (v" PRODUCT_BRANCH ")</a><br>\n"
- "<li><a href=\"" PRODUCT_URL_DOC "\">Online manual</a><br>\n"
- "</ul>"
- "</td>"
- "</tr></table>\n"
- ""
- );
-
- if (si->applet.ctx.stats.st_code) {
- switch (si->applet.ctx.stats.st_code) {
- case STAT_STATUS_DONE:
- chunk_appendf(&trash,
- "<p><div class=active3>"
- "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
- "Action processed successfully."
- "</div>\n", uri->uri_prefix);
- break;
- case STAT_STATUS_NONE:
- chunk_appendf(&trash,
- "<p><div class=active2>"
- "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
- "Nothing has changed."
- "</div>\n", uri->uri_prefix);
- break;
- case STAT_STATUS_PART:
- chunk_appendf(&trash,
- "<p><div class=active2>"
- "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
- "Action partially processed.<br>"
- "Some server names are probably unknown or ambiguous (duplicated names in the backend)."
- "</div>\n", uri->uri_prefix);
- break;
- case STAT_STATUS_ERRP:
- chunk_appendf(&trash,
- "<p><div class=active0>"
- "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
- "Action not processed because of invalid parameters."
- "<ul>"
- "<li>The action is maybe unknown.</li>"
- "<li>The backend name is probably unknown or ambiguous (duplicated names).</li>"
- "<li>Some server names are probably unknown or ambiguous (duplicated names in the backend).</li>"
- "</ul>"
- "</div>\n", uri->uri_prefix);
- break;
- case STAT_STATUS_EXCD:
- chunk_appendf(&trash,
- "<p><div class=active0>"
- "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
- "<b>Action not processed : the buffer couldn't store all the data.<br>"
- "You should retry with less servers at a time.</b>"
- "</div>\n", uri->uri_prefix);
- break;
- case STAT_STATUS_DENY:
- chunk_appendf(&trash,
- "<p><div class=active0>"
- "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
- "<b>Action denied.</b>"
- "</div>\n", uri->uri_prefix);
- break;
- default:
- chunk_appendf(&trash,
- "<p><div class=active6>"
- "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
- "Unexpected result."
- "</div>\n", uri->uri_prefix);
- }
- chunk_appendf(&trash,"<p>\n");
- }
+ srv_hlt_st[state],
+ (ref->state & SRV_RUNNING) ? (ref->health - ref->rise + 1) : (ref->health),
+ (ref->state & SRV_RUNNING) ? (ref->fall) : (ref->rise));
+
+ chunk_appendf(&trash,
+ /* weight, active, backup */
+ "%d,%d,%d,"
+ "",
+ (sv->eweight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
+ (sv->state & SRV_BACKUP) ? 0 : 1,
+ (sv->state & SRV_BACKUP) ? 1 : 0);
+
+ /* check failures: unique, fatal; last change, total downtime */
+ if (sv->state & SRV_CHECKED)
+ chunk_appendf(&trash,
+ "%lld,%lld,%d,%d,",
+ sv->counters.failed_checks, sv->counters.down_trans,
+ (int)(now.tv_sec - sv->last_change), srv_downtime(sv));
+ else
+ chunk_appendf(&trash, ",,,,");
+
+ /* queue limit, pid, iid, sid, */
+ chunk_appendf(&trash,
+ "%s,"
+ "%d,%d,%d,",
+ LIM2A0(sv->maxqueue, ""),
+ relative_pid, px->uuid, sv->puid);
+
+ /* throttle */
+ if ((sv->state & SRV_WARMINGUP) &&
+ now.tv_sec < sv->last_change + sv->slowstart &&
+ now.tv_sec >= sv->last_change)
+ chunk_appendf(&trash, "%d", (int)MAX(1, 100 * (now.tv_sec - sv->last_change) / sv->slowstart));
+
+ /* sessions: lbtot */
+ chunk_appendf(&trash, ",%lld,", sv->counters.cum_lbconn);
+
+ /* tracked */
+ if (sv->track)
+ chunk_appendf(&trash, "%s/%s,",
+ sv->track->proxy->id, sv->track->id);
+ else
+ chunk_appendf(&trash, ",");
+
+ /* type */
+ chunk_appendf(&trash, "%d,", STATS_TYPE_SV);
+
+ /* rate */
+ chunk_appendf(&trash, "%u,,%u,",
+ read_freq_ctr(&sv->sess_per_sec),
+ sv->counters.sps_max);
+
+ if (sv->state & SRV_CHECKED) {
+ /* check_status */
+ chunk_appendf(&trash, "%s,", get_check_status_info(sv->check.status));
+
+ /* check_code */
+ if (sv->check.status >= HCHK_STATUS_L57DATA)
+ chunk_appendf(&trash, "%u,", sv->check.code);
+ else
+ chunk_appendf(&trash, ",");
+
+ /* check_duration */
+ if (sv->check.status >= HCHK_STATUS_CHECKED)
+ chunk_appendf(&trash, "%lu,", sv->check.duration);
+ else
+ chunk_appendf(&trash, ",");
- if (bi_putchk(rep, &trash) == -1)
- return 0;
}
+ else
+ chunk_appendf(&trash, ",,,");
- si->applet.ctx.stats.px = proxy;
- si->applet.ctx.stats.px_st = STAT_PX_ST_INIT;
- si->conn->xprt_st = STAT_ST_LIST;
- /* fall through */
+ /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
+ if (px->mode == PR_MODE_HTTP) {
+ for (i=1; i<6; i++)
+ chunk_appendf(&trash, "%lld,", sv->counters.p.http.rsp[i]);
- case STAT_ST_LIST:
- /* dump proxies */
- while (si->applet.ctx.stats.px) {
- if (buffer_almost_full(rep->buf))
- return 0;
- px = si->applet.ctx.stats.px;
- /* skip the disabled proxies, global frontend and non-networked ones */
- if (px->state != PR_STSTOPPED && px->uuid > 0 && (px->cap & (PR_CAP_FE | PR_CAP_BE)))
- if (stats_dump_proxy(si, px, uri) == 0)
- return 0;
+ chunk_appendf(&trash, "%lld,", sv->counters.p.http.rsp[0]);
+ }
+ else
+ chunk_appendf(&trash, ",,,,,,");
- si->applet.ctx.stats.px = px->next;
- si->applet.ctx.stats.px_st = STAT_PX_ST_INIT;
+ /* failed health analyses */
+ chunk_appendf(&trash, "%lld,", sv->counters.failed_hana);
+
+ /* requests : req_rate, req_rate_max, req_tot, */
+ chunk_appendf(&trash, ",,,");
+
+ /* errors: cli_aborts, srv_aborts */
+ chunk_appendf(&trash, "%lld,%lld,",
+ sv->counters.cli_aborts, sv->counters.srv_aborts);
+
+ /* compression: in, out, bypassed, comp_rsp */
+ chunk_appendf(&trash, ",,,,");
+
+ /* finish with EOL */
+ chunk_appendf(&trash, "\n");
+ }
+ return 1;
+}
+
+/* Dumps a line for backend <px> to the trash for and uses the state from stream
+ * interface <si> and stats flags <flags>. The caller is responsible for clearing
+ * the trash if needed. Returns non-zero if it emits anything, zero otherwise.
+ */
+static int stats_dump_be_stats(struct stream_interface *si, struct proxy *px, int flags)
+{
+ struct chunk src;
+ int i;
+
+ if (!(px->cap & PR_CAP_BE))
+ return 0;
+
+ if ((si->applet.ctx.stats.flags & STAT_BOUND) && !(si->applet.ctx.stats.type & (1 << STATS_TYPE_BE)))
+ return 0;
+
+ if (!(si->applet.ctx.stats.flags & STAT_FMT_CSV)) { /* HTML mode */
+ chunk_appendf(&trash, "<tr class=\"backend\">");
+ if (px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) {
+ /* Column sub-heading for Enable or Disable server */
+ chunk_appendf(&trash, "<td></td>");
+ }
+ chunk_appendf(&trash, "<td class=ac");
+
+ if (flags & ST_SHLGNDS) {
+ /* balancing */
+ chunk_appendf(&trash, " title=\"balancing: %s",
+ backend_lb_algo_str(px->lbprm.algo & BE_LB_ALGO));
+
+ /* cookie */
+ if (px->cookie_name) {
+ chunk_appendf(&trash, ", cookie: '");
+ chunk_initlen(&src, px->cookie_name, 0, strlen(px->cookie_name));
+ chunk_htmlencode(&trash, &src);
+ chunk_appendf(&trash, "'");
+ }
+ chunk_appendf(&trash, "\"");
}
- /* here, we just have reached the last proxy */
- si->conn->xprt_st = STAT_ST_END;
- /* fall through */
+ chunk_appendf(&trash,
+ /* name */
+ ">%s<a name=\"%s/Backend\"></a>"
+ "<a class=lfsb href=\"#%s/Backend\">Backend</a>%s</td>"
+ /* queue : current, max */
+ "<td>%s</td><td>%s</td><td></td>"
+ /* sessions rate : current, max, limit */
+ "<td>%s</td><td>%s</td><td></td>"
+ "",
+ (flags & ST_SHLGNDS)?"<u>":"",
+ px->id, px->id,
+ (flags & ST_SHLGNDS)?"</u>":"",
+ 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));
- case STAT_ST_END:
- if (!(si->applet.ctx.stats.flags & STAT_FMT_CSV)) {
- chunk_appendf(&trash, "</body></html>\n");
- if (bi_putchk(rep, &trash) == -1)
- return 0;
+ chunk_appendf(&trash,
+ /* sessions: current, max, limit */
+ "<td>%s</td><td>%s</td><td>%s</td>"
+ "<td"
+ "",
+ U2H2(px->beconn), U2H3(px->be_counters.conn_max), U2H4(px->fullconn));
+
+ /* 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);
+
+ 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%%)\"",
+ 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);
}
- si->conn->xprt_st = STAT_ST_FIN;
- /* fall through */
+ chunk_appendf(&trash,
+ /* sessions: total, lbtot */
+ ">%s%s%s</td><td>%s</td>"
+ /* bytes: in */
+ "<td>%s</td><td"
+ "",
+ (px->mode == PR_MODE_HTTP)?"<u>":"",
+ U2H6(px->be_counters.cum_conn),
+ (px->mode == PR_MODE_HTTP)?"</u>":"",
+ 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);
- case STAT_ST_FIN:
- return 1;
+ chunk_appendf(&trash,
+ /* bytes: out */
+ ">%s%s%s</td>"
+ "",
+ (px->be_counters.comp_in || px->be_counters.comp_byp) ? "<u>":"",
+ U2H0(px->be_counters.bytes_out),
+ (px->be_counters.comp_in || px->be_counters.comp_byp) ? "</u>":"");
- default:
- /* unknown state ! */
- si->conn->xprt_st = STAT_ST_FIN;
- return -1;
+ chunk_appendf(&trash,
+ /* denied: req, resp */
+ "<td>%s</td><td>%s</td>"
+ /* errors : request, connect */
+ "<td></td><td>%s</td>"
+ /* errors : response */
+ "<td title=\"Connection resets during transfers: %lld client, %lld server\"><u>%s</u></td>"
+ /* warnings: retries, redispatches */
+ "<td>%lld</td><td>%lld</td>"
+ /* backend status: reflect backend status (up/down): we display UP
+ * if the backend has known working servers or if it has no server at
+ * all (eg: for stats). Then we display the total weight, number of
+ * active and backups. */
+ "<td class=ac>%s %s</td><td class=ac> </td><td class=ac>%d</td>"
+ "<td class=ac>%d</td><td class=ac>%d</td>"
+ "",
+ U2H0(px->be_counters.denied_req), U2H1(px->be_counters.denied_resp),
+ U2H2(px->be_counters.failed_conns),
+ 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" :
+ "<font color=\"red\"><b>DOWN</b></font>",
+ (px->lbprm.tot_weight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
+ px->srv_act, px->srv_bck);
+
+ chunk_appendf(&trash,
+ /* rest of backend: nothing, down transitions, total downtime, throttle */
+ "<td class=ac> </td><td>%d</td>"
+ "<td>%s</td>"
+ "<td></td>"
+ "</tr>",
+ px->down_trans,
+ px->srv?human_time(be_downtime(px), 1):" ");
+ }
+ else { /* CSV mode */
+ chunk_appendf(&trash,
+ /* pxid, name */
+ "%s,BACKEND,"
+ /* queue : current, max */
+ "%d,%d,"
+ /* sessions : current, max, limit, total */
+ "%d,%d,%d,%lld,"
+ /* bytes : in, out */
+ "%lld,%lld,"
+ /* denied: req, resp */
+ "%lld,%lld,"
+ /* errors : request, connect, response */
+ ",%lld,%lld,"
+ /* warnings: retries, redispatches */
+ "%lld,%lld,"
+ /* backend status: reflect backend status (up/down): we display UP
+ * if the backend has known working servers or if it has no server at
+ * all (eg: for stats). Then we display the total weight, number of
+ * active and backups. */
+ "%s,"
+ "%d,%d,%d,"
+ /* rest of backend: nothing, down transitions, last change, total downtime */
+ ",%d,%d,%d,,"
+ /* pid, iid, sid, throttle, lbtot, tracked, type */
+ "%d,%d,0,,%lld,,%d,"
+ /* rate, rate_lim, rate_max, */
+ "%u,,%u,"
+ /* check_status, check_code, check_duration */
+ ",,,",
+ px->id,
+ px->nbpend /* or px->totpend ? */, px->be_counters.nbpend_max,
+ px->beconn, px->be_counters.conn_max, px->fullconn, px->be_counters.cum_conn,
+ px->be_counters.bytes_in, px->be_counters.bytes_out,
+ px->be_counters.denied_req, px->be_counters.denied_resp,
+ px->be_counters.failed_conns, px->be_counters.failed_resp,
+ px->be_counters.retries, px->be_counters.redispatches,
+ (px->lbprm.tot_weight > 0 || !px->srv) ? "UP" : "DOWN",
+ (px->lbprm.tot_weight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
+ px->srv_act, px->srv_bck,
+ px->down_trans, (int)(now.tv_sec - px->last_change),
+ px->srv?be_downtime(px):0,
+ relative_pid, px->uuid,
+ px->be_counters.cum_lbconn, STATS_TYPE_BE,
+ read_freq_ctr(&px->be_sess_per_sec),
+ px->be_counters.sps_max);
+
+ /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
+ if (px->mode == PR_MODE_HTTP) {
+ for (i=1; i<6; i++)
+ chunk_appendf(&trash, "%lld,", px->be_counters.p.http.rsp[i]);
+ chunk_appendf(&trash, "%lld,", px->be_counters.p.http.rsp[0]);
+ }
+ else
+ chunk_appendf(&trash, ",,,,,,");
+
+ /* failed health analyses */
+ chunk_appendf(&trash, ",");
+
+ /* requests : req_rate, req_rate_max, req_tot, */
+ chunk_appendf(&trash, ",,,");
+
+ /* errors: cli_aborts, srv_aborts */
+ chunk_appendf(&trash, "%lld,%lld,",
+ px->be_counters.cli_aborts, px->be_counters.srv_aborts);
+
+ /* compression: in, out, bypassed */
+ chunk_appendf(&trash, "%lld,%lld,%lld,",
+ px->be_counters.comp_in, px->be_counters.comp_out, px->be_counters.comp_byp);
+
+ /* compression: comp_rsp */
+ chunk_appendf(&trash, "%lld,", px->be_counters.p.http.comp_rsp);
+
+ /* finish with EOL */
+ chunk_appendf(&trash, "\n");
}
+ return 1;
}
+/* Dumps the table header for proxy <px> to the trash for and uses the state from
+ * stream interface <si> and per-uri parameters <uri>. The caller is responsible
+ * for clearing the trash if needed. Returns non-zero if it emits anything, zero
+ * otherwise.
+ */
+static int stats_dump_px_hdr(struct stream_interface *si, struct proxy *px, struct uri_auth *uri)
+{
+ if (si->applet.ctx.stats.flags & STAT_FMT_CSV)
+ return 0;
+
+ if (px->cap & PR_CAP_BE && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) {
+ /* A form to enable/disable this proxy servers */
+ chunk_appendf(&trash,
+ "<form action=\"%s\" method=\"post\">",
+ uri->uri_prefix);
+ }
+
+ /* print a new table */
+ chunk_appendf(&trash,
+ "<table class=\"tbl\" width=\"100%%\">\n"
+ "<tr class=\"titre\">"
+ "<th class=\"pxname\" width=\"10%%\"");
+
+ if (uri->flags & ST_SHLGNDS) {
+ /* cap, mode, id */
+ chunk_appendf(&trash, " title=\"cap: %s, mode: %s, id: %d",
+ proxy_cap_str(px->cap), proxy_mode_str(px->mode),
+ px->uuid);
+ chunk_appendf(&trash, "\"");
+ }
+
+ chunk_appendf(&trash,
+ ">%s<a name=\"%s\"></a>"
+ "<a class=px href=\"#%s\">%s</a>%s</th>"
+ "<th class=\"%s\" width=\"90%%\">%s</th>"
+ "</tr>\n"
+ "</table>\n"
+ "<table class=\"tbl\" width=\"100%%\">\n"
+ "<tr class=\"titre\">",
+ (uri->flags & ST_SHLGNDS) ? "<u>":"",
+ px->id, px->id, px->id,
+ (uri->flags & ST_SHLGNDS) ? "</u>":"",
+ px->desc ? "desc" : "empty", px->desc ? px->desc : "");
+
+ if ((px->cap & PR_CAP_BE) && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) {
+ /* Column heading for Enable or Disable server */
+ chunk_appendf(&trash, "<th rowspan=2 width=1></th>");
+ }
+
+ chunk_appendf(&trash,
+ "<th rowspan=2></th>"
+ "<th colspan=3>Queue</th>"
+ "<th colspan=3>Session rate</th><th colspan=5>Sessions</th>"
+ "<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
+ "<th colspan=3>Errors</th><th colspan=2>Warnings</th>"
+ "<th colspan=9>Server</th>"
+ "</tr>\n"
+ "<tr class=\"titre\">"
+ "<th>Cur</th><th>Max</th><th>Limit</th>"
+ "<th>Cur</th><th>Max</th><th>Limit</th><th>Cur</th><th>Max</th>"
+ "<th>Limit</th><th>Total</th><th>LbTot</th><th>In</th><th>Out</th>"
+ "<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
+ "<th>Resp</th><th>Retr</th><th>Redis</th>"
+ "<th>Status</th><th>LastChk</th><th>Wght</th><th>Act</th>"
+ "<th>Bck</th><th>Chk</th><th>Dwn</th><th>Dwntme</th>"
+ "<th>Thrtle</th>\n"
+ "</tr>");
+ return 1;
+}
+
+/* Dumps the table trailer for proxy <px> to the trash for and uses the state from
+ * stream interface <si>. The caller is responsible for clearing the trash if needed.
+ * Returns non-zero if it emits anything, zero otherwise.
+ */
+static int stats_dump_px_end(struct stream_interface *si, struct proxy *px)
+{
+ if (si->applet.ctx.stats.flags & STAT_FMT_CSV)
+ return 0;
+
+ chunk_appendf(&trash, "</table>");
+
+ if ((px->cap & PR_CAP_BE) && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) {
+ /* close the form used to enable/disable this proxy servers */
+ chunk_appendf(&trash,
+ "Choose the action to perform on the checked servers : "
+ "<select name=action>"
+ "<option value=\"\"></option>"
+ "<option value=\"disable\">Disable</option>"
+ "<option value=\"enable\">Enable</option>"
+ "<option value=\"stop\">Soft Stop</option>"
+ "<option value=\"start\">Soft Start</option>"
+ "<option value=\"shutdown\">Kill Sessions</option>"
+ "</select>"
+ "<input type=\"hidden\" name=\"b\" value=\"#%d\">"
+ " <input type=\"submit\" value=\"Apply\">"
+ "</form>",
+ px->uuid);
+ }
+
+ chunk_appendf(&trash, "<p>\n");
+ return 1;
+}
/*
* Dumps statistics for a proxy.
return 1;
}
- if ((si->applet.ctx.stats.flags & STAT_BOUND) && (si->applet.ctx.stats.iid != -1) &&
- (px->uuid != si->applet.ctx.stats.iid))
+ if ((si->applet.ctx.stats.flags & STAT_BOUND) &&
+ (si->applet.ctx.stats.iid != -1) &&
+ (px->uuid != si->applet.ctx.stats.iid))
return 1;
si->applet.ctx.stats.px_st = STAT_PX_ST_TH;
/* fall through */
case STAT_PX_ST_TH:
- if (!(si->applet.ctx.stats.flags & STAT_FMT_CSV)) {
- if (px->cap & PR_CAP_BE && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) {
- /* A form to enable/disable this proxy servers */
- chunk_appendf(&trash,
- "<form action=\"%s\" method=\"post\">",
- uri->uri_prefix);
- }
-
- /* print a new table */
- chunk_appendf(&trash,
- "<table class=\"tbl\" width=\"100%%\">\n"
- "<tr class=\"titre\">"
- "<th class=\"pxname\" width=\"10%%\"");
-
- if (uri->flags&ST_SHLGNDS) {
- /* cap, mode, id */
- chunk_appendf(&trash, " title=\"cap: %s, mode: %s, id: %d",
- proxy_cap_str(px->cap), proxy_mode_str(px->mode),
- px->uuid);
-
- chunk_appendf(&trash, "\"");
- }
-
- chunk_appendf(&trash,
- ">%s<a name=\"%s\"></a>"
- "<a class=px href=\"#%s\">%s</a>%s</th>"
- "<th class=\"%s\" width=\"90%%\">%s</th>"
- "</tr>\n"
- "</table>\n"
- "<table class=\"tbl\" width=\"100%%\">\n"
- "<tr class=\"titre\">",
- (uri->flags & ST_SHLGNDS)?"<u>":"",
- px->id, px->id, px->id,
- (uri->flags & ST_SHLGNDS)?"</u>":"",
- px->desc ? "desc" : "empty", px->desc ? px->desc : "");
-
- if (px->cap & PR_CAP_BE && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) {
- /* Column heading for Enable or Disable server */
- chunk_appendf(&trash, "<th rowspan=2 width=1></th>");
- }
-
- chunk_appendf(&trash,
- "<th rowspan=2></th>"
- "<th colspan=3>Queue</th>"
- "<th colspan=3>Session rate</th><th colspan=5>Sessions</th>"
- "<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
- "<th colspan=3>Errors</th><th colspan=2>Warnings</th>"
- "<th colspan=9>Server</th>"
- "</tr>\n"
- "<tr class=\"titre\">"
- "<th>Cur</th><th>Max</th><th>Limit</th>"
- "<th>Cur</th><th>Max</th><th>Limit</th><th>Cur</th><th>Max</th>"
- "<th>Limit</th><th>Total</th><th>LbTot</th><th>In</th><th>Out</th>"
- "<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
- "<th>Resp</th><th>Retr</th><th>Redis</th>"
- "<th>Status</th><th>LastChk</th><th>Wght</th><th>Act</th>"
- "<th>Bck</th><th>Chk</th><th>Dwn</th><th>Dwntme</th>"
- "<th>Thrtle</th>\n"
- "</tr>");
-
+ if (stats_dump_px_hdr(si, px, uri))
if (bi_putchk(rep, &trash) == -1)
return 0;
- }
si->applet.ctx.stats.px_st = STAT_PX_ST_FE;
/* fall through */
case STAT_PX_ST_FE:
/* print the frontend */
- if ((px->cap & PR_CAP_FE) &&
- (!(si->applet.ctx.stats.flags & STAT_BOUND) || (si->applet.ctx.stats.type & (1 << STATS_TYPE_FE)))) {
- if (!(si->applet.ctx.stats.flags & STAT_FMT_CSV)) {
- chunk_appendf(&trash,
- /* name, queue */
- "<tr class=\"frontend\">");
-
- if (px->cap & PR_CAP_BE && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) {
- /* Column sub-heading for Enable or Disable server */
- chunk_appendf(&trash, "<td></td>");
- }
-
- chunk_appendf(&trash,
- "<td class=ac>"
- "<a name=\"%s/Frontend\"></a>"
- "<a class=lfsb href=\"#%s/Frontend\">Frontend</a></td>"
- "<td colspan=3></td>"
- "",
- px->id, px->id);
-
- if (px->mode == PR_MODE_HTTP) {
- chunk_appendf(&trash,
- /* sessions rate : current, max, limit */
- "<td title=\"Cur: %u req/s\"><u>%s</u></td><td title=\"Max: %u req/s\"><u>%s</u></td><td>%s</td>"
- "",
- 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, "-"));
- } else {
- chunk_appendf(&trash,
- /* sessions rate : current, max, limit */
- "<td>%s</td><td>%s</td><td>%s</td>"
- "",
- U2H0(read_freq_ctr(&px->fe_sess_per_sec)),
- U2H1(px->fe_counters.sps_max), LIM2A2(px->fe_sps_lim, "-"));
- }
-
- chunk_appendf(&trash,
- /* sessions: current, max, limit */
- "<td>%s</td><td>%s</td><td>%s</td>"
- "<td"
- "",
- U2H3(px->feconn), U2H4(px->fe_counters.conn_max), U2H5(px->maxconn));
-
- /* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */
- if (px->mode == PR_MODE_HTTP) {
- int i;
-
- chunk_appendf(&trash, " title=\"%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]);
-
- chunk_appendf(&trash, " other=%lld,", px->fe_counters.p.http.rsp[0]);
- chunk_appendf(&trash, " compressed=%lld (%d%%)",
- 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,
- /* sessions: total, lbtot */
- ">%s%s%s</td><td></td>"
- /* bytes : in */
- "<td>%s</td><td"
- "",
- (px->mode == PR_MODE_HTTP)?"<u>":"",
- U2H6(px->fe_counters.cum_sess),
- (px->mode == PR_MODE_HTTP)?"</u>":"",
- 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</td>"
- "",
- (px->fe_counters.comp_in || px->fe_counters.comp_byp) ? "<u>":"",
- U2H0(px->fe_counters.bytes_out),
- (px->fe_counters.comp_in || px->fe_counters.comp_byp) ? "</u>":"");
-
- chunk_appendf(&trash,
- /* denied: req, resp */
- "<td>%s</td><td>%s</td>"
- /* errors : request, connect, response */
- "<td>%s</td><td></td><td></td>"
- /* warnings: retries, redispatches */
- "<td></td><td></td>"
- /* server status : reflect frontend status */
- "<td class=ac>%s</td>"
- /* rest of server: nothing */
- "<td class=ac colspan=8></td></tr>"
- "",
- U2H0(px->fe_counters.denied_req), U2H1(px->fe_counters.denied_resp),
- U2H2(px->fe_counters.failed_req),
- px->state == PR_STREADY ? "OPEN" :
- px->state == PR_STFULL ? "FULL" : "STOP");
- } else {
- chunk_appendf(&trash,
- /* pxid, name, queue cur, queue max, */
- "%s,FRONTEND,,,"
- /* sessions : current, max, limit, total */
- "%d,%d,%d,%lld,"
- /* bytes : in, out */
- "%lld,%lld,"
- /* denied: req, resp */
- "%lld,%lld,"
- /* errors : request, connect, response */
- "%lld,,,"
- /* warnings: retries, redispatches */
- ",,"
- /* server status : reflect frontend status */
- "%s,"
- /* rest of server: nothing */
- ",,,,,,,,"
- /* pid, iid, sid, throttle, lbtot, tracked, type */
- "%d,%d,0,,,,%d,"
- /* rate, rate_lim, rate_max */
- "%u,%u,%u,"
- /* check_status, check_code, check_duration */
- ",,,",
- px->id,
- px->feconn, px->fe_counters.conn_max, px->maxconn, px->fe_counters.cum_sess,
- px->fe_counters.bytes_in, px->fe_counters.bytes_out,
- px->fe_counters.denied_req, px->fe_counters.denied_resp,
- px->fe_counters.failed_req,
- px->state == PR_STREADY ? "OPEN" :
- px->state == PR_STFULL ? "FULL" : "STOP",
- relative_pid, px->uuid, STATS_TYPE_FE,
- read_freq_ctr(&px->fe_sess_per_sec),
- px->fe_sps_lim, px->fe_counters.sps_max);
-
- /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
- if (px->mode == PR_MODE_HTTP) {
- int i;
-
- for (i=1; i<6; i++)
- chunk_appendf(&trash, "%lld,", px->fe_counters.p.http.rsp[i]);
-
- chunk_appendf(&trash, "%lld,", px->fe_counters.p.http.rsp[0]);
- } else {
- chunk_appendf(&trash, ",,,,,,");
- }
-
- /* failed health analyses */
- chunk_appendf(&trash, ",");
-
- /* requests : req_rate, req_rate_max, req_tot, */
- chunk_appendf(&trash, "%u,%u,%lld,",
- read_freq_ctr(&px->fe_req_per_sec),
- px->fe_counters.p.http.rps_max, px->fe_counters.p.http.cum_req);
-
- /* errors: cli_aborts, srv_aborts */
- chunk_appendf(&trash, ",,");
-
- /* compression: in, out, bypassed */
- chunk_appendf(&trash, "%lld,%lld,%lld,",
- px->fe_counters.comp_in, px->fe_counters.comp_out, px->fe_counters.comp_byp);
-
- /* compression: comp_rsp */
- chunk_appendf(&trash, "%lld,",
- px->fe_counters.p.http.comp_rsp);
-
- /* finish with EOL */
- chunk_appendf(&trash, "\n");
- }
-
+ if (stats_dump_fe_stats(si, px))
if (bi_putchk(rep, &trash) == -1)
return 0;
- }
si->applet.ctx.stats.l = px->conf.listeners.n;
si->applet.ctx.stats.px_st = STAT_PX_ST_LI;
continue;
}
- if (!(si->applet.ctx.stats.flags & STAT_FMT_CSV)) {
- chunk_appendf(&trash, "<tr class=socket>");
- if (px->cap & PR_CAP_BE && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) {
- /* Column sub-heading for Enable or Disable server */
- chunk_appendf(&trash, "<td></td>");
- }
- chunk_appendf(&trash, "<td class=ac");
-
- if (uri->flags&ST_SHLGNDS) {
- char str[INET6_ADDRSTRLEN];
- int port;
-
- chunk_appendf(&trash, " title=\"");
-
- port = get_host_port(&l->addr);
- switch (addr_to_str(&l->addr, str, sizeof(str))) {
- case AF_INET:
- chunk_appendf(&trash, "IPv4: %s:%d, ", str, port);
- break;
- case AF_INET6:
- chunk_appendf(&trash, "IPv6: [%s]:%d, ", str, port);
- break;
- case AF_UNIX:
- chunk_appendf(&trash, "unix, ");
- break;
- case -1:
- chunk_appendf(&trash, "(%s), ", strerror(errno));
- break;
- }
-
- /* id */
- chunk_appendf(&trash, "id: %d\"", l->luid);
- }
-
- chunk_appendf(&trash,
- /* name, queue */
- ">%s<a name=\"%s/+%s\"></a>"
- "<a class=lfsb href=\"#%s/+%s\">%s</a></td><td colspan=3>%s</td>"
- /* sessions rate: current, max, limit */
- "<td colspan=3> </td>"
- /* sessions: current, max, limit, total, lbtot */
- "<td>%s</td><td>%s</td><td>%s</td>"
- "<td>%s</td><td> </td>"
- /* bytes: in, out */
- "<td>%s</td><td>%s</td>"
- "",
- (uri->flags & ST_SHLGNDS)?"<u>":"",
- px->id, l->name, px->id, l->name, l->name,
- (uri->flags & ST_SHLGNDS)?"</u>":"",
- 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));
-
- chunk_appendf(&trash,
- /* denied: req, resp */
- "<td>%s</td><td>%s</td>"
- /* errors: request, connect, response */
- "<td>%s</td><td></td><td></td>"
- /* warnings: retries, redispatches */
- "<td></td><td></td>"
- /* server status: reflect listener status */
- "<td class=ac>%s</td>"
- /* rest of server: nothing */
- "<td class=ac colspan=8></td></tr>"
- "",
- U2H0(l->counters->denied_req), U2H1(l->counters->denied_resp),
- U2H2(l->counters->failed_req),
- (l->nbconn < l->maxconn) ? (l->state == LI_LIMITED) ? "WAITING" : "OPEN" : "FULL");
- } else {
- chunk_appendf(&trash,
- /* pxid, name, queue cur, queue max, */
- "%s,%s,,,"
- /* sessions: current, max, limit, total */
- "%d,%d,%d,%lld,"
- /* bytes: in, out */
- "%lld,%lld,"
- /* denied: req, resp */
- "%lld,%lld,"
- /* errors: request, connect, response */
- "%lld,,,"
- /* warnings: retries, redispatches */
- ",,"
- /* server status: reflect listener status */
- "%s,"
- /* rest of server: nothing */
- ",,,,,,,,"
- /* pid, iid, sid, throttle, lbtot, tracked, type */
- "%d,%d,%d,,,,%d,"
- /* rate, rate_lim, rate_max */
- ",,,"
- /* check_status, check_code, check_duration */
- ",,,"
- /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
- ",,,,,,"
- /* failed health analyses */
- ","
- /* requests : req_rate, req_rate_max, req_tot, */
- ",,,"
- /* errors: cli_aborts, srv_aborts */
- ",,"
- /* compression: in, out, bypassed, comp_rsp */
- ",,,,"
- "\n",
- px->id, l->name,
- l->nbconn, l->counters->conn_max,
- l->maxconn, l->counters->cum_conn,
- l->counters->bytes_in, l->counters->bytes_out,
- l->counters->denied_req, l->counters->denied_resp,
- l->counters->failed_req,
- (l->nbconn < l->maxconn) ? "OPEN" : "FULL",
- relative_pid, px->uuid, l->luid, STATS_TYPE_SO);
- }
-
- if (bi_putchk(rep, &trash) == -1)
- return 0;
+ /* print the frontend */
+ if (stats_dump_li_stats(si, px, l, uri ? uri->flags : 0))
+ if (bi_putchk(rep, &trash) == -1)
+ return 0;
}
si->applet.ctx.stats.sv = px->srv; /* may be NULL */
continue;
}
- if (!(si->applet.ctx.stats.flags & STAT_FMT_CSV)) {
- static char *srv_hlt_st[7] = { "DOWN", "DN %d/%d ↑",
- "UP %d/%d ↓", "UP",
- "NOLB %d/%d ↓", "NOLB",
- "<i>no check</i>" };
- if ((sv->state & SRV_MAINTAIN) || (svs->state & SRV_MAINTAIN)) {
- chunk_appendf(&trash,
- /* name */
- "<tr class=\"maintain\">"
- );
- }
- else {
- chunk_appendf(&trash,
- /* name */
- "<tr class=\"%s%d\">",
- (sv->state & SRV_BACKUP) ? "backup" : "active", sv_state);
- }
-
- if (px->cap & PR_CAP_BE && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) {
- chunk_appendf(&trash,
- "<td><input type=\"checkbox\" name=\"s\" value=\"%s\"></td>",
- sv->id);
- }
-
- chunk_appendf(&trash, "<td class=ac");
-
- if (uri->flags&ST_SHLGNDS) {
- char str[INET6_ADDRSTRLEN];
-
- chunk_appendf(&trash, " title=\"");
-
- switch (addr_to_str(&sv->addr, str, sizeof(str))) {
- case AF_INET:
- chunk_appendf(&trash, "IPv4: %s:%d, ", str, get_host_port(&sv->addr));
- break;
- case AF_INET6:
- chunk_appendf(&trash, "IPv6: [%s]:%d, ", str, get_host_port(&sv->addr));
- break;
- case AF_UNIX:
- chunk_appendf(&trash, "unix, ");
- break;
- case -1:
- chunk_appendf(&trash, "(%s), ", strerror(errno));
- break;
- default: /* address family not supported */
- break;
- }
-
- /* id */
- chunk_appendf(&trash, "id: %d", sv->puid);
-
- /* cookie */
- if (sv->cookie) {
- struct chunk src;
-
- chunk_appendf(&trash, ", cookie: '");
-
- chunk_initlen(&src, sv->cookie, 0, strlen(sv->cookie));
- chunk_htmlencode(&trash, &src);
-
- chunk_appendf(&trash, "'");
- }
-
- chunk_appendf(&trash, "\"");
- }
-
- chunk_appendf(&trash,
- ">%s<a name=\"%s/%s\"></a>"
- "<a class=lfsb href=\"#%s/%s\">%s</a>%s</td>"
- /* queue : current, max, limit */
- "<td>%s</td><td>%s</td><td>%s</td>"
- /* sessions rate : current, max, limit */
- "<td>%s</td><td>%s</td><td></td>"
- /* sessions: current, max, limit */
- "<td>%s</td><td>%s</td><td>%s</td>"
- "<td"
- "",
- (uri->flags & ST_SHLGNDS)?"<u>":"",
- px->id, sv->id, px->id, sv->id, sv->id,
- (uri->flags & ST_SHLGNDS)?"</u>":"",
- 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, "-"));
-
- /* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */
- if (px->mode == PR_MODE_HTTP) {
- int i;
-
- chunk_appendf(&trash, " title=\"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]);
- }
+ if (stats_dump_sv_stats(si, px, uri ? uri->flags : 0, sv, sv_state))
+ if (bi_putchk(rep, &trash) == -1)
+ return 0;
+ } /* for sv */
- chunk_appendf(&trash,
- /* sessions: total, lbtot */
- ">%s%s%s</td><td>%s</td>",
- (px->mode == PR_MODE_HTTP)?"<u>":"",
- U2H0(sv->counters.cum_sess),
- (px->mode == PR_MODE_HTTP)?"</u>":"",
- U2H1(sv->counters.cum_lbconn));
+ si->applet.ctx.stats.px_st = STAT_PX_ST_BE;
+ /* fall through */
- chunk_appendf(&trash,
- /* bytes : in, out */
- "<td>%s</td><td>%s</td>"
- /* denied: req, resp */
- "<td></td><td>%s</td>"
- /* errors : request, connect */
- "<td></td><td>%s</td>"
- /* errors : response */
- "<td title=\"Connection resets during transfers: %lld client, %lld server\"><u>%s</u></td>"
- /* warnings: retries, redispatches */
- "<td>%lld</td><td>%lld</td>"
- "",
- U2H0(sv->counters.bytes_in), U2H1(sv->counters.bytes_out),
- U2H2(sv->counters.failed_secu),
- U2H3(sv->counters.failed_conns),
- sv->counters.cli_aborts,
- sv->counters.srv_aborts,
- U2H6(sv->counters.failed_resp),
- sv->counters.retries, sv->counters.redispatches);
-
- /* status, lest check */
- chunk_appendf(&trash, "<td class=ac>");
-
- if (sv->state & SRV_MAINTAIN) {
- chunk_appendf(&trash, "%s ",
- human_time(now.tv_sec - sv->last_change, 1));
- chunk_appendf(&trash, "MAINT");
- }
- else if (svs != sv && svs->state & SRV_MAINTAIN) {
- chunk_appendf(&trash, "%s ",
- human_time(now.tv_sec - svs->last_change, 1));
- chunk_appendf(&trash, "MAINT(via)");
- }
- else if (svs->state & SRV_CHECKED) {
- chunk_appendf(&trash, "%s ",
- human_time(now.tv_sec - svs->last_change, 1));
-
- chunk_appendf(&trash,
- srv_hlt_st[sv_state],
- (svs->state & SRV_RUNNING) ? (svs->health - svs->rise + 1) : (svs->health),
- (svs->state & SRV_RUNNING) ? (svs->fall) : (svs->rise));
- }
+ case STAT_PX_ST_BE:
+ /* print the backend */
+ if (stats_dump_be_stats(si, px, uri ? uri->flags : 0))
+ if (bi_putchk(rep, &trash) == -1)
+ return 0;
- if (sv->state & SRV_CHECKED) {
- chunk_appendf(&trash, "</td><td class=ac title=\"%s",
- get_check_status_description(sv->check.status));
+ si->applet.ctx.stats.px_st = STAT_PX_ST_END;
+ /* fall through */
- if (*sv->check.desc) {
- struct chunk src;
+ case STAT_PX_ST_END:
+ if (stats_dump_px_end(si, px))
+ if (bi_putchk(rep, &trash) == -1)
+ return 0;
- chunk_appendf(&trash, ": ");
+ si->applet.ctx.stats.px_st = STAT_PX_ST_FIN;
+ /* fall through */
- chunk_initlen(&src, sv->check.desc, 0, strlen(sv->check.desc));
- chunk_htmlencode(&trash, &src);
- }
+ case STAT_PX_ST_FIN:
+ return 1;
- chunk_appendf(&trash, "\"><u> %s%s",
- (sv->state & SRV_CHK_RUNNING) ? "* " : "",
- get_check_status_info(sv->check.status));
+ default:
+ /* unknown state, we should put an abort() here ! */
+ return 1;
+ }
+}
- if (sv->check.status >= HCHK_STATUS_L57DATA)
- chunk_appendf(&trash, "/%d", sv->check.code);
+/* Dumps the HTTP stats head block to the trash for and uses the state from
+ * stream interface <si> and per-uri parameters <uri>. The caller is responsible
+ * for clearing the trash if needed.
+ */
+static void stats_dump_html_head(struct stream_interface *si, struct proxy *px, struct uri_auth *uri)
+{
+ /* WARNING! This must fit in the first buffer !!! */
+ chunk_appendf(&trash,
+ "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"
+ "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
+ "<html><head><title>Statistics Report for " PRODUCT_NAME "%s%s</title>\n"
+ "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
+ "<style type=\"text/css\"><!--\n"
+ "body {"
+ " font-family: arial, helvetica, sans-serif;"
+ " font-size: 12px;"
+ " font-weight: normal;"
+ " color: black;"
+ " background: white;"
+ "}\n"
+ "th,td {"
+ " font-size: 10px;"
+ "}\n"
+ "h1 {"
+ " font-size: x-large;"
+ " margin-bottom: 0.5em;"
+ "}\n"
+ "h2 {"
+ " font-family: helvetica, arial;"
+ " font-size: x-large;"
+ " font-weight: bold;"
+ " font-style: italic;"
+ " color: #6020a0;"
+ " margin-top: 0em;"
+ " margin-bottom: 0em;"
+ "}\n"
+ "h3 {"
+ " font-family: helvetica, arial;"
+ " font-size: 16px;"
+ " font-weight: bold;"
+ " color: #b00040;"
+ " background: #e8e8d0;"
+ " margin-top: 0em;"
+ " margin-bottom: 0em;"
+ "}\n"
+ "li {"
+ " margin-top: 0.25em;"
+ " margin-right: 2em;"
+ "}\n"
+ ".hr {margin-top: 0.25em;"
+ " border-color: black;"
+ " border-bottom-style: solid;"
+ "}\n"
+ ".titre {background: #20D0D0;color: #000000; font-weight: bold; text-align: center;}\n"
+ ".total {background: #20D0D0;color: #ffff80;}\n"
+ ".frontend {background: #e8e8d0;}\n"
+ ".socket {background: #d0d0d0;}\n"
+ ".backend {background: #e8e8d0;}\n"
+ ".active0 {background: #ff9090;}\n"
+ ".active1 {background: #ffd020;}\n"
+ ".active2 {background: #ffffa0;}\n"
+ ".active3 {background: #c0ffc0;}\n"
+ ".active4 {background: #ffffa0;}\n" /* NOLB state shows same as going down */
+ ".active5 {background: #a0e0a0;}\n" /* NOLB state shows darker than up */
+ ".active6 {background: #e0e0e0;}\n"
+ ".backup0 {background: #ff9090;}\n"
+ ".backup1 {background: #ff80ff;}\n"
+ ".backup2 {background: #c060ff;}\n"
+ ".backup3 {background: #b0d0ff;}\n"
+ ".backup4 {background: #c060ff;}\n" /* NOLB state shows same as going down */
+ ".backup5 {background: #90b0e0;}\n" /* NOLB state shows same as going down */
+ ".backup6 {background: #e0e0e0;}\n"
+ ".maintain {background: #c07820;}\n"
+ ".rls {letter-spacing: 0.2em; margin-right: 1px;}\n" /* right letter spacing (used for grouping digits) */
+ "\n"
+ "a.px:link {color: #ffff40; text-decoration: none;}"
+ "a.px:visited {color: #ffff40; text-decoration: none;}"
+ "a.px:hover {color: #ffffff; text-decoration: none;}"
+ "a.lfsb:link {color: #000000; text-decoration: none;}"
+ "a.lfsb:visited {color: #000000; text-decoration: none;}"
+ "a.lfsb:hover {color: #505050; text-decoration: none;}"
+ "\n"
+ "table.tbl { border-collapse: collapse; border-style: none;}\n"
+ "table.tbl td { text-align: right; border-width: 1px 1px 1px 1px; border-style: solid solid solid solid; padding: 2px 3px; border-color: gray; white-space: nowrap;}\n"
+ "table.tbl td.ac { text-align: center;}\n"
+ "table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray;}\n"
+ "table.tbl th.pxname { background: #b00040; color: #ffff40; font-weight: bold; border-style: solid solid none solid; padding: 2px 3px; white-space: nowrap;}\n"
+ "table.tbl th.empty { border-style: none; empty-cells: hide; background: white;}\n"
+ "table.tbl th.desc { background: white; border-style: solid solid none solid; text-align: left; padding: 2px 3px;}\n"
+ "\n"
+ "table.lgd { border-collapse: collapse; border-width: 1px; border-style: none none none solid; border-color: black;}\n"
+ "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"
+ "-->\n"
+ "</style></head>\n",
+ (uri->flags & ST_SHNODE) ? " on " : "",
+ (uri->flags & ST_SHNODE) ? (uri->node ? uri->node : global.node) : ""
+ );
+}
- if (sv->check.status >= HCHK_STATUS_CHECKED && sv->check.duration >= 0)
- chunk_appendf(&trash, " in %lums</u>", sv->check.duration);
- } else
- chunk_appendf(&trash, "</td><td>");
+/* Dumps the HTTP stats information block to the trash for and uses the state from
+ * stream interface <si> and per-uri parameters <uri>. The caller is responsible
+ * for clearing the trash if needed. Returns non-zero if it emits anything, zero
+ * otherwise.
+ */
+static int stats_dump_http_info(struct stream_interface *si, struct proxy *px, struct uri_auth *uri)
+{
+ unsigned int up = (now.tv_sec - start_date.tv_sec);
- chunk_appendf(&trash,
- /* weight */
- "</td><td class=ac>%d</td>"
- /* act, bck */
- "<td class=ac>%s</td><td class=ac>%s</td>"
- "",
- (sv->eweight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
- (sv->state & SRV_BACKUP) ? "-" : "Y",
- (sv->state & SRV_BACKUP) ? "Y" : "-");
-
- /* check failures: unique, fatal, down time */
- if (sv->state & SRV_CHECKED) {
- chunk_appendf(&trash, "<td title=\"Failed Health Checks%s\"><u>%lld",
- svs->observe?"/Health Analyses":"", svs->counters.failed_checks);
-
- if (svs->observe)
- chunk_appendf(&trash, "/%lld", svs->counters.failed_hana);
-
- chunk_appendf(&trash,
- "</u></td>"
- "<td>%lld</td><td>%s</td>"
- "",
- svs->counters.down_trans, human_time(srv_downtime(sv), 1));
- } else if (sv != svs)
- chunk_appendf(&trash,
- "<td class=ac colspan=3><a class=lfsb href=\"#%s/%s\">via %s/%s<a></td>",
- svs->proxy->id, svs->id, svs->proxy->id, svs->id);
- else
- chunk_appendf(&trash,
- "<td colspan=3></td>");
-
- /* throttle */
- if ((sv->state & SRV_WARMINGUP) &&
- now.tv_sec < sv->last_change + sv->slowstart &&
- now.tv_sec >= sv->last_change) {
- unsigned int ratio;
- ratio = MAX(1, 100 * (now.tv_sec - sv->last_change) / sv->slowstart);
- chunk_appendf(&trash,
- "<td class=ac>%d %%</td></tr>\n", ratio);
- } else {
- chunk_appendf(&trash,
- "<td class=ac>-</td></tr>\n");
- }
- } else {
- static char *srv_hlt_st[7] = { "DOWN,", "DOWN %d/%d,",
- "UP %d/%d,", "UP,",
- "NOLB %d/%d,", "NOLB,",
- "no check," };
- chunk_appendf(&trash,
- /* pxid, name */
- "%s,%s,"
- /* queue : current, max */
- "%d,%d,"
- /* sessions : current, max, limit, total */
- "%d,%d,%s,%lld,"
- /* bytes : in, out */
- "%lld,%lld,"
- /* denied: req, resp */
- ",%lld,"
- /* errors : request, connect, response */
- ",%lld,%lld,"
- /* warnings: retries, redispatches */
- "%lld,%lld,"
- "",
- px->id, sv->id,
- sv->nbpend, sv->counters.nbpend_max,
- sv->cur_sess, sv->counters.cur_sess_max, LIM2A0(sv->maxconn, ""), sv->counters.cum_sess,
- sv->counters.bytes_in, sv->counters.bytes_out,
- sv->counters.failed_secu,
- sv->counters.failed_conns, sv->counters.failed_resp,
- sv->counters.retries, sv->counters.redispatches);
-
- /* status */
- if (sv->state & SRV_MAINTAIN) {
- chunk_appendf(&trash, "MAINT,");
- }
- else if (svs != sv && svs->state & SRV_MAINTAIN) {
- chunk_appendf(&trash, "MAINT(via),");
- }
- else {
- chunk_appendf(&trash,
- srv_hlt_st[sv_state],
- (svs->state & SRV_RUNNING) ? (svs->health - svs->rise + 1) : (svs->health),
- (svs->state & SRV_RUNNING) ? (svs->fall) : (svs->rise));
- }
+ /* WARNING! this has to fit the first packet too.
+ * We are around 3.5 kB, add adding entries will
+ * become tricky if we want to support 4kB buffers !
+ */
+ if ((si->applet.ctx.stats.flags & STAT_FMT_CSV))
+ return 0;
- chunk_appendf(&trash,
- /* weight, active, backup */
- "%d,%d,%d,"
- "",
- (sv->eweight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
- (sv->state & SRV_BACKUP) ? 0 : 1,
- (sv->state & SRV_BACKUP) ? 1 : 0);
-
- /* check failures: unique, fatal; last change, total downtime */
- if (sv->state & SRV_CHECKED)
- chunk_appendf(&trash,
- "%lld,%lld,%d,%d,",
- sv->counters.failed_checks, sv->counters.down_trans,
- (int)(now.tv_sec - sv->last_change), srv_downtime(sv));
- else
- chunk_appendf(&trash,
- ",,,,");
+ chunk_appendf(&trash,
+ "<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
+ PRODUCT_NAME "%s</a></h1>\n"
+ "<h2>Statistics Report for pid %d%s%s%s%s</h2>\n"
+ "<hr width=\"100%%\" class=\"hr\">\n"
+ "<h3>> General process information</h3>\n"
+ "<table border=0><tr><td align=\"left\" nowrap width=\"1%%\">\n"
+ "<p><b>pid = </b> %d (process #%d, nbproc = %d)<br>\n"
+ "<b>uptime = </b> %dd %dh%02dm%02ds<br>\n"
+ "<b>system limits:</b> memmax = %s%s; ulimit-n = %d<br>\n"
+ "<b>maxsock = </b> %d; <b>maxconn = </b> %d; <b>maxpipes = </b> %d<br>\n"
+ "current conns = %d; current pipes = %d/%d; conn rate = %d/sec<br>\n"
+ "Running tasks: %d/%d; idle = %d %%<br>\n"
+ "</td><td align=\"center\" nowrap>\n"
+ "<table class=\"lgd\"><tr>\n"
+ "<td class=\"active3\"> </td><td class=\"noborder\">active UP </td>"
+ "<td class=\"backup3\"> </td><td class=\"noborder\">backup UP </td>"
+ "</tr><tr>\n"
+ "<td class=\"active2\"></td><td class=\"noborder\">active UP, going down </td>"
+ "<td class=\"backup2\"></td><td class=\"noborder\">backup UP, going down </td>"
+ "</tr><tr>\n"
+ "<td class=\"active1\"></td><td class=\"noborder\">active DOWN, going up </td>"
+ "<td class=\"backup1\"></td><td class=\"noborder\">backup DOWN, going up </td>"
+ "</tr><tr>\n"
+ "<td class=\"active0\"></td><td class=\"noborder\">active or backup DOWN </td>"
+ "<td class=\"active6\"></td><td class=\"noborder\">not checked </td>"
+ "</tr><tr>\n"
+ "<td class=\"maintain\"></td><td class=\"noborder\" colspan=\"3\">active or backup DOWN for maintenance (MAINT) </td>"
+ "</tr></table>\n"
+ "Note: UP with load-balancing disabled is reported as \"NOLB\"."
+ "</td>"
+ "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
+ "<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
+ "",
+ (uri->flags & ST_HIDEVER) ? "" : (STATS_VERSION_STRING),
+ pid, (uri->flags & ST_SHNODE) ? " on " : "",
+ (uri->flags & ST_SHNODE) ? (uri->node ? uri->node : global.node) : "",
+ (uri->flags & ST_SHDESC) ? ": " : "",
+ (uri->flags & ST_SHDESC) ? (uri->desc ? uri->desc : global.desc) : "",
+ pid, relative_pid, global.nbproc,
+ up / 86400, (up % 86400) / 3600,
+ (up % 3600) / 60, (up % 60),
+ global.rlimit_memmax ? ultoa(global.rlimit_memmax) : "unlimited",
+ global.rlimit_memmax ? " MB" : "",
+ global.rlimit_nofile,
+ global.maxsock, global.maxconn, global.maxpipes,
+ actconn, pipes_used, pipes_used+pipes_free, read_freq_ctr(&global.conn_per_sec),
+ run_queue_cur, nb_tasks_cur, idle_pct
+ );
+
+ if (si->applet.ctx.stats.flags & STAT_HIDE_DOWN)
+ chunk_appendf(&trash,
+ "<li><a href=\"%s%s%s\">Show all servers</a><br>\n",
+ uri->uri_prefix,
+ "",
+ (si->applet.ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
+ else
+ chunk_appendf(&trash,
+ "<li><a href=\"%s%s%s\">Hide 'DOWN' servers</a><br>\n",
+ uri->uri_prefix,
+ ";up",
+ (si->applet.ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
- /* queue limit, pid, iid, sid, */
- chunk_appendf(&trash,
- "%s,"
- "%d,%d,%d,",
- LIM2A0(sv->maxqueue, ""),
- relative_pid, px->uuid, sv->puid);
-
- /* throttle */
- if ((sv->state & SRV_WARMINGUP) &&
- now.tv_sec < sv->last_change + sv->slowstart &&
- now.tv_sec >= sv->last_change) {
- unsigned int ratio;
- ratio = MAX(1, 100 * (now.tv_sec - sv->last_change) / sv->slowstart);
- chunk_appendf(&trash, "%d", ratio);
- }
+ if (uri->refresh > 0) {
+ if (si->applet.ctx.stats.flags & STAT_NO_REFRESH)
+ chunk_appendf(&trash,
+ "<li><a href=\"%s%s%s\">Enable refresh</a><br>\n",
+ uri->uri_prefix,
+ (si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
+ "");
+ else
+ chunk_appendf(&trash,
+ "<li><a href=\"%s%s%s\">Disable refresh</a><br>\n",
+ uri->uri_prefix,
+ (si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
+ ";norefresh");
+ }
- /* sessions: lbtot */
- chunk_appendf(&trash, ",%lld,", sv->counters.cum_lbconn);
+ chunk_appendf(&trash,
+ "<li><a href=\"%s%s%s\">Refresh now</a><br>\n",
+ uri->uri_prefix,
+ (si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
+ (si->applet.ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
+
+ chunk_appendf(&trash,
+ "<li><a href=\"%s;csv%s\">CSV export</a><br>\n",
+ uri->uri_prefix,
+ (uri->refresh > 0) ? ";norefresh" : "");
+
+ chunk_appendf(&trash,
+ "</ul></td>"
+ "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
+ "<b>External resources:</b><ul style=\"margin-top: 0.25em;\">\n"
+ "<li><a href=\"" PRODUCT_URL "\">Primary site</a><br>\n"
+ "<li><a href=\"" PRODUCT_URL_UPD "\">Updates (v" PRODUCT_BRANCH ")</a><br>\n"
+ "<li><a href=\"" PRODUCT_URL_DOC "\">Online manual</a><br>\n"
+ "</ul>"
+ "</td>"
+ "</tr></table>\n"
+ ""
+ );
+
+ if (si->applet.ctx.stats.st_code) {
+ switch (si->applet.ctx.stats.st_code) {
+ case STAT_STATUS_DONE:
+ chunk_appendf(&trash,
+ "<p><div class=active3>"
+ "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
+ "Action processed successfully."
+ "</div>\n", uri->uri_prefix);
+ break;
+ case STAT_STATUS_NONE:
+ chunk_appendf(&trash,
+ "<p><div class=active2>"
+ "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
+ "Nothing has changed."
+ "</div>\n", uri->uri_prefix);
+ break;
+ case STAT_STATUS_PART:
+ chunk_appendf(&trash,
+ "<p><div class=active2>"
+ "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
+ "Action partially processed.<br>"
+ "Some server names are probably unknown or ambiguous (duplicated names in the backend)."
+ "</div>\n", uri->uri_prefix);
+ break;
+ case STAT_STATUS_ERRP:
+ chunk_appendf(&trash,
+ "<p><div class=active0>"
+ "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
+ "Action not processed because of invalid parameters."
+ "<ul>"
+ "<li>The action is maybe unknown.</li>"
+ "<li>The backend name is probably unknown or ambiguous (duplicated names).</li>"
+ "<li>Some server names are probably unknown or ambiguous (duplicated names in the backend).</li>"
+ "</ul>"
+ "</div>\n", uri->uri_prefix);
+ break;
+ case STAT_STATUS_EXCD:
+ chunk_appendf(&trash,
+ "<p><div class=active0>"
+ "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
+ "<b>Action not processed : the buffer couldn't store all the data.<br>"
+ "You should retry with less servers at a time.</b>"
+ "</div>\n", uri->uri_prefix);
+ break;
+ case STAT_STATUS_DENY:
+ chunk_appendf(&trash,
+ "<p><div class=active0>"
+ "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
+ "<b>Action denied.</b>"
+ "</div>\n", uri->uri_prefix);
+ break;
+ default:
+ chunk_appendf(&trash,
+ "<p><div class=active6>"
+ "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
+ "Unexpected result."
+ "</div>\n", uri->uri_prefix);
+ }
+ chunk_appendf(&trash, "<p>\n");
+ }
+ return 1;
+}
- /* tracked */
- if (sv->track)
- chunk_appendf(&trash, "%s/%s,",
- sv->track->proxy->id, sv->track->id);
- else
- chunk_appendf(&trash, ",");
+/* Dumps the HTTP stats trailer block to the trash for and uses the state from
+ * stream interface <si> and per-uri parameters <uri>. The caller is responsible
+ * for clearing the trash if needed. Returns non-zero if it emits anything, zero
+ * otherwise.
+ */
+static int stats_dump_http_end(struct stream_interface *si, struct proxy *px, struct uri_auth *uri)
+{
+ if (si->applet.ctx.stats.flags & STAT_FMT_CSV)
+ return 0;
- /* type */
- chunk_appendf(&trash, "%d,", STATS_TYPE_SV);
+ chunk_appendf(&trash, "</body></html>\n");
+ return 1;
+}
- /* rate */
- chunk_appendf(&trash, "%u,,%u,",
- read_freq_ctr(&sv->sess_per_sec),
- sv->counters.sps_max);
+/* This function dumps statistics in HTTP format onto the stream interface's
+ * read buffer. The xprt_ctx must have been zeroed first, and the flags
+ * properly set. It returns 0 if it had to stop writing data and an I/O is
+ * needed, 1 if the dump is finished and the session must be closed, or -1
+ * in case of any error.
+ */
+static int stats_dump_http(struct stream_interface *si, struct uri_auth *uri)
+{
+ struct session *s = si->conn->xprt_ctx;
+ struct channel *rep = si->ib;
+ struct proxy *px;
- if (sv->state & SRV_CHECKED) {
- /* check_status */
- chunk_appendf(&trash, "%s,", get_check_status_info(sv->check.status));
+ chunk_reset(&trash);
- /* check_code */
- if (sv->check.status >= HCHK_STATUS_L57DATA)
- chunk_appendf(&trash, "%u,", sv->check.code);
- else
- chunk_appendf(&trash, ",");
+ switch (si->conn->xprt_st) {
+ case STAT_ST_INIT:
+ chunk_appendf(&trash,
+ "HTTP/1.0 200 OK\r\n"
+ "Cache-Control: no-cache\r\n"
+ "Connection: close\r\n"
+ "Content-Type: %s\r\n",
+ (si->applet.ctx.stats.flags & STAT_FMT_CSV) ? "text/plain" : "text/html");
- /* check_duration */
- if (sv->check.status >= HCHK_STATUS_CHECKED)
- chunk_appendf(&trash, "%lu,", sv->check.duration);
- else
- chunk_appendf(&trash, ",");
+ if (uri->refresh > 0 && !(si->applet.ctx.stats.flags & STAT_NO_REFRESH))
+ chunk_appendf(&trash, "Refresh: %d\r\n",
+ uri->refresh);
- } else {
- chunk_appendf(&trash, ",,,");
- }
+ chunk_appendf(&trash, "\r\n");
- /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
- if (px->mode == PR_MODE_HTTP) {
- int i;
+ s->txn.status = 200;
+ if (bi_putchk(rep, &trash) == -1)
+ return 0;
- for (i=1; i<6; i++)
- chunk_appendf(&trash, "%lld,", sv->counters.p.http.rsp[i]);
+ if (!(s->flags & SN_ERR_MASK)) // this is not really an error but it is
+ s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy
+ if (!(s->flags & SN_FINST_MASK))
+ s->flags |= SN_FINST_R;
- chunk_appendf(&trash, "%lld,", sv->counters.p.http.rsp[0]);
- } else {
- chunk_appendf(&trash, ",,,,,,");
- }
+ if (s->txn.meth == HTTP_METH_HEAD) {
+ /* that's all we return in case of HEAD request */
+ si->conn->xprt_st = STAT_ST_FIN;
+ return 1;
+ }
- /* failed health analyses */
- chunk_appendf(&trash, "%lld,", sv->counters.failed_hana);
+ si->conn->xprt_st = STAT_ST_HEAD; /* let's start producing data */
+ /* fall through */
- /* requests : req_rate, req_rate_max, req_tot, */
- chunk_appendf(&trash, ",,,");
+ case STAT_ST_HEAD:
+ if (si->applet.ctx.stats.flags & STAT_FMT_CSV)
+ stats_dump_csv_header();
+ else
+ stats_dump_html_head(si, px, uri);
- /* errors: cli_aborts, srv_aborts */
- chunk_appendf(&trash, "%lld,%lld,",
- sv->counters.cli_aborts, sv->counters.srv_aborts);
+ if (bi_putchk(rep, &trash) == -1)
+ return 0;
- /* compression: in, out, bypassed, comp_rsp */
- chunk_appendf(&trash, ",,,,");
+ si->conn->xprt_st = STAT_ST_INFO;
+ /* fall through */
- /* finish with EOL */
- chunk_appendf(&trash, "\n");
- }
+ case STAT_ST_INFO:
+ if (stats_dump_http_info(si, px, uri)) {
if (bi_putchk(rep, &trash) == -1)
return 0;
- } /* for sv */
+ }
- si->applet.ctx.stats.px_st = STAT_PX_ST_BE;
+ si->applet.ctx.stats.px = proxy;
+ si->applet.ctx.stats.px_st = STAT_PX_ST_INIT;
+ si->conn->xprt_st = STAT_ST_LIST;
/* fall through */
- case STAT_PX_ST_BE:
- /* print the backend */
- if ((px->cap & PR_CAP_BE) &&
- (!(si->applet.ctx.stats.flags & STAT_BOUND) || (si->applet.ctx.stats.type & (1 << STATS_TYPE_BE)))) {
- if (!(si->applet.ctx.stats.flags & STAT_FMT_CSV)) {
- chunk_appendf(&trash, "<tr class=\"backend\">");
- if (px->cap & PR_CAP_BE && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) {
- /* Column sub-heading for Enable or Disable server */
- chunk_appendf(&trash, "<td></td>");
- }
- chunk_appendf(&trash, "<td class=ac");
-
- if (uri->flags&ST_SHLGNDS) {
- /* balancing */
- chunk_appendf(&trash, " title=\"balancing: %s",
- backend_lb_algo_str(px->lbprm.algo & BE_LB_ALGO));
-
- /* cookie */
- if (px->cookie_name) {
- struct chunk src;
+ case STAT_ST_LIST:
+ /* dump proxies */
+ while (si->applet.ctx.stats.px) {
+ if (buffer_almost_full(rep->buf))
+ return 0;
- chunk_appendf(&trash, ", cookie: '");
+ px = si->applet.ctx.stats.px;
+ /* skip the disabled proxies, global frontend and non-networked ones */
+ if (px->state != PR_STSTOPPED && px->uuid > 0 && (px->cap & (PR_CAP_FE | PR_CAP_BE)))
+ if (stats_dump_proxy(si, px, uri) == 0)
+ return 0;
- chunk_initlen(&src, px->cookie_name, 0, strlen(px->cookie_name));
- chunk_htmlencode(&trash, &src);
+ si->applet.ctx.stats.px = px->next;
+ si->applet.ctx.stats.px_st = STAT_PX_ST_INIT;
+ }
+ /* here, we just have reached the last proxy */
- chunk_appendf(&trash, "'");
- }
+ si->conn->xprt_st = STAT_ST_END;
+ /* fall through */
- chunk_appendf(&trash, "\"");
+ case STAT_ST_END:
+ if (stats_dump_http_end(si, px, uri)) {
+ if (bi_putchk(rep, &trash) == -1)
+ return 0;
+ }
- }
+ si->conn->xprt_st = STAT_ST_FIN;
+ /* fall through */
- chunk_appendf(&trash,
- /* name */
- ">%s<a name=\"%s/Backend\"></a>"
- "<a class=lfsb href=\"#%s/Backend\">Backend</a>%s</td>"
- /* queue : current, max */
- "<td>%s</td><td>%s</td><td></td>"
- /* sessions rate : current, max, limit */
- "<td>%s</td><td>%s</td><td></td>"
- "",
- (uri->flags & ST_SHLGNDS)?"<u>":"",
- px->id, px->id,
- (uri->flags & ST_SHLGNDS)?"</u>":"",
- 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));
+ case STAT_ST_FIN:
+ return 1;
- chunk_appendf(&trash,
- /* sessions: current, max, limit */
- "<td>%s</td><td>%s</td><td>%s</td>"
- "<td"
- "",
- U2H2(px->beconn), U2H3(px->be_counters.conn_max), U2H4(px->fullconn));
-
- /* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */
- if (px->mode == PR_MODE_HTTP) {
- int i;
-
- chunk_appendf(&trash, " title=\"%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%%)\"",
- 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);
- }
+ default:
+ /* unknown state ! */
+ si->conn->xprt_st = STAT_ST_FIN;
+ return -1;
+ }
+}
- chunk_appendf(&trash,
- /* sessions: total, lbtot */
- ">%s%s%s</td><td>%s</td>"
- /* bytes: in */
- "<td>%s</td><td"
- "",
- (px->mode == PR_MODE_HTTP)?"<u>":"",
- U2H6(px->be_counters.cum_conn),
- (px->mode == PR_MODE_HTTP)?"</u>":"",
- 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);
+/* We don't want to land on the posted stats page because a refresh will
+ * repost the data. We don't want this to happen on accident so we redirect
+ * the browse to the stats page with a GET.
+ */
+static int stats_http_redir(struct stream_interface *si, struct uri_auth *uri)
+{
+ struct session *s = si->conn->xprt_ctx;
- chunk_appendf(&trash,
- /* bytes: out */
- ">%s%s%s</td>"
- "",
- (px->be_counters.comp_in || px->be_counters.comp_byp) ? "<u>":"",
- U2H0(px->be_counters.bytes_out),
- (px->be_counters.comp_in || px->be_counters.comp_byp) ? "</u>":"");
+ chunk_reset(&trash);
- chunk_appendf(&trash,
- /* denied: req, resp */
- "<td>%s</td><td>%s</td>"
- /* errors : request, connect */
- "<td></td><td>%s</td>"
- /* errors : response */
- "<td title=\"Connection resets during transfers: %lld client, %lld server\"><u>%s</u></td>"
- /* warnings: retries, redispatches */
- "<td>%lld</td><td>%lld</td>"
- /* backend status: reflect backend status (up/down): we display UP
- * if the backend has known working servers or if it has no server at
- * all (eg: for stats). Then we display the total weight, number of
- * active and backups. */
- "<td class=ac>%s %s</td><td class=ac> </td><td class=ac>%d</td>"
- "<td class=ac>%d</td><td class=ac>%d</td>"
- "",
- U2H0(px->be_counters.denied_req), U2H1(px->be_counters.denied_resp),
- U2H2(px->be_counters.failed_conns),
- 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" :
- "<font color=\"red\"><b>DOWN</b></font>",
- (px->lbprm.tot_weight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
- px->srv_act, px->srv_bck);
+ switch (si->conn->xprt_st) {
+ case STAT_ST_INIT:
+ chunk_appendf(&trash,
+ "HTTP/1.0 303 See Other\r\n"
+ "Cache-Control: no-cache\r\n"
+ "Content-Type: text/plain\r\n"
+ "Connection: close\r\n"
+ "Location: %s;st=%s",
+ uri->uri_prefix,
+ ((si->applet.ctx.stats.st_code > STAT_STATUS_INIT) &&
+ (si->applet.ctx.stats.st_code < STAT_STATUS_SIZE) &&
+ stat_status_codes[si->applet.ctx.stats.st_code]) ?
+ stat_status_codes[si->applet.ctx.stats.st_code] :
+ stat_status_codes[STAT_STATUS_UNKN]);
+ chunk_appendf(&trash, "\r\n\r\n");
- chunk_appendf(&trash,
- /* rest of backend: nothing, down transitions, total downtime, throttle */
- "<td class=ac> </td><td>%d</td>"
- "<td>%s</td>"
- "<td></td>"
- "</tr>",
- px->down_trans,
- px->srv?human_time(be_downtime(px), 1):" ");
- } else {
- chunk_appendf(&trash,
- /* pxid, name */
- "%s,BACKEND,"
- /* queue : current, max */
- "%d,%d,"
- /* sessions : current, max, limit, total */
- "%d,%d,%d,%lld,"
- /* bytes : in, out */
- "%lld,%lld,"
- /* denied: req, resp */
- "%lld,%lld,"
- /* errors : request, connect, response */
- ",%lld,%lld,"
- /* warnings: retries, redispatches */
- "%lld,%lld,"
- /* backend status: reflect backend status (up/down): we display UP
- * if the backend has known working servers or if it has no server at
- * all (eg: for stats). Then we display the total weight, number of
- * active and backups. */
- "%s,"
- "%d,%d,%d,"
- /* rest of backend: nothing, down transitions, last change, total downtime */
- ",%d,%d,%d,,"
- /* pid, iid, sid, throttle, lbtot, tracked, type */
- "%d,%d,0,,%lld,,%d,"
- /* rate, rate_lim, rate_max, */
- "%u,,%u,"
- /* check_status, check_code, check_duration */
- ",,,",
- px->id,
- px->nbpend /* or px->totpend ? */, px->be_counters.nbpend_max,
- px->beconn, px->be_counters.conn_max, px->fullconn, px->be_counters.cum_conn,
- px->be_counters.bytes_in, px->be_counters.bytes_out,
- px->be_counters.denied_req, px->be_counters.denied_resp,
- px->be_counters.failed_conns, px->be_counters.failed_resp,
- px->be_counters.retries, px->be_counters.redispatches,
- (px->lbprm.tot_weight > 0 || !px->srv) ? "UP" : "DOWN",
- (px->lbprm.tot_weight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
- px->srv_act, px->srv_bck,
- px->down_trans, (int)(now.tv_sec - px->last_change),
- px->srv?be_downtime(px):0,
- relative_pid, px->uuid,
- px->be_counters.cum_lbconn, STATS_TYPE_BE,
- read_freq_ctr(&px->be_sess_per_sec),
- px->be_counters.sps_max);
-
- /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
- if (px->mode == PR_MODE_HTTP) {
- int i;
-
- for (i=1; i<6; i++)
- chunk_appendf(&trash, "%lld,", px->be_counters.p.http.rsp[i]);
-
- chunk_appendf(&trash, "%lld,", px->be_counters.p.http.rsp[0]);
- } else {
- chunk_appendf(&trash, ",,,,,,");
- }
+ if (bi_putchk(si->ib, &trash) == -1)
+ return 0;
- /* failed health analyses */
- chunk_appendf(&trash, ",");
+ s->txn.status = 303;
- /* requests : req_rate, req_rate_max, req_tot, */
- chunk_appendf(&trash, ",,,");
+ if (!(s->flags & SN_ERR_MASK)) // this is not really an error but it is
+ s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy
+ if (!(s->flags & SN_FINST_MASK))
+ s->flags |= SN_FINST_R;
- /* errors: cli_aborts, srv_aborts */
- chunk_appendf(&trash, "%lld,%lld,",
- px->be_counters.cli_aborts, px->be_counters.srv_aborts);
+ si->conn->xprt_st = STAT_ST_FIN;
+ return 1;
+ }
+ return 1;
+}
- /* compression: in, out, bypassed */
- chunk_appendf(&trash, "%lld,%lld,%lld,",
- px->be_counters.comp_in, px->be_counters.comp_out, px->be_counters.comp_byp);
+/* This I/O handler runs as an applet embedded in a stream interface. It is
+ * used to send HTTP stats over a TCP socket. The mechanism is very simple.
+ * si->applet.st0 becomes non-zero once the transfer is finished. The handler
+ * automatically unregisters itself once transfer is complete.
+ */
+static void http_stats_io_handler(struct stream_interface *si)
+{
+ struct session *s = si->conn->xprt_ctx;
+ struct channel *req = si->ob;
+ struct channel *res = si->ib;
- /* compression: comp_rsp */
- chunk_appendf(&trash, "%lld,",
- px->be_counters.p.http.comp_rsp);
+ if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
+ goto out;
- /* finish with EOL */
- chunk_appendf(&trash, "\n");
+ /* check that the output is not closed */
+ if (res->flags & (CF_SHUTW|CF_SHUTW_NOW))
+ si->applet.st0 = 1;
+ if (!si->applet.st0) {
+ if (s->txn.meth == HTTP_METH_POST) {
+ if (stats_http_redir(si, s->be->uri_auth)) {
+ si->applet.st0 = 1;
+ si_shutw(si);
}
- if (bi_putchk(rep, &trash) == -1)
- return 0;
- }
-
- si->applet.ctx.stats.px_st = STAT_PX_ST_END;
- /* fall through */
-
- case STAT_PX_ST_END:
- if (!(si->applet.ctx.stats.flags & STAT_FMT_CSV)) {
- chunk_appendf(&trash, "</table>");
-
- if (px->cap & PR_CAP_BE && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) {
- /* close the form used to enable/disable this proxy servers */
- chunk_appendf(&trash,
- "Choose the action to perform on the checked servers : "
- "<select name=action>"
- "<option value=\"\"></option>"
- "<option value=\"disable\">Disable</option>"
- "<option value=\"enable\">Enable</option>"
- "<option value=\"stop\">Soft Stop</option>"
- "<option value=\"start\">Soft Start</option>"
- "<option value=\"shutdown\">Kill Sessions</option>"
- "</select>"
- "<input type=\"hidden\" name=\"b\" value=\"#%d\">"
- " <input type=\"submit\" value=\"Apply\">"
- "</form>",
- px->uuid);
+ } else {
+ if (stats_dump_http(si, s->be->uri_auth)) {
+ si->applet.st0 = 1;
+ si_shutw(si);
}
+ }
+ }
- chunk_appendf(&trash, "<p>\n");
+ if ((res->flags & CF_SHUTR) && (si->state == SI_ST_EST))
+ si_shutw(si);
- if (bi_putchk(rep, &trash) == -1)
- return 0;
- }
+ if ((req->flags & CF_SHUTW) && (si->state == SI_ST_EST) && si->applet.st0) {
+ si_shutr(si);
+ res->flags |= CF_READ_NULL;
+ }
- si->applet.ctx.stats.px_st = STAT_PX_ST_FIN;
- /* fall through */
+ /* update all other flags and resync with the other side */
+ si_update(si);
- case STAT_PX_ST_FIN:
- return 1;
+ /* we don't want to expire timeouts while we're processing requests */
+ si->ib->rex = TICK_ETERNITY;
+ si->ob->wex = TICK_ETERNITY;
- default:
- /* unknown state, we should put an abort() here ! */
- return 1;
+ out:
+ if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO)) {
+ /* check that we have released everything then unregister */
+ stream_int_unregister_handler(si);
}
}
+
static inline const char *get_conn_ctrl_name(const struct connection *conn)
{
if (!conn->ctrl)