mirror of
https://github.com/haproxy/haproxy.git
synced 2026-04-13 21:06:13 -04:00
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:
parent
bf05a014db
commit
6f177cd01e
9 changed files with 410 additions and 11 deletions
|
|
@ -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] */
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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>]")
|
||||
|
|
|
|||
|
|
@ -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))));
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue