Miroslav Zagorac bab0ea7b77 MEDIUM: otel: implemented scope execution and span management
Implemented the scope execution engine that creates OTel spans, evaluates
sample expressions to collect telemetry data, and manages span lifecycle
during request and response processing.

The scope runner flt_otel_scope_run() was expanded from a stub into a
complete implementation that evaluates ACL conditions on the scope,
extracts span contexts from HTTP headers when configured, iterates over
the scope's span definitions calling flt_otel_scope_run_span() for each,
marks and finishes completed spans, and cleans up unused runtime
resources.

The span runner flt_otel_scope_run_span() creates OTel spans via the
tracer with optional parent references (from other spans or extracted
contexts), collects telemetry by calling flt_otel_sample_add() for each
configured attribute, event, baggage and status entry, then applies the
collected data to the span (attributes, events with their own key-value
arrays, baggage items, and status code with description) and injects the
span context into HTTP headers when configured.

The sample evaluation layer converts HAProxy sample expressions into OTel
telemetry data.  flt_otel_sample_add() evaluates each sample expression
against the stream, converts the result via flt_otel_sample_to_value()
which preserves native types (booleans as OTELC_VALUE_BOOL, integers as
OTELC_VALUE_INT64, all others as strings), and routes the key-value pair
to the appropriate collector based on the sample type (attribute, event,
baggage, or status).  The key-value arrays grow dynamically using the
FLT_OTEL_ATTR_INIT_SIZE and FLT_OTEL_ATTR_INC_SIZE constants.

Span finishing is handled in two phases: flt_otel_scope_finish_mark()
marks spans and contexts for completion using exact name matching or
wildcards ("*" for all, "*req*" for request-direction, "*res*" for
response-direction), and flt_otel_scope_finish_marked() ends all marked
spans with a common monotonic timestamp and destroys their contexts.
2026-04-13 09:23:26 +02:00

165 lines
7.7 KiB
C

