MINOR: stream: Add samples to get number of bytes received or sent on each side

req.in and req.out samples can now be used to get the number of bytes
received by a client and send to the server. And res.in and res.out samples
can be used to get the number of bytes received by a server and send to the
client. These info are stored in the logs structure inside a stream.

This patch is related to issue #1617.
This commit is contained in:
Christopher Faulet 2025-11-04 18:06:19 +01:00
parent 629fbbce19
commit ac9201f929
6 changed files with 53 additions and 7 deletions

View File

@ -23116,6 +23116,10 @@ fe_client_timeout integer
fe_defbe string fe_defbe string
fe_id integer fe_id integer
fe_name string fe_name string
req.in integer
req.out integer
res.in integer
res.out integer
res.timer.data integer res.timer.data integer
sc0_bytes_in_rate([<table>]) integer sc0_bytes_in_rate([<table>]) integer
sc0_bytes_out_rate([<table>]) integer sc0_bytes_out_rate([<table>]) integer
@ -23751,6 +23755,28 @@ fe_name : string
backends to check from which frontend it was called, or to stick all users backends to check from which frontend it was called, or to stick all users
coming via a same frontend to the same server. coming via a same frontend to the same server.
req.in : integer
This returns the number of bytes received from the client. The value
corresponds to what was received by HAProxy, including some headers and some
internal encoding overhead. Request compression does not affect the value
reported here.
req.out : integer
This returns the number of bytes sent to the server. The value corresponds to
what was sent by HAProxy, including some headers and some internal encoding
overhead. Request compression affects the value reported here.
res.in : integer
This returns the number of bytes received from the server. The value
corresponds to what was received by HAProxy, including some headers and some
internal encoding overhead. Response compression does not affect the value
reported here.
res.out : integer
This returns the number of bytes sent to the client. The value corresponds to
what was sent by HAProxy, including some headers and some internal encoding
overhead. Response compression affects the value reported here.
res.timer.data : integer res.timer.data : integer
this is the total transfer time of the response payload till the last byte this is the total transfer time of the response payload till the last byte
sent to the client. In HTTP it starts after the last response header (after sent to the client. In HTTP it starts after the last response header (after

View File

@ -225,6 +225,10 @@ struct strm_logs {
unsigned long t_close; /* total stream duration */ unsigned long t_close; /* total stream duration */
unsigned long srv_queue_pos; /* number of streams de-queued while waiting for a connection slot on this server */ unsigned long srv_queue_pos; /* number of streams de-queued while waiting for a connection slot on this server */
unsigned long prx_queue_pos; /* number of streams de-qeuued while waiting for a connection slot on this instance */ unsigned long prx_queue_pos; /* number of streams de-qeuued while waiting for a connection slot on this instance */
long long req_in; /* number of bytes received from the client */
long long req_out; /* number of bytes sent to the server */
long long res_in; /* number of bytes received from the server */
long long res_out; /* number of bytes sent to the client */
long long bytes_in; /* number of bytes transferred from the client to the server */ long long bytes_in; /* number of bytes transferred from the client to the server */
long long bytes_out; /* number of bytes transferred from the server to the client */ long long bytes_out; /* number of bytes transferred from the server to the client */
}; };

View File

@ -3488,6 +3488,8 @@ int pcli_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
/* don't count other requests' data */ /* don't count other requests' data */
s->logs.bytes_in -= ci_data(&s->req); s->logs.bytes_in -= ci_data(&s->req);
s->logs.bytes_out -= ci_data(&s->res); s->logs.bytes_out -= ci_data(&s->res);
s->logs.req_in -= ci_data(&s->req);
s->logs.res_in -= ci_data(&s->res);
/* we may need to know the position in the queue */ /* we may need to know the position in the queue */
pendconn_free(s); pendconn_free(s);
@ -3524,6 +3526,8 @@ int pcli_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
s->logs.bytes_in = s->req.total = ci_data(&s->req); s->logs.bytes_in = s->req.total = ci_data(&s->req);
s->logs.bytes_out = s->res.total = ci_data(&s->res); s->logs.bytes_out = s->res.total = ci_data(&s->res);
s->logs.req_in = s->scf->bytes_in = ci_data(&s->req);
s->logs.res_in = s->scb->bytes_in = ci_data(&s->res);
stream_del_srv_conn(s); stream_del_srv_conn(s);
if (objt_server(s->target)) { if (objt_server(s->target)) {

View File

@ -2002,9 +2002,9 @@ int http_process_res_common(struct stream *s, struct channel *rep, int an_bit, s
*/ */
if (do_log) { if (do_log) {
s->logs.t_close = s->logs.t_data; /* to get a valid end date */ s->logs.t_close = s->logs.t_data; /* to get a valid end date */
s->logs.bytes_out = htx->data; s->logs.res_in = s->logs.bytes_out = htx->data;
s->do_log(s, log_orig(LOG_ORIG_TXN_RESPONSE, LOG_ORIG_FL_NONE)); s->do_log(s, log_orig(LOG_ORIG_TXN_RESPONSE, LOG_ORIG_FL_NONE));
s->logs.bytes_out = 0; s->logs.res_in = s->logs.bytes_out = 0;
} }
done: done:

View File

@ -5444,11 +5444,12 @@ static int smp_fetch_bytes(const struct arg *args, struct sample *smp, const cha
if (!logs) if (!logs)
return 0; return 0;
if (kw[6] == 'i') { /* bytes_in */ if (kw[2] == 'q') /* req.in or req.out */
smp->data.u.sint = logs->bytes_in; smp->data.u.sint = (kw[4] == 'i') ? logs->req_in : logs->req_out;
} else { /* bytes_out */ if (kw[2] == 's') /* res.in or res.out */
smp->data.u.sint = logs->bytes_out; smp->data.u.sint = (kw[4] == 'i') ? logs->res_in : logs->res_out;
} else /* bytes_in or bytes_out */
smp->data.u.sint = (kw[6] == 'i') ? logs->bytes_in : logs->bytes_out;
return 1; return 1;
} }
@ -5496,10 +5497,14 @@ static struct sample_fetch_kw_list smp_logs_kws = {ILH, {
{ "fc.timer.handshake", smp_fetch_conn_timers, 0, NULL, SMP_T_SINT, SMP_USE_L4CLI }, /* "Th" */ { "fc.timer.handshake", smp_fetch_conn_timers, 0, NULL, SMP_T_SINT, SMP_USE_L4CLI }, /* "Th" */
{ "fc.timer.total", smp_fetch_conn_timers, 0, NULL, SMP_T_SINT, SMP_USE_SSFIN }, /* "Tt" */ { "fc.timer.total", smp_fetch_conn_timers, 0, NULL, SMP_T_SINT, SMP_USE_SSFIN }, /* "Tt" */
{ "req.in", smp_fetch_bytes, 0, NULL, SMP_T_SINT, SMP_USE_INTRN },
{ "req.out", smp_fetch_bytes, 0, NULL, SMP_T_SINT, SMP_USE_INTRN },
{ "req.timer.idle", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV }, /* "Ti" */ { "req.timer.idle", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV }, /* "Ti" */
{ "req.timer.tq", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV }, /* "Tq" */ { "req.timer.tq", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV }, /* "Tq" */
{ "req.timer.hdr", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV }, /* "TR" */ { "req.timer.hdr", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV }, /* "TR" */
{ "req.timer.queue", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV }, /* "Tw" */ { "req.timer.queue", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV }, /* "Tw" */
{ "res.in", smp_fetch_bytes, 0, NULL, SMP_T_SINT, SMP_USE_INTRN },
{ "res.out", smp_fetch_bytes, 0, NULL, SMP_T_SINT, SMP_USE_INTRN },
{ "res.timer.data", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_RSFIN }, /* "Td" */ { "res.timer.data", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_RSFIN }, /* "Td" */
{ "res.timer.hdr", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_HRSHV }, /* "Tr" */ { "res.timer.hdr", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_HRSHV }, /* "Tr" */
{ /* END */ }, { /* END */ },

View File

@ -368,6 +368,8 @@ struct stream *stream_new(struct session *sess, struct stconn *sc, struct buffer
s->logs.t_connect = -1; s->logs.t_connect = -1;
s->logs.t_data = -1; s->logs.t_data = -1;
s->logs.t_close = 0; s->logs.t_close = 0;
s->logs.req_in = s->logs.req_out = 0;
s->logs.res_in = s->logs.res_out = 0;
s->logs.bytes_in = s->logs.bytes_out = 0; s->logs.bytes_in = s->logs.bytes_out = 0;
s->logs.prx_queue_pos = 0; /* we get the number of pending conns before us */ s->logs.prx_queue_pos = 0; /* we get the number of pending conns before us */
s->logs.srv_queue_pos = 0; /* we will get this number soon */ s->logs.srv_queue_pos = 0; /* we will get this number soon */
@ -864,6 +866,11 @@ void stream_process_counters(struct stream *s)
stkctr_inc_bytes_out_ctr(&sess->stkctr[i], bytes); stkctr_inc_bytes_out_ctr(&sess->stkctr[i], bytes);
} }
} }
s->logs.req_in = s->scf->bytes_in;
s->logs.req_out = s->scb->bytes_out;
s->logs.res_in = s->scb->bytes_in;
s->logs.res_out = s->scf->bytes_out;
} }
/* Abort processing on the both channels in same time */ /* Abort processing on the both channels in same time */