/* * Functions dedicated to statistics output * * Copyright 2000-2008 Willy Tarreau * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* This function parses a "stats" statement in the "global" section. It returns * -1 if there is any error, otherwise zero. If it returns -1, it may write an * error message into ther buffer, for at most bytes, trailing * zero included. The trailing '\n' must not be written. The function must be * called with pointing to the first word after "stats". */ static int stats_parse_global(char **args, int section_type, struct proxy *curpx, struct proxy *defpx, char *err, int errlen) { args++; if (!strcmp(args[0], "socket")) { struct sockaddr_un su; int cur_arg; if (*args[1] == 0) { snprintf(err, errlen, "'stats socket' in global section expects a path to a UNIX socket"); return -1; } if (global.stats_sock.state != LI_NEW) { snprintf(err, errlen, "'stats socket' already specified in global section"); return -1; } su.sun_family = AF_UNIX; strncpy(su.sun_path, args[1], sizeof(su.sun_path)); su.sun_path[sizeof(su.sun_path) - 1] = 0; memcpy(&global.stats_sock.addr, &su, sizeof(su)); // guaranteed to fit global.stats_sock.state = LI_INIT; global.stats_sock.options = LI_O_NONE; global.stats_sock.accept = uxst_event_accept; global.stats_sock.handler = process_uxst_stats; global.stats_sock.private = NULL; cur_arg = 2; while (*args[cur_arg]) { if (!strcmp(args[cur_arg], "uid")) { global.stats_sock.perm.ux.uid = atol(args[cur_arg + 1]); cur_arg += 2; } else if (!strcmp(args[cur_arg], "gid")) { global.stats_sock.perm.ux.gid = atol(args[cur_arg + 1]); cur_arg += 2; } else if (!strcmp(args[cur_arg], "mode")) { global.stats_sock.perm.ux.mode = strtol(args[cur_arg + 1], NULL, 8); cur_arg += 2; } else if (!strcmp(args[cur_arg], "user")) { struct passwd *user; user = getpwnam(args[cur_arg + 1]); if (!user) { snprintf(err, errlen, "unknown user '%s' in 'global' section ('stats user')", args[cur_arg + 1]); return -1; } global.stats_sock.perm.ux.uid = user->pw_uid; cur_arg += 2; } else if (!strcmp(args[cur_arg], "group")) { struct group *group; group = getgrnam(args[cur_arg + 1]); if (!group) { snprintf(err, errlen, "unknown group '%s' in 'global' section ('stats group')", args[cur_arg + 1]); return -1; } global.stats_sock.perm.ux.gid = group->gr_gid; cur_arg += 2; } else { snprintf(err, errlen, "'stats socket' only supports 'user', 'uid', 'group', 'gid', and 'mode'"); return -1; } } uxst_add_listener(&global.stats_sock); global.maxsock++; } else if (!strcmp(args[0], "timeout")) { unsigned timeout; const char *res = parse_time_err(args[1], &timeout, TIME_UNIT_MS); if (res) { snprintf(err, errlen, "unexpected character '%c' in 'stats timeout' in 'global' section", *res); return -1; } if (!timeout) { snprintf(err, errlen, "a positive value is expected for 'stats timeout' in 'global section'"); return -1; } global.stats_timeout = MS_TO_TICKS(timeout); } else if (!strcmp(args[0], "maxconn")) { int maxconn = atol(args[1]); if (maxconn <= 0) { snprintf(err, errlen, "a positive value is expected for 'stats maxconn' in 'global section'"); return -1; } global.maxsock -= global.stats_sock.maxconn; global.stats_sock.maxconn = maxconn; global.maxsock += global.stats_sock.maxconn; } else { snprintf(err, errlen, "'stats' only supports 'socket', 'maxconn' and 'timeout' in 'global' section"); return -1; } return 0; } int print_csv_header(struct chunk *msg, int size) { return chunk_printf(msg, size, "# 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," "\n"); } /* * Produces statistics data for the session . Expects to be called with * s->cli_state == CL_STSHUTR. It *may* make use of informations from . * s->data_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. */ int stats_dump_raw(struct session *s, struct uri_auth *uri) { struct buffer *rep = s->rep; struct proxy *px; struct chunk msg; unsigned int up; msg.len = 0; msg.str = trash; switch (s->data_state) { case DATA_ST_INIT: /* the function had not been called yet, let's prepare the * buffer for a response. */ client_retnclose(s, &msg); s->data_state = DATA_ST_HEAD; /* fall through */ case DATA_ST_HEAD: if (s->data_ctx.stats.flags & STAT_SHOW_STAT) { print_csv_header(&msg, sizeof(trash)); if (buffer_write_chunk(rep, &msg) >= 0) return 0; } s->data_state = DATA_ST_INFO; /* fall through */ case DATA_ST_INFO: up = (now.tv_sec - start_date.tv_sec); if (s->data_ctx.stats.flags & STAT_SHOW_INFO) { chunk_printf(&msg, sizeof(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" "CurrConns: %d\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, actconn ); if (buffer_write_chunk(rep, &msg) >= 0) return 0; } s->data_ctx.stats.px = proxy; s->data_ctx.stats.px_st = DATA_ST_PX_INIT; s->data_ctx.stats.sv = NULL; s->data_ctx.stats.sv_st = 0; s->data_state = DATA_ST_LIST; /* fall through */ case DATA_ST_LIST: /* dump proxies */ if (s->data_ctx.stats.flags & STAT_SHOW_STAT) { while (s->data_ctx.stats.px) { px = s->data_ctx.stats.px; /* skip the disabled proxies and non-networked ones */ if (px->state != PR_STSTOPPED && (px->cap & (PR_CAP_FE | PR_CAP_BE))) if (stats_dump_proxy(s, px, NULL) == 0) return 0; s->data_ctx.stats.px = px->next; s->data_ctx.stats.px_st = DATA_ST_PX_INIT; } /* here, we just have reached the last proxy */ } s->data_state = DATA_ST_END; /* fall through */ case DATA_ST_END: s->data_state = DATA_ST_FIN; return 1; case DATA_ST_FIN: return 1; default: /* unknown state ! */ return -1; } } /* * Produces statistics data for the session . Expects to be called with * s->cli_state == CL_STSHUTR. It stops by itself by unsetting the SN_SELF_GEN * flag from the session, which it uses to keep on being called when there is * free space in the buffer, of simply by letting an empty buffer upon return. * s->data_ctx must have been zeroed before the first call, and the flags 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. */ int stats_dump_http(struct session *s, struct uri_auth *uri) { struct buffer *rep = s->rep; struct proxy *px; struct chunk msg; unsigned int up; msg.len = 0; msg.str = trash; switch (s->data_state) { case DATA_ST_INIT: /* the function had not been called yet */ s->flags |= SN_SELF_GEN; // more data will follow chunk_printf(&msg, sizeof(trash), "HTTP/1.0 200 OK\r\n" "Cache-Control: no-cache\r\n" "Connection: close\r\n" "Content-Type: %s\r\n", (s->data_ctx.stats.flags & STAT_FMT_CSV) ? "text/plain" : "text/html"); if (uri->refresh > 0 && !(s->data_ctx.stats.flags & STAT_NO_REFRESH)) chunk_printf(&msg, sizeof(trash), "Refresh: %d\r\n", uri->refresh); chunk_printf(&msg, sizeof(trash), "\r\n"); s->txn.status = 200; client_retnclose(s, &msg); // send the start of the response. msg.len = 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 */ s->data_state = DATA_ST_FIN; s->flags &= ~SN_SELF_GEN; return 1; } s->data_state = DATA_ST_HEAD; /* let's start producing data */ /* fall through */ case DATA_ST_HEAD: if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) { /* WARNING! This must fit in the first buffer !!! */ chunk_printf(&msg, sizeof(trash), "Statistics Report for " PRODUCT_NAME "\n" "\n" "\n"); } else { print_csv_header(&msg, sizeof(trash)); } if (buffer_write_chunk(rep, &msg) >= 0) return 0; s->data_state = DATA_ST_INFO; /* fall through */ case DATA_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 (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) { chunk_printf(&msg, sizeof(trash), "

" PRODUCT_NAME "%s

\n" "

Statistics Report for pid %d

\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
\n" "maxconn = %d (current conns = %d)
\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
\n" "Note: UP with load-balancing disabled is reported as \"NOLB\"." "
" "Display option:
    " "", (uri->flags&ST_HIDEVER)?"":(STATS_VERSION_STRING), pid, 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, actconn ); if (s->data_ctx.stats.flags & STAT_HIDE_DOWN) chunk_printf(&msg, sizeof(trash), "
  • Show all servers
    \n", uri->uri_prefix, "", (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : ""); else chunk_printf(&msg, sizeof(trash), "
  • Hide 'DOWN' servers
    \n", uri->uri_prefix, ";up", (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : ""); if (uri->refresh > 0) { if (s->data_ctx.stats.flags & STAT_NO_REFRESH) chunk_printf(&msg, sizeof(trash), "
  • Enable refresh
    \n", uri->uri_prefix, (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "", ""); else chunk_printf(&msg, sizeof(trash), "
  • Disable refresh
    \n", uri->uri_prefix, (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "", ";norefresh"); } chunk_printf(&msg, sizeof(trash), "
  • Refresh now
    \n", uri->uri_prefix, (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "", (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : ""); chunk_printf(&msg, sizeof(trash), "
  • CSV export
    \n", uri->uri_prefix, (uri->refresh > 0) ? ";norefresh" : ""); chunk_printf(&msg, sizeof(trash), "
" "External ressources:" "
\n" "" ); if (buffer_write_chunk(rep, &msg) >= 0) return 0; } s->data_ctx.stats.px = proxy; s->data_ctx.stats.px_st = DATA_ST_PX_INIT; s->data_state = DATA_ST_LIST; /* fall through */ case DATA_ST_LIST: /* dump proxies */ while (s->data_ctx.stats.px) { px = s->data_ctx.stats.px; /* skip the disabled proxies and non-networked ones */ if (px->state != PR_STSTOPPED && (px->cap & (PR_CAP_FE | PR_CAP_BE))) if (stats_dump_proxy(s, px, uri) == 0) return 0; s->data_ctx.stats.px = px->next; s->data_ctx.stats.px_st = DATA_ST_PX_INIT; } /* here, we just have reached the last proxy */ s->data_state = DATA_ST_END; /* fall through */ case DATA_ST_END: if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) { chunk_printf(&msg, sizeof(trash), "\n"); if (buffer_write_chunk(rep, &msg) >= 0) return 0; } s->data_state = DATA_ST_FIN; /* fall through */ case DATA_ST_FIN: s->flags &= ~SN_SELF_GEN; return 1; default: /* unknown state ! */ s->flags &= ~SN_SELF_GEN; 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. */ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri) { struct buffer *rep = s->rep; struct server *sv, *svs; /* server and server-state, server-state=server or server->tracked */ struct chunk msg; msg.len = 0; msg.str = trash; switch (s->data_ctx.stats.px_st) { case DATA_ST_PX_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 ((s->data_ctx.stats.flags & STAT_BOUND) && (s->data_ctx.stats.iid != -1) && (px->uuid != s->data_ctx.stats.iid)) return 1; s->data_ctx.stats.px_st = DATA_ST_PX_TH; /* fall through */ case DATA_ST_PX_TH: if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) { /* print a new table */ chunk_printf(&msg, sizeof(trash), "\n" "" "" "" "\n" "" "" "" "" "" "" "\n" "" "" "" "" "" "" "" "\n" "", px->id); if (buffer_write_chunk(rep, &msg) >= 0) return 0; } s->data_ctx.stats.px_st = DATA_ST_PX_FE; /* fall through */ case DATA_ST_PX_FE: /* print the frontend */ if ((px->cap & PR_CAP_FE) && (!(s->data_ctx.stats.flags & STAT_BOUND) || (s->data_ctx.stats.type & (1 << STATS_TYPE_FE)))) { if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) { chunk_printf(&msg, sizeof(trash), /* name, queue */ "" /* sessions : current, max, limit, total, lbtot */ "" "" "" /* bytes : in, out */ "" /* denied: req, resp */ "" /* errors : request, connect, response */ "" /* warnings: retries, redispatches */ "" /* server status : reflect frontend status */ "" /* rest of server: nothing */ "" "", px->feconn, px->feconn_max, px->maxconn, px->cum_feconn, px->bytes_in, px->bytes_out, px->denied_req, px->denied_resp, px->failed_req, px->state == PR_STRUN ? "OPEN" : px->state == PR_STIDLE ? "FULL" : "STOP"); } else { chunk_printf(&msg, sizeof(trash), /* pxid, name, queue cur, queue max, */ "%s,FRONTEND,,," /* sessions : current, max, limit, total */ "%d,%d,%d,%d," /* bytes : in, out */ "%lld,%lld," /* denied: req, resp */ "%d,%d," /* errors : request, connect, response */ "%d,,," /* warnings: retries, redispatches */ ",," /* server status : reflect frontend status */ "%s," /* rest of server: nothing */ ",,,,,,,," /* pid, iid, sid, throttle, lbtot, tracked, type */ "%d,%d,0,,,,%d," "\n", px->id, px->feconn, px->feconn_max, px->maxconn, px->cum_feconn, px->bytes_in, px->bytes_out, px->denied_req, px->denied_resp, px->failed_req, px->state == PR_STRUN ? "OPEN" : px->state == PR_STIDLE ? "FULL" : "STOP", relative_pid, px->uuid, STATS_TYPE_FE); } if (buffer_write_chunk(rep, &msg) >= 0) return 0; } s->data_ctx.stats.sv = px->srv; /* may be NULL */ s->data_ctx.stats.px_st = DATA_ST_PX_SV; /* fall through */ case DATA_ST_PX_SV: /* stats.sv has been initialized above */ for (; s->data_ctx.stats.sv != NULL; s->data_ctx.stats.sv = sv->next) { int sv_state; /* 0=DOWN, 1=going up, 2=going down, 3=UP, 4,5=NOLB, 6=unchecked */ sv = s->data_ctx.stats.sv; if (s->data_ctx.stats.flags & STAT_BOUND) { if (!(s->data_ctx.stats.type & (1 << STATS_TYPE_SV))) break; if (s->data_ctx.stats.sid != -1 && sv->puid != s->data_ctx.stats.sid) continue; } if (sv->tracked) svs = sv->tracked; 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) && (s->data_ctx.stats.flags & STAT_HIDE_DOWN)) { /* do not report servers which are DOWN */ s->data_ctx.stats.sv = sv->next; continue; } if (!(s->data_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" }; chunk_printf(&msg, sizeof(trash), /* name */ "" /* queue : current, max, limit */ "" /* sessions : current, max, limit, total, lbtot */ "" "" "" /* bytes : in, out */ "" /* denied: req, resp */ "" /* errors : request, connect, response */ "\n" /* warnings: retries, redispatches */ "" "", (sv->state & SRV_BACKUP) ? "backup" : "active", sv_state, sv->id, sv->nbpend, sv->nbpend_max, LIM2A0(sv->maxqueue, "-"), sv->cur_sess, sv->cur_sess_max, LIM2A1(sv->maxconn, "-"), sv->cum_sess, sv->cum_lbconn, sv->bytes_in, sv->bytes_out, sv->failed_secu, sv->failed_conns, sv->failed_resp, sv->retries, sv->redispatches); /* status */ chunk_printf(&msg, sizeof(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_printf(&msg, sizeof(trash), "" "" "", svs->failed_checks, svs->down_trans, human_time(srv_downtime(sv), 1)); else if (sv != svs) chunk_printf(&msg, sizeof(trash), "", svs->proxy->id, svs->id ); else chunk_printf(&msg, sizeof(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_printf(&msg, sizeof(trash), "\n", ratio); } else { chunk_printf(&msg, sizeof(trash), "\n"); } } else { static char *srv_hlt_st[7] = { "DOWN,", "DOWN %d/%d,", "UP %d/%d,", "UP,", "NOLB %d/%d,", "NOLB,", "no check," }; chunk_printf(&msg, sizeof(trash), /* pxid, name */ "%s,%s," /* queue : current, max */ "%d,%d," /* sessions : current, max, limit, total */ "%d,%d,%s,%d," /* bytes : in, out */ "%lld,%lld," /* denied: req, resp */ ",%d," /* errors : request, connect, response */ ",%d,%d," /* warnings: retries, redispatches */ "%u,%u," "", px->id, sv->id, sv->nbpend, sv->nbpend_max, sv->cur_sess, sv->cur_sess_max, LIM2A0(sv->maxconn, ""), sv->cum_sess, sv->bytes_in, sv->bytes_out, sv->failed_secu, sv->failed_conns, sv->failed_resp, sv->retries, sv->redispatches); /* status */ chunk_printf(&msg, sizeof(trash), srv_hlt_st[sv_state], (sv->state & SRV_RUNNING) ? (sv->health - sv->rise + 1) : (sv->health), (sv->state & SRV_RUNNING) ? (sv->fall) : (sv->rise)); chunk_printf(&msg, sizeof(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_printf(&msg, sizeof(trash), "%d,%d,%d,%d,", sv->failed_checks, sv->down_trans, now.tv_sec - sv->last_change, srv_downtime(sv)); else chunk_printf(&msg, sizeof(trash), ",,,,"); /* queue limit, pid, iid, sid, */ chunk_printf(&msg, sizeof(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_printf(&msg, sizeof(trash), "%d", ratio); } /* sessions: lbtot */ chunk_printf(&msg, sizeof(trash), ",%d,", sv->cum_lbconn); /* tracked */ if (sv->tracked) chunk_printf(&msg, sizeof(trash), "%s/%s,", sv->tracked->proxy->id, sv->tracked->id); else chunk_printf(&msg, sizeof(trash), ","); /* type, then EOL */ chunk_printf(&msg, sizeof(trash), "%d,\n", STATS_TYPE_SV); } if (buffer_write_chunk(rep, &msg) >= 0) return 0; } /* for sv */ s->data_ctx.stats.px_st = DATA_ST_PX_BE; /* fall through */ case DATA_ST_PX_BE: /* print the backend */ if ((px->cap & PR_CAP_BE) && (!(s->data_ctx.stats.flags & STAT_BOUND) || (s->data_ctx.stats.type & (1 << STATS_TYPE_BE)))) { if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) { chunk_printf(&msg, sizeof(trash), /* name */ "" /* queue : current, max */ "" /* sessions : current, max, limit, total, lbtot */ "" "" "" /* bytes : in, out */ "" /* denied: req, resp */ "" /* errors : request, connect, response */ "\n" /* 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. */ "" "", px->nbpend /* or px->totpend ? */, px->nbpend_max, px->beconn, px->beconn_max, px->fullconn, px->cum_beconn, px->cum_lbconn, px->bytes_in, px->bytes_out, px->denied_req, px->denied_resp, px->failed_conns, px->failed_resp, px->retries, px->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_printf(&msg, sizeof(trash), /* rest of backend: nothing, down transitions, total downtime, throttle */ "" "" "" "", px->down_trans, px->srv?human_time(be_downtime(px), 1):" "); } else { chunk_printf(&msg, sizeof(trash), /* pxid, name */ "%s,BACKEND," /* queue : current, max */ "%d,%d," /* sessions : current, max, limit, total */ "%d,%d,%d,%d," /* bytes : in, out */ "%lld,%lld," /* denied: req, resp */ "%d,%d," /* errors : request, connect, response */ ",%d,%d," /* warnings: retries, redispatches */ "%u,%u," /* 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,,%d,,%d," "\n", px->id, px->nbpend /* or px->totpend ? */, px->nbpend_max, px->beconn, px->beconn_max, px->fullconn, px->cum_beconn, px->bytes_in, px->bytes_out, px->denied_req, px->denied_resp, px->failed_conns, px->failed_resp, px->retries, px->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, now.tv_sec - px->last_change, px->srv?be_downtime(px):0, relative_pid, px->uuid, px->cum_lbconn, STATS_TYPE_BE); } if (buffer_write_chunk(rep, &msg) >= 0) return 0; } s->data_ctx.stats.px_st = DATA_ST_PX_END; /* fall through */ case DATA_ST_PX_END: if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) { chunk_printf(&msg, sizeof(trash), "
%s
QueueSessionsBytesDeniedErrorsWarningsServer
CurMaxLimitCurMaxLimitTotalLbTotInOutReqRespReqConnRespRetrRedisStatusWghtActBckChkDwnDwntmeThrtle
Frontend%d%d%d%d%lld%lld%d%d%d%s
%s%d%d%s%d%d%s%d%d%lld%lld%d%d%d%u%u"); if (sv->state & SRV_CHECKED) chunk_printf(&msg, sizeof(trash), "%s ", human_time(now.tv_sec - sv->last_change, 1)); chunk_printf(&msg, sizeof(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_printf(&msg, sizeof(trash), /* weight */ "%d%s%s%d%d%svia %s/%s%d %%
-
Backend%d%d%d%d%d%d%d%lld%lld%d%d%d%d%u%u%s %s%d%d%d %d%s

\n"); if (buffer_write_chunk(rep, &msg) >= 0) return 0; } s->data_ctx.stats.px_st = DATA_ST_PX_FIN; /* fall through */ case DATA_ST_PX_FIN: return 1; default: /* unknown state, we should put an abort() here ! */ return 1; } } static struct cfg_kw_list cfg_kws = {{ },{ { CFG_GLOBAL, "stats", stats_parse_global }, { 0, NULL, NULL }, }}; __attribute__((constructor)) static void __dumpstats_module_init(void) { cfg_register_keywords(&cfg_kws); } /* * Local variables: * c-indent-level: 8 * c-basic-offset: 8 * End: */