#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) \
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), \
#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) \
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, \
* 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. */
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. */
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. */
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] */
{ \
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)); \
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); \
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)
#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"
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>]")
(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))));
)
+/***
+ * 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
LIST_INIT(&(retptr->spans));
LIST_INIT(&(retptr->spans_to_finish));
LIST_INIT(&(retptr->instruments));
+ LIST_INIT(&(retptr->log_records));
)
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));
)
}
+/***
+ * 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
* 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.
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)
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);
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;
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();
}
}
}
+ 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)));
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 {
}
+/***
+ * 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
* 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,
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]);
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
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
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