--- /dev/null
+Comparison test configuration (cmp/)
+====================================
+
+The 'cmp' test is a simplified standalone configuration made for comparison with
+other tracing implementations. It uses a reduced set of events and a compact
+span hierarchy without context propagation, groups or metrics. This
+configuration is closer to a typical production deployment.
+
+All response-side scopes (http_response, http_response-error, server_session_end
+and client_session_end) share the on-http-response event, which means they fire
+in a single batch at response time.
+
+
+Files
+-----
+
+ cmp/otel.cfg OTel filter configuration (scopes)
+ cmp/haproxy.cfg HAProxy frontend/backend configuration
+ cmp/otel.yml Exporter, processor, reader and provider definitions
+ run-cmp.sh Convenience script to launch HAProxy with this config
+
+
+Events
+------
+
+ T = Trace (span)
+
+ This configuration produces traces only -- no metrics or log-records.
+
+ Request analyzer events:
+
+ Event Scope T
+ --------------------------------------------------------
+ on-client-session-start client_session_start x
+ on-frontend-tcp-request frontend_tcp_request x
+ on-frontend-http-request frontend_http_request x
+ on-backend-tcp-request backend_tcp_request x
+ on-backend-http-request backend_http_request x
+ on-server-unavailable server_unavailable x
+
+ Response analyzer events:
+
+ Event Scope T
+ --------------------------------------------------------
+ on-server-session-start server_session_start x
+ on-tcp-response tcp_response x
+ on-http-response http_response x
+ on-http-response http_response-error x (conditional)
+ on-http-response server_session_end - (finish only)
+ on-http-response client_session_end - (finish only)
+
+ The http_response-error scope fires conditionally when the ACL
+ acl-http-status-ok (status 100:399) does not match, setting an error status
+ on the "HTTP response" span.
+
+ The server_session_end and client_session_end scopes are bound to the
+ on-http-response event and only perform finish operations.
+
+
+Span hierarchy
+--------------
+
+ "HAProxy session" (root)
+ +-- "Client session"
+ +-- "Frontend TCP request"
+ +-- "Frontend HTTP request"
+ +-- "Backend TCP request"
+ +-- "Backend HTTP request"
+
+ "HAProxy session" (root)
+ +-- "Server session"
+ +-- "TCP response"
+ +-- "HTTP response"
+
+
+Running the test
+----------------
+
+From the test/ directory:
+
+ % ./run-cmp.sh [/path/to/haproxy] [pidfile]
+
+If no arguments are given, the script looks for the haproxy binary three
+directories up from the current working directory. The backend origin server
+must be running on 127.0.0.1:8000.
--- /dev/null
+Context propagation test configuration (ctx/)
+=============================================
+
+The 'ctx' test is a standalone configuration that uses inject/extract context
+propagation on every scope. Spans are opened using extracted span contexts
+stored in HAProxy variables as parent references instead of direct span names.
+This adds the overhead of context serialization, variable storage and
+deserialization on every scope execution.
+
+The event coverage matches the 'sa' configuration. The key difference is the
+propagation mechanism: each scope injects its context into a numbered variable
+(otel_ctx_1 through otel_ctx_17) and the next scope extracts from that variable
+to establish the parent relationship.
+
+The client_session_start event is split into two scopes (client_session_start_1
+and client_session_start_2) to demonstrate inject/extract between scopes
+handling the same event.
+
+
+Files
+-----
+
+ ctx/otel.cfg OTel filter configuration (scopes, groups, contexts)
+ ctx/haproxy.cfg HAProxy frontend/backend configuration
+ ctx/otel.yml Exporter, processor, reader and provider definitions
+ run-ctx.sh Convenience script to launch HAProxy with this config
+
+
+Events
+------
+
+ T = Trace (span)
+
+ This configuration produces traces only -- no metrics or log-records.
+
+ Stream lifecycle events:
+
+ Event Scope T
+ ----------------------------------------------------------
+ on-client-session-start client_session_start_1 x
+ on-client-session-start client_session_start_2 x
+
+ Request analyzer events:
+
+ Event Scope T
+ --------------------------------------------------------------------
+ on-frontend-tcp-request frontend_tcp_request x
+ on-http-wait-request http_wait_request x
+ on-http-body-request http_body_request x
+ on-frontend-http-request frontend_http_request x
+ on-switching-rules-request switching_rules_request x
+ on-backend-tcp-request backend_tcp_request x
+ on-backend-http-request backend_http_request x
+ on-process-server-rules-request process_server_rules_request x
+ on-http-process-request http_process_request x
+ on-tcp-rdp-cookie-request tcp_rdp_cookie_request x
+ on-process-sticking-rules-request process_sticking_rules_request x
+ on-client-session-end client_session_end -
+ on-server-unavailable server_unavailable -
+
+ Response analyzer events:
+
+ Event Scope T
+ --------------------------------------------------------------------
+ on-server-session-start server_session_start x
+ on-tcp-response tcp_response x
+ on-http-wait-response http_wait_response x
+ on-process-store-rules-response process_store_rules_response x
+ on-http-response http_response x
+ on-http-response http_response-error x (conditional)
+ on-server-session-end server_session_end -
+
+ The http_response_group (http_response_1, http_response_2) and
+ http_after_response_group (http_after_response) are invoked via http-response
+ and http-after-response directives in haproxy.cfg.
+
+
+Context propagation chain
+-------------------------
+
+ Each scope injects its span context into a HAProxy variable and the next scope
+ extracts it. The variable names and their flow:
+
+ otel_ctx_1 "HAProxy session" -> client_session_start_2
+ otel_ctx_2 "Client session" -> frontend_tcp_request
+ otel_ctx_3 "Frontend TCP request" -> http_wait_request
+ otel_ctx_4 "HTTP wait request" -> http_body_request
+ otel_ctx_5 "HTTP body request" -> frontend_http_request
+ otel_ctx_6 "Frontend HTTP request" -> switching_rules_request
+ otel_ctx_7 "Switching rules request" -> backend_tcp_request
+ otel_ctx_8 "Backend TCP request" -> backend_http_request
+ otel_ctx_9 "Backend HTTP request" -> process_server_rules_request
+ otel_ctx_10 "Process server rules request" -> http_process_request
+ otel_ctx_11 "HTTP process request" -> tcp_rdp_cookie_request
+ otel_ctx_12 "TCP RDP cookie request" -> process_sticking_rules_request
+ otel_ctx_13 "Process sticking rules req." -> server_session_start
+ otel_ctx_14 "Server session" -> tcp_response
+ otel_ctx_15 "TCP response" -> http_wait_response
+ otel_ctx_16 "HTTP wait response" -> process_store_rules_response
+ otel_ctx_17 "Process store rules response" -> http_response
+
+ All contexts use both use-headers and use-vars injection modes, except
+ otel_ctx_14 and otel_ctx_15 which use use-vars only.
+
+
+Span hierarchy
+--------------
+
+ The span hierarchy is identical to the 'sa' configuration, but parent
+ relationships are established through extracted contexts rather than direct
+ span name references.
+
+ Request path:
+
+ "HAProxy session" (root) [otel_ctx_1]
+ +-- "Client session" [otel_ctx_2]
+ +-- "Frontend TCP request" [otel_ctx_3]
+ +-- "HTTP wait request" [otel_ctx_4]
+ +-- "HTTP body request" [otel_ctx_5]
+ +-- "Frontend HTTP request" [otel_ctx_6]
+ +-- "Switching rules request" [otel_ctx_7]
+ +-- "Backend TCP request" [otel_ctx_8]
+ +-- (continues to process_sticking_rules_request)
+
+ Response path:
+
+ "HAProxy session" [otel_ctx_1]
+ +-- "Server session" [otel_ctx_14]
+ +-- "TCP response" [otel_ctx_15]
+ +-- "HTTP wait response" [otel_ctx_16]
+ +-- "Process store rules response" [otel_ctx_17]
+ +-- "HTTP response"
+
+ Auxiliary spans:
+
+ "HAProxy session"
+ +-- "HAProxy response" (http_after_response_group, on error)
+
+
+Running the test
+----------------
+
+From the test/ directory:
+
+ % ./run-ctx.sh [/path/to/haproxy] [pidfile]
+
+If no arguments are given, the script looks for the haproxy binary three
+directories up from the current working directory. The backend origin server
+must be running on 127.0.0.1:8000.
--- /dev/null
+Empty test configuration (empty/)
+=================================
+
+The 'empty' test is a minimal configuration that loads the OTel filter without
+defining any scopes, events or groups. The instrumentation block contains only
+the config directive pointing to the YAML pipeline definition.
+
+This configuration verifies that the filter initializes and shuts down cleanly
+when no telemetry is configured. It exercises the full YAML parsing path
+(exporters, processors, readers, samplers, providers and signals) without
+producing any trace, metric or log-record data.
+
+
+Files
+-----
+
+ empty/otel.cfg OTel filter configuration (instrumentation only)
+ empty/haproxy.cfg HAProxy frontend/backend configuration
+ empty/otel.yml Exporter, processor, reader and provider definitions
+
+
+Events
+------
+
+ No events are registered. The filter is loaded and attached to the frontend
+ but performs no per-stream processing.
+
+
+YAML pipeline
+-------------
+
+ Despite the empty filter configuration, the otel.yml file defines a complete
+ pipeline with all three signal types to verify that the YAML parser handles
+ the full configuration without errors:
+
+ Signal Exporter Processor / Reader
+ -----------------------------------------------------------
+ traces exporter_traces_otlp_http processor_traces_batch
+ metrics exporter_metrics_otlp_http reader_metrics
+ logs exporter_logs_otlp_http processor_logs_batch
+
+ Additional exporter definitions (otlp_file, otlp_grpc, ostream, memory,
+ zipkin, elasticsearch) are present in the YAML but are not wired into the
+ active signal pipelines.
+
+
+Running the test
+----------------
+
+There is no dedicated run script for the empty configuration. To run it
+manually from the test/ directory:
+
+ % /path/to/haproxy -f haproxy-common.cfg -f empty/haproxy.cfg
--- /dev/null
+Frontend / backend test configuration (fe/ + be/)
+=================================================
+
+The 'fe-be' test uses two cascaded HAProxy instances to demonstrate
+inter-process trace context propagation via HTTP headers. The frontend instance
+(fe/) creates the root trace and injects span context into the HTTP request
+headers. The backend instance (be/) extracts that context and continues the
+trace as a child of the frontend's span.
+
+The two instances run as separate processes: the frontend listens on port 10080
+and proxies to the backend on port 11080, which in turn proxies to the origin
+server on port 8000.
+
+
+Files
+-----
+
+ fe/otel.cfg OTel filter configuration for the frontend instance
+ fe/haproxy.cfg HAProxy configuration for the frontend instance
+ be/otel.cfg OTel filter configuration for the backend instance
+ be/haproxy.cfg HAProxy configuration for the backend instance
+ run-fe-be.sh Convenience script to launch both instances
+
+
+Events
+------
+
+ T = Trace (span)
+
+ Both instances produce traces only -- no metrics or log-records.
+
+ Frontend (fe/) events:
+
+ Event Scope T
+ --------------------------------------------------------
+ on-client-session-start client_session_start x
+ on-frontend-tcp-request frontend_tcp_request x
+ on-frontend-http-request frontend_http_request x
+ on-backend-tcp-request backend_tcp_request x
+ on-backend-http-request backend_http_request x
+ on-client-session-end client_session_end -
+ on-server-session-start server_session_start x
+ on-tcp-response tcp_response x
+ on-http-response http_response x
+ on-server-session-end server_session_end -
+
+ Backend (be/) events:
+
+ Event Scope T
+ --------------------------------------------------------
+ on-frontend-http-request frontend_http_request x
+ on-backend-tcp-request backend_tcp_request x
+ on-backend-http-request backend_http_request x
+ on-client-session-end client_session_end -
+ on-server-session-start server_session_start x
+ on-tcp-response tcp_response x
+ on-http-response http_response x
+ on-server-session-end server_session_end -
+
+ The backend starts its trace at on-frontend-http-request where it extracts
+ the span context injected by the frontend. Earlier request events
+ (on-client-session-start, on-frontend-tcp-request) are not needed because
+ the context is not yet available in the HTTP headers at that point.
+
+
+Context propagation
+-------------------
+
+ The frontend injects context into HTTP headers in the backend_http_request
+ scope:
+
+ span "HAProxy session"
+ inject "otel-ctx" use-headers
+
+ The backend extracts that context in its frontend_http_request scope:
+
+ extract "otel-ctx" use-headers
+ span "HAProxy session" parent "otel-ctx" root
+
+
+Span hierarchy
+--------------
+
+ Frontend (fe/):
+
+ "HAProxy session" (root)
+ +-- "Client session"
+ +-- "Frontend TCP request"
+ +-- "Frontend HTTP request"
+ +-- "Backend TCP request"
+ +-- "Backend HTTP request"
+
+ "HAProxy session" (root)
+ +-- "Server session"
+ +-- "TCP response"
+ +-- "HTTP response"
+
+ Backend (be/):
+
+ "HAProxy session" (root, parent: frontend's "HAProxy session")
+ +-- "Client session"
+ +-- "Frontend HTTP request"
+ +-- "Backend TCP request"
+ +-- "Backend HTTP request"
+
+ "HAProxy session" (root)
+ +-- "Server session"
+ +-- "TCP response"
+ +-- "HTTP response"
+
+
+Running the test
+----------------
+
+From the test/ directory:
+
+ % ./run-fe-be.sh [/path/to/haproxy] [pidfile]
+
+If no arguments are given, the script looks for the haproxy binary three
+directories up from the current working directory. The backend origin server
+must be running on 127.0.0.1:8000.
+
+The script launches both HAProxy instances in the background and waits.
+Press CTRL-C to stop both instances.
--- /dev/null
+Full event coverage test configuration (full/)
+==============================================
+
+The 'full' test is a standalone single-instance configuration that exercises
+every supported OTel filter event with all three signal types: traces (spans),
+metrics (instruments) and logs (log-records).
+
+It extends the 'sa' (standalone) configuration by adding the events that 'sa'
+does not cover and by attaching log-records to every scope.
+
+
+Files
+-----
+
+ full/otel.cfg OTel filter configuration (scopes, groups, instruments)
+ full/haproxy.cfg HAProxy frontend/backend configuration
+ full/otel.yml Exporter, processor, reader and provider definitions
+ run-full.sh Convenience script to launch HAProxy with this config
+
+
+Events
+------
+
+The table below lists every event defined in include/event.h together with the
+scope that handles it and the signals produced by that scope.
+
+ T = Trace (span) M = Metric (instrument) L = Log (log-record)
+
+ Stream lifecycle events:
+
+ Event Scope T M L
+ ---------------------------------------------------------------
+ on-stream-start on_stream_start x x x
+ on-stream-stop on_stream_stop - - x
+ on-idle-timeout on_idle_timeout x x x
+ on-backend-set on_backend_set x x x
+
+ Request analyzer events:
+
+ Event Scope T M L
+ --------------------------------------------------------------------------
+ on-client-session-start client_session_start x x x
+ on-frontend-tcp-request frontend_tcp_request x x x
+ on-http-wait-request http_wait_request x - x
+ on-http-body-request http_body_request x - x
+ on-frontend-http-request frontend_http_request x x x
+ on-switching-rules-request switching_rules_request x - x
+ on-backend-tcp-request backend_tcp_request x x x
+ on-backend-http-request backend_http_request x - x
+ on-process-server-rules-request process_server_rules_request x - x
+ on-http-process-request http_process_request x - x
+ on-tcp-rdp-cookie-request tcp_rdp_cookie_request x - x
+ on-process-sticking-rules-request process_sticking_rules_request x - x
+ on-http-headers-request http_headers_request x x x
+ on-http-end-request http_end_request x x x
+ on-client-session-end client_session_end - x x
+ on-server-unavailable server_unavailable - - x
+
+ Response analyzer events:
+
+ Event Scope T M L
+ --------------------------------------------------------------------------
+ on-server-session-start server_session_start x x x
+ on-tcp-response tcp_response x x x
+ on-http-wait-response http_wait_response x - x
+ on-process-store-rules-response process_store_rules_response x - x
+ on-http-response http_response x x x
+ on-http-headers-response http_headers_response x x x
+ on-http-end-response http_end_response x x x
+ on-http-reply http_reply x x x
+ on-server-session-end server_session_end - x x
+
+ Additionally, the http_response-error scope fires conditionally on the
+ on-http-response event when the response status is outside the 100-399 range,
+ setting an error status on the "HTTP response" span.
+
+ The http_response_group (http_response_1, http_response_2) and
+ http_after_response_group (http_after_response) are invoked via http-response
+ and http-after-response directives in haproxy.cfg.
+
+
+Instruments
+-----------
+
+Every instrument definition has at least one corresponding update.
+
+ Instrument name Type Defined in Updated in
+ -------------------------------------------------------------------------------
+ haproxy.sessions.active udcnt_int on_stream_start client_session_end
+ haproxy.fe.connections gauge_int on_stream_start http_response
+ idle.count cnt_int on_idle_timeout on_idle_timeout
+ haproxy.backend.set cnt_int on_backend_set on_backend_set
+ haproxy.client.session.start cnt_int client_session_start client_session_end
+ haproxy.tcp.request.fe cnt_int frontend_tcp_request frontend_http_request
+ haproxy.http.requests cnt_int frontend_http_request http_response
+ haproxy.http.latency hist_int frontend_http_request frontend_http_request,
+ http_response
+ haproxy.tcp.request.be cnt_int backend_tcp_request backend_http_request
+ haproxy.http.headers.request cnt_int http_headers_request http_end_request
+ haproxy.http.end.request cnt_int http_end_request client_session_end
+ haproxy.server.session.start cnt_int server_session_start server_session_end
+ haproxy.tcp.response cnt_int tcp_response http_wait_response
+ haproxy.http.headers.response cnt_int http_headers_response http_end_response
+ haproxy.http.end.response cnt_int http_end_response http_reply
+ haproxy.http.reply cnt_int http_reply server_session_end
+
+
+Span hierarchy
+--------------
+
+ Request path:
+
+ "HAProxy session" (root)
+ +-- "Client session"
+ +-- "Frontend TCP request"
+ +-- "HTTP wait request"
+ +-- "HTTP body request"
+ +-- "Frontend HTTP request" [link: "HAProxy session"]
+ +-- "Switching rules request"
+ +-- "Backend TCP request"
+ +-- "Backend HTTP request"
+ +-- "Process server rules request"
+ +-- "HTTP process request"
+ +-- "TCP RDP cookie request"
+ +-- "Process sticking rules request"
+ +-- "HTTP headers request"
+ +-- "HTTP end request"
+
+ Response path:
+
+ "HAProxy session" (root)
+ +-- "Server session" [link: "HAProxy session", "Client session"]
+ +-- "TCP response"
+ +-- "HTTP wait response"
+ +-- "Process store rules response"
+ +-- "HTTP response"
+ +-- "HTTP headers response"
+ +-- "HTTP end response"
+ +-- "HTTP reply"
+
+ Auxiliary spans:
+
+ "HAProxy session"
+ +-- "Backend set"
+ +-- "heartbeat" (on-idle-timeout, periodic)
+ +-- "HAProxy response" (http_after_response_group, on error)
+
+
+Running the test
+----------------
+
+From the test/ directory:
+
+ % ./run-full.sh [/path/to/haproxy] [pidfile]
+
+If no arguments are given, the script looks for the haproxy binary three
+directories up from the current working directory. The backend origin server
+must be running on 127.0.0.1:8000.
--- /dev/null
+Standalone test configuration (sa/)
+=====================================
+
+The 'sa' test is a standalone single-instance configuration that
+exercises most HAProxy filter events with spans, attributes, events,
+links, baggage, status, metrics, logs and groups. It represents the
+most comprehensive single-instance configuration and is used as the
+worst-case scenario in speed tests.
+
+Six events are not covered by this configuration: on-backend-set,
+on-http-headers-request, on-http-end-request, on-http-headers-response,
+on-http-end-response and on-http-reply. The 'full' configuration
+extends 'sa' with those events.
+
+
+Files
+------
+
+ sa/otel.cfg OTel filter configuration (scopes, groups, instruments)
+ sa/haproxy.cfg HAProxy frontend/backend configuration
+ sa/otel.yml Exporter, processor, reader and provider definitions
+ run-sa.sh Convenience script to launch HAProxy with this config
+
+
+Events
+-------
+
+ T = Trace (span) M = Metric (instrument) L = Log (log-record)
+
+ Stream lifecycle events:
+
+ Event Scope T M L
+ ---------------------------------------------------------------
+ on-stream-start on_stream_start x x x
+ on-stream-stop on_stream_stop - - -
+ on-idle-timeout on_idle_timeout x x x
+
+ Request analyzer events:
+
+ Event Scope T M L
+ --------------------------------------------------------------------------
+ on-client-session-start client_session_start x - -
+ on-frontend-tcp-request frontend_tcp_request x - -
+ on-http-wait-request http_wait_request x - -
+ on-http-body-request http_body_request x - -
+ on-frontend-http-request frontend_http_request x x x
+ on-switching-rules-request switching_rules_request x - -
+ on-backend-tcp-request backend_tcp_request x - -
+ on-backend-http-request backend_http_request x - -
+ on-process-server-rules-request process_server_rules_request x - -
+ on-http-process-request http_process_request x - -
+ on-tcp-rdp-cookie-request tcp_rdp_cookie_request x - -
+ on-process-sticking-rules-request process_sticking_rules_request x - -
+ on-client-session-end client_session_end - x -
+ on-server-unavailable server_unavailable - - -
+
+ Response analyzer events:
+
+ Event Scope T M L
+ --------------------------------------------------------------------------
+ on-server-session-start server_session_start x - -
+ on-tcp-response tcp_response x - -
+ on-http-wait-response http_wait_response x - -
+ on-process-store-rules-response process_store_rules_response x - -
+ on-http-response http_response x x -
+ on-server-session-end server_session_end - - -
+
+ Additionally, the http_response-error scope fires conditionally on the
+ on-http-response event when the response status is outside the 100-399
+ range, setting an error status on the "HTTP response" span.
+
+ The http_response_group (http_response_1, http_response_2) and
+ http_after_response_group (http_after_response) are invoked via
+ http-response and http-after-response directives in haproxy.cfg.
+
+
+Instruments
+------------
+
+ Instrument name Type Defined in Updated in
+ --------------------------------------------------------------------------
+ haproxy.sessions.active udcnt_int on_stream_start client_session_end
+ haproxy.fe.connections gauge_int on_stream_start http_response
+ idle.count cnt_int on_idle_timeout on_idle_timeout
+ haproxy.http.requests cnt_int frontend_http_request http_response
+ haproxy.http.latency hist_int frontend_http_request frontend_http_request,
+ http_response
+
+
+Span hierarchy
+---------------
+
+ Request path:
+
+ "HAProxy session" (root)
+ +-- "Client session"
+ +-- "Frontend TCP request"
+ +-- "HTTP wait request"
+ +-- "HTTP body request"
+ +-- "Frontend HTTP request" [link: "HAProxy session"]
+ +-- "Switching rules request"
+ +-- "Backend TCP request"
+ +-- "Backend HTTP request"
+ +-- "Process server rules request"
+ +-- "HTTP process request"
+ +-- "TCP RDP cookie request"
+ +-- "Process sticking rules request"
+
+ Response path:
+
+ "HAProxy session" (root)
+ +-- "Server session" [link: "HAProxy session", "Client session"]
+ +-- "TCP response"
+ +-- "HTTP wait response"
+ +-- "Process store rules response"
+ +-- "HTTP response"
+
+ Auxiliary spans:
+
+ "HAProxy session"
+ +-- "heartbeat" (on-idle-timeout, periodic)
+ +-- "HAProxy response" (http_after_response_group, on error)
+
+
+Running the test
+-----------------
+
+From the test/ directory:
+
+ % ./run-sa.sh [/path/to/haproxy] [pidfile]
+
+If no arguments are given, the script looks for the haproxy binary three
+directories up from the current working directory. The backend origin
+server must be running on 127.0.0.1:8000.