diff --git a/addons/otel/include/conf.h b/addons/otel/include/conf.h index a9ba38075..99c37cad4 100644 --- a/addons/otel/include/conf.h +++ b/addons/otel/include/conf.h @@ -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. */ }; /* diff --git a/addons/otel/src/conf.c b/addons/otel/src/conf.c index e5a5279eb..f2f46b1d3 100644 --- a/addons/otel/src/conf.c +++ b/addons/otel/src/conf.c @@ -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)); ) diff --git a/addons/otel/src/parser.c b/addons/otel/src/parser.c index 2186b0d3c..18627b62b 100644 --- a/addons/otel/src/parser.c +++ b/addons/otel/src/parser.c @@ -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 in . - * Creates a conf_sample structure with optional data (event name or - * status code), then parses sample expressions. When is 0, all remaining - * arguments are parsed; otherwise at most expressions are parsed. + * Parses a complete sample definition starting at index in the + * array. A new conf_sample structure is allocated and initialized + * via flt_otel_conf_sample_init_ex() with the optional data (an + * event name or a status code), then the sample expressions are parsed. + * + * When [] 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 is 0 all remaining valid arguments are consumed; otherwise at + * most 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; diff --git a/addons/otel/src/util.c b/addons/otel/src/util.c index c54e741e5..b1920d8b1 100644 --- a/addons/otel/src/util.c +++ b/addons/otel/src/util.c @@ -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; + } } } }