MINOR: otel: changed instrument attr to use sample expressions

Replaced the static key-value attribute storage in update-form instruments
with sample-evaluated attributes, matching the log-record attr change.
The 'attr' keyword now accepts a key and a HAProxy sample expression
evaluated at runtime.

The struct (conf.h) changed from otelc_kv/attr_len to a list of
flt_otel_conf_sample entries.  The parser (parser.c) calls
flt_otel_parse_cfg_sample() with n=1 per attr keyword.  At runtime
(event.c) each attribute is evaluated via flt_otel_sample_eval() and
added via flt_otel_sample_add_kv() to a bare flt_otel_scope_data_kv,
which is passed to the meter.

Updated documentation, debug macro and test configurations.
This commit is contained in:
Miroslav Zagorac 2026-04-06 06:33:40 +02:00 committed by William Lallemand
parent 651e9fd8a7
commit 7c66bb5497
11 changed files with 64 additions and 34 deletions

View File

@ -628,7 +628,8 @@ instrument { update <name> [<attr>] | <type> <name> [<aggr>] [<desc>] [<unit>] <
To update an existing instrument (previously created in another scope), use
'update' followed by the name of the instrument. Optional attributes can be
added using the 'attr' keyword followed by key-value pairs.
added using the 'attr' keyword followed by a key and a sample expression
evaluated at runtime.
Supported instrument types:
- cnt_int : counter (uint64)
@ -656,7 +657,7 @@ instrument { update <name> [<attr>] | <type> <name> [<aggr>] [<desc>] [<unit>] <
instrument cnt_int "my_counter" desc "Counter" value int(1)
instrument hist_int "my_hist" aggr exp_histogram desc "Latency" value lat_ns_tot unit "ns"
instrument hist_int "my_hist2" desc "Latency" value lat_ns_tot unit "ns" bounds "100 1000 10000 100000"
instrument update "my_counter" attr "key1" "val1"
instrument update "my_counter" attr "key1" str("val1")
Arguments :
type - the instrument type (see list above)
@ -666,7 +667,7 @@ instrument { update <name> [<attr>] | <type> <name> [<aggr>] [<desc>] [<unit>] <
unit - optional unit string for the instrument
value - sample expression providing the measurement value
bounds - optional histogram bucket boundaries (hist_int only)
attr - attribute key-value pairs (update form only)
attr - attribute key and sample expression (update form only)
log-record <severity> [id <integer>] [event <name>] [span <span-name>] [attr <key> <sample>] ... <sample> ...

View File

@ -204,13 +204,13 @@ strings without list linkage.
samples Sample expressions for the value.
bounds Histogram bucket boundaries (create only).
bounds_num Number of histogram bucket boundaries.
attr Instrument attributes (update only).
attr_len Number of instrument attributes.
attributes Instrument attributes (update only, flt_otel_conf_sample).
ref Resolved create-form instrument (update only).
Instruments come in two forms: create-form (defines a new metric with type,
description, unit, and optional histogram bounds) and update-form (references
an existing instrument via the ref pointer).
an existing instrument via the ref pointer). Update-form attributes are stored
as flt_otel_conf_sample entries and evaluated at runtime.
4.7 flt_otel_conf_log_record

View File

@ -320,7 +320,7 @@ Supported keywords:
instrument <type> <name> [aggr <aggregation>] [desc <description>] [unit <unit>] value <sample> [bounds <bounds>]
instrument update <name> [attr <key> <value> ...]
instrument update <name> [attr <key> <sample> ...]
Create or update a metric instrument.
Supported types:
@ -358,7 +358,7 @@ Supported keywords:
instrument cnt_int "name_cnt_int" desc "Integer Counter" value int(1),add(2) unit "unit"
instrument hist_int "name_hist" aggr exp_histogram desc "Latency" value lat_ns_tot unit "ns"
instrument hist_int "name_hist2" desc "Latency" value lat_ns_tot unit "ns" bounds "100 1000 10000"
instrument update "name_cnt_int" attr "attr_1_key" "attr_1_value"
instrument update "name_cnt_int" attr "attr_1_key" str("attr_1_value")
log-record <severity> [id <integer>] [event <name>] [span <span-name>] [attr <key> <sample>] ... <sample> ...

View File

@ -139,10 +139,11 @@ src/event.c
Event dispatching, metrics recording and scope/span execution engine.
flt_otel_scope_run_instrument_record
Records a measurement for a synchronous metric instrument. Evaluates the
sample expression from the create-form instrument (instr_ref) and submits
the value to the meter via update_instrument_kv_n(), using per-scope
attributes from the update-form instrument (instr).
Records a measurement for a synchronous metric instrument. Evaluates
update-form attributes via flt_otel_sample_eval() and
flt_otel_sample_add_kv(), evaluates the sample expression from the
create-form instrument (instr_ref), and submits the value to the meter
via update_instrument_kv_n().
flt_otel_scope_run_instrument
Processes all metric instruments for a scope. Runs in two passes: the

View File

@ -1052,8 +1052,7 @@ The flt_otel_conf_instrument structure (conf.h) holds:
samples List of sample expressions for the instrument value.
bounds Histogram bucket boundaries array (create-form only).
bounds_num Number of histogram bucket boundaries.
attr Key-value attribute array (update-form only).
attr_len Number of attributes.
attributes List of flt_otel_conf_sample entries (update-form only).
ref Pointer to the create-form instrument (update-form only).
18.5.3 Meter Initialization and Startup

View File

@ -85,10 +85,10 @@
flt_otel_list_dump(&((p)->ph_scopes)))
#define FLT_OTEL_DBG_CONF_INSTRUMENT(h,p) \
OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%" PRId64 " %d %d '%s' '%s' %s %p %zu %p %zu %p }", (p), \
OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%" PRId64 " %d %d '%s' '%s' %s %s %p %zu %p }", (p), \
FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->idx, (p)->type, (p)->aggr_type, OTELC_STR_ARG((p)->description), \
OTELC_STR_ARG((p)->unit), flt_otel_list_dump(&((p)->samples)), (p)->attr, (p)->attr_len, (p)->ref, \
(p)->bounds_num, (p)->bounds)
OTELC_STR_ARG((p)->unit), flt_otel_list_dump(&((p)->samples)), flt_otel_list_dump(&((p)->attributes)), \
(p)->ref, (p)->bounds_num, (p)->bounds)
#define FLT_OTEL_DBG_CONF_LOG_RECORD(h,p) \
OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%d %" PRId64 " '%s' '%s' %s %s }", (p), \
@ -197,8 +197,7 @@ struct flt_otel_conf_instrument {
struct list samples; /* Sample expressions for the value. */
double *bounds; /* Histogram bucket boundaries (create only). */
size_t bounds_num; /* Number of histogram bucket boundaries. */
struct otelc_kv *attr; /* Instrument attributes (update only). */
size_t attr_len; /* Number of instrument attributes. */
struct list attributes; /* Instrument attributes (update only, flt_otel_conf_sample). */
struct flt_otel_conf_instrument *ref; /* Resolved create-form instrument (update only). */
};

