From ea9d05de02a214ff59ea5311a3349db21afe96ac Mon Sep 17 00:00:00 2001 From: Miroslav Zagorac Date: Sun, 12 Apr 2026 11:17:11 +0200 Subject: [PATCH] MEDIUM: otel: added context propagation via carrier interfaces Added the span context injection and extraction layer that bridges the OTel C wrapper's propagation API with HAProxy's HTTP headers and text map carriers. The new otelc.c module implements four public functions that wrap the OTel C wrapper's context propagation methods: flt_otel_inject_text_map() and flt_otel_inject_http_headers() serialize a span's context into a text map or HTTP headers carrier for outbound propagation, while flt_otel_extract_text_map() and flt_otel_extract_http_headers() deserialize an inbound carrier into an otelc_span_context for parent linking. Each direction uses a pair of callbacks registered on the carrier structure. The injection writers (flt_otel_text_map_writer_set_cb and flt_otel_http_headers_writer_set_cb) store key-value pairs emitted by the SDK into the carrier's text map via OTELC_TEXT_MAP_ADD(). The extraction readers (flt_otel_text_map_reader_foreach_key_cb and flt_otel_http_headers_reader_foreach_key_cb) iterate the carrier's text map entries and pass each pair to the SDK's handler callback. The scope context initialization in flt_otel_scope_context_init() now calls flt_otel_extract_http_headers() to extract the span context from the provided text map carrier and stores it in the scope context structure, making extracted contexts available for parent linking in subsequent span creation. --- addons/otel/Makefile | 1 + addons/otel/include/include.h | 1 + addons/otel/include/otelc.h | 27 ++++ addons/otel/src/otelc.c | 289 ++++++++++++++++++++++++++++++++++ addons/otel/src/scope.c | 12 +- 5 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 addons/otel/include/otelc.h create mode 100644 addons/otel/src/otelc.c diff --git a/addons/otel/Makefile b/addons/otel/Makefile index c9f919526..d1436f50c 100644 --- a/addons/otel/Makefile +++ b/addons/otel/Makefile @@ -54,6 +54,7 @@ OPTIONS_OBJS += \ addons/otel/src/conf.o \ addons/otel/src/event.o \ addons/otel/src/filter.o \ + addons/otel/src/otelc.o \ addons/otel/src/parser.o \ addons/otel/src/pool.o \ addons/otel/src/scope.o \ diff --git a/addons/otel/include/include.h b/addons/otel/include/include.h index 103166b4d..6902a4fd2 100644 --- a/addons/otel/include/include.h +++ b/addons/otel/include/include.h @@ -32,6 +32,7 @@ #include "conf.h" #include "conf_funcs.h" #include "filter.h" +#include "otelc.h" #include "parser.h" #include "pool.h" #include "scope.h" diff --git a/addons/otel/include/otelc.h b/addons/otel/include/otelc.h new file mode 100644 index 000000000..0c04f049c --- /dev/null +++ b/addons/otel/include/otelc.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef _OTEL_OTELC_H_ +#define _OTEL_OTELC_H_ + +/* Inject span context into a text map carrier. */ +int flt_otel_inject_text_map(const struct otelc_span *span, struct otelc_text_map_writer *carrier); + +/* Inject span context into an HTTP headers carrier. */ +int flt_otel_inject_http_headers(const struct otelc_span *span, struct otelc_http_headers_writer *carrier); + +/* Extract span context from a text map carrier. */ +struct otelc_span_context *flt_otel_extract_text_map(struct otelc_tracer *tracer, struct otelc_text_map_reader *carrier, const struct otelc_text_map *text_map); + +/* Extract span context from an HTTP headers carrier. */ +struct otelc_span_context *flt_otel_extract_http_headers(struct otelc_tracer *tracer, struct otelc_http_headers_reader *carrier, const struct otelc_text_map *text_map); + +#endif /* _OTEL_OTELC_H_ */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/addons/otel/src/otelc.c b/addons/otel/src/otelc.c new file mode 100644 index 000000000..69338002e --- /dev/null +++ b/addons/otel/src/otelc.c @@ -0,0 +1,289 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "../include/include.h" + + +/*** + * NAME + * flt_otel_text_map_writer_set_cb - text map injection writer callback + * + * SYNOPSIS + * static int flt_otel_text_map_writer_set_cb(struct otelc_text_map_writer *writer, const char *key, const char *value) + * + * ARGUMENTS + * writer - text map writer instance + * key - context key name + * value - context key value + * + * DESCRIPTION + * Writer callback for text map injection. Called by the OTel C wrapper + * library during span context injection to store each key-value pair in the + * 's text map. + * + * RETURN VALUE + * Returns the result of OTELC_TEXT_MAP_ADD(). + */ +static int flt_otel_text_map_writer_set_cb(struct otelc_text_map_writer *writer, const char *key, const char *value) +{ + OTELC_FUNC("%p, \"%s\", \"%s\"", writer, OTELC_STR_ARG(key), OTELC_STR_ARG(value)); + + OTELC_RETURN_INT(OTELC_TEXT_MAP_ADD(&(writer->text_map), key, 0, value, 0, OTELC_TEXT_MAP_AUTO)); +} + + +/*** + * NAME + * flt_otel_http_headers_writer_set_cb - HTTP headers injection writer callback + * + * SYNOPSIS + * static int flt_otel_http_headers_writer_set_cb(struct otelc_http_headers_writer *writer, const char *key, const char *value) + * + * ARGUMENTS + * writer - HTTP headers writer instance + * key - context key name + * value - context key value + * + * DESCRIPTION + * Writer callback for HTTP headers injection. Called by the OTel C wrapper + * library during span context injection to store each key-value pair in the + * 's text map. + * + * RETURN VALUE + * Returns the result of OTELC_TEXT_MAP_ADD(). + */ +static int flt_otel_http_headers_writer_set_cb(struct otelc_http_headers_writer *writer, const char *key, const char *value) +{ + OTELC_FUNC("%p, \"%s\", \"%s\"", writer, OTELC_STR_ARG(key), OTELC_STR_ARG(value)); + + OTELC_RETURN_INT(OTELC_TEXT_MAP_ADD(&(writer->text_map), key, 0, value, 0, OTELC_TEXT_MAP_AUTO)); +} + + +/*** + * NAME + * flt_otel_inject_text_map - text map context injection + * + * SYNOPSIS + * int flt_otel_inject_text_map(const struct otelc_span *span, struct otelc_text_map_writer *carrier) + * + * ARGUMENTS + * span - span instance to inject context from + * carrier - text map writer carrier + * + * DESCRIPTION + * Injects the span context into a text map carrier. Initializes the + * structure, sets the writer callback to + * flt_otel_text_map_writer_set_cb(), and delegates to the 's + * inject_text_map() method. + * + * RETURN VALUE + * Returns the result of the 's inject_text_map() method, + * or FLT_OTEL_RET_ERROR if arguments are NULL. + */ +int flt_otel_inject_text_map(const struct otelc_span *span, struct otelc_text_map_writer *carrier) +{ + OTELC_FUNC("%p, %p", span, carrier); + + if ((span == NULL) || (carrier == NULL)) + OTELC_RETURN_INT(FLT_OTEL_RET_ERROR); + + (void)memset(carrier, 0, sizeof(*carrier)); + carrier->set = flt_otel_text_map_writer_set_cb; + + OTELC_RETURN_INT(OTELC_OPS(span, inject_text_map, carrier)); +} + + +/*** + * NAME + * flt_otel_inject_http_headers - HTTP headers context injection + * + * SYNOPSIS + * int flt_otel_inject_http_headers(const struct otelc_span *span, struct otelc_http_headers_writer *carrier) + * + * ARGUMENTS + * span - span instance to inject context from + * carrier - HTTP headers writer carrier + * + * DESCRIPTION + * Injects the span context into an HTTP headers carrier. Initializes the + * structure, sets the writer callback to + * flt_otel_http_headers_writer_set_cb(), and delegates to the 's + * inject_http_headers() method. + * + * RETURN VALUE + * Returns the result of the 's inject_http_headers() method, + * or FLT_OTEL_RET_ERROR if arguments are NULL. + */ +int flt_otel_inject_http_headers(const struct otelc_span *span, struct otelc_http_headers_writer *carrier) +{ + OTELC_FUNC("%p, %p", span, carrier); + + if ((span == NULL) || (carrier == NULL)) + OTELC_RETURN_INT(FLT_OTEL_RET_ERROR); + + (void)memset(carrier, 0, sizeof(*carrier)); + carrier->set = flt_otel_http_headers_writer_set_cb; + + OTELC_RETURN_INT(OTELC_OPS(span, inject_http_headers, carrier)); +} + + +/*** + * NAME + * flt_otel_text_map_reader_foreach_key_cb - text map extraction reader callback + * + * SYNOPSIS + * static int flt_otel_text_map_reader_foreach_key_cb(const struct otelc_text_map_reader *reader, int (*handler)(void *arg, const char *key, const char *value), void *arg) + * + * ARGUMENTS + * reader - text map reader instance + * handler - callback function invoked for each key-value pair + * arg - opaque argument passed to the handler + * + * DESCRIPTION + * Reader callback for text map extraction. Iterates over all key-value + * pairs in the 's text map and invokes for each. Iteration + * stops if the returns -1. + * + * RETURN VALUE + * Returns the last return value, or 0 if the text map is empty. + */ +static int flt_otel_text_map_reader_foreach_key_cb(const struct otelc_text_map_reader *reader, int (*handler)(void *arg, const char *key, const char *value), void *arg) +{ + size_t i; + int retval = 0; + + OTELC_FUNC("%p, %p, %p", reader, handler, arg); + + for (i = 0; (retval != -1) && (i < reader->text_map.count); i++) { + OTELC_DBG(OTELC, "\"%s\" -> \"%s\"", OTELC_STR_ARG(reader->text_map.key[i]), OTELC_STR_ARG(reader->text_map.value[i])); + + retval = handler(arg, reader->text_map.key[i], reader->text_map.value[i]); + } + + OTELC_RETURN_INT(retval); +} + + +/*** + * NAME + * flt_otel_http_headers_reader_foreach_key_cb - HTTP headers extraction reader callback + * + * SYNOPSIS + * static int flt_otel_http_headers_reader_foreach_key_cb(const struct otelc_http_headers_reader *reader, int (*handler)(void *arg, const char *key, const char *value), void *arg) + * + * ARGUMENTS + * reader - HTTP headers reader instance + * handler - callback function invoked for each key-value pair + * arg - opaque argument passed to the handler + * + * DESCRIPTION + * Reader callback for HTTP headers extraction. Iterates over all key-value + * pairs in the 's text map and invokes for each. Iteration + * stops if the returns -1. + * + * RETURN VALUE + * Returns the last return value, or 0 if the text map is empty. + */ +static int flt_otel_http_headers_reader_foreach_key_cb(const struct otelc_http_headers_reader *reader, int (*handler)(void *arg, const char *key, const char *value), void *arg) +{ + size_t i; + int retval = 0; + + OTELC_FUNC("%p, %p, %p", reader, handler, arg); + + for (i = 0; (retval != -1) && (i < reader->text_map.count); i++) { + OTELC_DBG(OTELC, "\"%s\" -> \"%s\"", OTELC_STR_ARG(reader->text_map.key[i]), OTELC_STR_ARG(reader->text_map.value[i])); + + retval = handler(arg, reader->text_map.key[i], reader->text_map.value[i]); + } + + OTELC_RETURN_INT(retval); +} + + +/*** + * NAME + * flt_otel_extract_text_map - text map context extraction + * + * SYNOPSIS + * struct otelc_span_context *flt_otel_extract_text_map(struct otelc_tracer *tracer, struct otelc_text_map_reader *carrier, const struct otelc_text_map *text_map) + * + * ARGUMENTS + * tracer - OTel tracer instance + * carrier - text map reader carrier + * text_map - text map containing the context data (or NULL) + * + * DESCRIPTION + * Extracts a span context from a text map carrier via the . + * Initializes the structure, sets the foreach_key callback to + * flt_otel_text_map_reader_foreach_key_cb(), and copies the data + * into the . Delegates to the 's extract_text_map() method. + * + * RETURN VALUE + * Returns a pointer to the extracted span context, or NULL on failure. + */ +struct otelc_span_context *flt_otel_extract_text_map(struct otelc_tracer *tracer, struct otelc_text_map_reader *carrier, const struct otelc_text_map *text_map) +{ + OTELC_FUNC("%p, %p, %p", tracer, carrier, text_map); + + if ((tracer == NULL) || (carrier == NULL)) + OTELC_RETURN_PTR(NULL); + + (void)memset(carrier, 0, sizeof(*carrier)); + carrier->foreach_key = flt_otel_text_map_reader_foreach_key_cb; + + if (text_map != NULL) + (void)memcpy(&(carrier->text_map), text_map, sizeof(carrier->text_map)); + + OTELC_RETURN_PTR(OTELC_OPS(tracer, extract_text_map, carrier)); +} + + +/*** + * NAME + * flt_otel_extract_http_headers - HTTP headers context extraction + * + * SYNOPSIS + * struct otelc_span_context *flt_otel_extract_http_headers(struct otelc_tracer *tracer, struct otelc_http_headers_reader *carrier, const struct otelc_text_map *text_map) + * + * ARGUMENTS + * tracer - OTel tracer instance + * carrier - HTTP headers reader carrier + * text_map - text map containing the context data (or NULL) + * + * DESCRIPTION + * Extracts a span context from an HTTP headers carrier via the . + * Initializes the structure, sets the foreach_key callback to + * flt_otel_http_headers_reader_foreach_key_cb(), and copies the + * data into the . Delegates to the 's + * extract_http_headers() method. + * + * RETURN VALUE + * Returns a pointer to the extracted span context, or NULL on failure. + */ +struct otelc_span_context *flt_otel_extract_http_headers(struct otelc_tracer *tracer, struct otelc_http_headers_reader *carrier, const struct otelc_text_map *text_map) +{ + OTELC_FUNC("%p, %p, %p", tracer, carrier, text_map); + + if ((tracer == NULL) || (carrier == NULL)) + OTELC_RETURN_PTR(NULL); + + (void)memset(carrier, 0, sizeof(*carrier)); + carrier->foreach_key = flt_otel_http_headers_reader_foreach_key_cb; + + if (text_map != NULL) + (void)memcpy(&(carrier->text_map), text_map, sizeof(carrier->text_map)); + + OTELC_RETURN_PTR(OTELC_OPS(tracer, extract_http_headers, carrier)); +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/addons/otel/src/scope.c b/addons/otel/src/scope.c index 303fc2a07..63effc6a0 100644 --- a/addons/otel/src/scope.c +++ b/addons/otel/src/scope.c @@ -271,7 +271,9 @@ void flt_otel_scope_span_free(struct flt_otel_scope_span **ptr) */ 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 flt_otel_scope_context *retptr = NULL; + 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)); @@ -290,10 +292,18 @@ struct flt_otel_scope_context *flt_otel_scope_context_init(struct flt_otel_runti 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);