diff --git a/addons/otel/test/full/haproxy.cfg b/addons/otel/test/full/haproxy.cfg new file mode 100644 index 000000000..0decae74c --- /dev/null +++ b/addons/otel/test/full/haproxy.cfg @@ -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 index 000000000..78f70fab0 --- /dev/null +++ b/addons/otel/test/full/otel.cfg @@ -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 index 000000000..03811bf1b --- /dev/null +++ b/addons/otel/test/full/otel.yml @@ -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 index 000000000..bfa9b1967 --- /dev/null +++ b/addons/otel/test/run-full.sh @@ -0,0 +1,16 @@ +#!/bin/sh -u +# +# Copyright 2026 HAProxy Technologies, Miroslav Zagorac +# +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