]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: otel: added log-format support to the sample parser and runtime
authorMiroslav Zagorac <mzagorac@haproxy.com>
Tue, 3 Feb 2026 20:36:11 +0000 (21:36 +0100)
committerWilliam Lallemand <wlallemand@haproxy.com>
Mon, 13 Apr 2026 07:23:26 +0000 (09:23 +0200)
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.

addons/otel/include/conf.h
addons/otel/src/conf.c
addons/otel/src/parser.c
addons/otel/src/util.c

index a9ba3807535bacb5f8b515ebb03821c98f3decaf..99c37cad4f3a687b1487337d28e9893ee07f3f1f 100644 (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. */
 };
 
 /*
index e5a5279eb69ad6c90cbe61817ab8e6acb9917810..f2f46b1d3f088eebeb4de8b144f514d0f4ec2248 100644 (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));
 )
 
 
index 2186b0d3ce3a6c15056f4dc181ae03cb6a10f086..18627b62b2863d614607e708fee859651eb0882f 100644 (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;
index c54e741e5572ff9a08ae4639d10160af40c8176b..b1920d8b1cc9afc8a9c59841bd46aa4cbd903745 100644 (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));
+       /* Evaluate the sample: log-format path or expression list path. */
+       if (sample->lf_used) {
+               /*
+                * Log-format path: evaluate the log-format expression into a
+                * dynamically allocated buffer.
+                */
+               chunk_init(&buffer, OTELC_CALLOC(1, global.tune.bufsize), global.tune.bufsize);
+               if (buffer.area == NULL) {
+                       FLT_OTEL_ERR("out of memory");
 
-               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);
+                       retval = FLT_OTEL_RET_ERROR;
                } else {
-                       OTELC_DBG(NOTICE, "WARNING: failed to fetch '%s' value", expr->fmt_expr);
+                       buffer.data = build_logline(s, buffer.area, buffer.size, &(sample->lf_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 = "";
+                       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);
 
-               /*
-                * 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 {
-                       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");
+                       (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 = "";
+                       }
+
+                       /*
+                        * 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 {
+                               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");
+
+                                               retval = FLT_OTEL_RET_ERROR;
 
-                                       break;
+                                               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;
+                               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;
+                                       if (sample->num_exprs == ++idx) {
+                                               value.u_type       = OTELC_VALUE_DATA;
+                                               value.u.value_data = buffer.area;
+                                       }
                                }
                        }
                }