MINOR: stream: Add an option to "show sess" command to dump the captured URI

"show sess" command now supports a list of options that can be set after all
other possible arguments (<id>, all...). For now, "show-uri" is the only
supported option. With this options, the captured URI, if non-null, is added
to the dump of a stream, complete or now. The URI may be anonymized if
necessary.

This patch should fix the issue #663.
This commit is contained in:
Christopher Faulet 2024-11-18 15:17:13 +01:00
parent e9bc5937c9
commit fa43ca2ed0
2 changed files with 62 additions and 17 deletions

View File

@ -3321,7 +3321,7 @@ show servers state [<backend>]
srv_agent_addr: Server health agent address.
srv_agent_port: Server health agent port.
show sess
show sess [<options>*]
Dump all known active streams (formerly called "sessions"). Avoid doing this
on slow connections as this can be huge. This command is restricted and can
only be issued on sockets configured for levels "operator" or "admin". Note
@ -3329,8 +3329,9 @@ show sess
output reports less entries than really exist because it will dump all
existing streams up to the last one that was created before the command was
entered; those which die in the mean time will not appear.
For supported opitons, see below.
show sess <id> | older <age> | susp | all
show sess [<id> | older <age> | susp | all] [<options>*]
Display a lot of internal information about the matching streams. In the
first form, only the stream matching the specified stream identifier will
be shown. This identifier is the first field at the beginning of the lines in
@ -3347,6 +3348,12 @@ show sess <id> | older <age> | susp | all
is meant to be interpreted while checking function strm_dump_to_buffer() in
src/stream.c to figure the exact meaning of certain fields.
It is possible to set some options to customize the dump. Here are the
supported options:
- show-uri: Dump the transaction URI, as captured during the request
analysis. It is only displayed if it was captured.
show stat [domain <resolvers|proxy>] [{<iid>|<proxy>} <type> <sid>] \
[typed|json] [desc] [up|no-maint]
Dump statistics. The domain is used to select which statistics to print; dns

View File

