mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-04-23 15:21:02 +02:00
Added span link support, allowing a span to reference other spans or
extracted contexts without establishing a parent relationship.
Introduced the flt_otel_conf_link structure and added a links list to
flt_otel_conf_span. The parser accepted both an inline syntax on the span
declaration line ("span <name> link <target>") and a standalone multi-
argument form ("link <span> ..."), each creating a conf_link entry
appended to the span's link list.
At runtime, each configured link name was resolved against the active
spans and extracted contexts in the runtime context. Resolved references
were collected into flt_otel_scope_data_link entries and passed to the C
wrapper add_link API during span creation.
Initialization, cleanup, and debug dump routines were added for the link
data structures at both configuration and runtime levels.
746 lines
21 KiB
C
746 lines
21 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "../include/include.h"
|
|
|
|
|
|
/***
|
|
* NAME
|
|
* flt_otel_runtime_context_init - per-stream runtime context allocation
|
|
*
|
|
* SYNOPSIS
|
|
* struct flt_otel_runtime_context *flt_otel_runtime_context_init(struct stream *s, struct filter *f, char **err)
|
|
*
|
|
* ARGUMENTS
|
|
* s - the stream to which the context belongs
|
|
* f - the filter instance
|
|
* err - indirect pointer to error message string
|
|
*
|
|
* DESCRIPTION
|
|
* Allocates and initializes a per-stream runtime context from pool memory.
|
|
* It copies the hard-error, disabled and logging flags from the filter
|
|
* configuration, generates a UUID and stores it in the sess.otel.uuid
|
|
* HAProxy variable.
|
|
*
|
|
* RETURN VALUE
|
|
* Returns a pointer to the new runtime context, or NULL on failure.
|
|
*/
|
|
struct flt_otel_runtime_context *flt_otel_runtime_context_init(struct stream *s, struct filter *f, char **err)
|
|
{
|
|
const struct flt_otel_conf *conf = FLT_OTEL_CONF(f);
|
|
struct buffer uuid;
|
|
struct flt_otel_runtime_context *retptr = NULL;
|
|
|
|
OTELC_FUNC("%p, %p, %p:%p", s, f, OTELC_DPTR_ARGS(err));
|
|
|
|
retptr = flt_otel_pool_alloc(pool_head_otel_runtime_context, sizeof(*retptr), 1, err);
|
|
if (retptr == NULL)
|
|
OTELC_RETURN_PTR(retptr);
|
|
|
|
/* Initialize runtime context fields and generate a session UUID. */
|
|
retptr->stream = s;
|
|
retptr->filter = f;
|
|
retptr->flag_harderr = _HA_ATOMIC_LOAD(&(conf->instr->flag_harderr));
|
|
retptr->flag_disabled = _HA_ATOMIC_LOAD(&(conf->instr->flag_disabled));
|
|
retptr->logging = _HA_ATOMIC_LOAD(&(conf->instr->logging));
|
|
retptr->idle_timeout = 0;
|
|
retptr->idle_exp = TICK_ETERNITY;
|
|
LIST_INIT(&(retptr->spans));
|
|
LIST_INIT(&(retptr->contexts));
|
|
|
|
uuid = b_make(retptr->uuid, sizeof(retptr->uuid), 0, 0);
|
|
ha_generate_uuid_v4(&uuid);
|
|
|
|
#ifdef USE_OTEL_VARS
|
|
/*
|
|
* The HAProxy variable 'sess.otel.uuid' is registered here,
|
|
* after which its value is set to runtime context UUID.
|
|
*/
|
|
if (flt_otel_var_register(FLT_OTEL_VAR_UUID, err) != -1)
|
|
(void)flt_otel_var_set(s, FLT_OTEL_VAR_UUID, retptr->uuid, SMP_OPT_DIR_REQ, err);
|
|
#endif
|
|
|
|
FLT_OTEL_DBG_RUNTIME_CONTEXT("session context: ", retptr);
|
|
|
|
OTELC_RETURN_PTR(retptr);
|
|
}
|
|
|
|
|
|
/***
|
|
* NAME
|
|
* flt_otel_runtime_context_free - per-stream runtime context cleanup
|
|
*
|
|
* SYNOPSIS
|
|
* void flt_otel_runtime_context_free(struct filter *f)
|
|
*
|
|
* ARGUMENTS
|
|
* f - the filter instance whose runtime context is to be freed
|
|
*
|
|
* DESCRIPTION
|
|
* Frees the per-stream runtime context attached to <f>. It ends all active
|
|
* spans with the current monotonic timestamp, destroys all extracted
|
|
* contexts, and returns the pool memory.
|
|
*
|
|
* RETURN VALUE
|
|
* This function does not return a value.
|
|
*/
|
|
void flt_otel_runtime_context_free(struct filter *f)
|
|
{
|
|
struct flt_otel_runtime_context *rt_ctx = f->ctx;
|
|
|
|
OTELC_FUNC("%p", f);
|
|
|
|
if (rt_ctx == NULL)
|
|
OTELC_RETURN();
|
|
|
|
FLT_OTEL_DBG_RUNTIME_CONTEXT("session context: ", rt_ctx);
|
|
|
|
/* End all active spans with a common timestamp. */
|
|
if (!LIST_ISEMPTY(&(rt_ctx->spans))) {
|
|
struct timespec ts_steady;
|
|
struct flt_otel_scope_span *span, *span_back;
|
|
|
|
/* All spans should be completed at the same time. */
|
|
(void)clock_gettime(CLOCK_MONOTONIC, &ts_steady);
|
|
|
|
list_for_each_entry_safe(span, span_back, &(rt_ctx->spans), list) {
|
|
OTELC_OPSR(span->span, end_with_options, &ts_steady, OTELC_SPAN_STATUS_IGNORE, NULL);
|
|
flt_otel_scope_span_free(&span);
|
|
}
|
|
}
|
|
|
|
/* Destroy all extracted span contexts. */
|
|
if (!LIST_ISEMPTY(&(rt_ctx->contexts))) {
|
|
struct flt_otel_scope_context *ctx, *ctx_back;
|
|
|
|
list_for_each_entry_safe(ctx, ctx_back, &(rt_ctx->contexts), list)
|
|
flt_otel_scope_context_free(&ctx);
|
|
}
|
|
|
|
flt_otel_pool_free(pool_head_otel_runtime_context, &(f->ctx));
|
|
|
|
OTELC_RETURN();
|
|
}
|
|
|
|
|
|
/***
|
|
* NAME
|
|
* flt_otel_scope_span_init - scope span lookup or creation
|
|
*
|
|
* SYNOPSIS
|
|
* 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)
|
|
*
|
|
* ARGUMENTS
|
|
* rt_ctx - the runtime context owning the span list
|
|
* id - the span operation name
|
|
* id_len - length of the <id> string
|
|
* ref_id - the parent span or context name, or NULL
|
|
* ref_id_len - length of the <ref_id> string
|
|
* dir - the sample fetch direction (SMP_OPT_DIR_REQ/RES)
|
|
* err - indirect pointer to error message string
|
|
*
|
|
* DESCRIPTION
|
|
* Finds an existing scope span by <id> in the runtime context or creates a
|
|
* new one. If <ref_id> is set, it resolves the parent reference by searching
|
|
* the span list first, then the extracted context list.
|
|
*
|
|
* RETURN VALUE
|
|
* Returns the existing or new scope span, or NULL on failure.
|
|
*/
|
|
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)
|
|
{
|
|
struct otelc_span *ref_span = NULL;
|
|
struct otelc_span_context *ref_ctx = NULL;
|
|
struct flt_otel_scope_span *span, *retptr = NULL;
|
|
struct flt_otel_scope_context *ctx;
|
|
|
|
OTELC_FUNC("%p, \"%s\", %zu, \"%s\", %zu, %u, %p:%p", rt_ctx, OTELC_STR_ARG(id), id_len, OTELC_STR_ARG(ref_id), ref_id_len, dir, OTELC_DPTR_ARGS(err));
|
|
|
|
if ((rt_ctx == NULL) || (id == NULL))
|
|
OTELC_RETURN_PTR(retptr);
|
|
|
|
/* Return the existing span if one matches this ID. */
|
|
list_for_each_entry(span, &(rt_ctx->spans), list)
|
|
if (FLT_OTEL_CONF_STR_CMP(span->id, id)) {
|
|
OTELC_DBG(NOTICE, "found span %p", span);
|
|
|
|
OTELC_RETURN_PTR(span);
|
|
}
|
|
|
|
/* Resolve the parent reference from spans or contexts. */
|
|
if (ref_id != NULL) {
|
|
list_for_each_entry(span, &(rt_ctx->spans), list)
|
|
if (FLT_OTEL_CONF_STR_CMP(span->id, ref_id)) {
|
|
ref_span = span->span;
|
|
|
|
break;
|
|
}
|
|
|
|
if (ref_span != NULL) {
|
|
OTELC_DBG(NOTICE, "found referenced span %p", span);
|
|
} else {
|
|
list_for_each_entry(ctx, &(rt_ctx->contexts), list)
|
|
if (FLT_OTEL_CONF_STR_CMP(ctx->id, ref_id)) {
|
|
ref_ctx = ctx->context;
|
|
|
|
break;
|
|
}
|
|
|
|
if (ref_ctx != NULL) {
|
|
OTELC_DBG(NOTICE, "found referenced context %p", ctx);
|
|
} else {
|
|
FLT_OTEL_ERR("cannot find referenced span/context '%s'", ref_id);
|
|
|
|
OTELC_RETURN_PTR(retptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
retptr = flt_otel_pool_alloc(pool_head_otel_scope_span, sizeof(*retptr), 1, err);
|
|
if (retptr == NULL)
|
|
OTELC_RETURN_PTR(retptr);
|
|
|
|
/* Populate the new scope span and insert it into the list. */
|
|
retptr->id = id;
|
|
retptr->id_len = id_len;
|
|
retptr->smp_opt_dir = dir;
|
|
retptr->ref_span = ref_span;
|
|
retptr->ref_ctx = ref_ctx;
|
|
LIST_INSERT(&(rt_ctx->spans), &(retptr->list));
|
|
|
|
FLT_OTEL_DBG_SCOPE_SPAN("new span ", retptr);
|
|
|
|
OTELC_RETURN_PTR(retptr);
|
|
}
|
|
|
|
|
|
/***
|
|
* NAME
|
|
* flt_otel_scope_span_free - scope span cleanup
|
|
*
|
|
* SYNOPSIS
|
|
* void flt_otel_scope_span_free(struct flt_otel_scope_span **ptr)
|
|
*
|
|
* ARGUMENTS
|
|
* ptr - pointer to the scope span pointer to free
|
|
*
|
|
* DESCRIPTION
|
|
* Frees a scope span entry pointed to by <ptr> and removes it from its list.
|
|
* If the OTel span is still active (non-NULL), the function refuses to free
|
|
* it and returns immediately.
|
|
*
|
|
* RETURN VALUE
|
|
* This function does not return a value.
|
|
*/
|
|
void flt_otel_scope_span_free(struct flt_otel_scope_span **ptr)
|
|
{
|
|
OTELC_FUNC("%p:%p", OTELC_DPTR_ARGS(ptr));
|
|
|
|
if ((ptr == NULL) || (*ptr == NULL))
|
|
OTELC_RETURN();
|
|
|
|
FLT_OTEL_DBG_SCOPE_SPAN("", *ptr);
|
|
|
|
/* If the span is still active, do nothing. */
|
|
if ((*ptr)->span != NULL) {
|
|
OTELC_DBG(NOTICE, "cannot finish active span");
|
|
|
|
OTELC_RETURN();
|
|
}
|
|
|
|
FLT_OTEL_LIST_DEL(&((*ptr)->list));
|
|
flt_otel_pool_free(pool_head_otel_scope_span, (void **)ptr);
|
|
|
|
OTELC_RETURN();
|
|
}
|
|
|
|
|
|
/***
|
|
* NAME
|
|
* flt_otel_scope_context_init - scope context extraction
|
|
*
|
|
* SYNOPSIS
|
|
* 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)
|
|
*
|
|
* ARGUMENTS
|
|
* rt_ctx - the runtime context owning the context list
|
|
* tracer - the OTel tracer used for context extraction
|
|
* id - the context name
|
|
* id_len - length of the <id> string
|
|
* text_map - the carrier text map to extract from
|
|
* dir - the sample fetch direction (SMP_OPT_DIR_REQ/RES)
|
|
* err - indirect pointer to error message string
|
|
*
|
|
* DESCRIPTION
|
|
* Finds an existing scope context by <id> in the runtime context or creates
|
|
* a new one by extracting the span context from the <text_map> carrier via
|
|
* the <tracer>.
|
|
*
|
|
* RETURN VALUE
|
|
* Returns the existing or new scope context, or NULL on failure.
|
|
*/
|
|
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)
|
|
{
|
|
struct otelc_http_headers_reader reader;
|
|
struct otelc_span_context *span_ctx;
|
|
struct flt_otel_scope_context *retptr = NULL;
|
|
|
|
OTELC_FUNC("%p, %p, \"%s\", %zu, %p, %u, %p:%p", rt_ctx, tracer, OTELC_STR_ARG(id), id_len, text_map, dir, OTELC_DPTR_ARGS(err));
|
|
|
|
if ((rt_ctx == NULL) || (tracer == NULL) || (id == NULL) || (text_map == NULL))
|
|
OTELC_RETURN_PTR(retptr);
|
|
|
|
/* Return the existing context if one matches this ID. */
|
|
list_for_each_entry(retptr, &(rt_ctx->contexts), list)
|
|
if (FLT_OTEL_CONF_STR_CMP(retptr->id, id)) {
|
|
OTELC_DBG(NOTICE, "found context %p", retptr);
|
|
|
|
OTELC_RETURN_PTR(retptr);
|
|
}
|
|
|
|
retptr = flt_otel_pool_alloc(pool_head_otel_scope_context, sizeof(*retptr), 1, err);
|
|
if (retptr == NULL)
|
|
OTELC_RETURN_PTR(retptr);
|
|
|
|
span_ctx = flt_otel_extract_http_headers(tracer, &reader, text_map);
|
|
if (span_ctx == NULL) {
|
|
flt_otel_scope_context_free(&retptr);
|
|
|
|
OTELC_RETURN_PTR(retptr);
|
|
}
|
|
|
|
/* Populate the new scope context and insert it into the list. */
|
|
retptr->id = id;
|
|
retptr->id_len = id_len;
|
|
retptr->smp_opt_dir = dir;
|
|
retptr->context = span_ctx;
|
|
LIST_INSERT(&(rt_ctx->contexts), &(retptr->list));
|
|
|
|
FLT_OTEL_DBG_SCOPE_CONTEXT("new context ", retptr);
|
|
|
|
OTELC_RETURN_PTR(retptr);
|
|
}
|
|
|
|
|
|
/***
|
|
* NAME
|
|
* flt_otel_scope_context_free - scope context cleanup
|
|
*
|
|
* SYNOPSIS
|
|
* void flt_otel_scope_context_free(struct flt_otel_scope_context **ptr)
|
|
*
|
|
* ARGUMENTS
|
|
* ptr - pointer to the scope context pointer to free
|
|
*
|
|
* DESCRIPTION
|
|
* Frees a scope context entry pointed to by <ptr>. It destroys the
|
|
* underlying OTel span context, removes the entry from its list, and
|
|
* returns the pool memory.
|
|
*
|
|
* RETURN VALUE
|
|
* This function does not return a value.
|
|
*/
|
|
void flt_otel_scope_context_free(struct flt_otel_scope_context **ptr)
|
|
{
|
|
OTELC_FUNC("%p:%p", OTELC_DPTR_ARGS(ptr));
|
|
|
|
if ((ptr == NULL) || (*ptr == NULL))
|
|
OTELC_RETURN();
|
|
|
|
FLT_OTEL_DBG_SCOPE_CONTEXT("", *ptr);
|
|
|
|
if ((*ptr)->context != NULL)
|
|
OTELC_OPSR((*ptr)->context, destroy);
|
|
|
|
FLT_OTEL_LIST_DEL(&((*ptr)->list));
|
|
flt_otel_pool_free(pool_head_otel_scope_context, (void **)ptr);
|
|
|
|
OTELC_RETURN();
|
|
}
|
|
|
|
|
|
#ifdef DEBUG_OTEL
|
|
|
|
/***
|
|
* NAME
|
|
* flt_otel_scope_data_dump - debug scope data dump
|
|
*
|
|
* SYNOPSIS
|
|
* void flt_otel_scope_data_dump(const struct flt_otel_scope_data *data)
|
|
*
|
|
* ARGUMENTS
|
|
* data - the scope data structure to dump
|
|
*
|
|
* DESCRIPTION
|
|
* Dumps the contents of a scope <data> structure for debugging: baggage
|
|
* key-value pairs, attributes, events with their attributes, span links,
|
|
* and the status code/description.
|
|
*
|
|
* RETURN VALUE
|
|
* This function does not return a value.
|
|
*/
|
|
void flt_otel_scope_data_dump(const struct flt_otel_scope_data *data)
|
|
{
|
|
size_t i;
|
|
|
|
if (data == NULL)
|
|
return;
|
|
|
|
if (data->baggage.attr == NULL) {
|
|
OTELC_DBG(WORKER, "baggage %p:{ }", &(data->baggage));
|
|
} else {
|
|
OTELC_DBG(WORKER, "baggage %p:{", &(data->baggage));
|
|
for (i = 0; i < data->baggage.cnt; i++)
|
|
OTELC_DBG_KV(WORKER, " ", data->baggage.attr + i);
|
|
OTELC_DBG(WORKER, "}");
|
|
}
|
|
|
|
if (data->attributes.attr == NULL) {
|
|
OTELC_DBG(WORKER, "attributes %p:{ }", &(data->attributes));
|
|
} else {
|
|
OTELC_DBG(WORKER, "attributes %p:{", &(data->attributes));
|
|
for (i = 0; i < data->attributes.cnt; i++)
|
|
OTELC_DBG_KV(WORKER, " ", data->attributes.attr + i);
|
|
OTELC_DBG(WORKER, "}");
|
|
}
|
|
|
|
if (LIST_ISEMPTY(&(data->events))) {
|
|
OTELC_DBG(WORKER, "events %p:{ }", &(data->events));
|
|
} else {
|
|
struct flt_otel_scope_data_event *event;
|
|
|
|
OTELC_DBG(WORKER, "events %p:{", &(data->events));
|
|
list_for_each_entry_rev(event, &(data->events), list) {
|
|
OTELC_DBG(WORKER, " '%s' %zu/%zu", event->name, event->cnt, event->size);
|
|
if (event->attr != NULL)
|
|
for (i = 0; i < event->cnt; i++)
|
|
OTELC_DBG_KV(WORKER, " ", event->attr + i);
|
|
}
|
|
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
|
|
FLT_OTEL_DBG_SCOPE_DATA_STATUS("status ", &(data->status));
|
|
}
|
|
|
|
#endif /* DEBUG_OTEL */
|
|
|
|
|
|
/***
|
|
* NAME
|
|
* flt_otel_scope_data_init - scope data zero-initialization
|
|
*
|
|
* SYNOPSIS
|
|
* void flt_otel_scope_data_init(struct flt_otel_scope_data *ptr)
|
|
*
|
|
* ARGUMENTS
|
|
* ptr - the scope data structure to initialize
|
|
*
|
|
* DESCRIPTION
|
|
* Zero-initializes the scope data structure pointed to by <ptr> and sets up
|
|
* the event and link list heads.
|
|
*
|
|
* RETURN VALUE
|
|
* This function does not return a value.
|
|
*/
|
|
void flt_otel_scope_data_init(struct flt_otel_scope_data *ptr)
|
|
{
|
|
OTELC_FUNC("%p", ptr);
|
|
|
|
if (ptr == NULL)
|
|
OTELC_RETURN();
|
|
|
|
(void)memset(ptr, 0, sizeof(*ptr));
|
|
LIST_INIT(&(ptr->events));
|
|
LIST_INIT(&(ptr->links));
|
|
|
|
OTELC_RETURN();
|
|
}
|
|
|
|
|
|
/***
|
|
* NAME
|
|
* flt_otel_scope_data_free - scope data cleanup
|
|
*
|
|
* SYNOPSIS
|
|
* void flt_otel_scope_data_free(struct flt_otel_scope_data *ptr)
|
|
*
|
|
* ARGUMENTS
|
|
* ptr - the scope data structure to free
|
|
*
|
|
* DESCRIPTION
|
|
* Frees all contents of the scope data structure pointed to by <ptr>: baggage
|
|
* and attribute key-value arrays, event entries with their attributes, link
|
|
* entries, and the status description string.
|
|
*
|
|
* RETURN VALUE
|
|
* This function does not return a value.
|
|
*/
|
|
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);
|
|
|
|
if (ptr == NULL)
|
|
OTELC_RETURN();
|
|
|
|
FLT_OTEL_DBG_SCOPE_DATA("", ptr);
|
|
|
|
/* Destroy all dynamic scope data contents. */
|
|
otelc_kv_destroy(&(ptr->baggage.attr), ptr->baggage.cnt);
|
|
otelc_kv_destroy(&(ptr->attributes.attr), ptr->attributes.cnt);
|
|
list_for_each_entry_safe(event, event_back, &(ptr->events), list) {
|
|
otelc_kv_destroy(&(event->attr), event->cnt);
|
|
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));
|
|
|
|
OTELC_RETURN();
|
|
}
|
|
|
|
|
|
/***
|
|
* NAME
|
|
* flt_otel_scope_finish_mark - mark spans and contexts for finishing
|
|
*
|
|
* SYNOPSIS
|
|
* int flt_otel_scope_finish_mark(const struct flt_otel_runtime_context *rt_ctx, const char *id, size_t id_len)
|
|
*
|
|
* ARGUMENTS
|
|
* rt_ctx - the runtime context containing spans and contexts
|
|
* id - the target name, or a wildcard ("*", "*req*", "*res*")
|
|
* id_len - length of the <id> string
|
|
*
|
|
* DESCRIPTION
|
|
* Marks spans and contexts for finishing. The <id> argument supports
|
|
* wildcards: "*" marks all spans and contexts, "*req*" marks the request
|
|
* channel only, "*res*" marks the response channel only. Otherwise, a named
|
|
* span or context is looked up by exact match.
|
|
*
|
|
* RETURN VALUE
|
|
* Returns the number of spans and contexts that were marked.
|
|
*/
|
|
int flt_otel_scope_finish_mark(const struct flt_otel_runtime_context *rt_ctx, const char *id, size_t id_len)
|
|
{
|
|
struct flt_otel_scope_span *span;
|
|
struct flt_otel_scope_context *ctx;
|
|
int span_cnt = 0, ctx_cnt = 0, retval;
|
|
|
|
OTELC_FUNC("%p, \"%s\", %zu", rt_ctx, OTELC_STR_ARG(id), id_len);
|
|
|
|
/* Handle wildcard finish marks: all, request-only, response-only. */
|
|
if (FLT_OTEL_STR_CMP(FLT_OTEL_SCOPE_SPAN_FINISH_ALL, id)) {
|
|
list_for_each_entry(span, &(rt_ctx->spans), list) {
|
|
span->flag_finish = 1;
|
|
span_cnt++;
|
|
}
|
|
|
|
list_for_each_entry(ctx, &(rt_ctx->contexts), list) {
|
|
ctx->flag_finish = 1;
|
|
ctx_cnt++;
|
|
}
|
|
|
|
OTELC_DBG(NOTICE, "marked %d span(s), %d context(s)", span_cnt, ctx_cnt);
|
|
}
|
|
else if (FLT_OTEL_STR_CMP(FLT_OTEL_SCOPE_SPAN_FINISH_REQ, id)) {
|
|
list_for_each_entry(span, &(rt_ctx->spans), list)
|
|
if (span->smp_opt_dir == SMP_OPT_DIR_REQ) {
|
|
span->flag_finish = 1;
|
|
span_cnt++;
|
|
}
|
|
|
|
list_for_each_entry(ctx, &(rt_ctx->contexts), list)
|
|
if (ctx->smp_opt_dir == SMP_OPT_DIR_REQ) {
|
|
ctx->flag_finish = 1;
|
|
ctx_cnt++;
|
|
}
|
|
|
|
OTELC_DBG(NOTICE, "marked REQuest channel %d span(s), %d context(s)", span_cnt, ctx_cnt);
|
|
}
|
|
else if (FLT_OTEL_STR_CMP(FLT_OTEL_SCOPE_SPAN_FINISH_RES, id)) {
|
|
list_for_each_entry(span, &(rt_ctx->spans), list)
|
|
if (span->smp_opt_dir == SMP_OPT_DIR_RES) {
|
|
span->flag_finish = 1;
|
|
span_cnt++;
|
|
}
|
|
|
|
list_for_each_entry(ctx, &(rt_ctx->contexts), list)
|
|
if (ctx->smp_opt_dir == SMP_OPT_DIR_RES) {
|
|
ctx->flag_finish = 1;
|
|
ctx_cnt++;
|
|
}
|
|
|
|
OTELC_DBG(NOTICE, "marked RESponse channel %d span(s), %d context(s)", span_cnt, ctx_cnt);
|
|
}
|
|
else {
|
|
list_for_each_entry(span, &(rt_ctx->spans), list)
|
|
if (FLT_OTEL_CONF_STR_CMP(span->id, id)) {
|
|
span->flag_finish = 1;
|
|
span_cnt++;
|
|
|
|
break;
|
|
}
|
|
|
|
list_for_each_entry(ctx, &(rt_ctx->contexts), list)
|
|
if (FLT_OTEL_CONF_STR_CMP(ctx->id, id)) {
|
|
ctx->flag_finish = 1;
|
|
ctx_cnt++;
|
|
|
|
break;
|
|
}
|
|
|
|
if (span_cnt > 0)
|
|
OTELC_DBG(NOTICE, "marked span '%s'", id);
|
|
if (ctx_cnt > 0)
|
|
OTELC_DBG(NOTICE, "marked context '%s'", id);
|
|
if ((span_cnt + ctx_cnt) == 0)
|
|
OTELC_DBG(NOTICE, "cannot find span/context '%s'", id);
|
|
}
|
|
|
|
retval = span_cnt + ctx_cnt;
|
|
|
|
OTELC_RETURN_INT(retval);
|
|
}
|
|
|
|
|
|
/***
|
|
* NAME
|
|
* flt_otel_scope_finish_marked - finish marked spans and contexts
|
|
*
|
|
* SYNOPSIS
|
|
* void flt_otel_scope_finish_marked(const struct flt_otel_runtime_context *rt_ctx, const struct timespec *ts_finish)
|
|
*
|
|
* ARGUMENTS
|
|
* rt_ctx - the runtime context containing spans and contexts
|
|
* ts_finish - the monotonic timestamp to use as the span end time
|
|
*
|
|
* DESCRIPTION
|
|
* Ends all spans and destroys all contexts that have been marked for
|
|
* finishing by flt_otel_scope_finish_mark(). Each span is ended with the
|
|
* <ts_finish> timestamp; each context's OTel span context is destroyed.
|
|
* The finish flags are cleared after processing.
|
|
*
|
|
* RETURN VALUE
|
|
* This function does not return a value.
|
|
*/
|
|
void flt_otel_scope_finish_marked(const struct flt_otel_runtime_context *rt_ctx, const struct timespec *ts_finish)
|
|
{
|
|
struct flt_otel_scope_span *span;
|
|
struct flt_otel_scope_context *ctx;
|
|
|
|
OTELC_FUNC("%p, %p", rt_ctx, ts_finish);
|
|
|
|
/* End all spans that have been marked for finishing. */
|
|
list_for_each_entry(span, &(rt_ctx->spans), list)
|
|
if (span->flag_finish) {
|
|
FLT_OTEL_DBG_SCOPE_SPAN("finishing span ", span);
|
|
|
|
OTELC_OPSR(span->span, end_with_options, ts_finish, OTELC_SPAN_STATUS_IGNORE, NULL);
|
|
|
|
span->flag_finish = 0;
|
|
}
|
|
|
|
/* Destroy all contexts that have been marked for finishing. */
|
|
list_for_each_entry(ctx, &(rt_ctx->contexts), list)
|
|
if (ctx->flag_finish) {
|
|
FLT_OTEL_DBG_SCOPE_CONTEXT("finishing context ", ctx);
|
|
|
|
if (ctx->context != NULL)
|
|
OTELC_OPSR(ctx->context, destroy);
|
|
|
|
ctx->flag_finish = 0;
|
|
}
|
|
|
|
OTELC_RETURN();
|
|
}
|
|
|
|
|
|
/***
|
|
* NAME
|
|
* flt_otel_scope_free_unused - remove unused spans and contexts
|
|
*
|
|
* SYNOPSIS
|
|
* void flt_otel_scope_free_unused(struct flt_otel_runtime_context *rt_ctx, struct channel *chn)
|
|
*
|
|
* ARGUMENTS
|
|
* rt_ctx - the runtime context to clean up
|
|
* chn - the channel for HTTP header cleanup
|
|
*
|
|
* DESCRIPTION
|
|
* Removes scope spans with a NULL OTel span and scope contexts with a NULL
|
|
* OTel context from the runtime context. For each removed context, the
|
|
* associated HTTP headers and HAProxy variables are also cleaned up via
|
|
* <chn>.
|
|
*
|
|
* RETURN VALUE
|
|
* This function does not return a value.
|
|
*/
|
|
void flt_otel_scope_free_unused(struct flt_otel_runtime_context *rt_ctx, struct channel *chn)
|
|
{
|
|
OTELC_FUNC("%p, %p", rt_ctx, chn);
|
|
|
|
if (rt_ctx == NULL)
|
|
OTELC_RETURN();
|
|
|
|
/* Remove spans that were never successfully created. */
|
|
if (!LIST_ISEMPTY(&(rt_ctx->spans))) {
|
|
struct flt_otel_scope_span *span, *span_back;
|
|
|
|
list_for_each_entry_safe(span, span_back, &(rt_ctx->spans), list)
|
|
if (span->span == NULL)
|
|
flt_otel_scope_span_free(&span);
|
|
}
|
|
|
|
/* Remove contexts that failed extraction and clean up their traces. */
|
|
if (!LIST_ISEMPTY(&(rt_ctx->contexts))) {
|
|
struct flt_otel_scope_context *ctx, *ctx_back;
|
|
|
|
list_for_each_entry_safe(ctx, ctx_back, &(rt_ctx->contexts), list)
|
|
if (ctx->context == NULL) {
|
|
/*
|
|
* All headers and variables associated with
|
|
* the context in question should be deleted.
|
|
*/
|
|
(void)flt_otel_http_headers_remove(chn, ctx->id, NULL);
|
|
#ifdef USE_OTEL_VARS
|
|
(void)flt_otel_vars_unset(rt_ctx->stream, FLT_OTEL_VARS_SCOPE, ctx->id, ctx->smp_opt_dir, NULL);
|
|
#endif
|
|
|
|
flt_otel_scope_context_free(&ctx);
|
|
}
|
|
}
|
|
|
|
FLT_OTEL_DBG_RUNTIME_CONTEXT("session context: ", rt_ctx);
|
|
|
|
OTELC_RETURN();
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* c-indent-level: 8
|
|
* c-basic-offset: 8
|
|
* End:
|
|
*
|
|
* vi: noexpandtab shiftwidth=8 tabstop=8
|
|
*/
|