[MEDIUM] Collect & provide separate statistics for sockets, v2
This patch allows to collect & provide separate statistics for each socket.
It can be very useful if you would like to distinguish between traffic
generate by local and remote users or between different types of remote
clients (peerings, domestic, foreign).
Currently no "Session rate" is supported, but adding it should be possible
if we found it useful.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index cd3d17b..a92d304 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -759,6 +759,7 @@
[no] option persist X - X X
[no] option redispatch X - X X
option smtpchk X - X X
+[no] option socket-stats X X X -
[no] option splice-auto X X X X
[no] option splice-request X X X X
[no] option splice-response X X X X
@@ -1104,6 +1105,8 @@
bind [<address>]:<port> [, ...] interface <interface>
bind [<address>]:<port> [, ...] mss <maxseg>
bind [<address>]:<port> [, ...] transparent
+bind [<address>]:<port> [, ...] id <id>
+bind [<address>]:<port> [, ...] name <name>
Define one or several listening addresses and/or ports in a frontend.
May be used in sections : defaults | frontend | listen | backend
no | yes | yes | no
@@ -1137,6 +1140,13 @@
work on other operating systems. The commonly advertised
value on Ethernet networks is 1460 = 1500(MTU) - 40(IP+TCP).
+ <id> is a persistent value for socket ID. Must be unique and
+ larger than 1000, as smaller values are reserved for
+ auto-assigned ids. Can only be used when defining only
+ a single socket.
+
+ <name> is an optional name provided for stats
+
transparent is an optional keyword which is supported only on certain
Linux kernels. It indicates that the addresses will be bound
even if they do not belong to the local machine. Any packet
@@ -2767,6 +2777,16 @@
See also : "option httpchk", "source"
+option socket-stats
+no option socket-stats
+
+ Enable or disable collecting & providing separate statistics for each socket.
+ May be used in sections : defaults | frontend | listen | backend
+ yes | yes | yes | no
+
+ Arguments : none
+
+
option splice-auto
no option splice-auto
Enable or disable automatic kernel acceleration on sockets in both directions
@@ -6573,7 +6593,7 @@
29. throttle: warm up status
30. lbtot: total number of times a server was selected
31. tracked: id of proxy/server if tracking is enabled
- 32. type (0=frontend, 1=backend, 2=server)
+ 32. type (0=frontend, 1=backend, 2=server, 3=socket)
33. rate: number of sessions per second over last elapsed second
34. rate_lim: limit on new sessions per second
35. rate_max: max number of new sessions per second
diff --git a/include/proto/dumpstats.h b/include/proto/dumpstats.h
index 700fc95..ea579f5 100644
--- a/include/proto/dumpstats.h
+++ b/include/proto/dumpstats.h
@@ -38,6 +38,7 @@
#define STATS_TYPE_FE 0
#define STATS_TYPE_BE 1
#define STATS_TYPE_SV 2
+#define STATS_TYPE_SO 3
/* unix stats socket states */
#define STAT_CLI_INIT 0 /* initial state */
diff --git a/include/proto/proxy.h b/include/proto/proxy.h
index f293b1b..c0ce2a3 100644
--- a/include/proto/proxy.h
+++ b/include/proto/proxy.h
@@ -69,6 +69,9 @@
static void inline proxy_inc_fe_ctr(struct listener *l, struct proxy *fe)
{
fe->counters.cum_feconn++;
+ if (l->counters)
+ l->counters->cum_conn++;
+
update_freq_ctr(&fe->fe_sess_per_sec, 1);
if (fe->fe_sess_per_sec.curr_ctr > fe->fe_sps_max)
fe->fe_sps_max = fe->fe_sess_per_sec.curr_ctr;
diff --git a/include/types/proto_http.h b/include/types/proto_http.h
index c0350d2..a9acfdd 100644
--- a/include/types/proto_http.h
+++ b/include/types/proto_http.h
@@ -159,6 +159,7 @@
DATA_ST_PX_INIT = 0,
DATA_ST_PX_TH,
DATA_ST_PX_FE,
+ DATA_ST_PX_LI,
DATA_ST_PX_SV,
DATA_ST_PX_BE,
DATA_ST_PX_END,
diff --git a/include/types/protocols.h b/include/types/protocols.h
index 14aee79..c89deb3 100644
--- a/include/types/protocols.h
+++ b/include/types/protocols.h
@@ -30,6 +30,7 @@
#include <common/config.h>
#include <common/mini-clist.h>
+#include <types/counters.h>
#include <types/task.h>
/* max length of a protcol name, including trailing zero */
@@ -80,6 +81,7 @@
int luid; /* listener universally unique ID, used for SNMP */
int state; /* state: NEW, INIT, ASSIGNED, LISTEN, READY, FULL */
int options; /* socket options : LI_O_* */
+ struct licounters *counters; /* statistics counters */
struct sockaddr_storage addr; /* the address we listen to */
struct protocol *proto; /* protocol this listener belongs to */
int nbconn; /* current number of connections on this listener */
diff --git a/include/types/proxy.h b/include/types/proxy.h
index 432111b..1c73d24 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -121,6 +121,7 @@
#define PR_O2_CLFLOG 0x00000400 /* log into clf format */
#define PR_O2_LOGHCHKS 0x00000800 /* log health checks */
#define PR_O2_INDEPSTR 0x00001000 /* independant streams, don't update rex on write */
+#define PR_O2_SOCKSTAT 0x00002000 /* collect & provide separate statistics for sockets */
struct error_snapshot {
struct timeval when; /* date of this event, (tv_sec == 0) means "never" */
diff --git a/include/types/session.h b/include/types/session.h
index d3dbe67..356f0ca 100644
--- a/include/types/session.h
+++ b/include/types/session.h
@@ -196,6 +196,7 @@
struct {
struct proxy *px;
struct server *sv;
+ struct listener *l;
short px_st, sv_st; /* DATA_ST_INIT or DATA_ST_DATA */
unsigned int flags; /* STAT_* */
int iid, type, sid; /* proxy id, type and service id if bounding of stats is enabled */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index ebb0713..5eab73d 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -139,6 +139,7 @@
{ "dontlog-normal", PR_O2_NOLOGNORM, PR_CAP_FE, 0 },
{ "log-separate-errors", PR_O2_LOGERRORS, PR_CAP_FE, 0 },
{ "log-health-checks", PR_O2_LOGHCHKS, PR_CAP_BE, 0 },
+ { "socket-stats", PR_O2_SOCKSTAT, PR_CAP_FE, 0 },
{ "tcp-smart-accept", PR_O2_SMARTACC, PR_CAP_FE, 0 },
{ "tcp-smart-connect", PR_O2_SMARTCON, PR_CAP_BE, 0 },
{ "independant-streams", PR_O2_INDEPSTR, PR_CAP_FE|PR_CAP_BE, 0 },
@@ -162,17 +163,16 @@
* - <port> is a numerical port from 1 to 65535 ;
* - <end> indicates to use the range from <port> to <end> instead (inclusive).
* This can be repeated as many times as necessary, separated by a coma.
- * The <tail> argument is a pointer to a current list which should be appended
- * to the tail of the new list. The pointer to the new list is returned.
+ * Function returns 1 for success or 0 if error.
*/
-static struct listener *str2listener(char *str, struct listener *tail)
+static int str2listener(char *str, struct proxy *curproxy)
{
struct listener *l;
char *c, *next, *range, *dupstr;
int port, end;
next = dupstr = strdup(str);
-
+
while (next && *next) {
struct sockaddr_storage ss;
@@ -243,8 +243,8 @@
for (; port <= end; port++) {
l = (struct listener *)calloc(1, sizeof(struct listener));
- l->next = tail;
- tail = l;
+ l->next = curproxy->listen;
+ curproxy->listen = l;
l->fd = -1;
l->addr = ss;
@@ -257,14 +257,16 @@
((struct sockaddr_in *)(&l->addr))->sin_port = htons(port);
tcpv4_add_listener(l);
}
+
+ l->luid = curproxy->next_lid++;
listeners++;
} /* end for(port) */
} /* end while(next) */
free(dupstr);
- return tail;
+ return 1;
fail:
free(dupstr);
- return NULL;
+ return 0;
}
/*
@@ -893,8 +895,7 @@
/* parse the listener address if any */
if ((curproxy->cap & PR_CAP_FE) && *args[2]) {
- curproxy->listen = str2listener(args[2], curproxy->listen);
- if (!curproxy->listen) {
+ if (!str2listener(args[2], curproxy)) {
err_code |= ERR_FATAL;
goto out;
}
@@ -1004,6 +1005,7 @@
curproxy->grace = defproxy.grace;
curproxy->uuid = next_pxid++; /* generate a uuid for this proxy */
curproxy->next_svid = 1; /* server id 0 is reserved */
+ curproxy->next_lid = 1; /* listener id 0 is reserved */
goto out;
}
@@ -1061,8 +1063,7 @@
}
last_listen = curproxy->listen;
- curproxy->listen = str2listener(args[1], last_listen);
- if (!curproxy->listen) {
+ if (!str2listener(args[1], curproxy)) {
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
@@ -1142,6 +1143,54 @@
goto out;
#endif
}
+
+ if (!strcmp(args[cur_arg], "name")) {
+ struct listener *l;
+
+ for (l = curproxy->listen; l != last_listen; l = l->next)
+ l->name = strdup(args[cur_arg + 1]);
+
+ cur_arg += 2;
+ continue;
+ }
+
+ if (!strcmp(args[cur_arg], "id")) {
+ struct listener *l;
+
+ if (curproxy->listen->next != last_listen) {
+ Alert("parsing [%s:%d]: '%s' can be only used with a single socket.\n",
+ file, linenum, args[cur_arg]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+
+ if (!*args[cur_arg + 1]) {
+ Alert("parsing [%s:%d]: '%s' expects an integer argument.\n",
+ file, linenum, args[cur_arg]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+
+ curproxy->listen->luid = atol(args[cur_arg + 1]);
+
+ if (curproxy->listen->luid < 1001) {
+ Alert("parsing [%s:%d]: custom id has to be > 1000\n",
+ file, linenum);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+
+ for (l = curproxy->listen; l != last_listen; l = l->next)
+ if (curproxy->listen != l && l->luid == curproxy->listen->luid) {
+ Alert("parsing [%s:%d]: custom id has to be unique but is duplicated in %s.\n",
+ file, linenum, args[1]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+ cur_arg += 2;
+ continue;
+ }
+
Alert("parsing [%s:%d] : '%s' only supports the 'transparent' and 'interface' options.\n",
file, linenum, args[0]);
err_code |= ERR_ALERT | ERR_FATAL;
@@ -4352,9 +4401,32 @@
curproxy->be_req_ana |= AN_REQ_PRST_RDP_COOKIE;
}
+ listener = NULL;
+ while (curproxy->listen) {
+ struct listener *next;
+
+ next = curproxy->listen->next;
+ curproxy->listen->next = listener;
+ listener = curproxy->listen;
+
+ if (!next)
+ break;
+
+ curproxy->listen = next;
+ }
+
/* adjust this proxy's listeners */
listener = curproxy->listen;
while (listener) {
+ /* enable separate counters */
+ if (curproxy->options2 & PR_O2_SOCKSTAT) {
+ listener->counters = (struct licounters *)calloc(1, sizeof(struct licounters));
+ if (!listener->name) {
+ sprintf(trash, "sock-%d", listener->luid);
+ listener->name = strdup(trash);
+ }
+ }
+
if (curproxy->options & PR_O_TCP_NOLING)
listener->options |= LI_O_NOLINGER;
listener->maxconn = curproxy->maxconn;
diff --git a/src/client.c b/src/client.c
index fe29b4c..3bd31cf 100644
--- a/src/client.c
+++ b/src/client.c
@@ -491,6 +491,11 @@
if (p->feconn > p->counters.feconn_max)
p->counters.feconn_max = p->feconn;
+ if (l->counters) {
+ if (l->nbconn > l->counters->conn_max)
+ l->counters->conn_max = l->nbconn;
+ }
+
actconn++;
totalconn++;
diff --git a/src/dumpstats.c b/src/dumpstats.c
index 777b9fc..f43ab16 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -308,12 +308,16 @@
if (strcmp(args[1], "counters") == 0) {
struct proxy *px;
struct server *sv;
+ struct listener *li;
for (px = proxy; px; px = px->next) {
memset(&px->counters, 0, sizeof(px->counters));
for (sv = px->srv; sv; sv = sv->next)
memset(&sv->counters, 0, sizeof(sv->counters));
+
+ for (li = px->listen; li; li = li->next)
+ memset(li->counters, 0, sizeof(*li->counters));
}
return 1;
@@ -782,6 +786,7 @@
".titre {background: #20D0D0;color: #000000; font-weight: bold;}\n"
".total {background: #20D0D0;color: #ffff80;}\n"
".frontend {background: #e8e8d0;}\n"
+ ".socket {background: #d0d0d0;}\n"
".backend {background: #e8e8d0;}\n"
".active0 {background: #ff9090;}\n"
".active1 {background: #ffd020;}\n"
@@ -983,6 +988,7 @@
{
struct buffer *rep = s->rep;
struct server *sv, *svs; /* server and server-state, server-state=server or server->tracked */
+ struct listener *l;
struct chunk msg;
chunk_init(&msg, trash, sizeof(trash));
@@ -1139,6 +1145,95 @@
return 0;
}
+ s->data_ctx.stats.l = px->listen; /* may be NULL */
+ s->data_ctx.stats.px_st = DATA_ST_PX_LI;
+ /* fall through */
+
+ case DATA_ST_PX_LI:
+ /* stats.l has been initialized above */
+ for (; s->data_ctx.stats.l != NULL; s->data_ctx.stats.l = l->next) {
+ l = s->data_ctx.stats.l;
+ if (!l->counters)
+ continue;
+
+ if (s->data_ctx.stats.flags & STAT_BOUND) {
+ if (!(s->data_ctx.stats.type & (1 << STATS_TYPE_SO)))
+ break;
+
+ if (s->data_ctx.stats.sid != -1 && l->luid != s->data_ctx.stats.sid)
+ continue;
+ }
+
+ if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
+ chunk_printf(&msg,
+ /* name, queue */
+ "<tr align=center class=\"socket\"><td>%s</td><td colspan=3></td>"
+ /* sessions rate: current, max, limit */
+ "<td align=right colspan=3> </td>"
+ /* sessions: current, max, limit, total, lbtot */
+ "<td align=right>%s</td><td align=right>%s</td><td align=right>%s</td>"
+ "<td align=right>%s</td><td align=right> </td>"
+ /* bytes: in, out */
+ "<td align=right>%s</td><td align=right>%s</td>"
+ "",
+ l->name,
+ 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_printf(&msg,
+ /* denied: req, resp */
+ "<td align=right>%s</td><td align=right>%s</td>"
+ /* errors: request, connect, response */
+ "<td align=right>%s</td><td align=right></td><td align=right></td>"
+ /* warnings: retries, redispatches */
+ "<td align=right></td><td align=right></td>"
+ /* server status: reflect listener status */
+ "<td align=center>%s</td>"
+ /* rest of server: nothing */
+ "<td align=center colspan=8></td></tr>"
+ "",
+ U2H0(l->counters->denied_req), U2H1(l->counters->denied_resp),
+ U2H2(l->counters->failed_req),
+ (l->nbconn < l->maxconn) ? "OPEN" : "FULL");
+ } else {
+ chunk_printf(&msg,
+ /* 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 */
+ ",,,"
+ "\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 (buffer_feed_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 */
diff --git a/src/proto_http.c b/src/proto_http.c
index 8772e6c..4638d09 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -1843,7 +1843,11 @@
http_capture_bad_message(&s->fe->invalid_req, s, req, msg, s->fe);
msg->msg_state = HTTP_MSG_ERROR;
req->analysers = 0;
+
s->fe->counters.failed_req++;
+ if (s->listener->counters)
+ s->listener->counters->failed_req++;
+
if (!(s->flags & SN_ERR_MASK))
s->flags |= SN_ERR_CLICL;
if (!(s->flags & SN_FINST_MASK))
@@ -1860,7 +1864,11 @@
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_408));
msg->msg_state = HTTP_MSG_ERROR;
req->analysers = 0;
+
s->fe->counters.failed_req++;
+ if (s->listener->counters)
+ s->listener->counters->failed_req++;
+
if (!(s->flags & SN_ERR_MASK))
s->flags |= SN_ERR_CLITO;
if (!(s->flags & SN_FINST_MASK))
@@ -1876,7 +1884,11 @@
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_400));
msg->msg_state = HTTP_MSG_ERROR;
req->analysers = 0;
+
s->fe->counters.failed_req++;
+ if (s->listener->counters)
+ s->listener->counters->failed_req++;
+
if (!(s->flags & SN_ERR_MASK))
s->flags |= SN_ERR_CLICL;
if (!(s->flags & SN_FINST_MASK))
@@ -2005,7 +2017,10 @@
txn->req.msg_state = HTTP_MSG_ERROR;
txn->status = 400;
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_400));
+
s->fe->counters.failed_req++;
+ if (s->listener->counters)
+ s->listener->counters->failed_req++;
return_prx_cond:
if (!(s->flags & SN_ERR_MASK))
@@ -2291,7 +2306,10 @@
txn->req.msg_state = HTTP_MSG_ERROR;
txn->status = 400;
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_400));
+
s->fe->counters.failed_req++;
+ if (s->listener->counters)
+ s->listener->counters->failed_req++;
return_prx_cond:
if (!(s->flags & SN_ERR_MASK))
@@ -2599,7 +2617,10 @@
txn->status = 400;
req->analysers = 0;
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_400));
+
s->fe->counters.failed_req++;
+ if (s->listener->counters)
+ s->listener->counters->failed_req++;
if (!(s->flags & SN_ERR_MASK))
s->flags |= SN_ERR_PRXCOND;
@@ -2640,7 +2661,10 @@
req->analysers = 0;
req->analyse_exp = TICK_ETERNITY;
+
s->fe->counters.failed_req++;
+ if (s->listener->counters)
+ s->listener->counters->failed_req++;
if (!(s->flags & SN_ERR_MASK))
s->flags |= SN_ERR_PRXCOND;
@@ -3016,7 +3040,11 @@
if (txn->flags & TX_SVDENY) {
if (t->srv)
t->srv->counters.failed_secu++;
+
cur_proxy->counters.denied_resp++;
+ if (t->listener->counters)
+ t->listener->counters->denied_resp++;
+
goto return_srv_prx_502;
}
@@ -3157,7 +3185,11 @@
*/
if (t->srv)
t->srv->counters.failed_secu++;
+
cur_proxy->counters.denied_resp++;
+ if (t->listener->counters)
+ t->listener->counters->denied_resp++;
+
Alert("Blocking cacheable cookie in response from instance %s, server %s.\n",
t->be->id, t->srv?t->srv->id:"<dispatch>");
send_log(t->be, LOG_ALERT,
@@ -3308,13 +3340,21 @@
case ACT_DENY:
txn->flags |= TX_CLDENY;
last_hdr = 1;
+
t->be->counters.denied_req++;
+ if (t->listener->counters)
+ t->listener->counters->denied_resp++;
+
break;
case ACT_TARPIT:
txn->flags |= TX_CLTARPIT;
last_hdr = 1;
+
t->be->counters.denied_req++;
+ if (t->listener->counters)
+ t->listener->counters->denied_resp++;
+
break;
case ACT_REPLACE:
@@ -3419,13 +3459,21 @@
case ACT_DENY:
txn->flags |= TX_CLDENY;
+
t->be->counters.denied_req++;
+ if (t->listener->counters)
+ t->listener->counters->denied_resp++;
+
done = 1;
break;
case ACT_TARPIT:
txn->flags |= TX_CLTARPIT;
+
t->be->counters.denied_req++;
+ if (t->listener->counters)
+ t->listener->counters->denied_resp++;
+
done = 1;
break;
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index df6d034..7bb5d7e 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -686,7 +686,11 @@
buffer_abort(req);
buffer_abort(s->rep);
req->analysers = 0;
+
s->fe->counters.failed_req++;
+ if (s->listener->counters)
+ s->listener->counters->failed_req++;
+
if (!(s->flags & SN_ERR_MASK))
s->flags |= SN_ERR_PRXCOND;
if (!(s->flags & SN_FINST_MASK))
diff --git a/src/session.c b/src/session.c
index 9d46fe6..447660e 100644
--- a/src/session.c
+++ b/src/session.c
@@ -146,6 +146,9 @@
if (s->srv)
s->srv->counters.bytes_in += bytes;
+
+ if (s->listener->counters)
+ s->listener->counters->bytes_in += bytes;
}
}
@@ -160,6 +163,9 @@
if (s->srv)
s->srv->counters.bytes_out += bytes;
+
+ if (s->listener->counters)
+ s->listener->counters->bytes_out += bytes;
}
}
}
@@ -1309,7 +1315,11 @@
{
if (!(s->flags & SN_FINST_MASK)) {
if (s->si[1].state < SI_ST_REQ) {
+
s->fe->counters.failed_req++;
+ if (s->listener->counters)
+ s->listener->counters->failed_req++;
+
s->flags |= SN_FINST_R;
}
else if (s->si[1].state == SI_ST_QUE)