MINOR: stconn: add a new pair of sf functions {bs,fs}.debug_str

These are passed to the underlying mux to retrieve debug information
at the mux level (stream/connection) as a string that's meant to be
added to logs.

The API is quite complex just because we can't pass any info to the
bottom function. So we construct a union and pass the argument as an
int, and expect the callee to fill that with its buffer in return.

Most likely the mux->ctl and ->sctl API should be reworked before
the release to simplify this.

The functions take an optional argument that is a bit mask of the
layers to dump:
  muxs=1
  muxc=2
  xprt=4
  conn=8
  sock=16

The default (0) logs everything available.
This commit is contained in:
Willy Tarreau 2024-07-30 17:57:37 +02:00
parent b681a9e488
commit 921e04bf87
3 changed files with 105 additions and 0 deletions

View File

@ -23966,11 +23966,13 @@ Summary of sample fetch methods in this section and their respective types:
keyword output type
----------------------------------------------------+-------------
bs.aborted boolean
bs.debug_str([<bitmap>]) string
bs.id integer
bs.rst_code integer
distcc_body(<token>[,<occ>]) binary
distcc_param(<token>[,<occ>]) integer
fs.aborted boolean
fs.debug_str([<bitmap>]) string
fs.id integer
fs.rst_code integer
payload(<offset>,<length>) binary
@ -24008,6 +24010,28 @@ bs.aborted: boolean
Returns true is an abort was received from the server for the current
stream. Otherwise false is returned.
bs.debug_str([<bitmap>]) : string
This function is meant to be used by developers during certain complex
troubleshooting sessions. It extracts some internal states from the lower
layers of the backend stream and connection, and arranges them as a string,
generally in the form of a series of "name=value" delimited with spaces. The
<bitmap> optional argument indicates what layer(s) to extract information
from, and is an arithmetic OR (or a sum) of the following values:
- socket layer: 16
- connection layer: 8
- transport layer (e.g. SSL): 4
- mux connection: 2
- mux stream: 1
These values might change across versions. The default value of zero is
special and enables all layers. Please do not rely on the output of this
function for long-term production monitoring. It is meant to evolve even
within a stable branch, as the needs for increased details arise. One use
typical use case is to concatenate these information at the very end of a
log-format, along with fs.debug_str(). Example:
log-format "$HAPROXY_HTTP_LOG_FMT fs=<%[fs.debug_str]> bs=<%[bs.debug_str]>"
bs.id : integer
Returns the multiplexer's stream ID on the server side. It is the
multiplexer's responsibility to return the appropriate information.
@ -24048,6 +24072,28 @@ fs.aborted: boolean
Returns true is an abort was received from the client for the current
stream. Otherwise false is returned.
fs.debug_str([<bitmap>]) : string
This function is meant to be used by developers during certain complex
troubleshooting sessions. It extracts some internal states from the lower
layers of the frontend stream and connection, and arranges them as a string,
generally in the form of a series of "name=value" delimited with spaces. The
<bitmap> optional argument indicates what layer(s) to extract information
from, and is an arithmetic OR (or a sum) of the following values:
- socket layer: 16
- connection layer: 8
- transport layer (e.g. SSL): 4
- mux connection: 2
- mux stream: 1
These values might change across versions. The default value of zero is
special and enables all layers. Please do not rely on the output of this
function for long-term production monitoring. It is meant to evolve even
within a stable branch, as the needs for increased details arise. One use
typical use case is to concatenate these information at the very end of a
log-format, along with bs.debug_str(). Example:
log-format "$HAPROXY_HTTP_LOG_FMT fs=<%[fs.debug_str]> bs=<%[bs.debug_str]>"
fs.id : integer
Returns the multiplexer's stream ID on the client side. It is the
multiplexer's responsibility to return the appropriate information. For

View File

