diff --git a/include/proto/dumpstats.h b/include/proto/dumpstats.h index b6a689c3a..e9b2f76e1 100644 --- a/include/proto/dumpstats.h +++ b/include/proto/dumpstats.h @@ -28,8 +28,6 @@ /* Flags for applet.ctx.stats.flags */ #define STAT_FMT_CSV 0x00000001 /* dump the stats in CSV format instead of HTML */ -#define STAT_SHOW_STAT 0x00000002 /* dump the stats part */ -#define STAT_SHOW_INFO 0x00000004 /* dump the info part */ #define STAT_HIDE_DOWN 0x00000008 /* hide 'down' servers in the stats page */ #define STAT_NO_REFRESH 0x00000010 /* do not automatically refresh the stats page */ #define STAT_ADMIN 0x00000020 /* indicate a stats admin level */ @@ -48,12 +46,13 @@ #define STAT_CLI_PROMPT 3 /* display the prompt (first output, same code) */ #define STAT_CLI_PRINT 4 /* display message in cli->msg */ -#define STAT_CLI_O_INFO 5 /* dump info/stats */ +#define STAT_CLI_O_INFO 5 /* dump info */ #define STAT_CLI_O_SESS 6 /* dump sessions */ #define STAT_CLI_O_ERR 7 /* dump errors */ #define STAT_CLI_O_TAB 8 /* dump tables */ #define STAT_CLI_O_CLR 9 /* clear tables */ #define STAT_CLI_O_SET 10 /* set entries in tables */ +#define STAT_CLI_O_STAT 11 /* dump stats */ extern struct si_applet http_stats_applet; diff --git a/src/dumpstats.c b/src/dumpstats.c index 31d24b0db..661aa3f53 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -1,7 +1,7 @@ /* * Functions dedicated to statistics output and the stats socket * - * Copyright 2000-2010 Willy Tarreau + * Copyright 2000-2012 Willy Tarreau * Copyright 2007-2009 Krzysztof Piotr Oledzki * * This program is free software; you can redistribute it and/or @@ -61,7 +61,8 @@ #include #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); @@ -69,6 +70,32 @@ static int stats_table_request(struct stream_interface *si, int show); 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[] = @@ -361,26 +388,29 @@ static int stats_parse_global(char **args, int section_type, struct proxy *curpx 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 . The format is : @@ -910,16 +940,14 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line) 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; @@ -1615,7 +1643,11 @@ static void cli_io_handler(struct stream_interface *si) 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: @@ -1701,93 +1733,98 @@ static void cli_io_handler(struct stream_interface *si) } } +/* Dumps the raw stats information block to the trash for and uses the state from + * stream interface . 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; @@ -1795,28 +1832,1573 @@ static int stats_dump_raw_to_buffer(struct stream_interface *si) si->conn->xprt_st = STAT_ST_LIST; /* fall through */ + default: + /* dump proxies */ + 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; + } + /* here, we just have reached the last proxy */ + return 1; + } +} + +/* Dumps a frontend's line to the trash for the current proxy and uses + * the state from stream interface . The caller is responsible for clearing + * the trash if needed. Returns non-zero if it emits anything, zero otherwise. + */ +static int stats_dump_fe_stats(struct stream_interface *si, struct proxy *px) +{ + int i; + + if (!(px->cap & PR_CAP_FE)) + return 0; + + 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, + /* name, queue */ + ""); + + 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, ""); + } + + chunk_appendf(&trash, + "" + "" + "Frontend" + "" + "", + px->id, px->id); + + if (px->mode == PR_MODE_HTTP) + chunk_appendf(&trash, + /* sessions rate : current, max, limit */ + "%s%s%s" + "", + read_freq_ctr(&px->fe_req_per_sec), + U2H0(read_freq_ctr(&px->fe_sess_per_sec)), + px->fe_counters.p.http.rps_max, + U2H1(px->fe_counters.sps_max), + LIM2A2(px->fe_sps_lim, "-")); + else + chunk_appendf(&trash, + /* sessions rate : current, max, limit */ + "%s%s%s" + "", + 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 */ + "%s%s%s" + "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); + } + + chunk_appendf(&trash, + /* sessions: total, lbtot */ + ">%s%s%s" + /* bytes : in */ + "%smode == PR_MODE_HTTP)?"":"", + U2H6(px->fe_counters.cum_sess), + (px->mode == PR_MODE_HTTP)?"":"", + U2H7(px->fe_counters.bytes_in)); + + /* compression stats (via td title): comp_in, comp_out, comp_byp */ + chunk_appendf(&trash, " title=\"compression: in=%lld out=%lld bypassed=%lld savings=%d%%\"", + px->fe_counters.comp_in, px->fe_counters.comp_out, px->fe_counters.comp_byp, + px->fe_counters.comp_in ? + (int)((px->fe_counters.comp_in - px->fe_counters.comp_out)*100/px->fe_counters.comp_in) : 0); + + chunk_appendf(&trash, + /* bytes: out */ + ">%s%s%s" + "", + (px->fe_counters.comp_in || px->fe_counters.comp_byp) ? "":"", + U2H0(px->fe_counters.bytes_out), + (px->fe_counters.comp_in || px->fe_counters.comp_byp) ? "":""); + + chunk_appendf(&trash, + /* denied: req, resp */ + "%s%s" + /* errors : request, connect, response */ + "%s" + /* warnings: retries, redispatches */ + "" + /* server status : reflect frontend status */ + "%s" + /* rest of server: nothing */ + "" + "", + 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; +} + +/* Dumps a line for listener and proxy to the trash and uses the state + * from stream interface , and stats flags . The caller is responsible + * for clearing the trash if needed. Returns non-zero if it emits anything, zero + * otherwise. + */ +static int stats_dump_li_stats(struct stream_interface *si, struct proxy *px, struct listener *l, int flags) +{ + if (!(si->applet.ctx.stats.flags & STAT_FMT_CSV)) { + chunk_appendf(&trash, ""); + 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, ""); + } + chunk_appendf(&trash, "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" + "%s%s" + /* sessions rate: current, max, limit */ + " " + /* sessions: current, max, limit, total, lbtot */ + "%s%s%s" + "%s " + /* bytes: in, out */ + "%s%s" + "", + (flags & ST_SHLGNDS)?"":"", + px->id, l->name, px->id, l->name, l->name, + (flags & ST_SHLGNDS)?"":"", + U2H3(l->nbconn), U2H4(l->counters->conn_max), U2H5(l->maxconn), + U2H6(l->counters->cum_conn), U2H7(l->counters->bytes_in), U2H8(l->counters->bytes_out)); + + chunk_appendf(&trash, + /* denied: req, resp */ + "%s%s" + /* errors: request, connect, response */ + "%s" + /* warnings: retries, redispatches */ + "" + /* server status: reflect listener status */ + "%s" + /* rest of server: nothing */ + "" + "", + 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 { /* 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; +} + +/* Dumps a line for server and proxy to the trash and uses the state + * from stream interface , stats flags , and server state . + * The caller is responsible for clearing the trash if needed. Returns non-zero + * if it emits anything, zero otherwise. The 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_sv_stats(struct stream_interface *si, struct proxy *px, int flags, struct server *sv, int state) +{ + 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", + "no check" + }; + + if ((sv->state & SRV_MAINTAIN) || (ref->state & SRV_MAINTAIN)) + chunk_appendf(&trash, ""); + else + chunk_appendf(&trash, + "", + (sv->state & SRV_BACKUP) ? "backup" : "active", state); + + if ((px->cap & PR_CAP_BE) && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) + chunk_appendf(&trash, + "", + sv->id); + + chunk_appendf(&trash, "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) { + 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" + "%s%s" + /* queue : current, max, limit */ + "%s%s%s" + /* sessions rate : current, max, limit */ + "%s%s" + /* sessions: current, max, limit */ + "%s%s%s" + "" : "", + px->id, sv->id, px->id, sv->id, sv->id, + (flags & ST_SHLGNDS) ? "" : "", + U2H0(sv->nbpend), U2H1(sv->counters.nbpend_max), LIM2A2(sv->maxqueue, "-"), + U2H3(read_freq_ctr(&sv->sess_per_sec)), U2H4(sv->counters.sps_max), + U2H5(sv->cur_sess), U2H6(sv->counters.cur_sess_max), LIM2A7(sv->maxconn, "-")); + + /* 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]); + } + + chunk_appendf(&trash, + /* sessions: total, lbtot */ + ">%s%s%s%s", + (px->mode == PR_MODE_HTTP)?"":"", + U2H0(sv->counters.cum_sess), + (px->mode == PR_MODE_HTTP)?"":"", + U2H1(sv->counters.cum_lbconn)); + + chunk_appendf(&trash, + /* bytes : in, out */ + "%s%s" + /* denied: req, resp */ + "%s" + /* errors : request, connect */ + "%s" + /* errors : response */ + "%s" + /* warnings: retries, redispatches */ + "%lld%lld" + "", + U2H0(sv->counters.bytes_in), U2H1(sv->counters.bytes_out), + U2H2(sv->counters.failed_secu), + U2H3(sv->counters.failed_conns), + 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, ""); + + 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, + 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, "check.status)); + + if (*sv->check.desc) { + chunk_appendf(&trash, ": "); + chunk_initlen(&src, sv->check.desc, 0, strlen(sv->check.desc)); + chunk_htmlencode(&trash, &src); + } + + chunk_appendf(&trash, "\"> %s%s", + (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", sv->check.duration); + } + else + chunk_appendf(&trash, ""); + + chunk_appendf(&trash, + /* weight */ + "%d" + /* act, bck */ + "%s%s" + "", + (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, "%lld", + ref->observe?"/Health Analyses":"", ref->counters.failed_checks); + + if (ref->observe) + chunk_appendf(&trash, "/%lld", ref->counters.failed_hana); + + chunk_appendf(&trash, + "" + "%lld%s" + "", + ref->counters.down_trans, human_time(srv_downtime(sv), 1)); + } + else if (sv != ref) + chunk_appendf(&trash, + "via %s/%s", + ref->proxy->id, ref->id, ref->proxy->id, ref->id); + else + chunk_appendf(&trash, ""); + + /* throttle */ + if ((sv->state & SRV_WARMINGUP) && + now.tv_sec < sv->last_change + sv->slowstart && + now.tv_sec >= sv->last_change) { + chunk_appendf(&trash, "%d %%\n", + (int)MAX(1, 100 * (now.tv_sec - sv->last_change) / sv->slowstart)); + } + else + chunk_appendf(&trash, "-\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, + 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, ","); + + } + else + chunk_appendf(&trash, ",,,"); + + /* 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]); + + chunk_appendf(&trash, "%lld,", sv->counters.p.http.rsp[0]); + } + else + chunk_appendf(&trash, ",,,,,,"); + + /* 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 to the trash for and uses the state from stream + * interface and stats 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, ""); + if (px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) { + /* Column sub-heading for Enable or Disable server */ + chunk_appendf(&trash, ""); + } + chunk_appendf(&trash, "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, "\""); + } + + chunk_appendf(&trash, + /* name */ + ">%s" + "Backend%s" + /* queue : current, max */ + "%s%s" + /* sessions rate : current, max, limit */ + "%s%s" + "", + (flags & ST_SHLGNDS)?"":"", + px->id, px->id, + (flags & ST_SHLGNDS)?"":"", + U2H0(px->nbpend) /* or px->totpend ? */, U2H1(px->be_counters.nbpend_max), + U2H2(read_freq_ctr(&px->be_sess_per_sec)), U2H3(px->be_counters.sps_max)); + + chunk_appendf(&trash, + /* sessions: current, max, limit */ + "%s%s%s" + "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); + } + + chunk_appendf(&trash, + /* sessions: total, lbtot */ + ">%s%s%s%s" + /* bytes: in */ + "%smode == PR_MODE_HTTP)?"":"", + U2H6(px->be_counters.cum_conn), + (px->mode == PR_MODE_HTTP)?"":"", + U2H7(px->be_counters.cum_lbconn), + U2H8(px->be_counters.bytes_in)); + + /* compression stats (via td title): comp_in, comp_out, comp_byp */ + chunk_appendf(&trash, " title=\"compression: in=%lld out=%lld bypassed=%lld savings=%d%%\"", + px->be_counters.comp_in, px->be_counters.comp_out, px->be_counters.comp_byp, + px->be_counters.comp_in ? + (int)((px->be_counters.comp_in - px->be_counters.comp_out)*100/px->be_counters.comp_in) : 0); + + chunk_appendf(&trash, + /* bytes: out */ + ">%s%s%s" + "", + (px->be_counters.comp_in || px->be_counters.comp_byp) ? "":"", + U2H0(px->be_counters.bytes_out), + (px->be_counters.comp_in || px->be_counters.comp_byp) ? "":""); + + chunk_appendf(&trash, + /* denied: req, resp */ + "%s%s" + /* errors : request, connect */ + "%s" + /* errors : response */ + "%s" + /* 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 %s %d" + "%d%d" + "", + 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" : + "DOWN", + (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 */ + " %d" + "%s" + "" + "", + 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 to the trash for and uses the state from + * stream interface and per-uri parameters . 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, + "
", + uri->uri_prefix); + } + + /* print a new table */ + chunk_appendf(&trash, + "\n" + "" + "" + "" + "\n" + "
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" + "%s%s%s
\n" + "\n" + "", + (uri->flags & ST_SHLGNDS) ? "":"", + px->id, px->id, px->id, + (uri->flags & ST_SHLGNDS) ? "":"", + px->desc ? "desc" : "empty", px->desc ? px->desc : ""); + + 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, ""); + } + + chunk_appendf(&trash, + "" + "" + "" + "" + "" + "" + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "\n" + ""); + return 1; +} + +/* Dumps the table trailer for proxy to the trash for and uses the state from + * stream interface . 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, "
QueueSession rateSessionsBytesDeniedErrorsWarningsServer
CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
"); + + 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 : " + "" + "" + " " + "
", + px->uuid); + } + + chunk_appendf(&trash, "

