[MEDIUM] add support for "show sess" in unix stats socket

It is now possible to list all known sessions by issuing "show sess"
on the unix stats socket. The format is not much evolved but it is
very useful for debugging.

The doc has been updated to reflect the new keyword.
This commit is contained in:
Willy Tarreau 2008-12-07 22:29:48 +01:00
parent 62e4f1dedd
commit 3dfe6cd095
5 changed files with 167 additions and 12 deletions

View File

@ -151,8 +151,10 @@ stats socket <path> [{uid | user} <uid>] [{gid | group} <gid>] [mode <mode>]
Creates a UNIX socket in stream mode at location <path>. Any previously
existing socket will be backed up then replaced. Connections to this socket
will get a CSV-formated output of the process statistics in response to the
"show stat" command followed by a line feed, and more general process
information in response to the "show info" command followed by a line feed.
"show stat" command followed by a line feed, more general process information
in response to the "show info" command followed by a line feed, and a
complete list of all existing sessions in response to the "show sess" command
followed by a line feed.
On platforms which support it, it is possible to restrict access to this
socket by specifying numerical IDs after "uid" and "gid", or valid user and
@ -4339,7 +4341,9 @@ Notes related to these keywords :
[to do]
2.7) CSV format
---------------
0. pxname: proxy name
1. svname: service name (FRONTEND for frontend, BACKEND for backend, any name
@ -4376,18 +4380,33 @@ Notes related to these keywords :
31. tracked: id of proxy/server if tracking is enabled
32. type (0=frontend, 1=backend, 2=server)
2.8) Unix Socket commands
-------------------------
- "show stat [<iid> <type> <sid>]": dump statistics in the cvs format. By
passing id, type and sid it is possible to dump only selected items:
- iid is a proxy id, -1 to dump everything
- type selects type of dumpable objects: 1 for frontend, 2 for backend, 4 for
server, -1 for everything. Values can be ORed, for example:
1+2=3 -> frontend+backend.
1+2+4=7 -> frontend+backend+server.
- sid is a service id, -1 to dump everything from the selected proxy.
The following commands are supported on the UNIX stats socket ; all of them
must be terminated by a line feed. It is important to understand that when
multiple haproxy processes are started on the same sockets, any process may
pick up the request and will output its own stats.
show stat [<iid> <type> <sid>]
Dump statistics in the CSV format. By passing <id>, <type> and <sid>, it is
possible to dump only selected items :
- <iid> is a proxy ID, -1 to dump everything
- <type> selects the type of dumpable objects : 1 for frontends, 2 for
backends, 4 for servers, -1 for everything. These values can be ORed,
for example:
1 + 2 = 3 -> frontend + backend.
1 + 2 + 4 = 7 -> frontend + backend + server.
- <sid> is a server ID, -1 to dump everything from the selected proxy.
show info
Dump info about haproxy status on current process.
show sess
Dump all known sessions. Avoid doing this on slow connections as this can
be huge.
- "show info": dump info about current haproxy status.
/*
* Local variables:

View File

@ -48,6 +48,7 @@ int stats_dump_raw(struct session *s, struct buffer *rep, struct uri_auth *uri);
void stats_dump_raw_to_buffer(struct session *s, struct buffer *req);
int stats_dump_http(struct session *s, struct buffer *rep, struct uri_auth *uri);
int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri);
void stats_dump_sess_to_buffer(struct session *s, struct buffer *rep);
#endif /* _PROTO_DUMPSTATS_H */

View File

@ -201,6 +201,9 @@ struct session {
unsigned int flags; /* STAT_* */
int iid, type, sid; /* proxy id, type and service id if bounding of stats is enabled */
} stats;
struct {
struct bref bref;
} sess;
} data_ctx; /* used by produce_content to dump the stats right now */
unsigned int uniq_id; /* unique ID used for the traces */
};

View File

