From ac9201f929832c59ad79e667c557a42234798ca8 Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Tue, 4 Nov 2025 18:06:19 +0100 Subject: [PATCH] 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. --- doc/configuration.txt | 26 ++++++++++++++++++++++++++ include/haproxy/stream-t.h | 4 ++++ src/cli.c | 4 ++++ src/http_ana.c | 4 ++-- src/sample.c | 15 ++++++++++----- src/stream.c | 7 +++++++ 6 files changed, 53 insertions(+), 7 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 42cd5f883..e7ef09429 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -23116,6 +23116,10 @@ fe_client_timeout integer fe_defbe string fe_id integer fe_name string +req.in integer +req.out integer +res.in integer +res.out integer res.timer.data integer sc0_bytes_in_rate([]) integer sc0_bytes_out_rate([
]) integer @@ -23751,6 +23755,28 @@ fe_name : string backends to check from which frontend it was called, or to stick all users 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 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 diff --git a/include/haproxy/stream-t.h b/include/haproxy/stream-t.h index 06f7e4be0..8a1ba8fb3 100644 --- a/include/haproxy/stream-t.h +++ b/include/haproxy/stream-t.h @@ -225,6 +225,10 @@ struct strm_logs { 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 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_out; /* number of bytes transferred from the server to the client */ }; diff --git a/src/cli.c b/src/cli.c index 5a19be559..22a6bf689 100644 --- a/src/cli.c +++ b/src/cli.c @@ -3488,6 +3488,8 @@ int pcli_wait_for_response(struct stream *s, struct channel *rep, int an_bit) /* don't count other requests' data */ s->logs.bytes_in -= ci_data(&s->req); 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 */ 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_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); if (objt_server(s->target)) { diff --git a/src/http_ana.c b/src/http_ana.c index 532a4860d..6630edec2 100644 --- a/src/http_ana.c +++ b/src/http_ana.c @@ -2002,9 +2002,9 @@ int http_process_res_common(struct stream *s, struct channel *rep, int an_bit, s */ if (do_log) { 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->logs.bytes_out = 0; + s->logs.res_in = s->logs.bytes_out = 0; } done: diff --git a/src/sample.c b/src/sample.c index ce9dd9301..c4fc16afc 100644 --- a/src/sample.c +++ b/src/sample.c @@ -5444,11 +5444,12 @@ static int smp_fetch_bytes(const struct arg *args, struct sample *smp, const cha if (!logs) return 0; - if (kw[6] == 'i') { /* bytes_in */ - smp->data.u.sint = logs->bytes_in; - } else { /* bytes_out */ - smp->data.u.sint = logs->bytes_out; - } + if (kw[2] == 'q') /* req.in or req.out */ + smp->data.u.sint = (kw[4] == 'i') ? logs->req_in : logs->req_out; + if (kw[2] == 's') /* res.in or res.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; } @@ -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.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.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.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.hdr", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_HRSHV }, /* "Tr" */ { /* END */ }, diff --git a/src/stream.c b/src/stream.c index 07b6523ad..11cb96781 100644 --- a/src/stream.c +++ b/src/stream.c @@ -368,6 +368,8 @@ struct stream *stream_new(struct session *sess, struct stconn *sc, struct buffer s->logs.t_connect = -1; s->logs.t_data = -1; 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.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 */ @@ -864,6 +866,11 @@ void stream_process_counters(struct stream *s) 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 */ -- 2.47.3