\n"); + return 1; +} + +/* + * Dumps statistics for a proxy. + * Returns 0 if it had to stop dumping data because of lack of buffer space, + * ot non-zero if everything completed. + */ +static int stats_dump_proxy(struct stream_interface *si, struct proxy *px, struct uri_auth *uri) +{ + struct session *s = si->conn->xprt_ctx; + struct channel *rep = si->ib; + struct server *sv, *svs; /* server and server-state, server-state=server or server->track */ + struct listener *l; + + chunk_reset(&trash); + + switch (si->applet.ctx.stats.px_st) { + case STAT_PX_ST_INIT: + /* we are on a new proxy */ + + if (uri && uri->scope) { + /* we have a limited scope, we have to check the proxy name */ + struct stat_scope *scope; + int len; + + len = strlen(px->id); + scope = uri->scope; + + while (scope) { + /* match exact proxy name */ + if (scope->px_len == len && !memcmp(px->id, scope->px_id, len)) + break; + + /* match '.' which means 'self' proxy */ + if (!strcmp(scope->px_id, ".") && px == s->be) + break; + scope = scope->next; + } + + /* proxy name not found : don't dump anything */ + if (scope == NULL) + return 1; + } + + 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 (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 (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; + /* fall through */ + + case STAT_PX_ST_LI: + /* stats.l has been initialized above */ + for (; si->applet.ctx.stats.l != &px->conf.listeners; si->applet.ctx.stats.l = l->by_fe.n) { + if (buffer_almost_full(rep->buf)) + return 0; + + l = LIST_ELEM(si->applet.ctx.stats.l, struct listener *, by_fe); + if (!l->counters) + continue; + + if (si->applet.ctx.stats.flags & STAT_BOUND) { + if (!(si->applet.ctx.stats.type & (1 << STATS_TYPE_SO))) + break; + + if (si->applet.ctx.stats.sid != -1 && l->luid != si->applet.ctx.stats.sid) + continue; + } + + /* 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 */ + si->applet.ctx.stats.px_st = STAT_PX_ST_SV; + /* fall through */ + + case STAT_PX_ST_SV: + /* stats.sv has been initialized above */ + for (; si->applet.ctx.stats.sv != NULL; si->applet.ctx.stats.sv = sv->next) { + int sv_state; /* 0=DOWN, 1=going up, 2=going down, 3=UP, 4,5=NOLB, 6=unchecked */ + + if (buffer_almost_full(rep->buf)) + return 0; + + sv = si->applet.ctx.stats.sv; + + if (si->applet.ctx.stats.flags & STAT_BOUND) { + if (!(si->applet.ctx.stats.type & (1 << STATS_TYPE_SV))) + break; + + if (si->applet.ctx.stats.sid != -1 && sv->puid != si->applet.ctx.stats.sid) + continue; + } + + if (sv->track) + svs = sv->track; + else + svs = sv; + + /* FIXME: produce some small strings for "UP/DOWN x/y &#xxxx;" */ + if (!(svs->state & SRV_CHECKED)) + sv_state = 6; + else if (svs->state & SRV_RUNNING) { + if (svs->health == svs->rise + svs->fall - 1) + sv_state = 3; /* UP */ + else + sv_state = 2; /* going down */ + + if (svs->state & SRV_GOINGDOWN) + sv_state += 2; + } + else + if (svs->health) + sv_state = 1; /* going up */ + else + sv_state = 0; /* DOWN */ + + if (((sv_state == 0) || (sv->state & SRV_MAINTAIN)) && (si->applet.ctx.stats.flags & STAT_HIDE_DOWN)) { + /* do not report servers which are DOWN */ + si->applet.ctx.stats.sv = sv->next; + continue; + } + + if (stats_dump_sv_stats(si, px, uri ? uri->flags : 0, sv, sv_state)) + if (bi_putchk(rep, &trash) == -1) + return 0; + } /* for sv */ + + si->applet.ctx.stats.px_st = STAT_PX_ST_BE; + /* fall through */ + + 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; + + si->applet.ctx.stats.px_st = STAT_PX_ST_END; + /* fall through */ + + case STAT_PX_ST_END: + if (stats_dump_px_end(si, px)) + if (bi_putchk(rep, &trash) == -1) + return 0; + + si->applet.ctx.stats.px_st = STAT_PX_ST_FIN; + /* fall through */ + + case STAT_PX_ST_FIN: + return 1; + + default: + /* unknown state, we should put an abort() here ! */ + return 1; + } +} + +/* Dumps the HTTP stats head block to the trash for and uses the state from + * stream interface and per-uri parameters . 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, + "\n" + "Statistics Report for " PRODUCT_NAME "%s%s\n" + "\n" + "\n", + (uri->flags & ST_SHNODE) ? " on " : "", + (uri->flags & ST_SHNODE) ? (uri->node ? uri->node : global.node) : "" + ); +} + +/* Dumps the HTTP stats information block to the trash for and uses the state from + * stream interface and per-uri parameters . 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); + + /* 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, + "

