MINOR: otel: added log-record signal support

Added "log-record" as the third OpenTelemetry signal alongside traces
(span) and metrics (instrument).  This includes the
flt_otel_conf_log_record structure definition, parser keyword defines,
the otel-scope section parser with optional "id", "event", "span", and
"attr" keywords followed by sample fetch expressions or a log-format
string, init/free lifecycle, scope list wiring, log-format evaluation
in flt_otel_scope_run_instrument_record(), a test configuration example,
log-record span reference validation in flt_otel_check(), and logger
handle creation, startup, and teardown in the filter lifecycle.
This commit is contained in:
Miroslav Zagorac 2026-03-07 15:28:10 +01:00 committed by William Lallemand
parent bf05a014db
commit 6f177cd01e
9 changed files with 410 additions and 11 deletions

View file

@ -31,6 +31,13 @@
#define FLT_OTEL_CONF_HDR_FMT "%p:{ { '%.*s' %zu %d } "
#define FLT_OTEL_CONF_HDR_ARGS(p,m) (int)(p)->m##_len, (p)->m, (p)->m##_len, (p)->cfg_line
/*
* Special two-byte prefix that triggers automatic id generation in
* FLT_OTEL_CONF_FUNC_INIT(): the text after the prefix is combined
* with the configuration line number to form a unique identifier.
*/
#define FLT_OTEL_CONF_HDR_SPECIAL "\x1e\x1f"
#define FLT_OTEL_CONF_STR_CMP(s,S) ((s##_len == S##_len) && (memcmp(s, S, S##_len) == 0))
#define FLT_OTEL_DBG_CONF_SAMPLE_EXPR(h,p) \
@ -56,11 +63,11 @@
flt_otel_list_dump(&((p)->statuses)))
#define FLT_OTEL_DBG_CONF_SCOPE(h,p) \
OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%hhu %d %u %s %p %s %s %s %s }", (p), \
OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%hhu %d %u %s %p %s %s %s %s %s }", (p), \
FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->flag_used, (p)->event, (p)->idle_timeout, \
flt_otel_list_dump(&((p)->acls)), (p)->cond, flt_otel_list_dump(&((p)->contexts)), \
flt_otel_list_dump(&((p)->spans)), flt_otel_list_dump(&((p)->spans_to_finish)), \
flt_otel_list_dump(&((p)->instruments)))
flt_otel_list_dump(&((p)->instruments)), flt_otel_list_dump(&((p)->log_records)))
#define FLT_OTEL_DBG_CONF_GROUP(h,p) \
OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%hhu %s }", (p), \
@ -69,11 +76,12 @@
#define FLT_OTEL_DBG_CONF_PH(h,p) \
OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%p }", (p), FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->ptr)
#define FLT_OTEL_DBG_CONF_INSTR(h,p) \
OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "'%s' %p %p %u %hhu %hhu 0x%02hhx %p:%s 0x%08x %u %s %s %s }", (p), \
FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->config, (p)->tracer, (p)->meter, (p)->rate_limit, (p)->flag_harderr, \
(p)->flag_disabled, (p)->logging, &((p)->proxy_log), flt_otel_list_dump(&((p)->proxy_log.loggers)), \
(p)->analyzers, (p)->idle_timeout, flt_otel_list_dump(&((p)->acls)), flt_otel_list_dump(&((p)->ph_groups)), \
#define FLT_OTEL_DBG_CONF_INSTR(h,p) \
OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "'%s' %p %p %p %u %hhu %hhu 0x%02hhx %p:%s 0x%08x %u %s %s %s }", \
(p), FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->config, (p)->tracer, (p)->meter, (p)->logger, \
(p)->rate_limit, (p)->flag_harderr, (p)->flag_disabled, (p)->logging, &((p)->proxy_log), \
flt_otel_list_dump(&((p)->proxy_log.loggers)), (p)->analyzers, (p)->idle_timeout, \
flt_otel_list_dump(&((p)->acls)), flt_otel_list_dump(&((p)->ph_groups)), \
flt_otel_list_dump(&((p)->ph_scopes)))
#define FLT_OTEL_DBG_CONF_INSTRUMENT(h,p) \
@ -82,6 +90,11 @@
OTELC_STR_ARG((p)->unit), flt_otel_list_dump(&((p)->samples)), (p)->attr, (p)->attr_len, (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' %p %zu %p }", (p), \
FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->severity, (p)->event_id, OTELC_STR_ARG((p)->event_name), \
OTELC_STR_ARG((p)->span), (p)->attr, (p)->attr_len, flt_otel_list_dump(&((p)->samples)))
#define FLT_OTEL_DBG_CONF(h,p) \
OTELC_DBG(DEBUG, h "%p:{ %p '%s' '%s' %p %s %s }", (p), \
(p)->proxy, (p)->id, (p)->cfg_file, (p)->instr, \
@ -120,6 +133,7 @@ struct flt_otel_conf_sample_expr {
* flt_otel_conf_span->baggages
* flt_otel_conf_span->statuses (status_code -> extra.u.value_int32)
* flt_otel_conf_instrument->samples
* flt_otel_conf_log_record->samples
*/
struct flt_otel_conf_sample {
FLT_OTEL_CONF_HDR(key); /* The list containing sample names. */
@ -188,6 +202,21 @@ struct flt_otel_conf_instrument {
struct flt_otel_conf_instrument *ref; /* Resolved create-form instrument (update only). */
};
/*
* Log record configuration within a scope.
* flt_otel_conf_scope->log_records
*/
struct flt_otel_conf_log_record {
FLT_OTEL_CONF_HDR(id); /* Required by macro; member <id> is not used directly. */
otelc_log_severity_t severity; /* The severity level. */
int64_t event_id; /* Optional event identifier. */
char *event_name; /* Optional event name. */
char *span; /* Optional span reference. */
struct otelc_kv *attr; /* Log record attributes. */
size_t attr_len; /* Number of log record attributes. */
struct list samples; /* Sample expressions for the body. */
};
/* Configuration for a single event scope. */
struct flt_otel_conf_scope {
FLT_OTEL_CONF_HDR(id); /* The scope name. */
@ -200,6 +229,7 @@ struct flt_otel_conf_scope {
struct list spans; /* Declared spans. */
struct list spans_to_finish; /* The list of spans scheduled for finishing. */
struct list instruments; /* The list of metric instruments. */
struct list log_records; /* The list of log records. */
};
/* Configuration for a named group of scopes. */
@ -223,6 +253,7 @@ struct flt_otel_conf_instr {
char *config; /* The OpenTelemetry configuration file name. */
struct otelc_tracer *tracer; /* The OpenTelemetry tracer handle. */
struct otelc_meter *meter; /* The OpenTelemetry meter handle. */
struct otelc_logger *logger; /* The OpenTelemetry logger handle. */
uint32_t rate_limit; /* [0 2^32-1] <-> [0.0 100.0] */
bool flag_harderr; /* [0 1] */
bool flag_disabled; /* [0 1] */

View file

@ -14,6 +14,7 @@
{ \
struct flt_otel_conf_##_type_ *retptr = NULL; \
struct flt_otel_conf_##_type_ *ptr; \
char id_buffer[FLT_OTEL_ID_MAXLEN + 16]; \
size_t _id_##_len; \
\
OTELC_FUNC("\"%s\", %d, %p, %p:%p", OTELC_STR_ARG(id), line, head, OTELC_DPTR_ARGS(err)); \
@ -22,6 +23,11 @@
FLT_OTEL_ERR("name not set"); \
\
OTELC_RETURN_PTR(retptr); \
} \
else if ((id[0] == FLT_OTEL_CONF_HDR_SPECIAL[0]) && (id[1] == FLT_OTEL_CONF_HDR_SPECIAL[1])) { \
(void)snprintf(id_buffer, sizeof(id_buffer), "%s:%d", id + 2, line); \
\
id = id_buffer; \
} \
\
_id_##_len = strlen(id); \
@ -106,6 +112,7 @@ FLT_OTEL_CONF_FUNC_DECL(context)
FLT_OTEL_CONF_FUNC_DECL(span)
FLT_OTEL_CONF_FUNC_DECL(scope)
FLT_OTEL_CONF_FUNC_DECL(instrument)
FLT_OTEL_CONF_FUNC_DECL(log_record)
FLT_OTEL_CONF_FUNC_DECL(group)
FLT_OTEL_CONF_FUNC_DECL(instr)

View file

@ -26,6 +26,10 @@
#define FLT_OTEL_PARSE_INSTRUMENT_UNIT "unit"
#define FLT_OTEL_PARSE_INSTRUMENT_BOUNDS "bounds"
#define FLT_OTEL_PARSE_INSTRUMENT_AGGR "aggr"
#define FLT_OTEL_PARSE_LOG_RECORD_ID "id"
#define FLT_OTEL_PARSE_LOG_RECORD_EVENT "event"
#define FLT_OTEL_PARSE_LOG_RECORD_SPAN "span"
#define FLT_OTEL_PARSE_LOG_RECORD_ATTR "attr"
#define FLT_OTEL_PARSE_CTX_AUTONAME "-"
#define FLT_OTEL_PARSE_CTX_IGNORE_NAME '-'
#define FLT_OTEL_PARSE_CTX_USE_HEADERS "use-headers"
@ -117,6 +121,7 @@
FLT_OTEL_PARSE_SCOPE_DEF( STATUS, 1, NONE, 2, 0, "status", " <code> [<sample> ...]") \
FLT_OTEL_PARSE_SCOPE_DEF( FINISH, 0, NONE, 2, 0, "finish", " <name> ...") \
FLT_OTEL_PARSE_SCOPE_DEF( INSTRUMENT, 0, NONE, 3, 0, "instrument", " { update <name> [<attr> ...] | <type> <name> [<aggr>] [<desc>] [<unit>] <value> [<bounds>] }") \
FLT_OTEL_PARSE_SCOPE_DEF( LOG_RECORD, 0, NONE, 3, 0, "log-record", " <severity> [<id>] [<event>] [<span>] [<attr>] <sample>") \
FLT_OTEL_PARSE_SCOPE_DEF(IDLE_TIMEOUT, 0, NONE, 2, 2, "idle-timeout", " <time>") \
FLT_OTEL_PARSE_SCOPE_DEF( ACL, 0, CHAR, 3, 0, "acl", " <name> <criterion> [flags] [operator] <value> ...") \
FLT_OTEL_PARSE_SCOPE_DEF( ON_EVENT, 0, NONE, 2, 0, "otel-event", " <name> [{ if | unless } <condition>]")

View file

@ -383,6 +383,7 @@ static int flt_otel_cli_parse_status(char **args, char *payload, struct appctx *
(void)memprintf(&msg, "%s configuration: %s\n", msg, conf->instr->config);
(void)memprintf(&msg, "%s tracer: %s\n", msg, (conf->instr->tracer != NULL) ? "active" : "not initialized");
(void)memprintf(&msg, "%s meter: %s\n", msg, (conf->instr->meter != NULL) ? "active" : "not initialized");
(void)memprintf(&msg, "%s logger: %s\n", msg, (conf->instr->logger != NULL) ? "active" : "not initialized");
(void)memprintf(&msg, "%s rate limit: %.2f %%\n", msg, FLT_OTEL_U32_FLOAT(_HA_ATOMIC_LOAD(&(conf->instr->rate_limit))));
(void)memprintf(&msg, "%s hard errors: %s\n", msg, FLT_OTEL_STR_FLAG_YN(_HA_ATOMIC_LOAD(&(conf->instr->flag_harderr))));
(void)memprintf(&msg, "%s disabled: %s\n", msg, FLT_OTEL_STR_FLAG_YN(_HA_ATOMIC_LOAD(&(conf->instr->flag_disabled))));

View file

@ -544,6 +544,60 @@ FLT_OTEL_CONF_FUNC_FREE(instrument, id,
)
/***
* NAME
* flt_otel_conf_log_record_init - conf_log_record structure allocation
*
* SYNOPSIS
* struct flt_otel_conf_log_record *flt_otel_conf_log_record_init(const char *id, int line, struct list *head, char **err)
*
* ARGUMENTS
* id - identifier string to duplicate
* line - configuration file line number
* head - list to append to (or NULL)
* err - indirect pointer to error message string
*
* DESCRIPTION
* Allocates and initializes a conf_log_record structure. Initializes the
* sample expressions list. The <id> string is required by the macro but is
* not used directly; the severity level is stored separately. 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.
*/
FLT_OTEL_CONF_FUNC_INIT(log_record, id,
LIST_INIT(&(retptr->samples));
)
/***
* NAME
* flt_otel_conf_log_record_free - conf_log_record structure deallocation
*
* SYNOPSIS
* void flt_otel_conf_log_record_free(struct flt_otel_conf_log_record **ptr)
*
* ARGUMENTS
* ptr - a pointer to the address of a structure
*
* DESCRIPTION
* Deallocates memory used by the flt_otel_conf_log_record structure and its
* contents, then removes it from the list of structures of that type.
*
* RETURN VALUE
* This function does not return a value.
*/
FLT_OTEL_CONF_FUNC_FREE(log_record, id,
FLT_OTEL_DBG_CONF_LOG_RECORD("- conf_log_record free ", *ptr);
OTELC_SFREE((*ptr)->event_name);
OTELC_SFREE((*ptr)->span);
otelc_kv_destroy(&((*ptr)->attr), (*ptr)->attr_len);
FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->samples));
)
/***
* NAME
* flt_otel_conf_scope_init - conf_scope structure allocation
@ -572,6 +626,7 @@ FLT_OTEL_CONF_FUNC_INIT(scope, id,
LIST_INIT(&(retptr->spans));
LIST_INIT(&(retptr->spans_to_finish));
LIST_INIT(&(retptr->instruments));
LIST_INIT(&(retptr->log_records));
)
@ -608,6 +663,7 @@ FLT_OTEL_CONF_FUNC_FREE(scope, id,
FLT_OTEL_LIST_DESTROY(span, &((*ptr)->spans));
FLT_OTEL_LIST_DESTROY(str, &((*ptr)->spans_to_finish));
FLT_OTEL_LIST_DESTROY(instrument, &((*ptr)->instruments));
FLT_OTEL_LIST_DESTROY(log_record, &((*ptr)->log_records));
)

View file

@ -220,6 +220,137 @@ static int flt_otel_scope_run_instrument(struct stream *s, uint dir, struct flt_
}
/***
* NAME
* flt_otel_scope_run_log_record - log record emitter
*
* SYNOPSIS
* static int flt_otel_scope_run_log_record(struct stream *s, struct filter *f, uint dir, struct flt_otel_conf_scope *scope, struct otelc_logger *logger, const struct timespec *ts, char **err)
*
* ARGUMENTS
* s - the stream providing the sample context
* f - the filter instance
* dir - the sample fetch direction (SMP_OPT_DIR_REQ/RES)
* scope - the scope configuration containing the log record list
* logger - the OTel logger instance
* ts - the wall-clock timestamp for the log record
* err - indirect pointer to error message string
*
* DESCRIPTION
* Processes all log records configured in <scope>. For each record, checks
* whether the logger is enabled for the configured severity, evaluates the
* sample expressions into a body string, resolves the optional span reference
* against the runtime context, and emits the log record via the logger's
* log_span operation.
*
* RETURN VALUE
* Returns FLT_OTEL_RET_OK on success, FLT_OTEL_RET_ERROR on failure.
*/
static int flt_otel_scope_run_log_record(struct stream *s, struct filter *f, uint dir, struct flt_otel_conf_scope *scope, struct otelc_logger *logger, const struct timespec *ts, char **err)
{
struct flt_otel_conf_log_record *conf_log;
int retval = FLT_OTEL_RET_OK;
OTELC_FUNC("%p, %p, %u, %p, %p, %p, %p:%p", s, f, dir, scope, logger, ts, OTELC_DPTR_ARGS(err));
list_for_each_entry(conf_log, &(scope->log_records), list) {
struct flt_otel_conf_sample *sample;
struct flt_otel_conf_sample_expr *expr;
struct sample smp;
struct otelc_span *otel_span = NULL;
struct buffer buffer;
int rc;
OTELC_DBG(DEBUG, "run log-record '%s' -> '%s'", scope->id, conf_log->id);
/* Skip if the logger is not enabled for this severity. */
if (OTELC_OPS(logger, enabled, conf_log->severity) == 0)
continue;
/* The samples list has exactly one entry. */
sample = LIST_NEXT(&(conf_log->samples), typeof(sample), list);
(void)memset(&buffer, 0, sizeof(buffer));
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)
buffer.data = build_logline(s, buffer.area, buffer.size, &(sample->lf_expr));
} else {
/*
* Bare sample expression path: evaluate each expression
* and concatenate the results.
*/
list_for_each_entry(expr, &(sample->exprs), list) {
(void)memset(&smp, 0, sizeof(smp));
if (sample_process(s->be, s->sess, s, dir | SMP_OPT_FINAL, expr->expr, &smp) == NULL) {
OTELC_DBG(NOTICE, "WARNING: failed to fetch '%s'", expr->fmt_expr);
retval = FLT_OTEL_RET_ERROR;
break;
}
if (buffer.area == NULL) {
chunk_init(&buffer, OTELC_CALLOC(1, global.tune.bufsize), global.tune.bufsize);
if (buffer.area == NULL)
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;
break;
}
buffer.data += rc;
}
}
if (buffer.area == NULL) {
FLT_OTEL_ERR("out of memory");
retval = FLT_OTEL_RET_ERROR;
continue;
}
/*
* If the log record references a span, resolve it against the
* runtime context. A missing span is not fatal -- the log
* record is emitted without span correlation.
*/
if (conf_log->span != NULL) {
struct flt_otel_runtime_context *rt_ctx = FLT_OTEL_RT_CTX(f->ctx);
struct flt_otel_scope_span *sc_span;
list_for_each_entry(sc_span, &(rt_ctx->spans), list)
if (strcmp(sc_span->id, conf_log->span) == 0) {
otel_span = sc_span->span;
break;
}
if (otel_span == NULL)
OTELC_DBG(NOTICE, "WARNING: cannot find span '%s' for log-record", conf_log->span);
}
if (OTELC_OPS(logger, log_span, conf_log->severity, conf_log->event_id, conf_log->event_name, otel_span, ts, conf_log->attr, conf_log->attr_len, "%s", buffer.area) == OTELC_RET_ERROR)
retval = FLT_OTEL_RET_ERROR;
OTELC_SFREE(buffer.area);
}
OTELC_RETURN_INT(retval);
}
/***
* NAME
* flt_otel_scope_run_span - single span execution
@ -357,7 +488,8 @@ static int flt_otel_scope_run_span(struct stream *s, struct filter *f, struct ch
* from HTTP headers or HAProxy variables, iterates over configured spans
* (resolving links, evaluating sample expressions for attributes, events,
* baggage and status), calls flt_otel_scope_run_span() for each, processes
* metric instruments, then marks and finishes completed spans.
* metric instruments, emits log records, then marks and finishes completed
* spans.
*
* RETURN VALUE
* Returns FLT_OTEL_RET_OK on success, FLT_OTEL_RET_ERROR on failure.
@ -568,6 +700,11 @@ int flt_otel_scope_run(struct stream *s, struct filter *f, struct channel *chn,
if (flt_otel_scope_run_instrument(s, dir, conf_scope, conf->instr->meter, err) == FLT_OTEL_RET_ERROR)
retval = FLT_OTEL_RET_ERROR;
/* Emit log records. */
if (!LIST_ISEMPTY(&(conf_scope->log_records)))
if (flt_otel_scope_run_log_record(s, f, dir, conf_scope, conf->instr->logger, ts_system, err) == FLT_OTEL_RET_ERROR)
retval = FLT_OTEL_RET_ERROR;
/* Mark the configured spans for finishing and clean up. */
list_for_each_entry(span_to_finish, &(conf_scope->spans_to_finish), list)
if (flt_otel_scope_finish_mark(f->ctx, span_to_finish->str, span_to_finish->str_len) == FLT_OTEL_RET_ERROR)

View file

@ -200,6 +200,14 @@ static int flt_otel_lib_init(struct flt_otel_conf_instr *instr, char **err)
if (instr->meter == NULL) {
if (*err == NULL)
FLT_OTEL_ERR("%s", "failed to initialize OpenTelemetry meter");
OTELC_RETURN_INT(retval);
}
instr->logger = otelc_logger_create(err);
if (instr->logger == NULL) {
if (*err == NULL)
FLT_OTEL_ERR("%s", "failed to initialize OpenTelemetry logger");
} else {
otelc_ext_init(flt_otel_mem_malloc, flt_otel_mem_free, flt_otel_thread_id);
otelc_log_set_handler(flt_otel_log_handler_cb, NULL, false);
@ -415,6 +423,7 @@ static void flt_otel_ops_deinit(struct proxy *p, struct flt_conf *fconf)
struct flt_otel_conf **conf = (fconf == NULL) ? NULL : (typeof(conf))&(fconf->conf);
struct otelc_tracer *otel_tracer = NULL;
struct otelc_meter *otel_meter = NULL;
struct otelc_logger *otel_logger = NULL;
#ifdef DEBUG_OTEL
char buffer[BUFSIZ];
int i;
@ -448,12 +457,13 @@ static void flt_otel_ops_deinit(struct proxy *p, struct flt_conf *fconf)
if ((*conf)->instr != NULL) {
otel_tracer = (*conf)->instr->tracer;
otel_meter = (*conf)->instr->meter;
otel_logger = (*conf)->instr->logger;
}
flt_otel_conf_free(conf);
OTELC_MEMINFO();
flt_otel_pool_destroy();
otelc_deinit(&otel_tracer, &otel_meter, NULL);
otelc_deinit(&otel_tracer, &otel_meter, &otel_logger);
OTELC_RETURN();
}
@ -817,6 +827,45 @@ static int flt_otel_ops_check(struct proxy *p, struct flt_conf *fconf)
}
}
OTELC_DBG(DEBUG, "- defined log records ----------");
/*
* Validate log-record span references: for each log-record that
* names a span, verify that a span with that name exists in one
* of the configured scopes.
*/
list_for_each_entry(conf_scope, &(conf->scopes), list) {
struct flt_otel_conf_log_record *conf_log;
list_for_each_entry(conf_log, &(conf_scope->log_records), list) {
FLT_OTEL_DBG_CONF_LOG_RECORD(" ", conf_log);
if (conf_log->span != NULL) {
struct flt_otel_conf_scope *find_scope;
struct flt_otel_conf_span *find_span;
bool flag_found = false;
list_for_each_entry(find_scope, &(conf->scopes), list) {
list_for_each_entry(find_span, &(find_scope->spans), list)
if (strcmp(find_span->id, conf_log->span) == 0) {
flag_found = true;
break;
}
if (flag_found)
break;
}
if (!flag_found) {
FLT_OTEL_ALERT("'" FLT_OTEL_PARSE_SECTION_SCOPE_ID " '%s' : log-record references undefined span '%s''", conf_scope->id, conf_log->span);
retval++;
}
}
}
}
FLT_OTEL_DBG_LIST(conf, group, "", "defined", _group,
FLT_OTEL_DBG_CONF_GROUP(" ", _group);
FLT_OTEL_DBG_LIST(_group, ph_scope, " ", "used", _scope, FLT_OTEL_DBG_CONF_PH(" ", _scope)));
@ -876,6 +925,12 @@ static int flt_otel_ops_init_per_thread(struct proxy *p, struct flt_conf *fconf)
FLT_OTEL_ALERT("%s", conf->instr->meter->err);
}
if (retval != OTELC_RET_ERROR) {
retval = OTELC_OPS(conf->instr->logger, start);
if (retval == OTELC_RET_ERROR)
FLT_OTEL_ALERT("%s", conf->instr->logger->err);
}
if (retval != FLT_OTEL_RET_ERROR)
fconf->flags |= FLT_CFG_FL_HTX;
} else {

View file

@ -1146,6 +1146,107 @@ static int flt_otel_parse_cfg_instrument(const char *file, int line, char **args
}
/***
* NAME
* flt_otel_parse_cfg_log_record - log-record keyword parser
*
* SYNOPSIS
* static int flt_otel_parse_cfg_log_record(const char *file, int line, char **args, const struct flt_otel_parse_data *pdata, char **err)
*
* ARGUMENTS
* file - configuration file path
* line - configuration file line number
* args - configuration line arguments array
* pdata - keyword metadata (name, usage, argument limits)
* err - indirect pointer to error message string
*
* DESCRIPTION
* Parses the "log-record" keyword inside an otel-scope section. The first
* argument is a required severity level string. Optional keywords "id",
* "event", "span", and "attr" follow in any order. The remaining arguments
* at the end are parsed as fetch expressions or a log-format string.
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
* or a combination of ERR_* flags if an error is encountered.
*/
static int flt_otel_parse_cfg_log_record(const char *file, int line, char **args, const struct flt_otel_parse_data *pdata, char **err)
{
struct flt_otel_conf_log_record *log;
otelc_log_severity_t severity;
int i, retval = ERR_NONE;
OTELC_FUNC("\"%s\", %d, %p, %p, %p:%p", OTELC_STR_ARG(file), line, args, pdata, OTELC_DPTR_ARGS(err));
/* Look up the severity level from args[1]. */
severity = otelc_logger_severity_parse(args[1]);
if (severity == OTELC_LOG_SEVERITY_INVALID) {
FLT_OTEL_PARSE_ERR(err, "'%s' : invalid log severity", args[1]);
OTELC_RETURN_INT(retval);
}
log = flt_otel_conf_log_record_init(FLT_OTEL_CONF_HDR_SPECIAL "log-record", line, &(flt_otel_current_scope->log_records), err);
if (log == NULL) {
retval |= ERR_ABORT | ERR_ALERT;
OTELC_RETURN_INT(retval);
}
log->severity = severity;
/* Parse optional keywords starting from args[2]. */
for (i = 2; !(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(i); i++) {
if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_LOG_RECORD_ID)) {
if (!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 (log->event_id != 0)
FLT_OTEL_PARSE_ERR(err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
else if (!flt_otel_strtoll(args[++i], &(log->event_id), 0, LLONG_MAX, err))
retval |= ERR_ABORT | ERR_ALERT;
}
else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_LOG_RECORD_EVENT)) {
if (!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 (log->event_name != NULL)
FLT_OTEL_PARSE_ERR(err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
else
retval = flt_otel_parse_strdup(&(log->event_name), NULL, args[++i], err, args[0]);
}
else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_LOG_RECORD_SPAN)) {
if (!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 (log->span != NULL)
FLT_OTEL_PARSE_ERR(err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
else
retval = flt_otel_parse_strdup(&(log->span), NULL, args[++i], err, args[0]);
}
else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_LOG_RECORD_ATTR)) {
if (!FLT_OTEL_ARG_ISVALID(i + 1) || !FLT_OTEL_ARG_ISVALID(i + 2))
FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
else if (otelc_kv_add(&(log->attr), &(log->attr_len), args[i + 1], args[i + 2], strlen(args[i + 2])) == OTELC_RET_ERROR)
FLT_OTEL_PARSE_ERR(err, "'%s' : out of memory", args[0]);
else
i += 2;
}
else {
/*
* Not a recognized keyword -- the remaining arguments
* are sample fetch expressions or a log-format string.
*/
retval = flt_otel_parse_cfg_sample(file, line, args, i, 0, NULL, &(log->samples), err);
break;
}
}
if (!(retval & ERR_CODE) && LIST_ISEMPTY(&(log->samples)))
FLT_OTEL_PARSE_ERR(err, "'%s' : missing body expression (use '%s%s')", args[0], pdata->name, pdata->usage);
OTELC_RETURN_INT(retval);
}
/***
* NAME
* flt_otel_parse_cfg_scope - otel-scope section parser
@ -1162,8 +1263,8 @@ static int flt_otel_parse_cfg_instrument(const char *file, int line, char **args
* DESCRIPTION
* Section parser for the otel-scope configuration block. Handles keywords:
* scope ID, span (with optional root/parent/link modifiers), link, attribute,
* event, baggage, status, inject, extract, finish, instrument, acl, and
* otel-event (with optional if/unless conditions).
* event, baggage, status, inject, extract, finish, instrument, log-record,
* acl, and otel-event (with optional if/unless conditions).
*
* RETURN VALUE
* Returns ERR_NONE (== 0) in case of success,
@ -1369,6 +1470,9 @@ static int flt_otel_parse_cfg_scope(const char *file, int line, char **args, int
else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_INSTRUMENT) {
retval = flt_otel_parse_cfg_instrument(file, line, args, pdata, &err);
}
else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_LOG_RECORD) {
retval = flt_otel_parse_cfg_log_record(file, line, args, pdata, &err);
}
else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_ACL) {
if (FLT_OTEL_PARSE_KEYWORD(1, "or"))
FLT_OTEL_PARSE_ERR(&err, "'%s %s ...' : invalid ACL name", args[0], args[1]);

View file

@ -65,6 +65,7 @@
event "event_be" "be" be_id str(" ") be_name
event "event_ip" "dst" dst str(":") dst_port
event "event_fe" "fe" fe_id str(" ") fe_name
log-record trace id 1000 event "session-start" span "Client session" attr "attr_1_key" "attr_1_value" attr "attr_2_key" "attr_2_value" src str(":") src_port
acl acl-test-src-ip src 127.0.0.1
otel-event on-stream-start if acl-test-src-ip
@ -78,6 +79,7 @@
attribute "idle.elapsed" str("idle-check")
instrument cnt_int "idle.count" value int(1)
instrument update "idle.count"
log-record info str("heartbeat")
otel-event on-idle-timeout
otel-scope client_session_start
@ -107,6 +109,7 @@
attribute "http.url" url
attribute "http.version" str("HTTP/") req.ver
finish "HTTP body request"
log-record info id 1001 event "http-request" span "Frontend HTTP request" attr "http.method" "GET" method url
otel-event on-frontend-http-request
otel-scope switching_rules_request