mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-01-01 01:32:04 +00:00
[MEDIUM] stats: add "show table [<name>]" to dump a stick-table
It is now possible to dump a table's contents with keys, expire, use count, and various data using the command above on the stats socket. "show table" only shows main table stats, while "show table <name>" dumps table contents, only if the socket level is admin.
This commit is contained in:
parent
da7ff64aa9
commit
69f58c8058
@ -51,6 +51,7 @@
|
||||
#define STAT_CLI_O_INFO 5 /* dump info/stats */
|
||||
#define STAT_CLI_O_SESS 6 /* dump sessions */
|
||||
#define STAT_CLI_O_ERR 7 /* dump errors */
|
||||
#define STAT_CLI_O_TAB 8 /* dump tables */
|
||||
|
||||
|
||||
int stats_accept(struct session *s);
|
||||
@ -60,6 +61,7 @@ int stats_dump_raw_to_buffer(struct session *s, struct buffer *rep);
|
||||
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);
|
||||
int stats_dump_sess_to_buffer(struct session *s, struct buffer *rep);
|
||||
int stats_dump_table_to_buffer(struct session *s, struct buffer *rep);
|
||||
int stats_dump_errors_to_buffer(struct session *s, struct buffer *rep);
|
||||
void http_stats_io_handler(struct stream_interface *si);
|
||||
|
||||
|
@ -230,6 +230,11 @@ struct session {
|
||||
int ptr; /* <0: headers, >=0 : text pointer to restart from */
|
||||
int bol; /* pointer to beginning of current line */
|
||||
} errors;
|
||||
struct {
|
||||
void *target; /* table we want to dump, or NULL for all */
|
||||
struct proxy *proxy; /* table being currently dumped (first if NULL) */
|
||||
struct stksess *entry; /* last entry we were trying to dump (or first if NULL) */
|
||||
} table;
|
||||
struct {
|
||||
const char *msg; /* pointer to a persistent message to be returned in PRINT state */
|
||||
} cli;
|
||||
|
183
src/dumpstats.c
183
src/dumpstats.c
@ -64,6 +64,7 @@ const char stats_sock_usage_msg[] =
|
||||
" show stat : report counters for each proxy and server\n"
|
||||
" show errors : report last request and response errors for each proxy\n"
|
||||
" show sess [id] : report the list of current sessions or dump this session\n"
|
||||
" show table [id]: report table usage stats or dump this table's contents\n"
|
||||
" get weight : report a server's current weight\n"
|
||||
" set weight : change a server's weight\n"
|
||||
" set timeout : change a timeout setting\n"
|
||||
@ -377,6 +378,16 @@ int stats_sock_parse_request(struct stream_interface *si, char *line)
|
||||
s->data_state = DATA_ST_INIT;
|
||||
si->st0 = STAT_CLI_O_ERR; // stats_dump_errors_to_buffer
|
||||
}
|
||||
else if (strcmp(args[1], "table") == 0) {
|
||||
s->data_state = DATA_ST_INIT;
|
||||
if (*args[2])
|
||||
s->data_ctx.table.target = findproxy(args[2], 0);
|
||||
else
|
||||
s->data_ctx.table.target = NULL;
|
||||
s->data_ctx.table.proxy = NULL;
|
||||
s->data_ctx.table.entry = NULL;
|
||||
si->st0 = STAT_CLI_O_TAB; // stats_dump_table_to_buffer
|
||||
}
|
||||
else { /* neither "stat" nor "info" nor "sess" nor "errors"*/
|
||||
return 0;
|
||||
}
|
||||
@ -807,6 +818,10 @@ void stats_io_handler(struct stream_interface *si)
|
||||
if (stats_dump_errors_to_buffer(s, res))
|
||||
si->st0 = STAT_CLI_PROMPT;
|
||||
break;
|
||||
case STAT_CLI_O_TAB:
|
||||
if (stats_dump_table_to_buffer(s, res))
|
||||
si->st0 = STAT_CLI_PROMPT;
|
||||
break;
|
||||
default: /* abnormal state */
|
||||
si->st0 = STAT_CLI_PROMPT;
|
||||
break;
|
||||
@ -2769,6 +2784,174 @@ int stats_dump_sess_to_buffer(struct session *s, struct buffer *rep)
|
||||
}
|
||||
}
|
||||
|
||||
/* This function is called to send output to the response buffer.
|
||||
* It dumps the tables 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 returns 0 as long as it does not complete, non-zero upon completion.
|
||||
*/
|
||||
int stats_dump_table_to_buffer(struct session *s, struct buffer *rep)
|
||||
{
|
||||
struct chunk msg;
|
||||
struct ebmb_node *eb;
|
||||
int dt;
|
||||
|
||||
/*
|
||||
* We have 3 possible states in s->data_state :
|
||||
* - DATA_ST_INIT : the first call
|
||||
* - DATA_ST_INFO : the proxy pointer points to the next table to
|
||||
* dump, the entry pointer is NULL ;
|
||||
* - DATA_ST_LIST : the proxy pointer points to the current table
|
||||
* and the entry pointer points to the next entry to be dumped,
|
||||
* and the refcount on the next entry is held ;
|
||||
* - DATA_ST_END : nothing left to dump, the buffer may contain some
|
||||
* data though.
|
||||
*/
|
||||
|
||||
if (unlikely(rep->flags & (BF_WRITE_ERROR|BF_SHUTW))) {
|
||||
/* in case of abort, remove any refcount we might have set on an entry */
|
||||
if (s->data_state == DATA_ST_LIST)
|
||||
s->data_ctx.table.entry->ref_cnt--;
|
||||
return 1;
|
||||
}
|
||||
|
||||
chunk_init(&msg, trash, sizeof(trash));
|
||||
|
||||
while (s->data_state != DATA_ST_FIN) {
|
||||
switch (s->data_state) {
|
||||
case DATA_ST_INIT:
|
||||
s->data_ctx.table.proxy = s->data_ctx.table.target;
|
||||
if (!s->data_ctx.table.proxy)
|
||||
s->data_ctx.table.proxy = proxy;
|
||||
|
||||
s->data_ctx.table.entry = NULL;
|
||||
s->data_state = DATA_ST_INFO;
|
||||
break;
|
||||
|
||||
case DATA_ST_INFO:
|
||||
if (!s->data_ctx.table.proxy ||
|
||||
(s->data_ctx.table.target &&
|
||||
s->data_ctx.table.proxy != s->data_ctx.table.target)) {
|
||||
s->data_state = DATA_ST_END;
|
||||
break;
|
||||
}
|
||||
|
||||
if (s->data_ctx.table.proxy->table.size) {
|
||||
chunk_printf(&msg, "# table: %s, type: %ld, size:%d, used:%d\n",
|
||||
s->data_ctx.table.proxy->id,
|
||||
s->data_ctx.table.proxy->table.type,
|
||||
s->data_ctx.table.proxy->table.size,
|
||||
s->data_ctx.table.proxy->table.current);
|
||||
|
||||
/* any other information should be dumped here */
|
||||
|
||||
if (s->data_ctx.table.target &&
|
||||
s->listener->perm.ux.level < ACCESS_LVL_OPER)
|
||||
chunk_printf(&msg, "# contents not dumped due to insufficient privileges\n");
|
||||
|
||||
if (buffer_feed_chunk(rep, &msg) >= 0)
|
||||
return 0;
|
||||
|
||||
if (s->data_ctx.table.target &&
|
||||
s->listener->perm.ux.level >= ACCESS_LVL_OPER) {
|
||||
/* dump entries only if table explicitly requested */
|
||||
eb = ebmb_first(&s->data_ctx.table.proxy->table.keys);
|
||||
if (eb) {
|
||||
s->data_ctx.table.entry = ebmb_entry(eb, struct stksess, key);
|
||||
s->data_ctx.table.entry->ref_cnt++;
|
||||
s->data_state = DATA_ST_LIST;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
s->data_ctx.table.proxy = s->data_ctx.table.proxy->next;
|
||||
break;
|
||||
|
||||
case DATA_ST_LIST:
|
||||
chunk_printf(&msg, "%p:", s->data_ctx.table.entry);
|
||||
|
||||
if (s->data_ctx.table.proxy->table.type == STKTABLE_TYPE_IP) {
|
||||
char addr[16];
|
||||
inet_ntop(AF_INET,
|
||||
(const void *)&s->data_ctx.table.entry->key.key,
|
||||
addr, sizeof(addr));
|
||||
chunk_printf(&msg, " key=%s", addr);
|
||||
}
|
||||
else
|
||||
chunk_printf(&msg, " key=?");
|
||||
|
||||
chunk_printf(&msg, " use=%d exp=%d",
|
||||
s->data_ctx.table.entry->ref_cnt - 1,
|
||||
tick_remain(now_ms, s->data_ctx.table.entry->expire));
|
||||
|
||||
for (dt = 0; dt < STKTABLE_DATA_TYPES; dt++) {
|
||||
void *ptr;
|
||||
|
||||
if (s->data_ctx.table.proxy->table.data_ofs[dt] == 0)
|
||||
continue;
|
||||
if (stktable_data_types[dt].arg_type == ARG_T_DELAY)
|
||||
chunk_printf(&msg, " %s(%d)=",
|
||||
stktable_data_types[dt].name,
|
||||
s->data_ctx.table.proxy->table.data_arg[dt].u);
|
||||
else
|
||||
chunk_printf(&msg, " %s=", stktable_data_types[dt].name);
|
||||
|
||||
ptr = stktable_data_ptr(&s->data_ctx.table.proxy->table,
|
||||
s->data_ctx.table.entry,
|
||||
dt);
|
||||
switch (dt) {
|
||||
/* all entries using the same type can be folded */
|
||||
case STKTABLE_DT_SERVER_ID:
|
||||
case STKTABLE_DT_GPC0:
|
||||
case STKTABLE_DT_CONN_CNT:
|
||||
case STKTABLE_DT_CONN_CUR:
|
||||
case STKTABLE_DT_SESS_CNT:
|
||||
case STKTABLE_DT_HTTP_REQ_CNT:
|
||||
case STKTABLE_DT_HTTP_ERR_CNT:
|
||||
chunk_printf(&msg, "%u", stktable_data_cast(ptr, server_id));
|
||||
break;
|
||||
case STKTABLE_DT_CONN_RATE:
|
||||
case STKTABLE_DT_SESS_RATE:
|
||||
case STKTABLE_DT_HTTP_REQ_RATE:
|
||||
case STKTABLE_DT_HTTP_ERR_RATE:
|
||||
case STKTABLE_DT_BYTES_IN_RATE:
|
||||
case STKTABLE_DT_BYTES_OUT_RATE:
|
||||
chunk_printf(&msg, "%d",
|
||||
read_freq_ctr_period(&stktable_data_cast(ptr, conn_rate),
|
||||
s->data_ctx.table.proxy->table.data_arg[dt].u));
|
||||
break;
|
||||
case STKTABLE_DT_BYTES_IN_CNT:
|
||||
case STKTABLE_DT_BYTES_OUT_CNT:
|
||||
chunk_printf(&msg, "%lld", stktable_data_cast(ptr, bytes_in_cnt));
|
||||
break;
|
||||
}
|
||||
}
|
||||
chunk_printf(&msg, "\n");
|
||||
|
||||
if (buffer_feed_chunk(rep, &msg) >= 0)
|
||||
return 0;
|
||||
|
||||
s->data_ctx.table.entry->ref_cnt--;
|
||||
|
||||
eb = ebmb_next(&s->data_ctx.table.entry->key);
|
||||
if (eb) {
|
||||
s->data_ctx.table.entry = ebmb_entry(eb, struct stksess, key);
|
||||
s->data_ctx.table.entry->ref_cnt++;
|
||||
break;
|
||||
}
|
||||
|
||||
s->data_ctx.table.proxy = s->data_ctx.table.proxy->next;
|
||||
s->data_state = DATA_ST_INFO;
|
||||
break;
|
||||
|
||||
case DATA_ST_END:
|
||||
s->data_state = DATA_ST_FIN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* print a line of text buffer (limited to 70 bytes) to <out>. The format is :
|
||||
* <2 spaces> <offset=5 digits> <space or plus> <space> <70 chars max> <\n>
|
||||
* which is 60 chars per line. Non-printable chars \t, \n, \r and \e are
|
||||
|
Loading…
Reference in New Issue
Block a user