" + PRODUCT_NAME "%s

\n" + "

Statistics Report for pid %d%s%s%s%s

\n" + "
\n" + "

> General process information

\n" + "" + "" + "" + "
\n" + "

pid = %d (process #%d, nbproc = %d)
\n" + "uptime = %dd %dh%02dm%02ds
\n" + "system limits: memmax = %s%s; ulimit-n = %d
\n" + "maxsock = %d; maxconn = %d; maxpipes = %d
\n" + "current conns = %d; current pipes = %d/%d; conn rate = %d/sec
\n" + "Running tasks: %d/%d; idle = %d %%
\n" + "

\n" + "\n" + "" + "" + "\n" + "" + "" + "\n" + "" + "" + "\n" + "" + "" + "\n" + "" + "
 active UP  backup UP
active UP, going down backup UP, going down
active DOWN, going up backup DOWN, going up
active or backup DOWN  not checked
active or backup DOWN for maintenance (MAINT)  
\n" + "Note: UP with load-balancing disabled is reported as \"NOLB\"." + "
" + "Display option:
    " + "", + (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, + "
  • Show all servers
    \n", + uri->uri_prefix, + "", + (si->applet.ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : ""); + else + chunk_appendf(&trash, + "
  • Hide 'DOWN' servers
    \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, + "
  • Enable refresh
    \n", + uri->uri_prefix, + (si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "", + ""); + else + chunk_appendf(&trash, + "
  • Disable refresh
    \n", + uri->uri_prefix, + (si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "", + ";norefresh"); + } + + chunk_appendf(&trash, + "
  • Refresh now
    \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, + "
  • CSV export
    \n", + uri->uri_prefix, + (uri->refresh > 0) ? ";norefresh" : ""); + + chunk_appendf(&trash, + "
" + "External resources:" + "
\n" + "" + ); + + if (si->applet.ctx.stats.st_code) { + switch (si->applet.ctx.stats.st_code) { + case STAT_STATUS_DONE: + chunk_appendf(&trash, + "

" + "[X] " + "Action processed successfully." + "
\n", uri->uri_prefix); + break; + case STAT_STATUS_NONE: + chunk_appendf(&trash, + "

" + "[X] " + "Nothing has changed." + "
\n", uri->uri_prefix); + break; + case STAT_STATUS_PART: + chunk_appendf(&trash, + "

" + "[X] " + "Action partially processed.
" + "Some server names are probably unknown or ambiguous (duplicated names in the backend)." + "
\n", uri->uri_prefix); + break; + case STAT_STATUS_ERRP: + chunk_appendf(&trash, + "

" + "[X] " + "Action not processed because of invalid parameters." + "
    " + "
  • The action is maybe unknown.
  • " + "
  • The backend name is probably unknown or ambiguous (duplicated names).
  • " + "
  • Some server names are probably unknown or ambiguous (duplicated names in the backend).
  • " + "
" + "
\n", uri->uri_prefix); + break; + case STAT_STATUS_EXCD: + chunk_appendf(&trash, + "

" + "[X] " + "Action not processed : the buffer couldn't store all the data.
" + "You should retry with less servers at a time.
" + "
\n", uri->uri_prefix); + break; + case STAT_STATUS_DENY: + chunk_appendf(&trash, + "

" + "[X] " + "Action denied." + "
\n", uri->uri_prefix); + break; + default: + chunk_appendf(&trash, + "

" + "[X] " + "Unexpected result." + "
\n", uri->uri_prefix); + } + chunk_appendf(&trash, "

\n"); + } + return 1; +} + +/* Dumps the HTTP stats trailer block to the trash for and uses the state from + * stream interface and per-uri parameters . 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; + + chunk_appendf(&trash, "\n"); + 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. + */ +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; + + chunk_reset(&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"); + + if (uri->refresh > 0 && !(si->applet.ctx.stats.flags & STAT_NO_REFRESH)) + chunk_appendf(&trash, "Refresh: %d\r\n", + uri->refresh); + + chunk_appendf(&trash, "\r\n"); + + s->txn.status = 200; + if (bi_putchk(rep, &trash) == -1) + return 0; + + 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; + + 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; + } + + si->conn->xprt_st = STAT_ST_HEAD; /* let's start producing data */ + /* fall through */ + + 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); + + if (bi_putchk(rep, &trash) == -1) + return 0; + + si->conn->xprt_st = STAT_ST_INFO; + /* fall through */ + + case STAT_ST_INFO: + if (stats_dump_http_info(si, px, uri)) { + if (bi_putchk(rep, &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: /* 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; - } + while (si->applet.ctx.stats.px) { + if (buffer_almost_full(rep->buf)) + return 0; - 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 */ + 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; + + 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 */ si->conn->xprt_st = STAT_ST_END; /* fall through */ 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 */ @@ -1826,11 +3408,10 @@ static int stats_dump_raw_to_buffer(struct stream_interface *si) default: /* unknown state ! */ si->conn->xprt_st = STAT_ST_FIN; - return 1; + 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. @@ -1873,7 +3454,6 @@ static int stats_http_redir(struct stream_interface *si, struct uri_auth *uri) 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 @@ -1929,1488 +3509,6 @@ static void http_stats_io_handler(struct stream_interface *si) } -/* 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; - unsigned int up; - - chunk_reset(&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"); - - if (uri->refresh > 0 && !(si->applet.ctx.stats.flags & STAT_NO_REFRESH)) - chunk_appendf(&trash, "Refresh: %d\r\n", - uri->refresh); - - chunk_appendf(&trash, "\r\n"); - - s->txn.status = 200; - if (bi_putchk(rep, &trash) == -1) - return 0; - - 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; - - 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; - } - - si->conn->xprt_st = STAT_ST_HEAD; /* let's start producing data */ - /* fall through */ - - case STAT_ST_HEAD: - if (!(si->applet.ctx.stats.flags & STAT_FMT_CSV)) { - /* WARNING! This must fit in the first buffer !!! */ - chunk_appendf(&trash, - "\n" - "Statistics Report for " PRODUCT_NAME "%s%s\n" - "\n" - "\n", - (uri->flags&ST_SHNODE) ? " on " : "", - (uri->flags&ST_SHNODE) ? (uri->node ? uri->node : global.node) : "" - ); - } else { - print_csv_header(&trash); - } - if (bi_putchk(rep, &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); - - /* 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, - "

" - PRODUCT_NAME "%s

\n" - "

Statistics Report for pid %d%s%s%s%s

\n" - "
\n" - "

> General process information

\n" - "" - "" - "" - "
\n" - "

pid = %d (process #%d, nbproc = %d)
\n" - "uptime = %dd %dh%02dm%02ds
\n" - "system limits: memmax = %s%s; ulimit-n = %d
\n" - "maxsock = %d; maxconn = %d; maxpipes = %d
\n" - "current conns = %d; current pipes = %d/%d; conn rate = %d/sec
\n" - "Running tasks: %d/%d; idle = %d %%
\n" - "

\n" - "\n" - "" - "" - "\n" - "" - "" - "\n" - "" - "" - "\n" - "" - "" - "\n" - "" - "
 active UP  backup UP
active UP, going down backup UP, going down
active DOWN, going up backup DOWN, going up
active or backup DOWN  not checked
active or backup DOWN for maintenance (MAINT)  
\n" - "Note: UP with load-balancing disabled is reported as \"NOLB\"." - "
" - "Display option:
    " - "", - (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, - "
  • Show all servers
    \n", - uri->uri_prefix, - "", - (si->applet.ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : ""); - else - chunk_appendf(&trash, - "
  • Hide 'DOWN' servers
    \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, - "
  • Enable refresh
    \n", - uri->uri_prefix, - (si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "", - ""); - else - chunk_appendf(&trash, - "
  • Disable refresh
    \n", - uri->uri_prefix, - (si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "", - ";norefresh"); - } - - chunk_appendf(&trash, - "
  • Refresh now
    \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, - "
  • CSV export
    \n", - uri->uri_prefix, - (uri->refresh > 0) ? ";norefresh" : ""); - - chunk_appendf(&trash, - "
" - "External resources:" - "
\n" - "" - ); - - if (si->applet.ctx.stats.st_code) { - switch (si->applet.ctx.stats.st_code) { - case STAT_STATUS_DONE: - chunk_appendf(&trash, - "

" - "[X] " - "Action processed successfully." - "
\n", uri->uri_prefix); - break; - case STAT_STATUS_NONE: - chunk_appendf(&trash, - "

" - "[X] " - "Nothing has changed." - "
\n", uri->uri_prefix); - break; - case STAT_STATUS_PART: - chunk_appendf(&trash, - "

" - "[X] " - "Action partially processed.
" - "Some server names are probably unknown or ambiguous (duplicated names in the backend)." - "
\n", uri->uri_prefix); - break; - case STAT_STATUS_ERRP: - chunk_appendf(&trash, - "

" - "[X] " - "Action not processed because of invalid parameters." - "
    " - "
  • The action is maybe unknown.
  • " - "
  • The backend name is probably unknown or ambiguous (duplicated names).
  • " - "
  • Some server names are probably unknown or ambiguous (duplicated names in the backend).
  • " - "
" - "
\n", uri->uri_prefix); - break; - case STAT_STATUS_EXCD: - chunk_appendf(&trash, - "

" - "[X] " - "Action not processed : the buffer couldn't store all the data.
" - "You should retry with less servers at a time.
" - "
\n", uri->uri_prefix); - break; - case STAT_STATUS_DENY: - chunk_appendf(&trash, - "

" - "[X] " - "Action denied." - "
\n", uri->uri_prefix); - break; - default: - chunk_appendf(&trash, - "

" - "[X] " - "Unexpected result." - "
\n", uri->uri_prefix); - } - chunk_appendf(&trash,"

\n"); - } - - if (bi_putchk(rep, &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: - /* 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; - - 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 */ - - si->conn->xprt_st = STAT_ST_END; - /* fall through */ - - case STAT_ST_END: - if (!(si->applet.ctx.stats.flags & STAT_FMT_CSV)) { - chunk_appendf(&trash, "\n"); - if (bi_putchk(rep, &trash) == -1) - return 0; - } - - 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; - return -1; - } -} - - -/* - * Dumps statistics for a proxy. - * Returns 0 if it had to stop dumping data because of lack of buffer space, - * ot non-zero if everything completed. - */ -static int stats_dump_proxy(struct stream_interface *si, struct proxy *px, struct uri_auth *uri) -{ - struct session *s = si->conn->xprt_ctx; - struct channel *rep = si->ib; - struct server *sv, *svs; /* server and server-state, server-state=server or server->track */ - struct listener *l; - - chunk_reset(&trash); - - switch (si->applet.ctx.stats.px_st) { - case STAT_PX_ST_INIT: - /* we are on a new proxy */ - - if (uri && uri->scope) { - /* we have a limited scope, we have to check the proxy name */ - struct stat_scope *scope; - int len; - - len = strlen(px->id); - scope = uri->scope; - - while (scope) { - /* match exact proxy name */ - if (scope->px_len == len && !memcmp(px->id, scope->px_id, len)) - break; - - /* match '.' which means 'self' proxy */ - if (!strcmp(scope->px_id, ".") && px == s->be) - break; - scope = scope->next; - } - - /* proxy name not found : don't dump anything */ - if (scope == NULL) - return 1; - } - - 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, - "

", - uri->uri_prefix); - } - - /* print a new table */ - chunk_appendf(&trash, - "\n" - "" - "" - "" - "\n" - "
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" - "%s%s%s
\n" - "\n" - "", - (uri->flags & ST_SHLGNDS)?"":"", - px->id, px->id, px->id, - (uri->flags & ST_SHLGNDS)?"":"", - px->desc ? "desc" : "empty", px->desc ? px->desc : ""); - - 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, ""); - } - - chunk_appendf(&trash, - "" - "" - "" - "" - "" - "" - "\n" - "" - "" - "" - "" - "" - "" - "" - "" - "\n" - ""); - - 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 */ - ""); - - 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, ""); - } - - chunk_appendf(&trash, - "" - "" - "", - px->id, px->id); - - if (px->mode == PR_MODE_HTTP) { - chunk_appendf(&trash, - /* sessions rate : current, max, limit */ - "" - "", - 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 */ - "" - "", - 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 */ - "" - "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" - /* bytes : in */ - "mode == PR_MODE_HTTP)?"":"", - U2H6(px->fe_counters.cum_sess), - (px->mode == PR_MODE_HTTP)?"":"", - U2H7(px->fe_counters.bytes_in)); - - /* compression stats (via td title): comp_in, comp_out, comp_byp */ - chunk_appendf(&trash, " title=\"compression: in=%lld out=%lld bypassed=%lld savings=%d%%\"", - px->fe_counters.comp_in, px->fe_counters.comp_out, px->fe_counters.comp_byp, - px->fe_counters.comp_in ? - (int)((px->fe_counters.comp_in - px->fe_counters.comp_out)*100/px->fe_counters.comp_in) : 0); - - chunk_appendf(&trash, - /* bytes: out */ - ">%s%s%s" - "", - (px->fe_counters.comp_in || px->fe_counters.comp_byp) ? "":"", - U2H0(px->fe_counters.bytes_out), - (px->fe_counters.comp_in || px->fe_counters.comp_byp) ? "":""); - - chunk_appendf(&trash, - /* denied: req, resp */ - "" - /* errors : request, connect, response */ - "" - /* warnings: retries, redispatches */ - "" - /* server status : reflect frontend status */ - "" - /* rest of server: nothing */ - "" - "", - 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 (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; - /* fall through */ - - case STAT_PX_ST_LI: - /* stats.l has been initialized above */ - for (; si->applet.ctx.stats.l != &px->conf.listeners; si->applet.ctx.stats.l = l->by_fe.n) { - if (buffer_almost_full(rep->buf)) - return 0; - - l = LIST_ELEM(si->applet.ctx.stats.l, struct listener *, by_fe); - if (!l->counters) - continue; - - if (si->applet.ctx.stats.flags & STAT_BOUND) { - if (!(si->applet.ctx.stats.type & (1 << STATS_TYPE_SO))) - break; - - if (si->applet.ctx.stats.sid != -1 && l->luid != si->applet.ctx.stats.sid) - continue; - } - - if (!(si->applet.ctx.stats.flags & STAT_FMT_CSV)) { - chunk_appendf(&trash, ""); - 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, ""); - } - chunk_appendf(&trash, "" - /* sessions rate: current, max, limit */ - "" - /* sessions: current, max, limit, total, lbtot */ - "" - "" - /* bytes: in, out */ - "" - "", - (uri->flags & ST_SHLGNDS)?"":"", - px->id, l->name, px->id, l->name, l->name, - (uri->flags & ST_SHLGNDS)?"":"", - U2H3(l->nbconn), U2H4(l->counters->conn_max), U2H5(l->maxconn), - U2H6(l->counters->cum_conn), U2H7(l->counters->bytes_in), U2H8(l->counters->bytes_out)); - - chunk_appendf(&trash, - /* denied: req, resp */ - "" - /* errors: request, connect, response */ - "" - /* warnings: retries, redispatches */ - "" - /* server status: reflect listener status */ - "" - /* rest of server: nothing */ - "" - "", - 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; - } - - si->applet.ctx.stats.sv = px->srv; /* may be NULL */ - si->applet.ctx.stats.px_st = STAT_PX_ST_SV; - /* fall through */ - - case STAT_PX_ST_SV: - /* stats.sv has been initialized above */ - for (; si->applet.ctx.stats.sv != NULL; si->applet.ctx.stats.sv = sv->next) { - int sv_state; /* 0=DOWN, 1=going up, 2=going down, 3=UP, 4,5=NOLB, 6=unchecked */ - - if (buffer_almost_full(rep->buf)) - return 0; - - sv = si->applet.ctx.stats.sv; - - if (si->applet.ctx.stats.flags & STAT_BOUND) { - if (!(si->applet.ctx.stats.type & (1 << STATS_TYPE_SV))) - break; - - if (si->applet.ctx.stats.sid != -1 && sv->puid != si->applet.ctx.stats.sid) - continue; - } - - if (sv->track) - svs = sv->track; - else - svs = sv; - - /* FIXME: produce some small strings for "UP/DOWN x/y &#xxxx;" */ - if (!(svs->state & SRV_CHECKED)) - sv_state = 6; - else if (svs->state & SRV_RUNNING) { - if (svs->health == svs->rise + svs->fall - 1) - sv_state = 3; /* UP */ - else - sv_state = 2; /* going down */ - - if (svs->state & SRV_GOINGDOWN) - sv_state += 2; - } - else - if (svs->health) - sv_state = 1; /* going up */ - else - sv_state = 0; /* DOWN */ - - if (((sv_state == 0) || (sv->state & SRV_MAINTAIN)) && (si->applet.ctx.stats.flags & STAT_HIDE_DOWN)) { - /* do not report servers which are DOWN */ - si->applet.ctx.stats.sv = sv->next; - 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", - "no check" }; - if ((sv->state & SRV_MAINTAIN) || (svs->state & SRV_MAINTAIN)) { - chunk_appendf(&trash, - /* name */ - "" - ); - } - else { - chunk_appendf(&trash, - /* name */ - "", - (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, - "", - sv->id); - } - - chunk_appendf(&trash, "" - /* queue : current, max, limit */ - "" - /* sessions rate : current, max, limit */ - "" - /* sessions: current, max, limit */ - "" - "flags & ST_SHLGNDS)?"":"", - px->id, sv->id, px->id, sv->id, sv->id, - (uri->flags & ST_SHLGNDS)?"":"", - U2H0(sv->nbpend), U2H1(sv->counters.nbpend_max), LIM2A2(sv->maxqueue, "-"), - U2H3(read_freq_ctr(&sv->sess_per_sec)), U2H4(sv->counters.sps_max), - U2H5(sv->cur_sess), U2H6(sv->counters.cur_sess_max), LIM2A7(sv->maxconn, "-")); - - /* 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]); - } - - chunk_appendf(&trash, - /* sessions: total, lbtot */ - ">%s%s%s", - (px->mode == PR_MODE_HTTP)?"":"", - U2H0(sv->counters.cum_sess), - (px->mode == PR_MODE_HTTP)?"":"", - U2H1(sv->counters.cum_lbconn)); - - chunk_appendf(&trash, - /* bytes : in, out */ - "" - /* denied: req, resp */ - "" - /* errors : request, connect */ - "" - /* errors : response */ - "" - /* warnings: retries, redispatches */ - "" - "", - 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, "" - /* act, bck */ - "" - "", - (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, "" - "" - "", - svs->counters.down_trans, human_time(srv_downtime(sv), 1)); - } else if (sv != svs) - chunk_appendf(&trash, - "", - svs->proxy->id, svs->id, svs->proxy->id, svs->id); - else - chunk_appendf(&trash, - ""); - - /* 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, - "\n", ratio); - } else { - chunk_appendf(&trash, - "\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)); - } - - 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) { - unsigned int ratio; - ratio = MAX(1, 100 * (now.tv_sec - sv->last_change) / sv->slowstart); - chunk_appendf(&trash, "%d", ratio); - } - - /* 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, ","); - - } else { - chunk_appendf(&trash, ",,,"); - } - - /* 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,", sv->counters.p.http.rsp[i]); - - chunk_appendf(&trash, "%lld,", sv->counters.p.http.rsp[0]); - } else { - chunk_appendf(&trash, ",,,,,,"); - } - - /* 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"); - } - if (bi_putchk(rep, &trash) == -1) - return 0; - } /* for sv */ - - si->applet.ctx.stats.px_st = STAT_PX_ST_BE; - /* 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, ""); - 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, ""); - } - chunk_appendf(&trash, "" - /* queue : current, max */ - "" - /* sessions rate : current, max, limit */ - "" - "", - (uri->flags & ST_SHLGNDS)?"":"", - px->id, px->id, - (uri->flags & ST_SHLGNDS)?"":"", - U2H0(px->nbpend) /* or px->totpend ? */, U2H1(px->be_counters.nbpend_max), - U2H2(read_freq_ctr(&px->be_sess_per_sec)), U2H3(px->be_counters.sps_max)); - - chunk_appendf(&trash, - /* sessions: current, max, limit */ - "" - "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); - } - - chunk_appendf(&trash, - /* sessions: total, lbtot */ - ">%s%s%s" - /* bytes: in */ - "mode == PR_MODE_HTTP)?"":"", - U2H6(px->be_counters.cum_conn), - (px->mode == PR_MODE_HTTP)?"":"", - U2H7(px->be_counters.cum_lbconn), - U2H8(px->be_counters.bytes_in)); - - /* compression stats (via td title): comp_in, comp_out, comp_byp */ - chunk_appendf(&trash, " title=\"compression: in=%lld out=%lld bypassed=%lld savings=%d%%\"", - px->be_counters.comp_in, px->be_counters.comp_out, px->be_counters.comp_byp, - px->be_counters.comp_in ? - (int)((px->be_counters.comp_in - px->be_counters.comp_out)*100/px->be_counters.comp_in) : 0); - - chunk_appendf(&trash, - /* bytes: out */ - ">%s%s%s" - "", - (px->be_counters.comp_in || px->be_counters.comp_byp) ? "":"", - U2H0(px->be_counters.bytes_out), - (px->be_counters.comp_in || px->be_counters.comp_byp) ? "":""); - - chunk_appendf(&trash, - /* denied: req, resp */ - "" - /* errors : request, connect */ - "" - /* errors : response */ - "" - /* warnings: retries, redispatches */ - "" - /* 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. */ - "" - "" - "", - 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" : - "DOWN", - (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 */ - "" - "" - "" - "", - 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, ",,,,,,"); - } - - /* 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"); - - } - 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, "
QueueSession rateSessionsBytesDeniedErrorsWarningsServer
CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
" - "" - "Frontend%s%s%s%s%s%s%s%s%s%s%s%s%s%s
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" - "%s%s %s%s%s%s %s%s%s%s%s%s
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" - "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%lld%lld"); - - 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)); - } - - if (sv->state & SRV_CHECKED) { - chunk_appendf(&trash, "check.status)); - - if (*sv->check.desc) { - struct chunk src; - - chunk_appendf(&trash, ": "); - - chunk_initlen(&src, sv->check.desc, 0, strlen(sv->check.desc)); - chunk_htmlencode(&trash, &src); - } - - chunk_appendf(&trash, "\"> %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", sv->check.duration); - } else - chunk_appendf(&trash, ""); - - chunk_appendf(&trash, - /* weight */ - "%d%s%s%lld", - svs->observe?"/Health Analyses":"", svs->counters.failed_checks); - - if (svs->observe) - chunk_appendf(&trash, "/%lld", svs->counters.failed_hana); - - chunk_appendf(&trash, - "%lld%svia %s/%s%d %%
-
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; - - 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, "\""); - - } - - chunk_appendf(&trash, - /* name */ - ">%s" - "Backend%s%s%s%s%s%s%s%s%s%s%s%s%s%s%lld%lld%s %s %d%d%d %d%s
"); - - 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 : " - "" - "" - " " - "
", - px->uuid); - } - - chunk_appendf(&trash, "

\n"); - - if (bi_putchk(rep, &trash) == -1) - return 0; - } - - si->applet.ctx.stats.px_st = STAT_PX_ST_FIN; - /* fall through */ - - case STAT_PX_ST_FIN: - return 1; - - default: - /* unknown state, we should put an abort() here ! */ - return 1; - } -} - static inline const char *get_conn_ctrl_name(const struct connection *conn) { if (!conn->ctrl) diff --git a/src/proto_http.c b/src/proto_http.c index 535428471..f6535f24d 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -7631,9 +7631,6 @@ int stats_check_uri(struct stream_interface *si, struct http_txn *txn, struct pr } h++; } - - si->applet.ctx.stats.flags |= STAT_SHOW_STAT | STAT_SHOW_INFO; - return 1; }