@ -1081,6 +1081,132 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri)
}
}
/* This function is called to send output to the response buffer.
* It dumps the sessions states onto the output buffer <rep>.
* Expects to be called with client socket shut down on input.
* s->data_ctx must have been zeroed first, and the flags properly set.
* It automatically clears the HIJACK bit from the response buffer.
*/
void stats_dump_sess_to_buffer(struct session *s, struct buffer *rep)
{
struct chunk msg;
if (unlikely(rep->flags & (BF_WRITE_ERROR|BF_SHUTW))) {
/* If we're forced to shut down, we might have to remove our
* reference to the last session being dumped.
*/
if (s->data_state == DATA_ST_LIST) {
if (!LIST_ISEMPTY(&s->data_ctx.sess.bref.users))
LIST_DEL(&s->data_ctx.sess.bref.users);
}
s->data_state = DATA_ST_FIN;
buffer_stop_hijack(rep);
s->ana_state = STATS_ST_CLOSE;
return;
}
if (s->ana_state != STATS_ST_REP)
return;
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. We initialize the current session
* pointer to the first in the global list.
*/
stream_int_retnclose(rep->cons, &msg);
LIST_INIT(&s->data_ctx.sess.bref.users);
s->data_ctx.sess.bref.ref = sessions.n;
s->data_state = DATA_ST_LIST;
/* fall through */
case DATA_ST_LIST:
while (s->data_ctx.sess.bref.ref != &sessions) {
char pn[INET6_ADDRSTRLEN + strlen(":65535")];
struct session *curr_sess;
curr_sess = LIST_ELEM(s->data_ctx.sess.bref.ref, struct session *, list);
/* first, let's detach the back-ref from a possible previous session */
if (!LIST_ISEMPTY(&s->data_ctx.sess.bref.users))
LIST_DEL(&s->data_ctx.sess.bref.users);
chunk_printf(&msg, sizeof(trash),
"%p: proto=%s",
curr_sess,
curr_sess->listener->proto->name);
switch (curr_sess->listener->proto->sock_family) {
case AF_INET:
inet_ntop(AF_INET,
(const void *)&((struct sockaddr_in *)&curr_sess->cli_addr)->sin_addr,
pn, sizeof(pn));
chunk_printf(&msg, sizeof(trash),
" src=%s:%d fe=%s be=%s srv=%s",
pn,
ntohs(((struct sockaddr_in *)&curr_sess->cli_addr)->sin_port),
curr_sess->fe->id,
curr_sess->be->id,
curr_sess->srv ? curr_sess->srv->id : "<none>"
);
break;
case AF_INET6:
inet_ntop(AF_INET6,
(const void *)&((struct sockaddr_in6 *)(&curr_sess->cli_addr))->sin6_addr,
pn, sizeof(pn));
chunk_printf(&msg, sizeof(trash),
" src=%s:%d fe=%s be=%s srv=%s",
pn,
ntohs(((struct sockaddr_in6 *)&curr_sess->cli_addr)->sin6_port),
curr_sess->fe->id,
curr_sess->be->id,
curr_sess->srv ? curr_sess->srv->id : "<none>"
);
break;
case AF_UNIX:
/* no more information to print right now */
break;
}
chunk_printf(&msg, sizeof(trash),
" si=(%d,%d) as=%d age=%s",
curr_sess->si[0].state, curr_sess->si[1].state,
curr_sess->ana_state,
human_time(now.tv_sec - curr_sess->logs.tv_accept.tv_sec, 1));
chunk_printf(&msg, sizeof(trash),
" exp=%s\n",
curr_sess->task->expire ?
human_time(TICKS_TO_MS(tick_remain(now_ms, curr_sess->task->expire)),
TICKS_TO_MS(1000)) : "never");
if (buffer_write_chunk(rep, &msg) >= 0) {
/* let's try again later */
LIST_ADDQ(&curr_sess->back_refs, &s->data_ctx.sess.bref.users);
return;
}
s->data_ctx.sess.bref.ref = curr_sess->list.n;
}
s->data_state = DATA_ST_FIN;
/* fall through */
default:
s->data_state = DATA_ST_FIN;
buffer_stop_hijack(rep);
s->ana_state = STATS_ST_CLOSE;
return;
}
}
static struct cfg_kw_list cfg_kws = {{ },{
{ CFG_GLOBAL, "stats", stats_parse_global },
{ 0, NULL, NULL },

View File

@ -472,6 +472,8 @@ int uxst_event_accept(int fd) {
memset(&s->logs, 0, sizeof(s->logs));
memset(&s->txn, 0, sizeof(s->txn));
s->logs.tv_accept = now; /* corrected date for internal use */
s->data_state = DATA_ST_INIT;
s->data_source = DATA_SRC_NONE;
s->uniq_id = totalconn;
@ -611,7 +613,11 @@ int unix_sock_parse_request(struct session *s, char *line)
s->ana_state = STATS_ST_REP;
buffer_install_hijacker(s, s->rep, stats_dump_raw_to_buffer);
}
else { /* neither "stat" nor "info" */
else if (strcmp(args[1], "sess") == 0) {
s->ana_state = STATS_ST_REP;
buffer_install_hijacker(s, s->rep, stats_dump_sess_to_buffer);
}
else { /* neither "stat" nor "info" nor "sess" */
return 0;
}
}