]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: stream: Add samples to get number of bytes received or sent on each side
authorChristopher Faulet <cfaulet@haproxy.com>
Tue, 4 Nov 2025 17:06:19 +0000 (18:06 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Thu, 6 Nov 2025 14:01:28 +0000 (15:01 +0100)
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
include/haproxy/stream-t.h
src/cli.c
src/http_ana.c
src/sample.c
src/stream.c

index 42cd5f8830b72130c642c4075d02dae9d9d58c4a..e7ef09429f04f4d2db65e51f4d54cce63dcbf449 100644 (file)
@@ -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([<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
   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
index 06f7e4be0782e85bb6cbd9ccb137fb28180ed722..8a1ba8fb32caf9dbbaa38ee4efeae6d9424c2f54 100644 (file)
@@ -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 */
 };
index 5a19be559b7cd60c99c8958f3741cc7504d42277..22a6bf689d1faf0dcb1b190429548671ed854313 100644 (file)
--- 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)) {
index 532a4860de83fb972ed73a535cc8cf46d5b2921e..6630edec2e185189abf6495ce487a63e4db17a33 100644 (file)
@@ -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:
index ce9dd9301c7511d770e94d842ae5ed6f03ab8ec6..c4fc16afc7e287a4438da2f5cbb6aa74f4de1706 100644 (file)
@@ -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 */ },
index 07b6523ad353e33b08f9627f62027a40bed834c5..11cb96781db85a20661d82a6601e8df4534f4c83 100644 (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_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 */