@ -3250,6 +3250,7 @@ void list_services(FILE *out)
/* appctx context used by the "show sess" command */
/* flags used for show_sess_ctx.flags */
#define CLI_SHOWSESS_F_SUSP 0x00000001 /* show only suspicious streams */
#define CLI_SHOWSESS_F_DUMP_URI 0x00000002 /* Dump TXN's uri if available in dump */
struct show_sess_ctx {
struct bref bref; /* back-reference from the session being dumped */
@ -3264,11 +3265,13 @@ struct show_sess_ctx {
/* This function appends a complete dump of a stream state onto the buffer,
* possibly anonymizing using the specified anon_key. The caller is responsible
* for ensuring that enough room remains in the buffer to dump a complete
* stream at once. Each new output line will be prefixed with <pfx> if non-null,
* which is used to preserve indenting.
* for ensuring that enough room remains in the buffer to dump a complete stream
* at once. Each new output line will be prefixed with <pfx> if non-null, which
* is used to preserve indenting. The context <ctx>, if non-null, will be used
* to customize the dump.
*/
void strm_dump_to_buffer(struct buffer *buf, const struct stream *strm, const char *pfx, uint32_t anon_key)
static void __strm_dump_to_buffer(struct buffer *buf, const struct show_sess_ctx *ctx,
const struct stream *strm, const char *pfx, uint32_t anon_key)
{
struct stconn *scf, *scb;
struct tm tm;
@ -3411,12 +3414,16 @@ void strm_dump_to_buffer(struct buffer *buf, const struct stream *strm, const ch
" age=%s)\n",
human_time(ns_to_sec(now_ns) - ns_to_sec(strm->logs.request_ts), 1));
if (strm->txn)
if (strm->txn) {
chunk_appendf(buf,
"%s txn=%p flags=0x%x meth=%d status=%d req.st=%s rsp.st=%s req.f=0x%02x rsp.f=0x%02x\n", pfx,
"%s txn=%p flags=0x%x meth=%d status=%d req.st=%s rsp.st=%s req.f=0x%02x rsp.f=0x%02x", pfx,
strm->txn, strm->txn->flags, strm->txn->meth, strm->txn->status,
h1_msg_state_str(strm->txn->req.msg_state), h1_msg_state_str(strm->txn->rsp.msg_state),
strm->txn->req.flags, strm->txn->rsp.flags);
if (ctx && (ctx->flags & CLI_SHOWSESS_F_DUMP_URI) && strm->txn->uri)
chunk_appendf(buf, " uri=\"%s\"", HA_ANON_STR(anon_key, strm->txn->uri));
chunk_memcat(buf, "\n", 1);
}
scf = strm->scf;
chunk_appendf(buf, "%s scf=%p flags=0x%08x ioto=%s state=%s endp=%s,%p,0x%08x sub=%d", pfx,
@ -3623,6 +3630,14 @@ void strm_dump_to_buffer(struct buffer *buf, const struct stream *strm, const ch
}
}
/* Context-less function to append a complet dump of a stream state onto the
* buffer. It relies on __strm_dump_to_buffer.
*/
void strm_dump_to_buffer(struct buffer *buf, const struct stream *strm, const char *pfx, uint32_t anon_key)
{
__strm_dump_to_buffer(buf, NULL, strm, pfx, anon_key);
}
/* This function dumps a complete stream state onto the stream connector's
* read buffer. The stream has to be set in strm. It returns 0 if the output
* buffer is full and it needs to be called again, otherwise non-zero. It is
@ -3649,7 +3664,7 @@ static int stats_dump_full_strm_to_buffer(struct appctx *appctx, struct stream *
__fallthrough;
case 1:
strm_dump_to_buffer(&trash, strm, "", appctx->cli_anon_key);
__strm_dump_to_buffer(&trash, ctx, strm, "", appctx->cli_anon_key);
if (applet_putchk(appctx, &trash) == -1)
goto full;
@ -3667,6 +3682,7 @@ static int stats_dump_full_strm_to_buffer(struct appctx *appctx, struct stream *
static int cli_parse_show_sess(char **args, char *payload, struct appctx *appctx, void *private)
{
struct show_sess_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
int cur_arg = 2;
if (!cli_has_level(appctx, ACCESS_LVL_OPER))
return 1;
@ -3678,28 +3694,47 @@ static int cli_parse_show_sess(char **args, char *payload, struct appctx *appctx
ctx->pos = 0;
ctx->thr = 0;
if (*args[2] && strcmp(args[2], "older") == 0) {
if (*args[cur_arg] && strcmp(args[cur_arg], "older") == 0) {
unsigned timeout;
const char *res;
if (!*args[3])
if (!*args[cur_arg+1])
return cli_err(appctx, "Expects a minimum age (in seconds by default).\n");
res = parse_time_err(args[3], &timeout, TIME_UNIT_S);
res = parse_time_err(args[cur_arg+1], &timeout, TIME_UNIT_S);
if (res != 0)
return cli_err(appctx, "Invalid age.\n");
ctx->min_age = timeout;
ctx->target = (void *)-1; /* show all matching entries */
cur_arg +=2;
}
else if (*args[2] && strcmp(args[2], "susp") == 0) {
else if (*args[cur_arg] && strcmp(args[cur_arg], "susp") == 0) {
ctx->flags |= CLI_SHOWSESS_F_SUSP;
ctx->target = (void *)-1; /* show all matching entries */
cur_arg++;
}
else if (*args[2] && strcmp(args[2], "all") == 0)
else if (*args[cur_arg] && strcmp(args[cur_arg], "all") == 0) {
ctx->target = (void *)-1;
else if (*args[2])
ctx->target = (void *)strtoul(args[2], NULL, 0);
cur_arg++;
}
else if (*args[cur_arg]) {
ctx->target = (void *)strtoul(args[cur_arg], NULL, 0);
if (ctx->target)
cur_arg++;
}
/* show-sess options parsing */
while (*args[cur_arg]) {
if (*args[cur_arg] && strcmp(args[cur_arg], "show-uri") == 0) {
ctx->flags |= CLI_SHOWSESS_F_DUMP_URI;
}
else {
chunk_printf(&trash, "Unsupported option '%s'.\n", args[cur_arg]);
return cli_err(appctx, trash.area);
}
cur_arg++;
}
/* The back-ref must be reset, it will be detected and set by
* the dump code upon first invocation.
@ -3897,6 +3932,9 @@ static int cli_io_handler_dump_sess(struct appctx *appctx)
if (task_in_rq(curr_strm->task))
chunk_appendf(&trash, " run(nice=%d)", curr_strm->task->nice);
if ((ctx->flags & CLI_SHOWSESS_F_DUMP_URI) && curr_strm->txn && curr_strm->txn->uri)
chunk_appendf(&trash, " uri=\"%s\"", HA_ANON_CLI(curr_strm->txn->uri));
chunk_appendf(&trash, "\n");
if (applet_putchk(appctx, &trash) == -1) {