mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-04-20 22:15:40 +00:00
MINOR: sample: add us/ms support to date/http_date
It can be sometimes interesting to have a timestamp with a resolution of less than a second. It is currently painful to obtain this, because concatenation of date and date_us lead to a shorter timestamp during first 100ms of a second, which is not parseable and needs ugly ACLs in configuration to prepend 0s when needed. To improve this, add an optional <unit> parameter to date sample to report an integer with desired unit. Also support this unit in http_date converter to report a date string with sub-second precision.
This commit is contained in:
parent
e1583751b6
commit
ae6f125c7b
@ -13245,13 +13245,17 @@ hex2i
|
|||||||
Converts a hex string containing two hex digits per input byte to an
|
Converts a hex string containing two hex digits per input byte to an
|
||||||
integer. If the input value cannot be converted, then zero is returned.
|
integer. If the input value cannot be converted, then zero is returned.
|
||||||
|
|
||||||
http_date([<offset>])
|
http_date([<offset, unit>])
|
||||||
Converts an integer supposed to contain a date since epoch to a string
|
Converts an integer supposed to contain a date since epoch to a string
|
||||||
representing this date in a format suitable for use in HTTP header fields. If
|
representing this date in a format suitable for use in HTTP header fields. If
|
||||||
an offset value is specified, then it is a number of seconds that is added to
|
an offset value is specified, then it is added to the date before the
|
||||||
the date before the conversion is operated. This is particularly useful to
|
conversion is operated. This is particularly useful to emit Date header fields,
|
||||||
emit Date header fields, Expires values in responses when combined with a
|
Expires values in responses when combined with a positive offset, or
|
||||||
positive offset, or Last-Modified values when the offset is negative.
|
Last-Modified values when the offset is negative.
|
||||||
|
If a unit value is specified, then consider the timestamp as either
|
||||||
|
"s" for seconds (default behavior), "ms" for milliseconds, or "us" for
|
||||||
|
microseconds since epoch. Offset is assumed to have the same unit as
|
||||||
|
input timestamp.
|
||||||
|
|
||||||
in_table(<table>)
|
in_table(<table>)
|
||||||
Uses the string representation of the input sample to perform a look up in
|
Uses the string representation of the input sample to perform a look up in
|
||||||
@ -14062,18 +14066,29 @@ cpu_ns_tot : integer
|
|||||||
high cpu_calls count, for example when processing many HTTP chunks, and for
|
high cpu_calls count, for example when processing many HTTP chunks, and for
|
||||||
this reason it is often preferred to log cpu_ns_avg instead.
|
this reason it is often preferred to log cpu_ns_avg instead.
|
||||||
|
|
||||||
date([<offset>]) : integer
|
date([<offset>, <unit>]) : integer
|
||||||
Returns the current date as the epoch (number of seconds since 01/01/1970).
|
Returns the current date as the epoch (number of seconds since 01/01/1970).
|
||||||
If an offset value is specified, then it is a number of seconds that is added
|
|
||||||
to the current date before returning the value. This is particularly useful
|
If an offset value is specified, then it is added to the current date before
|
||||||
to compute relative dates, as both positive and negative offsets are allowed.
|
returning the value. This is particularly useful to compute relative dates,
|
||||||
|
as both positive and negative offsets are allowed.
|
||||||
It is useful combined with the http_date converter.
|
It is useful combined with the http_date converter.
|
||||||
|
|
||||||
|
<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, plus offset.
|
||||||
|
It is useful when a time resolution of less than a second is needed.
|
||||||
|
|
||||||
Example :
|
Example :
|
||||||
|
|
||||||
# set an expires header to now+1 hour in every response
|
# set an expires header to now+1 hour in every response
|
||||||
http-response set-header Expires %[date(3600),http_date]
|
http-response set-header Expires %[date(3600),http_date]
|
||||||
|
|
||||||
|
# set an expires header to now+1 hour in every response, with
|
||||||
|
# millisecond granularity
|
||||||
|
http-response set-header Expires %[date(3600000,ms),http_date(0,ms)]
|
||||||
|
|
||||||
date_us : integer
|
date_us : integer
|
||||||
Return the microseconds part of the date (the "second" part is returned by
|
Return the microseconds part of the date (the "second" part is returned by
|
||||||
date sample). This sample is coherent with the date sample as it is comes
|
date sample). This sample is coherent with the date sample as it is comes
|
||||||
|
@ -45,6 +45,7 @@ struct sample_fetch *find_sample_fetch(const char *kw, int len);
|
|||||||
struct sample_fetch *sample_fetch_getnext(struct sample_fetch *current, int *idx);
|
struct sample_fetch *sample_fetch_getnext(struct sample_fetch *current, int *idx);
|
||||||
struct sample_conv *sample_conv_getnext(struct sample_conv *current, int *idx);
|
struct sample_conv *sample_conv_getnext(struct sample_conv *current, int *idx);
|
||||||
int smp_resolve_args(struct proxy *p);
|
int smp_resolve_args(struct proxy *p);
|
||||||
|
int smp_check_date_unit(struct arg *args, char **err);
|
||||||
int smp_expr_output_type(struct sample_expr *expr);
|
int smp_expr_output_type(struct sample_expr *expr);
|
||||||
int c_none(struct sample *smp);
|
int c_none(struct sample *smp);
|
||||||
int smp_dup(struct sample *smp);
|
int smp_dup(struct sample *smp);
|
||||||
|
@ -33,10 +33,17 @@
|
|||||||
#include <proto/sample.h>
|
#include <proto/sample.h>
|
||||||
#include <proto/stream.h>
|
#include <proto/stream.h>
|
||||||
|
|
||||||
|
static int smp_check_http_date_unit(struct arg *args, struct sample_conv *conv,
|
||||||
|
const char *file, int line, char **err)
|
||||||
|
{
|
||||||
|
return smp_check_date_unit(args, err);
|
||||||
|
}
|
||||||
|
|
||||||
/* takes an UINT value on input supposed to represent the time since EPOCH,
|
/* takes an UINT value on input supposed to represent the time since EPOCH,
|
||||||
* adds an optional offset found in args[0] and emits a string representing
|
* adds an optional offset found in args[0] and emits a string representing
|
||||||
* the date in RFC-1123/5322 format.
|
* the date in RFC-1123/5322 format. If optional unit param in args[1] is
|
||||||
|
* provided, decode timestamp in milliseconds ("ms") or microseconds("us"),
|
||||||
|
* and use relevant output date format.
|
||||||
*/
|
*/
|
||||||
static int sample_conv_http_date(const struct arg *args, struct sample *smp, void *private)
|
static int sample_conv_http_date(const struct arg *args, struct sample *smp, void *private)
|
||||||
{
|
{
|
||||||
@ -44,23 +51,45 @@ static int sample_conv_http_date(const struct arg *args, struct sample *smp, voi
|
|||||||
const char mon[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
const char mon[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||||
struct buffer *temp;
|
struct buffer *temp;
|
||||||
struct tm *tm;
|
struct tm *tm;
|
||||||
/* With high numbers, the date returned can be negative, the 55 bits mask prevent this. */
|
int sec_frac = 0;
|
||||||
time_t curr_date = smp->data.u.sint & 0x007fffffffffffffLL;
|
time_t curr_date;
|
||||||
|
|
||||||
/* add offset */
|
/* add offset */
|
||||||
if (args && (args[0].type == ARGT_SINT))
|
if (args && (args[0].type == ARGT_SINT))
|
||||||
curr_date += args[0].data.sint;
|
smp->data.u.sint += args[0].data.sint;
|
||||||
|
|
||||||
|
/* report in milliseconds */
|
||||||
|
if (args && args[1].type == ARGT_SINT && args[1].data.sint == TIME_UNIT_MS) {
|
||||||
|
sec_frac = smp->data.u.sint % 1000;
|
||||||
|
smp->data.u.sint /= 1000;
|
||||||
|
}
|
||||||
|
/* report in microseconds */
|
||||||
|
else if (args && args[1].type == ARGT_SINT && args[1].data.sint == TIME_UNIT_US) {
|
||||||
|
sec_frac = smp->data.u.sint % 1000000;
|
||||||
|
smp->data.u.sint /= 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* With high numbers, the date returned can be negative, the 55 bits mask prevent this. */
|
||||||
|
curr_date = smp->data.u.sint & 0x007fffffffffffffLL;
|
||||||
|
|
||||||
tm = gmtime(&curr_date);
|
tm = gmtime(&curr_date);
|
||||||
if (!tm)
|
if (!tm)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
temp = get_trash_chunk();
|
temp = get_trash_chunk();
|
||||||
temp->data = snprintf(temp->area, temp->size - temp->data,
|
if (args && args[1].type == ARGT_SINT && args[1].data.sint != TIME_UNIT_S) {
|
||||||
"%s, %02d %s %04d %02d:%02d:%02d GMT",
|
temp->data = snprintf(temp->area, temp->size - temp->data,
|
||||||
day[tm->tm_wday], tm->tm_mday, mon[tm->tm_mon],
|
"%s, %02d %s %04d %02d:%02d:%02d.%d GMT",
|
||||||
1900+tm->tm_year,
|
day[tm->tm_wday], tm->tm_mday, mon[tm->tm_mon],
|
||||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
1900+tm->tm_year,
|
||||||
|
tm->tm_hour, tm->tm_min, tm->tm_sec, sec_frac);
|
||||||
|
} else {
|
||||||
|
temp->data = snprintf(temp->area, temp->size - temp->data,
|
||||||
|
"%s, %02d %s %04d %02d:%02d:%02d GMT",
|
||||||
|
day[tm->tm_wday], tm->tm_mday, mon[tm->tm_mon],
|
||||||
|
1900+tm->tm_year,
|
||||||
|
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||||
|
}
|
||||||
|
|
||||||
smp->data.u.str = *temp;
|
smp->data.u.str = *temp;
|
||||||
smp->data.type = SMP_T_STR;
|
smp->data.type = SMP_T_STR;
|
||||||
@ -328,7 +357,7 @@ static int smp_conv_res_capture(const struct arg *args, struct sample *smp, void
|
|||||||
|
|
||||||
/* Note: must not be declared <const> as its list will be overwritten */
|
/* Note: must not be declared <const> as its list will be overwritten */
|
||||||
static struct sample_conv_kw_list sample_conv_kws = {ILH, {
|
static struct sample_conv_kw_list sample_conv_kws = {ILH, {
|
||||||
{ "http_date", sample_conv_http_date, ARG1(0,SINT), NULL, SMP_T_SINT, SMP_T_STR},
|
{ "http_date", sample_conv_http_date, ARG2(0,SINT,STR), smp_check_http_date_unit, SMP_T_SINT, SMP_T_STR},
|
||||||
{ "language", sample_conv_q_preferred, ARG2(1,STR,STR), NULL, SMP_T_STR, SMP_T_STR},
|
{ "language", sample_conv_q_preferred, ARG2(1,STR,STR), NULL, SMP_T_STR, SMP_T_STR},
|
||||||
{ "capture-req", smp_conv_req_capture, ARG1(1,SINT), NULL, SMP_T_STR, SMP_T_STR},
|
{ "capture-req", smp_conv_req_capture, ARG1(1,SINT), NULL, SMP_T_STR, SMP_T_STR},
|
||||||
{ "capture-res", smp_conv_res_capture, ARG1(1,SINT), NULL, SMP_T_STR, SMP_T_STR},
|
{ "capture-res", smp_conv_res_capture, ARG1(1,SINT), NULL, SMP_T_STR, SMP_T_STR},
|
||||||
|
52
src/sample.c
52
src/sample.c
@ -2940,14 +2940,60 @@ smp_fetch_env(const struct arg *args, struct sample *smp, const char *kw, void *
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* retrieve the current local date in epoch time, and applies an optional offset
|
/* Validates the data unit argument passed to "date" fetch. Argument 1 support an
|
||||||
* of args[0] seconds.
|
* 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_date_unit(struct arg *args, char **err)
|
||||||
|
{
|
||||||
|
if (args[1].type == ARGT_STR) {
|
||||||
|
if (strcmp(args[1].data.str.area, "s") == 0) {
|
||||||
|
args[1].data.sint = TIME_UNIT_S;
|
||||||
|
}
|
||||||
|
else if (strcmp(args[1].data.str.area, "ms") == 0) {
|
||||||
|
args[1].data.sint = TIME_UNIT_MS;
|
||||||
|
}
|
||||||
|
else if (strcmp(args[1].data.str.area, "us") == 0) {
|
||||||
|
args[1].data.sint = TIME_UNIT_US;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
memprintf(err, "expects 's', 'ms' or 'us', got '%s'",
|
||||||
|
args[1].data.str.area);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
free(args[1].data.str.area);
|
||||||
|
args[1].data.str.area = NULL;
|
||||||
|
args[1].type = ARGT_SINT;
|
||||||
|
}
|
||||||
|
else if (args[1].type != ARGT_STOP) {
|
||||||
|
memprintf(err, "Unexpected arg type");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* retrieve the current local date in epoch time, converts it to milliseconds
|
||||||
|
* or microseconds if asked to in optional args[1] unit param, and applies an
|
||||||
|
* optional args[0] offset.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
smp_fetch_date(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
smp_fetch_date(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
||||||
{
|
{
|
||||||
smp->data.u.sint = date.tv_sec;
|
smp->data.u.sint = date.tv_sec;
|
||||||
|
|
||||||
|
/* report in milliseconds */
|
||||||
|
if (args && args[1].type == ARGT_SINT && args[1].data.sint == TIME_UNIT_MS) {
|
||||||
|
smp->data.u.sint *= 1000;
|
||||||
|
smp->data.u.sint += date.tv_usec / 1000;
|
||||||
|
}
|
||||||
|
/* report in microseconds */
|
||||||
|
else if (args && args[1].type == ARGT_SINT && args[1].data.sint == TIME_UNIT_US) {
|
||||||
|
smp->data.u.sint *= 1000000;
|
||||||
|
smp->data.u.sint += date.tv_usec;
|
||||||
|
}
|
||||||
|
|
||||||
/* add offset */
|
/* add offset */
|
||||||
if (args && args[0].type == ARGT_SINT)
|
if (args && args[0].type == ARGT_SINT)
|
||||||
smp->data.u.sint += args[0].data.sint;
|
smp->data.u.sint += args[0].data.sint;
|
||||||
@ -3259,7 +3305,7 @@ static struct sample_fetch_kw_list smp_kws = {ILH, {
|
|||||||
{ "always_false", smp_fetch_false, 0, NULL, SMP_T_BOOL, SMP_USE_INTRN },
|
{ "always_false", smp_fetch_false, 0, NULL, SMP_T_BOOL, SMP_USE_INTRN },
|
||||||
{ "always_true", smp_fetch_true, 0, NULL, SMP_T_BOOL, SMP_USE_INTRN },
|
{ "always_true", smp_fetch_true, 0, NULL, SMP_T_BOOL, SMP_USE_INTRN },
|
||||||
{ "env", smp_fetch_env, ARG1(1,STR), NULL, SMP_T_STR, SMP_USE_INTRN },
|
{ "env", smp_fetch_env, ARG1(1,STR), NULL, SMP_T_STR, SMP_USE_INTRN },
|
||||||
{ "date", smp_fetch_date, ARG1(0,SINT), NULL, SMP_T_SINT, SMP_USE_INTRN },
|
{ "date", smp_fetch_date, ARG2(0,SINT,STR), smp_check_date_unit, SMP_T_SINT, SMP_USE_INTRN },
|
||||||
{ "date_us", smp_fetch_date_us, 0, NULL, SMP_T_SINT, SMP_USE_INTRN },
|
{ "date_us", smp_fetch_date_us, 0, NULL, SMP_T_SINT, SMP_USE_INTRN },
|
||||||
{ "hostname", smp_fetch_hostname, 0, NULL, SMP_T_STR, SMP_USE_INTRN },
|
{ "hostname", smp_fetch_hostname, 0, NULL, SMP_T_STR, SMP_USE_INTRN },
|
||||||
{ "nbproc", smp_fetch_nbproc,0, NULL, SMP_T_SINT, SMP_USE_INTRN },
|
{ "nbproc", smp_fetch_nbproc,0, NULL, SMP_T_SINT, SMP_USE_INTRN },
|
||||||
|
Loading…
Reference in New Issue
Block a user