]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: otel: added context propagation via carrier interfaces
authorMiroslav Zagorac <mzagorac@haproxy.com>
Sun, 12 Apr 2026 09:17:11 +0000 (11:17 +0200)
committerWilliam Lallemand <wlallemand@haproxy.com>
Mon, 13 Apr 2026 07:23:26 +0000 (09:23 +0200)
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
addons/otel/include/include.h
addons/otel/include/otelc.h [new file with mode: 0644]
addons/otel/src/otelc.c [new file with mode: 0644]
addons/otel/src/scope.c

index c9f91952622c39368a179a00fe612e77269cbbf9..d1436f50c8bb4f23d2925aa7be528f6766b36595 100644 (file)
@@ -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  \
index 103166b4dba776d5c931547b90584528bc2cc000..6902a4fd2f9e9749d0c71ea6e31ad08bcb5844af 100644 (file)
@@ -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 (file)
index 0000000..0c04f04
--- /dev/null
@@ -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 (file)
index 0000000..6933800
--- /dev/null
@@ -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
+ *   <writer>'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
+ *   <writer>'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
+ *   <carrier> structure, sets the writer callback to
+ *   flt_otel_text_map_writer_set_cb(), and delegates to the <span>'s
+ *   inject_text_map() method.
+ *
+ * RETURN VALUE
+ *   Returns the result of the <span>'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
+ *   <carrier> structure, sets the writer callback to
+ *   flt_otel_http_headers_writer_set_cb(), and delegates to the <span>'s
+ *   inject_http_headers() method.
+ *
+ * RETURN VALUE
+ *   Returns the result of the <span>'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 <reader>'s text map and invokes <handler> for each.  Iteration
+ *   stops if the <handler> returns -1.
+ *
+ * RETURN VALUE
+ *   Returns the last <handler> 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 <reader>'s text map and invokes <handler> for each.  Iteration
+ *   stops if the <handler> returns -1.
+ *
+ * RETURN VALUE
+ *   Returns the last <handler> 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 <tracer>.
+ *   Initializes the <carrier> structure, sets the foreach_key callback to
+ *   flt_otel_text_map_reader_foreach_key_cb(), and copies the <text_map> data
+ *   into the <carrier>.  Delegates to the <tracer>'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 <tracer>.
+ *   Initializes the <carrier> structure, sets the foreach_key callback to
+ *   flt_otel_http_headers_reader_foreach_key_cb(), and copies the <text_map>
+ *   data into the <carrier>.  Delegates to the <tracer>'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
+ */
index 303fc2a0780351134026c18c77c4e45729ed5c08..63effc6a036ce88651d03f0e34ef308efe835d70 100644 (file)
@@ -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);