#define FLT_OTEL_DBG_CONF_CONTEXT(h,p) \
OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "0x%02hhx }", (p), FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->flags)
-#define FLT_OTEL_DBG_CONF_SPAN(h,p) \
- OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "'%s' %zu %s' %zu %hhu 0x%02hhx %s %s %s %s }", \
- (p), FLT_OTEL_CONF_HDR_ARGS(p, id), FLT_OTEL_STR_HDR_ARGS(p, ref_id), \
- FLT_OTEL_STR_HDR_ARGS(p, ctx_id), (p)->flag_root, (p)->ctx_flags, \
- flt_otel_list_dump(&((p)->attributes)), flt_otel_list_dump(&((p)->events)), \
- flt_otel_list_dump(&((p)->baggages)), flt_otel_list_dump(&((p)->statuses)))
+#define FLT_OTEL_DBG_CONF_SPAN(h,p) \
+ OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "'%s' %zu %s' %zu %hhu 0x%02hhx %s %s %s %s %s }", \
+ (p), FLT_OTEL_CONF_HDR_ARGS(p, id), FLT_OTEL_STR_HDR_ARGS(p, ref_id), \
+ FLT_OTEL_STR_HDR_ARGS(p, ctx_id), (p)->flag_root, (p)->ctx_flags, \
+ flt_otel_list_dump(&((p)->links)), flt_otel_list_dump(&((p)->attributes)), \
+ flt_otel_list_dump(&((p)->events)), flt_otel_list_dump(&((p)->baggages)), \
+ 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 }", (p), \
uint8_t flags; /* The type of storage from which the span context is extracted. */
};
+/* flt_otel_conf_span->links */
+struct flt_otel_conf_link {
+ FLT_OTEL_CONF_HDR(span); /* The list containing link names. */
+};
+
/*
* Span configuration within a scope.
* flt_otel_conf_scope->spans
FLT_OTEL_CONF_STR(ctx_id); /* The span context name, if used. */
uint8_t ctx_flags; /* The type of storage used for the span context. */
bool flag_root; /* Whether this is a root span. */
+ struct list links; /* The set of linked span names. */
struct list attributes; /* The set of key:value attributes. */
struct list events; /* The set of events with key-value attributes. */
struct list baggages; /* The set of key:value baggage items. */
FLT_OTEL_CONF_FUNC_DECL(ph)
FLT_OTEL_CONF_FUNC_DECL(sample_expr)
FLT_OTEL_CONF_FUNC_DECL(sample)
+FLT_OTEL_CONF_FUNC_DECL(link)
FLT_OTEL_CONF_FUNC_DECL(context)
FLT_OTEL_CONF_FUNC_DECL(span)
FLT_OTEL_CONF_FUNC_DECL(scope)
#define FLT_OTEL_PARSE_SPAN_ROOT "root"
#define FLT_OTEL_PARSE_SPAN_PARENT "parent"
+#define FLT_OTEL_PARSE_SPAN_LINK "link"
#define FLT_OTEL_PARSE_CTX_AUTONAME "-"
#define FLT_OTEL_PARSE_CTX_IGNORE_NAME '-'
#define FLT_OTEL_PARSE_CTX_USE_HEADERS "use-headers"
*/
#define FLT_OTEL_PARSE_SCOPE_DEFINES \
FLT_OTEL_PARSE_SCOPE_DEF( ID, 0, CHAR, 2, 2, "otel-scope", " <name>") \
- FLT_OTEL_PARSE_SCOPE_DEF( SPAN, 0, NONE, 2, 7, "span", " <name> [<reference>] [root]") \
+ FLT_OTEL_PARSE_SCOPE_DEF( SPAN, 0, NONE, 2, 7, "span", " <name> [<reference>] [<link>] [root]") \
+ FLT_OTEL_PARSE_SCOPE_DEF( LINK, 1, NONE, 2, 0, "link", " <span> ...") \
FLT_OTEL_PARSE_SCOPE_DEF( ATTRIBUTE, 1, NONE, 3, 0, "attribute", " <key> <sample> ...") \
FLT_OTEL_PARSE_SCOPE_DEF( EVENT, 1, NONE, 4, 0, "event", " <name> <key> <sample> ...") \
FLT_OTEL_PARSE_SCOPE_DEF( BAGGAGE, 1, VAR, 3, 0, "baggage", " <key> <sample> ...") \
#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_SCOPE_DATA(h,p) \
+ OTELC_DBG(DEBUG, h "%p:{ " FLT_OTEL_DBG_SCOPE_DATA_KV_FMT " " FLT_OTEL_DBG_SCOPE_DATA_KV_FMT " %s %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)), flt_otel_list_dump(&((p)->links)))
#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), \
struct list list; /* Used to chain this structure. */
};
+/* Span link referencing another span or span context. */
+struct flt_otel_scope_data_link {
+ struct otelc_span *span; /* Linked span, or NULL. */
+ struct otelc_span_context *context; /* Linked span context, or NULL. */
+ struct list list; /* Used to chain this structure. */
+};
+
+/* Span status code and description. */
struct flt_otel_scope_data_status {
int code; /* OTELC_SPAN_STATUS_* value. */
char *description; /* Span status description string. */
};
+/* Aggregated runtime data collected during scope execution. */
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 list links; /* Defined scope links. */
struct flt_otel_scope_data_status status; /* Defined scope status. */
};
)
+/***
+ * NAME
+ * flt_otel_conf_link_init - conf_link structure allocation
+ *
+ * SYNOPSIS
+ * struct flt_otel_conf_link *flt_otel_conf_link_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_link structure for a span link
+ * reference. The <id> string is duplicated and stored as the linked
+ * span name. 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(link, span, )
+
+
+/***
+ * NAME
+ * flt_otel_conf_link_free - conf_link structure deallocation
+ *
+ * SYNOPSIS
+ * void flt_otel_conf_link_free(struct flt_otel_conf_link **ptr)
+ *
+ * ARGUMENTS
+ * ptr - a pointer to the address of a structure
+ *
+ * DESCRIPTION
+ * Deallocates memory used by the flt_otel_conf_link 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(link, span,
+ FLT_OTEL_DBG_CONF_HDR("- conf_link free ", *ptr, span);
+)
+
+
/***
* NAME
* flt_otel_conf_ph_init - conf_ph placeholder structure allocation
* Returns a pointer to the initialized structure, or NULL on failure.
*/
FLT_OTEL_CONF_FUNC_INIT(span, id,
+ LIST_INIT(&(retptr->links));
LIST_INIT(&(retptr->attributes));
LIST_INIT(&(retptr->events));
LIST_INIT(&(retptr->baggages));
OTELC_SFREE((*ptr)->ref_id);
OTELC_SFREE((*ptr)->ctx_id);
+ FLT_OTEL_LIST_DESTROY(link, &((*ptr)->links));
FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->attributes));
FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->events));
FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->baggages));
OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
}
+ /* Add all resolved span links to the current span. */
+ if (!LIST_ISEMPTY(&(data->links))) {
+ struct flt_otel_scope_data_link *link;
+
+ list_for_each_entry(link, &(data->links), list) {
+ OTELC_DBG(DEBUG, "adding link %p %p", link->span, link->context);
+
+ if (OTELC_OPS(span->span, add_link, link->span, link->context, NULL, 0) == -1)
+ retval = FLT_OTEL_RET_ERROR;
+ }
+ }
+
/* Set baggage key-value pairs on the span. */
if (data->baggage.attr != NULL)
if (OTELC_OPS(span->span, set_baggage_kv_n, data->baggage.attr, data->baggage.cnt) == -1)
if (span == NULL)
retval = FLT_OTEL_RET_ERROR;
+ /*
+ * Resolve configured span links against the runtime context.
+ * Each link name is looked up first in the active spans, then
+ * in the extracted contexts.
+ */
+ if (!LIST_ISEMPTY(&(conf_span->links))) {
+ struct flt_otel_runtime_context *rt_ctx = FLT_OTEL_RT_CTX(f->ctx);
+ struct flt_otel_conf_link *conf_link;
+
+ list_for_each_entry(conf_link, &(conf_span->links), list) {
+ struct flt_otel_scope_data_link *data_link;
+ struct otelc_span *link_span = NULL;
+ struct otelc_span_context *link_ctx = NULL;
+ struct flt_otel_scope_span *sc_span;
+ struct flt_otel_scope_context *sc_ctx;
+
+ /* Try to find a matching span first. */
+ list_for_each_entry(sc_span, &(rt_ctx->spans), list)
+ if (FLT_OTEL_CONF_STR_CMP(sc_span->id, conf_link->span)) {
+ link_span = sc_span->span;
+
+ break;
+ }
+
+ /* If no span found, try to find a matching context. */
+ if (link_span == NULL) {
+ list_for_each_entry(sc_ctx, &(rt_ctx->contexts), list)
+ if (FLT_OTEL_CONF_STR_CMP(sc_ctx->id, conf_link->span)) {
+ link_ctx = sc_ctx->context;
+
+ break;
+ }
+ }
+
+ if ((link_span == NULL) && (link_ctx == NULL)) {
+ OTELC_DBG(NOTICE, "WARNING: cannot find linked span/context '%s'", conf_link->span);
+
+ continue;
+ }
+
+ data_link = OTELC_CALLOC(1, sizeof(*data_link));
+ if (data_link == NULL) {
+ retval = FLT_OTEL_RET_ERROR;
+
+ break;
+ }
+
+ data_link->span = link_span;
+ data_link->context = link_ctx;
+ LIST_APPEND(&(data.links), &(data_link->list));
+
+ OTELC_DBG(DEBUG, "resolved link '%s' -> %p %p", conf_link->span, link_span, link_ctx);
+ }
+ }
+
list_for_each_entry(sample, &(conf_span->attributes), list) {
OTELC_DBG(DEBUG, "adding attribute '%s' -> '%s'", sample->key, sample->fmt_string);
else
FLT_OTEL_PARSE_ERR(&err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
}
+ else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_SPAN_LINK)) {
+ if (FLT_OTEL_ARG_ISVALID(i + 1)) {
+ if (flt_otel_conf_link_init(args[++i], line, &(flt_otel_current_span->links), &err) == NULL)
+ retval |= ERR_ABORT | ERR_ALERT;
+ } else {
+ FLT_OTEL_PARSE_ERR(&err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
+ }
+ }
else {
FLT_OTEL_PARSE_ERR(&err, "'%s' : invalid argument (use '%s%s')", args[i], pdata->name, pdata->usage);
}
OTELC_DBG(DEBUG, "new span '%s' without reference", flt_otel_current_span->id);
}
}
+ else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_LINK) {
+ for (i = 1; !(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(i); i++)
+ if (flt_otel_conf_link_init(args[i], line, &(flt_otel_current_span->links), &err) == NULL)
+ retval |= ERR_ABORT | ERR_ALERT;
+ }
else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_ATTRIBUTE) {
retval = flt_otel_parse_cfg_sample(file, line, args, 2, 0, NULL, &(flt_otel_current_span->attributes), &err);
}
OTELC_DBG(WORKER, "}");
}
+ if (LIST_ISEMPTY(&(data->links))) {
+ OTELC_DBG(WORKER, "links %p:{ }", &(data->links));
+ } else {
+ struct flt_otel_scope_data_link *link;
+
+ OTELC_DBG(WORKER, "links %p:{", &(data->links));
+ list_for_each_entry(link, &(data->links), list)
+ OTELC_DBG(WORKER, " %p %p", link->span, link->context);
+ OTELC_DBG(WORKER, "}");
+ }
+
if ((data->status.code == 0) && (data->status.description == NULL))
OTELC_DBG(WORKER, "status %p:{ }", &(data->status));
else
(void)memset(ptr, 0, sizeof(*ptr));
LIST_INIT(&(ptr->events));
+ LIST_INIT(&(ptr->links));
OTELC_RETURN();
}
void flt_otel_scope_data_free(struct flt_otel_scope_data *ptr)
{
struct flt_otel_scope_data_event *event, *event_back;
+ struct flt_otel_scope_data_link *link, *link_back;
OTELC_FUNC("%p", ptr);
OTELC_SFREE(event->name);
OTELC_SFREE(event);
}
+ /* Free all resolved link entries. */
+ list_for_each_entry_safe(link, link_back, &(ptr->links), list)
+ OTELC_SFREE(link);
OTELC_SFREE(ptr->status.description);
(void)memset(ptr, 0, sizeof(*ptr));
otel-event on-http-body-request
otel-scope frontend_http_request
- span "Frontend HTTP request" parent "HTTP body request"
+ span "Frontend HTTP request" parent "HTTP body request" link "HAProxy session"
attribute "http.method" method
attribute "http.url" url
attribute "http.version" str("HTTP/") req.ver
otel-scope server_session_start
span "Server session" parent "HAProxy session"
+ link "HAProxy session" "Client session"
finish "Process sticking rules request"
otel-event on-server-session-start