]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: otel: test: added full-event test config
authorMiroslav Zagorac <mzagorac@haproxy.com>
Fri, 13 Mar 2026 21:12:49 +0000 (22:12 +0100)
committerWilliam Lallemand <wlallemand@haproxy.com>
Mon, 13 Apr 2026 07:23:26 +0000 (09:23 +0200)
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.

addons/otel/test/full/haproxy.cfg [new file with mode: 0644]
addons/otel/test/full/otel.cfg [new file with mode: 0644]
addons/otel/test/full/otel.yml [new file with mode: 0644]
addons/otel/test/run-full.sh [new file with mode: 0755]

diff --git a/addons/otel/test/full/haproxy.cfg b/addons/otel/test/full/haproxy.cfg
new file mode 100644 (file)
index 0000000..0decae7
--- /dev/null
@@ -0,0 +1,28 @@
+global
+    stats socket /tmp/haproxy.sock mode 666 level admin
+
+listen stats
+    mode http
+    bind *:8001
+    stats uri /
+    stats admin if TRUE
+    stats refresh 10s
+
+frontend otel-test-full-frontend
+    bind *:10080
+    default_backend servers-backend
+
+    # ACL used to distinguish successful from error responses
+    acl acl-http-status-ok status 100:399
+
+    # OTel filter
+    filter opentelemetry id otel-test-full config full/otel.cfg
+
+    # run response scopes for successful responses
+    http-response otel-group otel-test-full http_response_group if acl-http-status-ok
+
+    # run after-response scopes for error responses
+    http-after-response otel-group otel-test-full http_after_response_group if !acl-http-status-ok
+
+backend servers-backend
+    server server-1 127.0.0.1:8000
diff --git a/addons/otel/test/full/otel.cfg b/addons/otel/test/full/otel.cfg
new file mode 100644 (file)
index 0000000..78f70fa
--- /dev/null
@@ -0,0 +1,280 @@
+[otel-test-full]
+    otel-instrumentation otel-test-instrumentation
+        debug-level 0x77f
+        log localhost:514 local7 debug
+        config full/otel.yml
+        option dontlog-normal
+        option hard-errors
+        no option disabled
+        rate-limit 100.0
+
+        groups http_response_group
+        groups http_after_response_group
+
+        scopes on_stream_start
+        scopes on_stream_stop
+        scopes on_idle_timeout
+        scopes on_backend_set
+
+        scopes client_session_start
+        scopes frontend_tcp_request
+        scopes http_wait_request
+        scopes http_body_request
+        scopes frontend_http_request
+        scopes switching_rules_request
+        scopes backend_tcp_request
+        scopes backend_http_request
+        scopes process_server_rules_request
+        scopes http_process_request
+        scopes tcp_rdp_cookie_request
+        scopes process_sticking_rules_request
+        scopes http_headers_request
+        scopes http_end_request
+        scopes client_session_end
+        scopes server_unavailable
+
+        scopes server_session_start
+        scopes tcp_response
+        scopes http_wait_response
+        scopes process_store_rules_response
+        scopes http_response http_response-error
+        scopes http_headers_response
+        scopes http_end_response
+        scopes http_reply
+        scopes server_session_end
+
+    otel-group http_response_group
+        scopes http_response_1
+        scopes http_response_2
+
+    otel-scope http_response_1
+        span "HTTP response"
+            event "event_content" "hdr.content" res.hdr("content-type") str("; length: ") res.hdr("content-length") str(" bytes")
+
+    otel-scope http_response_2
+        span "HTTP response"
+            event "event_date" "hdr.date" res.hdr("date") str(" / ") res.hdr("last-modified")
+
+    otel-group http_after_response_group
+        scopes http_after_response
+
+    otel-scope http_after_response
+        span "HAProxy response" parent "HAProxy session"
+            status "error" str("http.status_code: ") status
+
+    otel-scope on_stream_start
+        instrument udcnt_int "haproxy.sessions.active" desc "Active sessions"      value int(1)  unit "{session}"
+        instrument gauge_int "haproxy.fe.connections"  desc "Frontend connections" value fe_conn unit "{connection}"
+        span "HAProxy session" root
+            baggage "haproxy_id" var(sess.otel.uuid)
+            event "event_ip" "src" src str(":") src_port
+            event "event_be" "be"  be_id str(" ") be_name
+            event "event_ip" "dst" dst str(":") dst_port
+            event "event_fe" "fe"  fe_id str(" ") fe_name
+        log-record trace id 1000 event "session-start" span "HAProxy session" attr "attr_1_key" "attr_1_value" attr "attr_2_key" "attr_2_value" src str(":") src_port
+        acl acl-test-src-ip src 127.0.0.1
+        otel-event on-stream-start if acl-test-src-ip
+
+    otel-scope on_stream_stop
+        finish *
+        log-record info event "session-stop" str("stream stopped")
+        otel-event on-stream-stop
+
+    otel-scope on_idle_timeout
+        idle-timeout 1s
+        span "heartbeat" parent "HAProxy session"
+            attribute "idle.elapsed" str("idle-check")
+        instrument cnt_int "idle.count" value int(1)
+        instrument update "idle.count"
+        log-record info str("heartbeat")
+        otel-event on-idle-timeout
+
+    otel-scope on_backend_set
+        span "Backend set" parent "HAProxy session"
+            attribute "backend.name" be_name
+            attribute "backend.id" be_id
+        instrument cnt_int "haproxy.backend.set" desc "Backend assignments" value int(1) unit "{assignment}"
+        instrument update "haproxy.backend.set"
+        log-record info id 1010 event "backend-set" span "Backend set" be_name
+        otel-event on-backend-set
+
+    otel-scope client_session_start
+        span "Client session" parent "HAProxy session"
+        instrument cnt_int "haproxy.client.session.start" desc "Client session starts" value int(1) unit "{session}"
+        log-record info id 1001 event "client-session-start" span "Client session" src str(":") src_port
+        otel-event on-client-session-start
+
+    otel-scope frontend_tcp_request
+        span "Frontend TCP request" parent "Client session"
+        instrument cnt_int "haproxy.tcp.request.fe" desc "Frontend TCP requests" value int(1) unit "{request}"
+        log-record info event "frontend-tcp-request" span "Frontend TCP request" src str(":") src_port
+        otel-event on-frontend-tcp-request
+
+    otel-scope http_wait_request
+        span "HTTP wait request" parent "Frontend TCP request"
+        finish "Frontend TCP request"
+        log-record info event "http-wait-request" span "HTTP wait request" str("waiting")
+        otel-event on-http-wait-request
+
+    otel-scope http_body_request
+        span "HTTP body request" parent "HTTP wait request"
+        finish "HTTP wait request"
+        log-record info event "http-body-request" span "HTTP body request" str("body")
+        otel-event on-http-body-request
+
+    otel-scope frontend_http_request
+        instrument cnt_int  "haproxy.http.requests" desc "HTTP request count"   value int(1)     unit "{request}"
+        instrument hist_int "haproxy.http.latency"  desc "HTTP request latency" value lat_ns_tot unit "ns" aggr "histogram" bounds "1000 1000000 1000000000"
+        instrument update "haproxy.http.latency" attr "phase" "request"
+        instrument update "haproxy.tcp.request.fe"
+        span "Frontend HTTP request" parent "HTTP body request" link "HAProxy session"
+            attribute "http.method" method
+            attribute "http.url" url
+            attribute "http.version" str("HTTP/") req.ver
+        finish "HTTP body request"
+        log-record info id 1002 event "http-request" span "Frontend HTTP request" attr "http.method" "GET" method url
+        otel-event on-frontend-http-request
+
+    otel-scope switching_rules_request
+        span "Switching rules request" parent "Frontend HTTP request"
+        finish "Frontend HTTP request"
+        log-record info event "switching-rules" span "Switching rules request" be_name
+        otel-event on-switching-rules-request
+
+    otel-scope backend_tcp_request
+        span "Backend TCP request" parent "Switching rules request"
+        finish "Switching rules request"
+        instrument cnt_int "haproxy.tcp.request.be" desc "Backend TCP requests" value int(1) unit "{request}"
+        log-record info event "backend-tcp-request" span "Backend TCP request" be_name
+        otel-event on-backend-tcp-request
+
+    otel-scope backend_http_request
+        instrument update "haproxy.tcp.request.be"
+        span "Backend HTTP request" parent "Backend TCP request"
+        finish "Backend TCP request"
+        log-record info event "backend-http-request" span "Backend HTTP request" be_name
+        otel-event on-backend-http-request
+
+    otel-scope process_server_rules_request
+        span "Process server rules request" parent "Backend HTTP request"
+        finish "Backend HTTP request"
+        log-record info event "server-rules" span "Process server rules request" str("processing")
+        otel-event on-process-server-rules-request
+
+    otel-scope http_process_request
+        span "HTTP process request" parent "Process server rules request"
+        finish "Process server rules request"
+        log-record info event "http-process" span "HTTP process request" str("processing")
+        otel-event on-http-process-request
+
+    otel-scope tcp_rdp_cookie_request
+        span "TCP RDP cookie request" parent "HTTP process request"
+        finish "HTTP process request"
+        log-record info event "tcp-rdp-cookie" span "TCP RDP cookie request" str("cookie")
+        otel-event on-tcp-rdp-cookie-request
+
+    otel-scope process_sticking_rules_request
+        span "Process sticking rules request" parent "TCP RDP cookie request"
+        finish "TCP RDP cookie request"
+        log-record info event "sticking-rules" span "Process sticking rules request" str("sticking")
+        otel-event on-process-sticking-rules-request
+
+    otel-scope http_headers_request
+        span "HTTP headers request" parent "Process sticking rules request"
+        finish "Process sticking rules request"
+        instrument cnt_int "haproxy.http.headers.request" desc "Request headers processed" value int(1) unit "{header}"
+        log-record info event "http-headers-request" span "HTTP headers request" method url
+        otel-event on-http-headers-request
+
+    otel-scope http_end_request
+        span "HTTP end request" parent "HTTP headers request"
+        finish "HTTP headers request"
+        instrument cnt_int "haproxy.http.end.request" desc "Request end events" value int(1) unit "{request}"
+        instrument update "haproxy.http.headers.request"
+        log-record info event "http-end-request" span "HTTP end request" str("end")
+        otel-event on-http-end-request
+
+    otel-scope client_session_end
+        instrument update "haproxy.sessions.active"
+        instrument update "haproxy.client.session.start"
+        instrument update "haproxy.http.end.request"
+        finish "*req*"
+        log-record info event "client-session-end" str("session ended")
+        otel-event on-client-session-end
+
+    otel-scope server_unavailable
+        finish "*req*" "*res*"
+        log-record warn event "server-unavailable" str("503 Service Unavailable")
+        otel-event on-server-unavailable
+
+    otel-scope server_session_start
+        span "Server session" parent "HAProxy session"
+        link "HAProxy session" "Client session"
+        finish "HTTP end request"
+        instrument cnt_int "haproxy.server.session.start" desc "Server session starts" value int(1) unit "{session}"
+        log-record info event "server-session-start" span "Server session" str("server session")
+        otel-event on-server-session-start
+
+    otel-scope tcp_response
+        span "TCP response" parent "Server session"
+        instrument cnt_int "haproxy.tcp.response" desc "TCP responses" value int(1) unit "{response}"
+        log-record info event "tcp-response" span "TCP response" str("tcp response")
+        otel-event on-tcp-response
+
+    otel-scope http_wait_response
+        instrument update "haproxy.tcp.response"
+        span "HTTP wait response" parent "TCP response"
+        finish "TCP response"
+        log-record info event "http-wait-response" span "HTTP wait response" str("waiting")
+        otel-event on-http-wait-response
+
+    otel-scope process_store_rules_response
+        span "Process store rules response" parent "HTTP wait response"
+        finish "HTTP wait response"
+        log-record info event "store-rules" span "Process store rules response" str("store rules")
+        otel-event on-process-store-rules-response
+
+    otel-scope http_response
+        instrument update "haproxy.http.requests" attr "phase" "response"
+        instrument update "haproxy.http.latency"  attr "phase" "response"
+        instrument update "haproxy.fe.connections"
+        span "HTTP response" parent "Process store rules response"
+            attribute "http.status_code" status
+        finish "Process store rules response"
+        log-record info id 1003 event "http-response" span "HTTP response" status
+        otel-event on-http-response
+
+    otel-scope http_response-error
+        span "HTTP response"
+            status "error" str("http.status_code: ") status
+        otel-event on-http-response if !acl-http-status-ok
+
+    otel-scope http_headers_response
+        span "HTTP headers response" parent "HTTP response"
+        finish "HTTP response"
+        instrument cnt_int "haproxy.http.headers.response" desc "Response headers processed" value int(1) unit "{header}"
+        log-record info event "http-headers-response" span "HTTP headers response" status
+        otel-event on-http-headers-response
+
+    otel-scope http_end_response
+        span "HTTP end response" parent "HTTP headers response"
+        finish "HTTP headers response"
+        instrument cnt_int "haproxy.http.end.response" desc "Response end events" value int(1) unit "{response}"
+        instrument update "haproxy.http.headers.response"
+        log-record info event "http-end-response" span "HTTP end response" str("end")
+        otel-event on-http-end-response
+
+    otel-scope http_reply
+        span "HTTP reply" parent "HTTP end response"
+        finish "HTTP end response"
+        instrument cnt_int "haproxy.http.reply" desc "HTTP replies" value int(1) unit "{reply}"
+        instrument update "haproxy.http.end.response"
+        log-record info event "http-reply" span "HTTP reply" status
+        otel-event on-http-reply
+
+    otel-scope server_session_end
+        instrument update "haproxy.server.session.start"
+        instrument update "haproxy.http.reply"
+        finish "*res*"
+        log-record info event "server-session-end" str("server session ended")
+        otel-event on-server-session-end
diff --git a/addons/otel/test/full/otel.yml b/addons/otel/test/full/otel.yml
new file mode 100644 (file)
index 0000000..03811bf
--- /dev/null
@@ -0,0 +1,246 @@
+exporters:
+  exporter_traces_otlp_file:
+    type:           otlp_file
+    thread_name:    "OTLP/file trace"
+    file_pattern:   "__full_traces_log-%F-%N"
+    alias_pattern:  "__traces_log-latest"
+    flush_interval: 30000000
+    flush_count:    256
+    file_size:      134217728
+    rotate_size:    5
+
+  exporter_traces_otlp_grpc:
+    type:                             otlp_grpc
+    thread_name:                      "OTLP/gRPC trace"
+    endpoint:                         "http://localhost:4317/v1/traces"
+    use_ssl_credentials:              false
+#   ssl_credentials_cacert_path:      ""
+#   ssl_credentials_cacert_as_string: ""
+#   ssl_client_key_path:              ""
+#   ssl_client_key_string:            ""
+#   ssl_client_cert_path:             ""
+#   ssl_client_cert_string:           ""
+#   timeout:                          10
+#   user_agent:                       ""
+#   max_threads:                      0
+#   compression:                      ""
+#   max_concurrent_requests:          0
+
+  exporter_traces_otlp_http:
+    type:                        otlp_http
+    thread_name:                 "OTLP/HTTP trace"
+    endpoint:                    "http://localhost:4318/v1/traces"
+    content_type:                json
+    json_bytes_mapping:          hexid
+    debug:                       false
+    timeout:                     10
+    http_headers:
+      - X-OTel-Header-1:         "OTLP HTTP traces test header #1"
+      - X-OTel-Header-2:         "OTLP HTTP traces test header #2"
+    max_concurrent_requests:     64
+    max_requests_per_connection: 8
+    ssl_insecure_skip_verify:    true
+#   ssl_ca_cert_path:            ""
+#   ssl_ca_cert_string:          ""
+#   ssl_client_key_path:         ""
+#   ssl_client_key_string:       ""
+#   ssl_client_cert_path:        ""
+#   ssl_client_cert_string:      ""
+#   ssl_min_tls:                 ""
+#   ssl_max_tls:                 ""
+#   ssl_cipher:                  ""
+#   ssl_cipher_suite:            ""
+#   compression:                 ""
+
+  exporter_traces_dev_null:
+    type:     ostream
+    filename: /dev/null
+
+  exporter_traces_ostream:
+    type:     ostream
+    filename: __full_traces
+
+  exporter_traces_memory:
+    type:        memory
+    buffer_size: 256
+
+  exporter_traces_zipkin:
+    type:           zipkin
+    endpoint:       "http://localhost:9411/api/v2/spans"
+    format:         json
+    service_name:   "zipkin-service"
+#   ipv4:           ""
+#   ipv6:           ""
+
+  exporter_metrics_otlp_file:
+    type:           otlp_file
+    thread_name:    "OTLP/file metr"
+    file_pattern:   "__full_metrics_log-%F-%N"
+    alias_pattern:  "__metrics_log-latest"
+    flush_interval: 30000000
+    flush_count:    256
+    file_size:      134217728
+    rotate_size:    5
+
+  exporter_metrics_otlp_grpc:
+    type:                otlp_grpc
+    thread_name:         "OTLP/gRPC metr"
+    endpoint:            "http://localhost:4317/v1/metrics"
+    use_ssl_credentials: false
+
+  exporter_metrics_otlp_http:
+    type:                        otlp_http
+    thread_name:                 "OTLP/HTTP metr"
+    endpoint:                    "http://localhost:4318/v1/metrics"
+    content_type:                json
+    debug:                       false
+    timeout:                     10
+    http_headers:
+      - X-OTel-Header-1:         "OTLP HTTP metrics test header #1"
+      - X-OTel-Header-2:         "OTLP HTTP metrics test header #2"
+    max_concurrent_requests:     64
+    max_requests_per_connection: 8
+    ssl_insecure_skip_verify:    true
+
+  exporter_metrics_dev_null:
+    type:     ostream
+    filename: /dev/null
+
+  exporter_metrics_ostream:
+    type:     ostream
+    filename: __full_metrics
+
+  exporter_metrics_memory:
+    type:        memory
+    buffer_size: 256
+
+  exporter_logs_otlp_file:
+    type:           otlp_file
+    thread_name:    "OTLP/file logs"
+    file_pattern:   "__full_logs_log-%F-%N"
+    alias_pattern:  "__logs_log-latest"
+    flush_interval: 30000000
+    flush_count:    256
+    file_size:      134217728
+    rotate_size:    5
+
+  exporter_logs_otlp_grpc:
+    type:                otlp_grpc
+    thread_name:         "OTLP/gRPC logs"
+    endpoint:            "http://localhost:4317/v1/logs"
+    use_ssl_credentials: false
+
+  exporter_logs_otlp_http:
+    type:                        otlp_http
+    thread_name:                 "OTLP/HTTP logs"
+    endpoint:                    "http://localhost:4318/v1/logs"
+    content_type:                json
+    debug:                       false
+    timeout:                     10
+    http_headers:
+      - X-OTel-Header-1:         "OTLP HTTP logs test header #1"
+      - X-OTel-Header-2:         "OTLP HTTP logs test header #2"
+    max_concurrent_requests:     64
+    max_requests_per_connection: 8
+    ssl_insecure_skip_verify:    true
+
+  exporter_logs_dev_null:
+    type:     ostream
+    filename: /dev/null
+
+  exporter_logs_ostream:
+    type:     ostream
+    filename: __full_logs
+
+  exporter_logs_elasticsearch:
+    type:             elasticsearch
+    host:             localhost
+    port:             9200
+    index:            logs
+    response_timeout: 30
+    debug:            false
+    http_headers:
+      - X-OTel-Header-1: "Elasticsearch logs test header #1"
+      - X-OTel-Header-2: "Elasticsearch logs test header #2"
+
+readers:
+  reader_metrics:
+    thread_name:     "reader metr"
+    export_interval: 10000
+    export_timeout:  5000
+
+samplers:
+  sampler_traces:
+#   type:  always_on
+#   type:  always_off
+#   type:  trace_id_ratio_based
+#   ratio: 1.0
+    type:     parent_based
+    delegate: always_on
+
+processors:
+  processor_traces_batch:
+    type:                  batch
+    thread_name:           "proc/batch trac"
+    # Note: when the queue is half full, a preemptive notification is sent
+    # to start the export call.
+    max_queue_size:        2048
+    # Time interval (in ms) between two consecutive exports
+    schedule_delay:        5000
+    # Export 'max_export_batch_size' after every `schedule_delay' milliseconds.
+    max_export_batch_size: 512
+
+  processor_traces_single:
+    type:                  single
+
+  processor_logs_batch:
+    type:                  batch
+    thread_name:           "proc/batch logs"
+    max_queue_size:        2048
+    schedule_delay:        5000
+    max_export_batch_size: 512
+
+  processor_logs_single:
+    type:                  single
+
+providers:
+  provider_traces:
+    resources:
+      - service.version:     "1.0.0"
+      - service.instance.id: "id-full"
+      - service.name:        "full"
+      - service.namespace:   "HAProxy traces test"
+
+  provider_metrics:
+    resources:
+      - service.version:     "1.0.0"
+      - service.instance.id: "id-full"
+      - service.name:        "full"
+      - service.namespace:   "HAProxy metrics test"
+
+  provider_logs:
+    resources:
+      - service.version:     "1.0.0"
+      - service.instance.id: "id-full"
+      - service.name:        "full"
+      - service.namespace:   "HAProxy logs test"
+
+signals:
+  traces:
+    scope_name: "HAProxy OTEL - traces"
+    exporters:  exporter_traces_otlp_http
+    samplers:   sampler_traces
+    processors: processor_traces_batch
+    providers:  provider_traces
+
+  metrics:
+    scope_name: "HAProxy OTEL - metrics"
+    exporters:  exporter_metrics_otlp_http
+    readers:    reader_metrics
+    providers:  provider_metrics
+
+  logs:
+    scope_name: "HAProxy OTEL - logs"
+    exporters:  exporter_logs_otlp_http
+    processors: processor_logs_batch
+    providers:  provider_logs
diff --git a/addons/otel/test/run-full.sh b/addons/otel/test/run-full.sh
new file mode 100755 (executable)
index 0000000..bfa9b19
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh -u
+#
+# Copyright 2026 HAProxy Technologies, Miroslav Zagorac <mzagorac@haproxy.com>
+#
+SH_ARG_HAPROXY="${1:-$(realpath -L ${PWD}/../../../haproxy)}"
+SH_ARG_PIDFILE="${2:-haproxy.pid}"
+       SH_ARGS="-f haproxy-common.cfg -f full/haproxy.cfg -p "${SH_ARG_PIDFILE}""
+    SH_LOG_DIR="_logs"
+        SH_LOG="${SH_LOG_DIR}/_log-$(basename "${0}" .sh)-$(date +%s)"
+
+
+test -x "${SH_ARG_HAPROXY}" || exit 1
+mkdir -p "${SH_LOG_DIR}"    || exit 2
+
+echo "executing: ${SH_ARG_HAPROXY} ${SH_ARGS}" >${SH_LOG}
+"${SH_ARG_HAPROXY}" ${SH_ARGS} >>"${SH_LOG}" 2>&1