MINOR: otel: added log-format support to the sample parser and runtime

Extended flt_otel_parse_cfg_sample() to accept log-format strings in
addition to bare sample expressions.  Added lf_expr and lf_used fields
to flt_otel_conf_sample.

Extended flt_otel_sample_add() to evaluate log-format expressions when
lf_used was set.
This commit is contained in:
Miroslav Zagorac 2026-02-03 21:36:11 +01:00 committed by William Lallemand
parent 40ac0fd932
commit bc6402f609
4 changed files with 103 additions and 51 deletions

View file

@ -37,9 +37,9 @@
OTELC_DBG(DEBUG, h "%p:{ '%s' %p }", (p), (p)->fmt_expr, (p)->expr)
#define FLT_OTEL_DBG_CONF_SAMPLE(h,p) \
OTELC_DBG(DEBUG, h "%p:{ '%s' '%s' %s %s %d }", (p), \
OTELC_DBG(DEBUG, h "%p:{ '%s' '%s' %s %s %d %p %hhu }", (p), \
(p)->key, (p)->fmt_string, otelc_value_dump(&((p)->extra), ""), \
flt_otel_list_dump(&((p)->exprs)), (p)->num_exprs)
flt_otel_list_dump(&((p)->exprs)), (p)->num_exprs, &((p)->lf_expr), (p)->lf_used)
#define FLT_OTEL_DBG_CONF_HDR(h,p,i) \
OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "}", (p), FLT_OTEL_CONF_HDR_ARGS(p, i))
@ -118,6 +118,8 @@ struct flt_otel_conf_sample {
struct otelc_value extra; /* Optional supplementary data. */
struct list exprs; /* Used to chain sample expressions. */
int num_exprs; /* Number of defined expressions. */
struct lf_expr lf_expr; /* The log-format expression. */
bool lf_used; /* Whether lf_expr is used instead of exprs. */
};
/*

View file

@ -212,6 +212,7 @@ FLT_OTEL_CONF_FUNC_FREE(sample_expr, fmt_expr,
*/
FLT_OTEL_CONF_FUNC_INIT(sample, key,
LIST_INIT(&(retptr->exprs));
lf_expr_init(&(retptr->lf_expr));
)
@ -327,6 +328,7 @@ FLT_OTEL_CONF_FUNC_FREE(sample, key,
if ((*ptr)->extra.u_type == OTELC_VALUE_DATA)
OTELC_SFREE((*ptr)->extra.u.value_data);
FLT_OTEL_LIST_DESTROY(sample_expr, &((*ptr)->exprs));
lf_expr_deinit(&((*ptr)->lf_expr));
)

View file