/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef _OTEL_SCOPE_H_
#define _OTEL_SCOPE_H_
#define FLT_OTEL_SCOPE_SPAN_FINISH_REQ "*req*"
#define FLT_OTEL_SCOPE_SPAN_FINISH_RES "*res*"
#define FLT_OTEL_SCOPE_SPAN_FINISH_ALL "*"
#define FLT_OTEL_RT_CTX(p) ((struct flt_otel_runtime_context *)(p))
#define FLT_OTEL_DBG_SCOPE_SPAN(h,p) \
OTELC_DBG(DEBUG, h "%p:{ '%s' %zu %u %hhu %p %p %p }", (p), \
FLT_OTEL_STR_HDR_ARGS(p, id), (p)->smp_opt_dir, \
(p)->flag_finish, (p)->span, (p)->ref_span, (p)->ref_ctx)
#define FLT_OTEL_DBG_SCOPE_CONTEXT(h,p) \
OTELC_DBG(DEBUG, h "%p:{ '%s' %zu %u %hhu %p }", (p), \
FLT_OTEL_STR_HDR_ARGS(p, id), (p)->smp_opt_dir, \
(p)->flag_finish, (p)->context)
#define FLT_OTEL_DBG_SCOPE_DATA_EVENT(h,p) \
OTELC_DBG(DEBUG, h "%p:{ '%s' %p %zu %zu %s }", &(p), \
(p).name, (p).attr, (p).cnt, (p).size, \
flt_otel_list_dump(&((p).list)))
#define FLT_OTEL_DBG_SCOPE_DATA_STATUS(h,p) \
OTELC_DBG(DEBUG, h "%p:{ %d '%s' }", (p), (p)->code, OTELC_STR_ARG((p)->description))
#define FLT_OTEL_DBG_SCOPE_DATA_KV_FMT "%p:{ %p %zu %zu }"
#define FLT_OTEL_DBG_SCOPE_DATA_KV_ARGS(p) &(p), (p).attr, (p).cnt, (p).size
#define FLT_OTEL_DBG_SCOPE_DATA(h,p) \
OTELC_DBG(DEBUG, h "%p:{ " FLT_OTEL_DBG_SCOPE_DATA_KV_FMT " " FLT_OTEL_DBG_SCOPE_DATA_KV_FMT " %s }", (p), \
FLT_OTEL_DBG_SCOPE_DATA_KV_ARGS((p)->baggage), FLT_OTEL_DBG_SCOPE_DATA_KV_ARGS((p)->attributes), \
flt_otel_list_dump(&((p)->events)))
#define FLT_OTEL_DBG_RUNTIME_CONTEXT(h,p) \
OTELC_DBG(DEBUG, h "%p:{ %p %p '%s' %hhu %hhu 0x%02hhx 0x%08x %u %d %s %s }", (p), \
(p)->stream, (p)->filter, (p)->uuid, (p)->flag_harderr, (p)->flag_disabled, \
(p)->logging, (p)->analyzers, (p)->idle_timeout, (p)->idle_exp, \
flt_otel_list_dump(&((p)->spans)), flt_otel_list_dump(&((p)->contexts)))
/* Anonymous struct containing a const string pointer and its length. */
#define FLT_OTEL_CONST_STR_HDR(p) \
struct { \
const char *p; \
size_t p##_len; \
}
/* Growable key-value array for span attributes or baggage. */
struct flt_otel_scope_data_kv {
struct otelc_kv *attr; /* Key-value array for storing attributes. */
size_t cnt; /* Number of currently used array elements. */
size_t size; /* Total number of array elements. */
};
/* Named event with its own key-value attribute array. */
struct flt_otel_scope_data_event {
char *name; /* Event name, not used for other data types. */
struct otelc_kv *attr; /* Key-value array for storing attributes. */
size_t cnt; /* Number of currently used array elements. */
size_t size; /* Total number of array elements. */
struct list list; /* Used to chain this structure. */
};
struct flt_otel_scope_data_status {
int code; /* OTELC_SPAN_STATUS_* value. */
char *description; /* Span status description string. */
};
struct flt_otel_scope_data {
struct flt_otel_scope_data_kv baggage; /* Defined scope baggage. */
struct flt_otel_scope_data_kv attributes; /* Defined scope attributes. */
struct list events; /* Defined scope events. */
struct flt_otel_scope_data_status status; /* Defined scope status. */
};
/* flt_otel_runtime_context->spans */
struct flt_otel_scope_span {
FLT_OTEL_CONST_STR_HDR(id); /* The span operation name/len. */
uint smp_opt_dir; /* SMP_OPT_DIR_RE(Q|S) */
bool flag_finish; /* Whether the span is marked for completion. */
struct otelc_span *span; /* The current span. */
struct otelc_span *ref_span; /* Span to which the current span refers. */
struct otelc_span_context *ref_ctx; /* Span context to which the current span refers. */
struct list list; /* Used to chain this structure. */
};
/* flt_otel_runtime_context->contexts */
struct flt_otel_scope_context {
FLT_OTEL_CONST_STR_HDR(id); /* The span context name/len. */
uint smp_opt_dir; /* SMP_OPT_DIR_RE(Q|S) */
bool flag_finish; /* Whether the span context is marked for completion. */
struct otelc_span_context *context; /* The current span context. */
struct list list; /* Used to chain this structure. */
};
/* The runtime filter context attached to a stream. */
struct flt_otel_runtime_context {
struct stream *stream; /* The stream to which the filter is attached. */
struct filter *filter; /* The OpenTelemetry filter. */
char uuid[40]; /* Randomly generated UUID. */
bool flag_harderr; /* [0 1] */
bool flag_disabled; /* [0 1] */
uint8_t logging; /* [0 1 3] */
uint analyzers; /* Executed channel analyzers. */
uint idle_timeout; /* Idle timeout interval in milliseconds (0 = off). */
int idle_exp; /* Tick at which the next idle timeout fires. */
struct list spans; /* The scope spans. */
struct list contexts; /* The scope contexts. */
};
#ifndef DEBUG_OTEL
# define flt_otel_scope_data_dump(...) while (0)
#else
/* Dump scope data contents for debugging. */
void flt_otel_scope_data_dump(const struct flt_otel_scope_data *data);
#endif
/* Allocate and initialize a runtime context for a stream. */
struct flt_otel_runtime_context *flt_otel_runtime_context_init(struct stream *s, struct filter *f, char **err);
/* Free the runtime context attached to a filter. */
void flt_otel_runtime_context_free(struct filter *f);
/* Allocate and initialize a scope span in the runtime context. */
struct flt_otel_scope_span *flt_otel_scope_span_init(struct flt_otel_runtime_context *rt_ctx, const char *id, size_t id_len, const char *ref_id, size_t ref_id_len, uint dir, char **err);
/* Free a scope span and remove it from the runtime context. */
void flt_otel_scope_span_free(struct flt_otel_scope_span **ptr);
/* Allocate and initialize a scope context in the runtime context. */
struct flt_otel_scope_context *flt_otel_scope_context_init(struct flt_otel_runtime_context *rt_ctx, struct otelc_tracer *tracer, const char *id, size_t id_len, const struct otelc_text_map *text_map, uint dir, char **err);
/* Free a scope context and remove it from the runtime context. */
void flt_otel_scope_context_free(struct flt_otel_scope_context **ptr);
/* Initialize scope data arrays and lists. */
void flt_otel_scope_data_init(struct flt_otel_scope_data *ptr);
/* Free all scope data contents. */
void flt_otel_scope_data_free(struct flt_otel_scope_data *ptr);
/* Mark a span for finishing by name in the runtime context. */
int flt_otel_scope_finish_mark(const struct flt_otel_runtime_context *rt_ctx, const char *id, size_t id_len);
/* End all spans that have been marked for finishing. */
void flt_otel_scope_finish_marked(const struct flt_otel_runtime_context *rt_ctx, const struct timespec *ts_finish);
/* Free scope spans and contexts no longer needed by a channel. */
void flt_otel_scope_free_unused(struct flt_otel_runtime_context *rt_ctx, struct channel *chn);
#endif /* _OTEL_SCOPE_H_ */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*
* vi: noexpandtab shiftwidth=8 tabstop=8
*/