View File

@ -502,8 +502,9 @@ FLT_OTEL_CONF_FUNC_FREE(span, id,
* DESCRIPTION
* Allocates and initializes a conf_instrument structure. Sets the instrument
* type and meter index to OTELC_METRIC_INSTRUMENT_UNSET and initializes the
* samples list. The <id> string is duplicated and stored as the instrument
* name. If <head> is non-NULL, the structure is appended to the list.
* samples and attributes lists. The <id> string is duplicated and stored as
* the instrument name. If <head> is non-NULL, the structure is appended to
* the list.
*
* RETURN VALUE
* Returns a pointer to the initialized structure, or NULL on failure.
@ -513,6 +514,7 @@ FLT_OTEL_CONF_FUNC_INIT(instrument, id,
retptr->type = OTELC_METRIC_INSTRUMENT_UNSET;
retptr->aggr_type = OTELC_METRIC_AGGREGATION_UNSET;
LIST_INIT(&(retptr->samples));
LIST_INIT(&(retptr->attributes));
)
@ -540,7 +542,7 @@ FLT_OTEL_CONF_FUNC_FREE(instrument, id,
OTELC_SFREE((*ptr)->unit);
FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->samples));
OTELC_SFREE((*ptr)->bounds);
otelc_kv_destroy(&((*ptr)->attr), (*ptr)->attr_len);
FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->attributes));
)

View File