@ -331,8 +331,16 @@ enum mux_ctl_type {
/* sctl command used by mux->sctl() */
enum mux_sctl_type {
MUX_SCTL_SID, /* Return the mux stream ID as output, as a signed 64bits integer */
MUX_SCTL_DBG_STR, /* takes a mux_sctl_dbg_str_ctx argument, reads flags and returns debug info */
};
#define MUX_SCTL_DBG_STR_L_MUXS 0x00000001 // info from mux stream
#define MUX_SCTL_DBG_STR_L_MUXC 0x00000002 // info from mux connection
#define MUX_SCTL_DBG_STR_L_XPRT 0x00000004 // info from xprt layer
#define MUX_SCTL_DBG_STR_L_CONN 0x00000008 // info from struct connection layer
#define MUX_SCTL_DBG_STR_L_SOCK 0x00000010 // info from socket layer (quic_conn as well)
/* response for ctl MUX_STATUS */
#define MUX_STATUS_READY (1 << 0)
@ -688,6 +696,15 @@ struct tlv_ssl {
uint8_t sub_tlv[VAR_ARRAY];
}__attribute__((packed));
/* context for a MUX_SCTL_DBG_STR call */
union mux_sctl_dbg_str_ctx {
struct {
uint debug_flags; // union of MUX_SCTL_DBG_STR_L_*
} arg; // sctl argument for the call
struct {
struct buffer buf;
} ret; // sctl return contents
};
/* This structure is used to manage idle connections, their locking, and the
* list of such idle connections to be removed. It is per-thread and must be

View File

@ -12,6 +12,7 @@
#include <haproxy/api.h>
#include <haproxy/applet.h>
#include <haproxy/arg.h>
#include <haproxy/connection.h>
#include <haproxy/check.h>
#include <haproxy/filters.h>
@ -2364,6 +2365,45 @@ void sc_conn_commit_endp_upgrade(struct stconn *sc)
BUG_ON(!sc->sedesc);
}
/* Return a debug string exposing the internals of the front or back
* stream/connection when supported. It will be protocol-dependent and will
* change over time like the output of "show fd" or "show sess all".
*/
static int smp_fetch_debug_str(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
struct connection *conn;
struct stconn *sc;
union mux_sctl_dbg_str_ctx sctl_ctx = { };
if (!smp->strm)
return 0;
sc = (kw[0] == 'f' ? smp->strm->scf : smp->strm->scb);
conn = sc_conn(sc);
if (!conn)
return 0;
/* a missing mux is necessarily on the backend, and may arrive later */
if (!conn->mux) {
smp->flags |= SMP_F_MAY_CHANGE;
return 0;
}
/* Not implemented, return nothing */
if (!conn->mux->sctl)
return 0;
sctl_ctx.arg.debug_flags = args->data.sint ? args->data.sint : ~0U;
if (conn->mux->sctl(sc, MUX_SCTL_DBG_STR, &sctl_ctx) == -1)
return 0;
smp->data.type = SMP_T_STR;
smp->flags = SMP_F_VOL_TEST | SMP_F_MAY_CHANGE;
smp->data.u.str = sctl_ctx.ret.buf;
return 1;
}
/* return the frontend or backend mux stream ID.
*/
static int
@ -2459,9 +2499,11 @@ smp_fetch_strm_rst_code(const struct arg *args, struct sample *smp, const char *
* common denominator, the type that can be casted into all other ones.
*/
static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
{ "bs.debug_str", smp_fetch_debug_str, ARG1(0,SINT), NULL, SMP_T_STR, SMP_USE_L5SRV },
{ "bs.id", smp_fetch_sid, 0, NULL, SMP_T_SINT, SMP_USE_L5SRV },
{ "bs.aborted", smp_fetch_strm_aborted, 0, NULL, SMP_T_SINT, SMP_USE_L5SRV },
{ "bs.rst_code", smp_fetch_strm_rst_code, 0, NULL, SMP_T_SINT, SMP_USE_L5SRV },
{ "fs.debug_str", smp_fetch_debug_str, ARG1(0,SINT), NULL, SMP_T_STR, SMP_USE_L5CLI },
{ "fs.id", smp_fetch_sid, 0, NULL, SMP_T_STR, SMP_USE_L5CLI },
{ "fs.aborted", smp_fetch_strm_aborted, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI },
{ "fs.rst_code", smp_fetch_strm_rst_code, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI },