Commit graph

26776 commits

Author SHA1 Message Date
Christopher Faulet
ebb801d7c8 BUG/MEDIUM: cli: Properly handle too big payload on a command line
When command line is parsed, when the payload was too big the error was not
properly handled. Instead of leaving the parsing function to print the
error, we looped infinitly trying to parse remaining data.

When the command line is too big, we must exit the parsing function in
CLI_ST_PRINT_ERR state. Instead of exiting the function, we only left the
while loop, setting this way the cli applet in CLI_ST_PROMPT state.

This patch must be backported as far as 3.2.
2026-04-13 15:18:47 +02:00
Egor Shestakov
c82b10b8d2 MINOR: tools: memvprintf(): remove <out> check that always true
memvprintf() exits early if the <out> is NULL, so the further NULL check is
redundant.

No backport needed.
2026-04-13 14:36:52 +02:00
Ilia Shipitsin
76f74d7a55 CI: build WolfSSL job with asan enabled
Reference: https://github.com/haproxy/haproxy/issues/3317

this allows to distribute memory checking to WolfSSL code as well

Only applies on the WolfSSL weekly job which build the wolfssl git
version.
2026-04-13 14:03:30 +02:00
Tim Duesterhus
801d028790 CLEANUP: http_fetch: Use local unique_id variable in smp_fetch_uniqueid()
Instead of relying on the implementation detail that
`stream_generate_unique_id()` will store the unique ID in `strm->unique_id` we
should use the returned value, especially since that one is already checked in
the `isttest()`.

Reviewed-by: Volker Dusch <github@wallbash.com>
2026-04-13 14:02:29 +02:00
Tim Duesterhus
44a461a19f CLEANUP: stream: Reduce duplication in stream_generate_unique_id()
The return value of the `if()` and `else` branch is identical. We can just move
it out of conditional paths.

Reviewed-by: Volker Dusch <github@wallbash.com>
2026-04-13 14:02:29 +02:00
Tim Duesterhus
f778e6feb0 CLEANUP: stream: Explain the two-step initialization in stream_generate_unique_id()
This two-step initialization of `strm->unique_id` looks like a refactoring
target. Add a comment to prevent regressions of the fix in
fb7b5c8a53.
2026-04-13 14:02:29 +02:00
Tim Duesterhus
756ad19f04 CLEANUP: log: Return size_t from sess_build_logline_orig()
`sess_build_logline_orig()` takes a `size_t maxsize` as input and accordingly
should also return `size_t` instead of `int` as the resulting length. In
practice most of the callers already stored the result in a `size_t` anyways.
The few places that used an `int` were adjusted.

This Coccinelle patch was used to check for completeness:

    @@
    type T != size_t;
    T var;
    @@

    (
    * var = build_logline(...)
    |
    * var = build_logline_orig(...)
    |
    * var = sess_build_logline(...)
    |
    * var = sess_build_logline_orig(...)
    )

Reviewed-by: Volker Dusch <github@wallbash.com>
2026-04-13 14:02:29 +02:00
Tim Duesterhus
34c17608e7 BUG/MINOR: log: Fix error message when using unavailable fetch in logfmt
The following configuration:

    defaults
    	unique-id-format TEST-%[srv_name]

    frontend fe_http
    	mode http

    	bind :::8080 v4v6

Emitted the following error:

    [ALERT]    (219835) : Parsing [./patch.cfg:2]: failed to parse unique-id : sample fetch <srv_name]> may not be reliably used here because it needs 'server' which is not available here.

The `]` in the name of the sample fetch should not be there.

This bug exists since at least HAProxy 2.4, which is the oldest supported
version. The fix should be backported there.

Reviewed-by: Volker Dusch <github@wallbash.com>
2026-04-13 14:02:29 +02:00
Amaury Denoyelle
34c9ded340 BUG/MINOR: quic: do not use hardcoded values in QMux TP frame builder
Reuse QUIC transport parameters value set in xprt_qstrm layer in frame
builder function. Prior to this patch, mux_quic would use different
values from the advertised ones.

No need to backport.
2026-04-13 13:38:11 +02:00
William Lallemand
073240044e CI: github: add bash to the musl job
Previous commit 6e67b59 ("CI: Consistently set up VTest with
./.github/actions/setup-vtest") requires bash to use the github action.

This commit adds bash to the list of installed package in alpine.
2026-04-13 11:28:51 +02:00
Amaury Denoyelle
175717f5be MINOR: mux_quic: remove duplicate QMux local transport params
Some checks are pending
Contrib / build (push) Waiting to run
alpine/musl / gcc (push) Waiting to run
VTest / Generate Build Matrix (push) Waiting to run
VTest / (push) Blocked by required conditions
Windows / Windows, gcc, all features (push) Waiting to run
When QMux was first implemented, values used for emitted transport
parameters in xprt_qstrm and local flow control in mux_quic were
initialized separately. This is error prone in particular if a value is
change in one layer but not the other.

This patch fixes this by using xprt_qstrm_lparams() in QMux init
function. Mux flow control is then loaded with these values. Thus all
values are now initialized in a single place which is xprt_qstrm_init().
2026-04-13 09:38:46 +02:00
Tim Duesterhus
6e67b59aca CI: Consistently set up VTest with ./.github/actions/setup-vtest
Two jobs still used `scripts/build-vtest.sh` directly, which seems like an
oversight.
2026-04-13 09:34:07 +02:00
Tim Duesterhus
a7c2cf9274 CI: Merge aws-lc-template.yml into aws-lc.yml
There is no need to have an entirely separate workflow, when we can just use a
build matrix for the variation.
2026-04-13 09:34:07 +02:00
Tim Duesterhus
5ea919fa7c CI: Merge aws-lc.yml and aws-lc-fips.yml into aws-lc.yml
These two jobs run on exactly the same triggers and are effectively variations
of each other. There is no need to have two separate workflows for them.
2026-04-13 09:34:07 +02:00
Tim Duesterhus
86430ab5a4 CI: Simplify version extraction with haproxy -vq
Instead of running `awk` on the output of `haproxy -v` to extract the bare
version number, we can use `haproxy -vq`.
2026-04-13 09:34:07 +02:00
Tim Duesterhus
c6b9ba80ae CI: Update to actions/checkout@v6
No functional change, but we should keep this current.

see 5f4ddb54b0
see 5c923f1869
see b81a7f428b
2026-04-13 09:34:07 +02:00
Tim Duesterhus
abcf2d757d CI: Fix regular expression escaping in matrix.py
This fixes:

    .github/matrix.py:72: SyntaxWarning: "\." is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\."? A raw string is also an option.
      return re.match('^v[0-9]+(\.[0-9]+)*$', version_string)
    .github/matrix.py:89: SyntaxWarning: "\." is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\."? A raw string is also an option.
      return re.match('^AWS-LC-FIPS-[0-9]+(\.[0-9]+)*$', version_string)
    .github/matrix.py:106: SyntaxWarning: "\." is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\."? A raw string is also an option.
      return re.match('^v[0-9]+(\.[0-9]+)*-stable$', version_string)
2026-04-13 09:34:07 +02:00
Tim Duesterhus
5d6a09580a CI: Wrap all if: conditions in ${{ }}
While `if:` also works with a bare condition, it is a best practice to always
wrap "dynamic placeholders" in `${{ }}`.

See: https://github.blog/changelog/2026-01-29-github-actions-smarter-editing-clearer-debugging-and-a-new-case-function/#better-if-condition-handling
2026-04-13 09:34:07 +02:00
Tim Duesterhus
a4737cca08 CI: Consistently add a top-level permissions definition to GHA workflows
This makes it easy to verify the permissions and to apply them to all jobs
within a given workflow.
2026-04-13 09:34:07 +02:00
Tim Duesterhus
991d5dabe0 CI: Drop obsolete packages: write permission from quic-interop-*.yml
This is no longer necessary since dfe1de4335.
2026-04-13 09:34:07 +02:00
Miroslav Zagorac
40d4fa29fd MINOR: ot: renamed the variable dbg_indent_level to flt_ot_dbg_indent_level
The thread-local variable dbg_indent_level used a generic name that could
collide with identifiers in other compilation units.  Renamed it to
flt_ot_dbg_indent_level so that it carried the flt_ot_ prefix consistent
with the rest of the OpenTracing filter namespace.  The rename covered the
declaration, definition, and all macro references in debug.h, parser.c and
util.c.
2026-04-13 09:23:30 +02:00
Miroslav Zagorac
f2629342b2 BUILD: ot: removed explicit include path when building opentracing filter
The -Iaddons/ot/include flag in OT_CFLAGS allowed source files to use a
bare #include "include.h", which was fragile because it depended on the
compiler search path.  Removed that flag from the Makefile and changed
every source file under addons/ot/src/ to use the relative include path
../include/include.h instead.  This made header resolution explicit and
consistent with standard addon conventions.
2026-04-13 09:23:30 +02:00
Miroslav Zagorac
124e32ad78 BUG/MINOR: ot: fixed wrong NULL check in flt_ot_parse_cfg_group()
After calling flt_ot_conf_group_init() and storing the result in
flt_ot_current_group, the code incorrectly checked flt_ot_current_config
for NULL instead of the newly assigned flt_ot_current_group.  This meant
a failed group init was never detected and the error path was never taken.
2026-04-13 09:23:30 +02:00
Miroslav Zagorac
9fa34e592a BUG/MINOR: ot: removed dead code in flt_ot_parse_cfg_str()
The local variable str was declared but never assigned a value other than
NULL.  The error-handling block that called flt_ot_conf_str_free(&str) on
it was therefore a no-op.  Removed both the unused variable and the dead
cleanup path.
2026-04-13 09:23:30 +02:00
Miroslav Zagorac
de41121c0c CLEANUP: ot: use the item API for the variables trees
In flt_ot_vars_scope_dump(), switched from cebu64_first()/cebu64_next() to
cebu64_imm_first()/cebu64_imm_next() for iterating the variable name trees.
Since this function only reads variables under a read lock, the immutable
traversal API is the correct choice.  Also updated the container_of()
member from 'node' to 'name_node' to match the current struct var layout.
2026-04-13 09:23:30 +02:00
Miroslav Zagorac
dae6fa9c6f DOC: otel: added README.md overview document
Added a Markdown-formatted overview covering features, build instructions,
configuration, scope keywords, CLI commands, performance benchmarks and
test configurations.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
7c66bb5497 MINOR: otel: changed instrument attr to use sample expressions
Replaced the static key-value attribute storage in update-form instruments
with sample-evaluated attributes, matching the log-record attr change.
The 'attr' keyword now accepts a key and a HAProxy sample expression
evaluated at runtime.

The struct (conf.h) changed from otelc_kv/attr_len to a list of
flt_otel_conf_sample entries.  The parser (parser.c) calls
flt_otel_parse_cfg_sample() with n=1 per attr keyword.  At runtime
(event.c) each attribute is evaluated via flt_otel_sample_eval() and
added via flt_otel_sample_add_kv() to a bare flt_otel_scope_data_kv,
which is passed to the meter.

Updated documentation, debug macro and test configurations.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
651e9fd8a7 MINOR: otel: changed log-record attr to use sample expressions
Replaced the static key-value attribute storage in log-record with
sample-evaluated attributes.  The 'attr' keyword now accepts a key and a
HAProxy sample expression evaluated at runtime, instead of a static string
value.

The struct (conf.h) changed from otelc_kv/attr_len to a list of
flt_otel_conf_sample entries.  The parser (parser.c) calls
flt_otel_parse_cfg_sample() with n=1 per attr keyword.  At runtime
(event.c) each attribute is evaluated via flt_otel_sample_eval() and added
via flt_otel_sample_add_kv() to a bare flt_otel_scope_data_kv, which is
passed to logger->log_span().

Updated documentation, debug macro and test configurations.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
d96ce16cef MINOR: otel: added flt_otel_sample_eval and exposed flt_otel_sample_add_kv
Factored the sample evaluation logic out of flt_otel_sample_add() into a
new flt_otel_sample_eval() function that evaluates a sample definition
into an otelc_value.  Both the log-format path and the bare sample
expression path are handled, with a flag_native parameter controlling
native type preservation for single-expression samples.
flt_otel_sample_add() now calls flt_otel_sample_eval() and dispatches the
result.

Made flt_otel_sample_add_kv() non-static so callers outside util.c can
add key-value pairs directly to a bare flt_otel_scope_data_kv without
requiring the full flt_otel_scope_data structure.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
20c740db8c DOC: otel: added cross-cutting design patterns document
Added README-design covering the internal design patterns that span
multiple source files: X-macro code generation, type-safe generic macros,
error handling architecture, thread safety model, sample evaluation
pipeline, rate limiting, memory management, context propagation, debug
infrastructure, idle timeout mechanism, group action integration and CLI
runtime control.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
bb2c512d29 DOC: otel: test: added speed test guide and benchmark results
The test directory gained a speed test guide (README-test-speed)
explaining how to run performance benchmarks at various rate-limit levels,
together with benchmark result files for the standalone, composite,
context-propagation, and frontend-backend test configurations.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
081a8fd5cf DOC: otel: test: added test README-* files
Added README documentation for each test configuration (sa, cmp, ctx,
fe-be, empty, full) describing event coverage, signal usage, instrument
tables, span hierarchies and run instructions.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
8d8d7e4602 DOC: otel: added documentation
Added the full documentation set for the OpenTelemetry filter.

The main README served as the user-facing guide covering build
instructions, core OpenTelemetry concepts, the complete filter
configuration reference, usage examples with worked scenarios,
CLI commands, and known limitations.

Supplementary documents provided a detailed configuration guide with
worked examples (README-configuration), an internal C structure reference
for developers (README-conf), a function reference organized by source
file (README-func), an architecture and implementation review
(README-implementation), and miscellaneous notes (README-misc).
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
579d1247f2 MINOR: otel: test: added full-event test config
Added the 'full' test configuration that exercises all 29 supported OTel
filter events with all three signal types (traces, metrics, logs).  Every
instrument definition has a corresponding update.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
6f177cd01e MINOR: otel: added log-record signal support
Added "log-record" as the third OpenTelemetry signal alongside traces
(span) and metrics (instrument).  This includes the
flt_otel_conf_log_record structure definition, parser keyword defines,
the otel-scope section parser with optional "id", "event", "span", and
"attr" keywords followed by sample fetch expressions or a log-format
string, init/free lifecycle, scope list wiring, log-format evaluation
in flt_otel_scope_run_instrument_record(), a test configuration example,
log-record span reference validation in flt_otel_check(), and logger
handle creation, startup, and teardown in the filter lifecycle.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
bf05a014db MINOR: otel: added metrics instrument support
Added the "instrument" keyword to otel-scope sections for recording metric
measurements alongside traces.

Introduced flt_otel_conf_instrument holding instrument type, description,
unit, sample expressions, and optional key-value attributes.  The
supported synchronous integer-precision instrument types were counters,
histograms, up-down counters, and gauges.

Instruments followed a two-form design: a "create" form defined a new
instrument with its type and value expression, while an "update" form
recorded measurements against an existing instrument with per-scope
attributes.

Instrument creation was performed lazily at first use with HA_ATOMIC_CAS
to guarantee thread-safe one-time initialization.  The configuration
check phase validated that every update-form had a matching create-form
definition and that create-form names were unique across all scopes.

The meter lifecycle was integrated into filter init and deinit, starting
the meter alongside the tracer and shutting it down during cleanup.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
eaa05d2af3 MINOR: otel: added span link support
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.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
d8e69f07dc MINOR: otel: test: added test and benchmark suite for the OTel filter
Added a test suite under addons/otel/test/ for the OpenTelemetry filter.
Five scenarios exercise different filter capabilities: standalone (sa)
covers all hook points including idle-timeout heartbeats, metrics and log
records; compact (cmp) covers the full request/response lifecycle with
ACL-based error handling; context (ctx) tests explicit inject/extract
propagation through numbered context variables; frontend/backend (fe/be)
tests distributed tracing across two HAProxy instances; and empty tests
bare filter initialisation with no active scopes.

A performance benchmarking script (test-speed.sh) uses wrk to measure
throughput and latency at different rate-limit settings (100% through 0%,
disabled, and filter-off).  Each scenario includes comprehensive YAML
exporter definitions covering OTLP file/gRPC/HTTP, ostream, memory,
Zipkin, and Elasticsearch backends.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
bc6402f609 MINOR: otel: added log-format support to the sample parser and runtime
Extended flt_otel_parse_cfg_sample() to accept log-format strings in
addition to bare sample expressions.  Added lf_expr and lf_used fields
to flt_otel_conf_sample.

Extended flt_otel_sample_add() to evaluate log-format expressions when
lf_used was set.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
40ac0fd932 MEDIUM: otel: added group action for rule-based scope execution
Added the "otel-group" action keyword that allows executing a named group
of OTel scopes from HAProxy TCP and HTTP action rule contexts.

The new group.c module registers the "otel-group" keyword for all four
action contexts (tcp-request, tcp-response, http-request, http-response)
and implements the action lifecycle callbacks.

The parser flt_otel_group_parse() accepts a filter ID and group ID as
arguments, duplicates them into the action rule's argument slots, and
wires up the check, action, and release callbacks.

The post-parse validator flt_otel_group_check() resolves the filter ID and
group ID string references into direct configuration pointers by searching
the proxy's filter list for a matching OTel filter and then looking up the
named group within that filter's configuration.

The action handler flt_otel_group_action() retrieves the filter and group
configuration from the resolved rule arguments, verifies the filter is
attached to the stream and not disabled, then iterates through all scopes
in the group and executes each via flt_otel_scope_run() with a shared
timestamp pair.  This allows operators to trigger OTel instrumentation
conditionally from HAProxy rules, for example applying different tracing
scopes based on ACL conditions or request properties.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
cb4cb1065c MEDIUM: otel: added CLI commands for runtime filter management
Added HAProxy CLI commands that allow runtime inspection and modification
of OTel filter settings without requiring a configuration reload.

The new cli.c module registers CLI keywords under the "otel" prefix and
implements the following commands: flt_otel_cli_parse_status() displays a
comprehensive status report of all OTel filter instances including filter
ID, proxy, disabled state, hard-error mode, logging state, rate limit,
analyzer bits, and SDK diagnostic message count;
flt_otel_cli_parse_disabled() enables or disables filtering across all
instances; flt_otel_cli_parse_option() toggles the hard-error mode that
controls whether errors disable the filter for a stream or are silently
ignored; flt_otel_cli_parse_logging() manages the logging state with
support for off, on, and dontlog-normal modes; flt_otel_cli_parse_rate()
adjusts the sampling rate limit as a floating-point percentage; and
flt_otel_cli_parse_debug() sets the debug verbosity level in debug builds.
All modifications are applied atomically across every OTel filter instance
in every proxy.

The CLI initialization is called from flt_otel_ops_init() during filter
startup via flt_otel_cli_init(), which registers the keyword table through
cli_register_kw().

Supporting changes include the FLT_OTEL_U32_FLOAT macro for converting the
internal uint32_t rate representation to a human-readable percentage, the
FLT_OTEL_PROXIES_LIST_START/END iteration macros for traversing all OTel
filter instances across the proxy list, and flt_otel_filters_dump() for
debug logging of filter instances.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
b1498910ec MINOR: otel: added prefix-based variable scanning
Introduced an alternative variable scanning strategy that directly walked
the CEB tree of HAProxy's variable store instead of maintaining a separate
tracking buffer.

The Makefile auto-detected whether struct var carried a "name" member
by inspecting include/haproxy/vars-t.h and conditionally defined
USE_OTEL_VARS_NAME.  When enabled, the tracking buffer (flt_otel_ctx) and
its callback type were compiled out and replaced by direct tree walks.

flt_otel_vars_unset() walked the CEB tree for the resolved scope, removed
every variable whose normalized name matched the given prefix followed by
a dot, and adjusted the variable accounting.  flt_otel_vars_get()
performed the same prefix scan under a read lock, denormalized each
matching variable name back to its original OTel form, and assembled the
results into an otelc_text_map.

A helper flt_otel_vars_get_scope() was added to resolve scope name strings
("txn", "sess", "proc", "req", "res") to the corresponding HAProxy
variable store.  The set path skipped the tracking buffer update when
prefix scanning was available.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
2d6a15ac00 MEDIUM: otel: added HAProxy variable storage for context propagation
Added support for storing OTel span context in HAProxy transaction
variables as an alternative to HTTP headers, enabled by the OTEL_USE_VARS
compile flag.

The new vars.c module implements variable-based context propagation
through the HAProxy variable subsystem.  Variable names are constructed
from a configurable prefix and the OTel propagation key, with dots
normalized to underscores for HAProxy variable name compatibility
and denormalized back during retrieval.  The module provides
flt_otel_var_register() to pre-register variables at parse time,
flt_otel_var_set() and flt_otel_vars_unset() to store and clear context
key-value pairs in the txn scope, flt_otel_vars_get() to collect all
variables matching a prefix into an otelc_text_map for context extraction,
and flt_otel_vars_dump() for debug logging of all OTel variables.

The inject/extract keywords in the scope parser now accept an optional
"use-vars" argument alongside "use-headers", controlled by the new
FLT_OTEL_CTX_USE_VARS flag.  Both storage types can be used simultaneously
on the same span context, allowing context to be propagated through both
HTTP headers and variables.

The scope runner in event.c was extended to handle variable-based
context in parallel with headers: during extraction, it reads matching
variables via flt_otel_vars_get() when FLT_OTEL_CTX_USE_VARS is set;
during injection, it stores each propagation key as a variable via
flt_otel_var_set().  The unused resource cleanup now also unsets context
variables when removing failed extraction contexts.

The filter attach callback registers and sets the sess.otel.uuid variable
with the generated session UUID, making the trace identifier available to
HAProxy log formats and ACL expressions.

The feature is conditionally compiled: the OTEL_USE_VARS flag controls
whether vars.c is included in the build and whether the "use-vars" keyword
is available in the configuration parser.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
8effcac6f5 MEDIUM: otel: added HTTP header operations for context propagation
Added the HTTP header manipulation layer that enables span context
injection into and extraction from HAProxy's HTX message buffers,
completing the end-to-end context propagation path.

The new http.c module implements three public functions:
flt_otel_http_headers_get() extracts HTTP headers matching a name prefix
from the channel's HTX buffer into an otelc_text_map structure, stripping
the prefix and separator dash from header names before storage;
flt_otel_http_header_set() constructs a full header name from a prefix and
suffix joined by a dash, removes all existing occurrences, and optionally
adds the header with a new value; and flt_otel_http_headers_remove()
removes all headers matching a given prefix.  A debug-only
flt_otel_http_headers_dump() logs all HTTP headers from a channel at
NOTICE level.

The scope runner in event.c now extracts propagation contexts from HTTP
headers before processing spans: for each configured extract context, it
calls flt_otel_http_headers_get() to read matching headers into a text
map, then passes the text map to flt_otel_scope_context_init() which
extracts the OTel span context from the carrier.  After span execution,
the span runner injects the span context back into HTTP headers via
flt_otel_inject_http_headers() followed by flt_otel_http_header_set()
for each propagation key.

The unused resource cleanup in flt_otel_scope_free_unused() now also
removes contexts that failed extraction by deleting their associated
HTTP headers via flt_otel_http_headers_remove() before freeing the scope
context structure.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
ea9d05de02 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.
2026-04-13 09:23:26 +02:00
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
Miroslav Zagorac
3184470339 MEDIUM: otel: wired OTel C wrapper library integration
Connected the OpenTelemetry C wrapper library to the filter lifecycle by
implementing the library initialization, tracer creation, memory and
thread callbacks, shutdown sequence, and span completion.

The flt_otel_lib_init() function now verifies the C wrapper library
version against the compiled headers, calls otelc_init() with the absolute
configuration file path, and creates the tracer via otelc_tracer_create().
On success, it registers HAProxy pool-based memory callbacks
(flt_otel_mem_malloc, flt_otel_mem_free) and a thread ID callback
(flt_otel_thread_id) through otelc_ext_init(), so the C++ SDK allocates
span and context objects from pool_head_otel_span_context.  A custom log
handler (flt_otel_log_handler_cb) is registered via otelc_log_set_handler()
to count OTel SDK internal diagnostic messages in the flt_otel_drop_cnt
counter.

The per-thread init callback now starts the tracer thread via
OTELC_OPS(tracer, start) instead of unconditionally returning success.

The deinit callback saves the tracer handle before freeing the
configuration, then shuts down the library via otelc_deinit() after the
pool is destroyed, ensuring the ext callbacks remain valid while the
configuration structures are still being freed.  In debug builds, it logs
wrapper statistics, attach counters, and per-event HTX usage counters
before shutdown.

The runtime context cleanup in flt_otel_runtime_context_free() now ends
all active spans with a common monotonic timestamp via
OTELC_OPSR(span, end_with_options) before freeing them.  The scope context
cleanup in flt_otel_scope_context_free() now destroys the underlying OTel
span context via OTELC_OPSR(context, destroy).

The parser gained static storage for the debug memory tracker
(OTELC_DBG_MEM) and its initialization in the parse entry point, used when
compiled with the OTELC_DBG_MEM flag.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
2e962a5443 MEDIUM: otel: implemented filter callbacks and event dispatcher
Replaced the stub filter callbacks with full implementations that dispatch
OTel events through the scope execution engine, and added the supporting
debug, error handling and utility infrastructure.

The filter lifecycle callbacks (init, deinit, init_per_thread) now
initialize the OpenTelemetry C wrapper library, create the tracer from the
instrumentation configuration file, enable HTX stream filtering, and clean
up the configuration and memory pools on shutdown.

The stream callbacks (attach, stream_start, stream_set_backend,
stream_stop, detach, check_timeouts) create the per-stream runtime context
on attach with rate-limit based sampling, fire the corresponding OTel
events (on-stream-start, on-backend-set, on-stream-stop), manage the
idle timeout timer with reschedule logic in detach, and free the runtime
context in check_timeouts.  The attach callback also registers the
required pre and post channel analyzers from the instrumentation
configuration.

The channel callbacks (start_analyze, pre_analyze, post_analyze,
end_analyze) register per-channel analyzers, map analyzer bits to event
indices via flt_otel_get_event(), and dispatch the matching events.
The end_analyze callback also fires the on-server-unavailable event
when response analyzers were configured but never executed.

The HTTP callbacks (http_headers, http_end, http_reply, and the debug-only
http_payload and http_reset) dispatch their respective request/response
events based on the channel direction.

The event dispatcher flt_otel_event_run() in event.c iterates over all
scopes matching a given event index and calls flt_otel_scope_run() for
each, sharing a common monotonic and wall-clock timestamp across all spans
within a single event.

Error handling is centralized in flt_otel_return_int() and
flt_otel_return_void(), which implement the hard-error/soft-error policy:
hard errors disable the filter for the stream, soft errors are silently
cleared.

The new debug.h header provides conditional debug macros
(FLT_OTEL_DBG_ARGS, FLT_OTEL_DBG_BUF) and the FLT_OTEL_LOG macro for
structured logging through the instrumentation's log server list.  The
utility layer gained debug-only label functions for channel direction,
proxy mode, stream position, filter type, and analyzer bit name lookups.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
f05a6735b1 MEDIUM: otel: added memory pool and runtime scope layer
Added the memory pool management and the runtime scope layer that track
per-stream OTel spans and contexts during request processing.

The pool layer in pool.c manages HAProxy memory pools for the runtime
structures used by the filter: scope spans, scope contexts, runtime
contexts, and span contexts.  Each pool is conditionally compiled via
USE_POOL_OTEL_* macros defined in config.h and registered with
REGISTER_POOL().  The allocation functions (flt_otel_pool_alloc,
flt_otel_pool_strndup, flt_otel_pool_free) transparently fall back to
heap allocation when the corresponding pool is not enabled.  Trash buffer
helpers (flt_otel_trash_alloc, flt_otel_trash_free) provide scratch space
using either HAProxy's trash chunk pool or direct heap allocation.

The scope layer in scope.c implements the per-stream runtime state.  The
flt_otel_runtime_context structure is allocated when a stream starts and
holds the stream and filter references, hard-error/disabled/logging flags
copied from the instrumentation configuration, idle timeout state, a
generated UUID, and lists of active scope spans and extracted scope
contexts.  Scope spans (flt_otel_scope_span) carry the operation name,
fetch direction, the OTel span handle, and optional parent references
resolved from other spans or extracted contexts.  Scope contexts
(flt_otel_scope_context) hold an extracted span context obtained from
a carrier text map via the tracer.  The scope data structures
(flt_otel_scope_data) aggregate growable key-value arrays for attributes
and baggage, a linked list of named events with their own attribute
arrays, and a span status code with description, representing the
telemetry collected during a single event execution.
2026-04-13 09:23:26 +02:00
Miroslav Zagorac
c0fd39457f MEDIUM: otel: added post-parse configuration check
Implemented the flt_otel_ops_check() callback that validates the parsed
OTel filter configuration after all HAProxy configuration sections have
been processed.

The check callback performs the following validations: resolves deferred
sample fetch arguments under full frontend and backend capabilities,
verifies uniqueness of filter IDs across all proxies, ensures the
instrumentation section and its configuration file are present, checks
for duplicate group and scope section names, verifies that groups are not
empty, resolves group-to-scope and instrumentation-to-group/scope
cross-references by linking placeholder entries to their definitions,
detects unused scopes, counts root spans and warns when the count differs
from one, and accumulates the required channel analyzer bits from all used
scopes into the instrumentation configuration.

The commit also added the flt_otel_counters structure to track per-event
diagnostic counters in debug builds, the FLT_OTEL_ALERT macro for
filter-scoped error messages, and the FLT_OTEL_DBG_LIST macro for
iterating and dumping named configuration lists.
2026-04-13 09:23:26 +02:00