@ -40,10 +40,33 @@ static int flt_otel_scope_run_instrument_record(struct stream *s, uint dir, stru
struct flt_otel_conf_sample_expr *expr;
struct sample smp;
struct otelc_value value;
struct flt_otel_scope_data_kv instr_attr;
int retval = FLT_OTEL_RET_OK;
OTELC_FUNC("%p, %u, %p, %p, %p, %p:%p", s, dir, meter, instr_ref, instr, OTELC_DPTR_ARGS(err));
/* Evaluate instrument attributes from sample expressions. */
(void)memset(&instr_attr, 0, sizeof(instr_attr));
list_for_each_entry(sample, &(instr->attributes), list) {
struct otelc_value attr_value;
OTELC_DBG(DEBUG, "adding instrument attribute '%s' -> '%s'", sample->key, sample->fmt_string);
if (flt_otel_sample_eval(s, dir, sample, true, &attr_value, err) == FLT_OTEL_RET_ERROR) {
retval = FLT_OTEL_RET_ERROR;
continue;
}
if (flt_otel_sample_add_kv(&instr_attr, sample->key, &attr_value) == FLT_OTEL_RET_ERROR) {
if (attr_value.u_type == OTELC_VALUE_DATA)
OTELC_SFREE(attr_value.u.value_data);
retval = FLT_OTEL_RET_ERROR;
}
}
/* The samples list always contains exactly one entry. */
sample = LIST_NEXT(&(instr_ref->samples), struct flt_otel_conf_sample *, list);
@ -58,6 +81,8 @@ static int flt_otel_scope_run_instrument_record(struct stream *s, uint dir, stru
if (smp.data.u.str.area == NULL) {
FLT_OTEL_ERR("out of memory");
otelc_kv_destroy(&(instr_attr.attr), instr_attr.cnt);
OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
}
@ -104,10 +129,12 @@ static int flt_otel_scope_run_instrument_record(struct stream *s, uint dir, stru
}
if (retval != FLT_OTEL_RET_ERROR)
if (OTELC_OPS(meter, update_instrument_kv_n, HA_ATOMIC_LOAD(&(instr_ref->idx)), &value, instr->attr, instr->attr_len) == OTELC_RET_ERROR)
if (OTELC_OPS(meter, update_instrument_kv_n, HA_ATOMIC_LOAD(&(instr_ref->idx)), &value, instr_attr.attr, instr_attr.cnt) == OTELC_RET_ERROR)
retval = FLT_OTEL_RET_ERROR;
}
otelc_kv_destroy(&(instr_attr.attr), instr_attr.cnt);
if (sample->lf_used)
OTELC_SFREE(smp.data.u.str.area);

View File

@ -1060,10 +1060,11 @@ static int flt_otel_parse_cfg_instrument(const char *file, int line, char **args
if (flag_add_attr) {
if (!FLT_OTEL_ARG_ISVALID(i) || !FLT_OTEL_ARG_ISVALID(i + 1))
FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
else if (otelc_kv_add(&(instr->attr), &(instr->attr_len), args[i], args[i + 1], strlen(args[i + 1])) == OTELC_RET_ERROR)
FLT_OTEL_PARSE_ERR(err, "'%s' : out of memory", args[0]);
else
i++;
else {
retval = flt_otel_parse_cfg_sample(file, line, args, i + 1, 1, NULL, &(instr->attributes), err);
if (!(retval & ERR_CODE))
i++;
}
}
else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_INSTRUMENT_ATTR)) {
flag_add_attr = true;
@ -1073,7 +1074,7 @@ static int flt_otel_parse_cfg_instrument(const char *file, int line, char **args
}
}
if (flag_add_attr && (instr->attr_len == 0))
if (flag_add_attr && LIST_ISEMPTY(&(instr->attributes)))
FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
}
else {

View File

@ -125,7 +125,7 @@
otel-scope frontend_http_request
instrument cnt_int "haproxy.http.requests" desc "HTTP request count" value int(1) unit "{request}"
instrument hist_int "haproxy.http.latency" desc "HTTP request latency" value lat_ns_tot unit "ns" aggr "histogram" bounds "1000 1000000 1000000000"
instrument update "haproxy.http.latency" attr "phase" "request"
instrument update "haproxy.http.latency" attr "phase" str("request")
instrument update "haproxy.tcp.request.fe"
span "Frontend HTTP request" parent "HTTP body request" link "HAProxy session"
attribute "http.method" method
@ -235,8 +235,8 @@
otel-event on-process-store-rules-response
otel-scope http_response
instrument update "haproxy.http.requests" attr "phase" "response"
instrument update "haproxy.http.latency" attr "phase" "response"
instrument update "haproxy.http.requests" attr "phase" str("response")
instrument update "haproxy.http.latency" attr "phase" str("response")
instrument update "haproxy.fe.connections"
span "HTTP response" parent "Process store rules response"
attribute "http.status_code" status

View File

@ -103,7 +103,7 @@
otel-scope frontend_http_request
instrument cnt_int "haproxy.http.requests" desc "HTTP request count" value int(1) unit "{request}"
instrument hist_int "haproxy.http.latency" desc "HTTP request latency" value lat_ns_tot unit "ns"
instrument update "haproxy.http.latency" attr "phase" "request"
instrument update "haproxy.http.latency" attr "phase" str("request")
span "Frontend HTTP request" parent "HTTP body request" link "HAProxy session"
attribute "http.method" method
attribute "http.url" url
@ -177,8 +177,8 @@
otel-event on-process-store-rules-response
otel-scope http_response
instrument update "haproxy.http.requests" attr "phase" "response"
instrument update "haproxy.http.latency" attr "phase" "response"
instrument update "haproxy.http.requests" attr "phase" str("response")
instrument update "haproxy.http.latency" attr "phase" str("response")
instrument update "haproxy.fe.connections"
span "HTTP response" parent "Process store rules response"
attribute "http.status_code" status