MINOR: sample: accept_date / request_date return %Ts / %tr timestamp values

Implement %[accept_date] which returns the same as %Ts log-format tag.
Implement %[request_date] which is a timestamp for %tr.

accept_date and request_date take an faculative unit argument which can
be 's', 'ms' or 'us'.

The goal is to be able to convert these 2 timestamps to HAProxy date
format like its done with %T, %tr, %trg etc
This commit is contained in:
William Lallemand 2023-07-13 16:18:47 +02:00
parent 2a46bfe239
commit 739c4e5b1e
2 changed files with 110 additions and 0 deletions

View File

@ -19760,6 +19760,24 @@ An optional table may be specified with the "sc*" form, in which case the
currently tracked key will be looked up into this alternate table instead of
the table currently being tracked.
accept_date([<unit>]) : integer
This is the exact date when the connection was received by HAProxy
(which might be very slightly different from the date observed on the
network if there was some queuing in the system's backlog). This is usually
the same date which may appear in any upstream firewall's log. When used in
HTTP mode, the accept_date field will be reset to the first moment the
connection is ready to receive a new request (end of previous response for
HTTP/1, immediately after previous request for HTTP/2).
Returns a value in number of seconds since epoch.
<unit> is facultative, and can be set to "s" for seconds (default behavior),
"ms" for milliseconds or "us" for microseconds.
If unit is set, return value is an integer reflecting either seconds,
milliseconds or microseconds since epoch.
It is useful when a time resolution of less than a second is needed.
bc_dst : ip
This is the destination ip address of the connection on the server side,
which is the server address HAProxy connected to. It is of type IP and works
@ -21885,6 +21903,19 @@ hdr([<name>[,<occ>]]) : string
Note that contrary to the hdr() sample fetch method, the hdr_* ACL keywords
unambiguously apply to the request headers.
request_date([<unit>]) : integer
This is the exact date when the first byte of the HTTP request was received
by HAProxy (log-format tag %tr). This is computed from accept_date +
handshake time (%Th) + idle time (%Ti).
Returns a value in number of seconds since epoch.
<unit> is facultative, and can be set to "s" for seconds (default behavior),
"ms" for milliseconds or "us" for microseconds.
If unit is set, return value is an integer reflecting either seconds,
milliseconds or microseconds since epoch.
It is useful when a time resolution of less than a second is needed.
req.fhdr(<name>[,<occ>]) : string
This returns the full value of the last occurrence of header <name> in an
HTTP request. It differs from req.hdr() in that any commas present in the

View File

@ -497,12 +497,91 @@ smp_fetch_fc_reordering(const struct arg *args, struct sample *smp, const char *
#endif
#endif // TCP_INFO
/* Validates the data unit argument passed to "accept_date" fetch. Argument 0 support an
* optional string representing the unit of the result: "s" for seconds, "ms" for
* milliseconds and "us" for microseconds.
* Returns 0 on error and non-zero if OK.
*/
int smp_check_accept_date_unit(struct arg *args, char **err)
{
if (args[0].type == ARGT_STR) {
long long int unit;
if (strcmp(args[0].data.str.area, "s") == 0) {
unit = TIME_UNIT_S;
}
else if (strcmp(args[0].data.str.area, "ms") == 0) {
unit = TIME_UNIT_MS;
}
else if (strcmp(args[0].data.str.area, "us") == 0) {
unit = TIME_UNIT_US;
}
else {
memprintf(err, "expects 's', 'ms' or 'us', got '%s'",
args[0].data.str.area);
return 0;
}
chunk_destroy(&args[0].data.str);
args[0].type = ARGT_SINT;
args[0].data.sint = unit;
}
else if (args[0].type != ARGT_STOP) {
memprintf(err, "Unexpected arg type");
return 0;
}
return 1;
}
/* retrieve the accept or request date in epoch time, converts it to milliseconds
* or microseconds if asked to in optional args[1] unit param */
static int
smp_fetch_accept_date(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
struct strm_logs *logs;
struct timeval tv;
if (!smp->strm)
return 0;
logs = &smp->strm->logs;
if (kw[0] == 'r') { /* request_date */
tv_ms_add(&tv, &logs->accept_date, logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0);
} else { /* accept_date */
tv.tv_sec = logs->accept_date.tv_sec;
tv.tv_usec = logs->accept_date.tv_usec;
}
smp->data.u.sint = tv.tv_sec;
/* report in milliseconds */
if (args[0].type == ARGT_SINT && args[0].data.sint == TIME_UNIT_MS) {
smp->data.u.sint *= 1000;
smp->data.u.sint += tv.tv_usec / 1000;
}
/* report in microseconds */
else if (args[0].type == ARGT_SINT && args[0].data.sint == TIME_UNIT_US) {
smp->data.u.sint *= 1000000;
smp->data.u.sint += tv.tv_usec;
}
smp->data.type = SMP_T_SINT;
smp->flags |= SMP_F_VOL_TEST | SMP_F_MAY_CHANGE;
return 1;
}
/* Note: must not be declared <const> as its list will be overwritten.
* Note: fetches that may return multiple types should be declared using the
* appropriate pseudo-type. If not available it must be declared as the lowest
* common denominator, the type that can be casted into all other ones.
*/
static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
/* timestamps */
{ "accept_date", smp_fetch_accept_date, ARG1(0,STR), smp_check_accept_date_unit, SMP_T_SINT, SMP_USE_L4CLI },
{ "request_date", smp_fetch_accept_date, ARG1(0,STR), smp_check_accept_date_unit, SMP_T_SINT, SMP_USE_HRQHP },
{ "bc_dst", smp_fetch_dst, 0, NULL, SMP_T_ADDR, SMP_USE_L4SRV },
{ "bc_dst_port", smp_fetch_dport, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV },
{ "bc_src", smp_fetch_src, 0, NULL, SMP_T_ADDR, SMP_USE_L4SRV },