@ -317,10 +317,21 @@ static int flt_otel_parse_cfg_sample_expr(const char *file, int line, char **arg
* err - indirect pointer to error message string
*
* DESCRIPTION
* Parses a complete sample definition starting at index <idx> in <args>.
* Creates a conf_sample structure with optional <extra> data (event name or
* status code), then parses sample expressions. When <n> is 0, all remaining
* arguments are parsed; otherwise at most <n> expressions are parsed.
* Parses a complete sample definition starting at index <idx> in the
* <args> array. A new conf_sample structure is allocated and initialized
* via flt_otel_conf_sample_init_ex() with the optional <extra> data (an
* event name or a status code), then the sample expressions are parsed.
*
* When <args>[<idx>] contains the "%[" sequence, the argument is parsed
* as a log-format string via parse_logformat_string(): the lf_used flag
* is set and the result is stored in the lf_expr member while the exprs
* list remains empty. Otherwise the arguments are treated as bare sample
* expressions: the proxy configuration context is set and the function
* calls flt_otel_parse_cfg_sample_expr() in a loop to populate exprs.
*
* When <n> is 0 all remaining valid arguments are consumed; otherwise at
* most <n> expressions are parsed. On error the allocated conf_sample
* structure is freed before returning.
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success, or a combination of ERR_* flags
@ -338,7 +349,25 @@ static int flt_otel_parse_cfg_sample(const char *file, int line, char **args, in
if (sample == NULL)
FLT_OTEL_PARSE_ERR(err, "'%s' : out of memory", args[0]);
if (!(retval & ERR_CODE)) {
if (retval & ERR_CODE) {
/* Do nothing. */
}
else if (strstr(args[idx], "%[") != NULL) {
/*
* Log-format path: parse the single argument as a log-format
* string into the sample structure.
*/
sample->lf_used = 1;
if (parse_logformat_string(args[idx], flt_otel_current_config->proxy, &(sample->lf_expr), LOG_OPT_HTTP, SMP_VAL_FE_LOG_END, err) == 0)
retval |= ERR_ABORT | ERR_ALERT;
else
OTELC_DBG(DEBUG, "sample '%s' -> log-format '%s' added", sample->key, sample->fmt_string);
}
else {
/*
* Bare sample expression path.
*/
flt_otel_current_config->proxy->conf.args.ctx = ARGC_OTEL;
flt_otel_current_config->proxy->conf.args.file = file;
flt_otel_current_config->proxy->conf.args.line = line;

View file

@ -887,59 +887,78 @@ int flt_otel_sample_add(struct stream *s, uint dir, struct flt_otel_conf_sample
(void)memset(&value, 0, sizeof(value));
(void)memset(&buffer, 0, sizeof(buffer));
list_for_each_entry(expr, &(sample->exprs), list) {
FLT_OTEL_DBG_CONF_SAMPLE_EXPR("sample expression ", expr);
(void)memset(&smp, 0, sizeof(smp));
if (sample_process(s->be, s->sess, s, dir | SMP_OPT_FINAL, expr->expr, &smp) != NULL) {
OTELC_DBG(DEBUG, "data type %d: '%s'", smp.data.type, expr->fmt_expr);
} else {
OTELC_DBG(NOTICE, "WARNING: failed to fetch '%s' value", expr->fmt_expr);
/*
* In case the fetch failed, we will set the result
* (sample) to an empty static string.
*/
(void)memset(&(smp.data), 0, sizeof(smp.data));
smp.data.type = SMP_T_STR;
smp.data.u.str.area = "";
}
/* Evaluate the sample: log-format path or expression list path. */
if (sample->lf_used) {
/*
* If we have only one expression to process, then the data
* type that is the result of the expression is converted to
* an equivalent data type (if possible) that is written to
* the tracer.
*
* If conversion is not possible, or if we have multiple
* expressions to process, then the result is converted to
* a string and as such sent to the tracer.
* Log-format path: evaluate the log-format expression into a
* dynamically allocated buffer.
*/
if ((sample->num_exprs == 1) && ((type == FLT_OTEL_EVENT_SAMPLE_ATTRIBUTE) || (type == FLT_OTEL_EVENT_SAMPLE_EVENT))) {
if (flt_otel_sample_to_value(sample->key, &(smp.data), &value, err) == FLT_OTEL_RET_ERROR)
retval = FLT_OTEL_RET_ERROR;
chunk_init(&buffer, OTELC_CALLOC(1, global.tune.bufsize), global.tune.bufsize);
if (buffer.area == NULL) {
FLT_OTEL_ERR("out of memory");
retval = FLT_OTEL_RET_ERROR;
} else {
if (buffer.area == NULL) {
chunk_init(&buffer, OTELC_CALLOC(1, global.tune.bufsize), global.tune.bufsize);
if (buffer.area == NULL) {
FLT_OTEL_ERR("out of memory");
buffer.data = build_logline(s, buffer.area, buffer.size, &(sample->lf_expr));
retval = FLT_OTEL_RET_ERROR;
value.u_type = OTELC_VALUE_DATA;
value.u.value_data = buffer.area;
}
} else {
list_for_each_entry(expr, &(sample->exprs), list) {
FLT_OTEL_DBG_CONF_SAMPLE_EXPR("sample expression ", expr);
break;
}
(void)memset(&smp, 0, sizeof(smp));
if (sample_process(s->be, s->sess, s, dir | SMP_OPT_FINAL, expr->expr, &smp) != NULL) {
OTELC_DBG(DEBUG, "data type %d: '%s'", smp.data.type, expr->fmt_expr);
} else {
OTELC_DBG(NOTICE, "WARNING: failed to fetch '%s' value", expr->fmt_expr);
/*
* In case the fetch failed, we will set the result
* (sample) to an empty static string.
*/
(void)memset(&(smp.data), 0, sizeof(smp.data));
smp.data.type = SMP_T_STR;
smp.data.u.str.area = "";
}
rc = flt_otel_sample_to_str(&(smp.data), buffer.area + buffer.data, buffer.size - buffer.data, err);
if (rc == FLT_OTEL_RET_ERROR) {
retval = FLT_OTEL_RET_ERROR;
/*
* If we have only one expression to process, then the data
* type that is the result of the expression is converted to
* an equivalent data type (if possible) that is written to
* the tracer.
*
* If conversion is not possible, or if we have multiple
* expressions to process, then the result is converted to
* a string and as such sent to the tracer.
*/
if ((sample->num_exprs == 1) && ((type == FLT_OTEL_EVENT_SAMPLE_ATTRIBUTE) || (type == FLT_OTEL_EVENT_SAMPLE_EVENT))) {
if (flt_otel_sample_to_value(sample->key, &(smp.data), &value, err) == FLT_OTEL_RET_ERROR)
retval = FLT_OTEL_RET_ERROR;
} else {
buffer.data += rc;
if (buffer.area == NULL) {
chunk_init(&buffer, OTELC_CALLOC(1, global.tune.bufsize), global.tune.bufsize);
if (buffer.area == NULL) {
FLT_OTEL_ERR("out of memory");
if (sample->num_exprs == ++idx) {
value.u_type = OTELC_VALUE_DATA;
value.u.value_data = buffer.area;
retval = FLT_OTEL_RET_ERROR;
break;
}
}
rc = flt_otel_sample_to_str(&(smp.data), buffer.area + buffer.data, buffer.size - buffer.data, err);
if (rc == FLT_OTEL_RET_ERROR) {
retval = FLT_OTEL_RET_ERROR;
} else {
buffer.data += rc;
if (sample->num_exprs == ++idx) {
value.u_type = OTELC_VALUE_DATA;
value.u.value_data = buffer.area;
}
}
}
}