+++ /dev/null
-Miroslav Zagorac <mzagorac@haproxy.com>
+++ /dev/null
-Miroslav Zagorac <mzagorac@haproxy.com>
+++ /dev/null
-# USE_OTEL : enable the OpenTelemetry filter
-# OTEL_DEBUG : compile the OpenTelemetry filter in debug mode
-# OTEL_INC : force the include path to libopentelemetry-c-wrapper
-# OTEL_LIB : force the lib path to libopentelemetry-c-wrapper
-# OTEL_RUNPATH : add libopentelemetry-c-wrapper RUNPATH to haproxy executable
-# OTEL_USE_VARS : allows the use of variables for the OpenTelemetry context
-
-OTEL_DEFINE =
-OTEL_CFLAGS =
-OTEL_LDFLAGS =
-OTEL_DEBUG_EXT =
-OTEL_PKGSTAT =
-OTELC_WRAPPER = opentelemetry-c-wrapper
-
-ifneq ($(OTEL_DEBUG:0=),)
-OTEL_DEBUG_EXT = _dbg
-OTEL_DEFINE = -DDEBUG_OTEL
-endif
-
-ifeq ($(OTEL_INC),)
-OTEL_PKGSTAT = $(shell pkg-config --exists $(OTELC_WRAPPER)$(OTEL_DEBUG_EXT); echo $$?)
-OTEL_CFLAGS = $(shell pkg-config --silence-errors --cflags $(OTELC_WRAPPER)$(OTEL_DEBUG_EXT))
-else
-ifneq ($(wildcard $(OTEL_INC)/$(OTELC_WRAPPER)/.*),)
-OTEL_CFLAGS = -I$(OTEL_INC) $(if $(OTEL_DEBUG),-DOTELC_DBG_MEM)
-endif
-endif
-
-ifeq ($(OTEL_PKGSTAT),)
-ifeq ($(OTEL_CFLAGS),)
-$(error OpenTelemetry C wrapper : can't find headers)
-endif
-else
-ifneq ($(OTEL_PKGSTAT),0)
-$(error OpenTelemetry C wrapper : can't find package)
-endif
-endif
-
-ifeq ($(OTEL_LIB),)
-OTEL_LDFLAGS = $(shell pkg-config --silence-errors --libs $(OTELC_WRAPPER)$(OTEL_DEBUG_EXT))
-else
-ifneq ($(wildcard $(OTEL_LIB)/lib$(OTELC_WRAPPER).*),)
-OTEL_LDFLAGS = -L$(OTEL_LIB) -l$(OTELC_WRAPPER)$(OTEL_DEBUG_EXT)
-ifneq ($(OTEL_RUNPATH),)
-OTEL_LDFLAGS += -Wl,--rpath,$(OTEL_LIB)
-endif
-endif
-endif
-
-ifeq ($(OTEL_LDFLAGS),)
-$(error OpenTelemetry C wrapper : can't find library)
-endif
-
-OPTIONS_OBJS += \
- addons/otel/src/cli.o \
- addons/otel/src/conf.o \
- addons/otel/src/event.o \
- addons/otel/src/filter.o \
- addons/otel/src/group.o \
- addons/otel/src/http.o \
- addons/otel/src/otelc.o \
- addons/otel/src/parser.o \
- addons/otel/src/pool.o \
- addons/otel/src/scope.o \
- addons/otel/src/util.o
-
-ifneq ($(OTEL_USE_VARS:0=),)
-OTEL_DEFINE += -DUSE_OTEL_VARS
-OPTIONS_OBJS += addons/otel/src/vars.o
-
-# Auto-detect whether struct var has a 'name' member. When present,
-# prefix-based variable scanning can be used instead of the tracking
-# buffer approach.
-OTEL_VAR_HAS_NAME := $(shell awk '/^struct var \{/,/^\}/' include/haproxy/vars-t.h 2>/dev/null | grep -q '[*]name;' && echo 1)
-ifneq ($(OTEL_VAR_HAS_NAME),)
-OTEL_DEFINE += -DUSE_OTEL_VARS_NAME
-endif
-endif
-
-OTEL_CFLAGS := $(OTEL_CFLAGS) -Iaddons/otel/include $(OTEL_DEFINE)
+++ /dev/null
- -----------------------------------------
- The HAProxy OpenTelemetry filter (OTel)
- Version 1.0
- ( Last update: 2026-03-18 )
- -----------------------------------------
- Author : Miroslav Zagorac
- Contact : mzagorac at haproxy dot com
-
-
-SUMMARY
---------
-
- 0. Terms
- 1. Introduction
- 2. Build instructions
- 3. Basic concepts in OpenTelemetry
- 4. OTel configuration
- 4.1. OTel scope
- 4.2. "otel-instrumentation" section
- 4.3. "otel-scope" section
- 4.4. "otel-group" section
- 5. Examples
- 5.1. Benchmarking results
- 6. OTel CLI
- 7. Known bugs and limitations
-
-
-0. Terms
----------
-
-* OTel: The HAProxy OpenTelemetry filter
-
-OTel is the HAProxy filter that allows you to send telemetry data (traces,
-metrics and logs) to observability backends via the OpenTelemetry protocol.
-
-
-1. Introduction
-----------------
-
-Nowadays there is a growing need to divide a process into microservices and
-there is a problem of monitoring the work of the same process. One way to solve
-this problem is to use a distributed tracing service in a central location.
-
-The OTel filter is the successor to the OpenTracing (OT) filter and is built on
-the OpenTelemetry standard, which unifies distributed tracing, metrics and
-logging into a single observability framework. Unlike the older OpenTracing
-filter which relied on vendor-specific tracer plugins, the OTel filter uses the
-OpenTelemetry protocol (OTLP) to export data directly to any compatible backend.
-
-The OTel filter is a standard HAProxy filter, so what applies to others also
-applies to this one (of course, by that I mean what is described in the
-documentation, more precisely in the doc/internals/filters.txt file).
-
-The OTel filter activation is done explicitly by specifying it in the HAProxy
-configuration. If this is not done, the OTel filter in no way participates in
-the work of HAProxy.
-
-As for the impact on HAProxy speed, this is documented with test results located
-in the test directory (see section 5.1). The speed of operation depends on the
-way the filter is used and the complexity of the configuration. In typical
-production use with a rate limit of 10% or less, the performance impact should
-be negligible (see the 'rate-limit' keyword).
-
-The OTel filter allows intensive use of ACLs, which can be defined anywhere in
-the configuration. Thus, it is possible to use the filter only for those
-connections that are of interest to us.
-
-
-2. Build instructions
-----------------------
-
-OTel is the HAProxy filter and as such is compiled together with HAProxy.
-
-To communicate with an OpenTelemetry compatible backend, the OTel filter uses
-the OpenTelemetry C Wrapper library (which again uses the OpenTelemetry C++
-SDK). This means that we must have the library installed on the system on which
-we want to compile or use HAProxy.
-
-Instructions for compiling and installing the required library can be found at
-https://github.com/haproxytech/opentelemetry-c-wrapper .
-
-The OTel filter can be more easily compiled using the pkg-config tool, if we
-have the OpenTelemetry C Wrapper library installed so that it contains
-pkg-config files (which have the .pc extension). If the pkg-config tool cannot
-be used, then the path to the directory where the include files and libraries
-are located can be explicitly specified.
-
-Below are examples of the two ways to compile HAProxy with the OTel filter, the
-first using the pkg-config tool and the second explicitly specifying the path to
-the OpenTelemetry C Wrapper include and library.
-
-Note: prompt '%' indicates that the command is executed under an unprivileged
- user, while prompt '#' indicates that the command is executed under the
- root user.
-
-Example of compiling HAProxy using the pkg-config tool (assuming the
-OpenTelemetry C Wrapper library is installed in the /opt directory):
-
- % PKG_CONFIG_PATH=/opt/lib/pkgconfig make -j8 USE_OTEL=1 TARGET=linux-glibc
-
-The OTel filter can also be compiled in debug mode as follows:
-
- % PKG_CONFIG_PATH=/opt/lib/pkgconfig make -j8 USE_OTEL=1 OTEL_DEBUG=1 TARGET=linux-glibc
-
-HAProxy compilation example explicitly specifying path to the OpenTelemetry C
-Wrapper include and library:
-
- % make -j8 USE_OTEL=1 OTEL_INC=/opt/include OTEL_LIB=/opt/lib TARGET=linux-glibc
-
-In case we want to use debug mode, then it looks like this:
-
- % make -j8 USE_OTEL=1 OTEL_DEBUG=1 OTEL_INC=/opt/include OTEL_LIB=/opt/lib TARGET=linux-glibc
-
-To enable OpenTelemetry context propagation via HAProxy variables (in addition
-to HTTP headers), add the OTEL_USE_VARS=1 option:
-
- % PKG_CONFIG_PATH=/opt/lib/pkgconfig make -j8 USE_OTEL=1 OTEL_USE_VARS=1 TARGET=linux-glibc
-
-If the library we want to use is not installed on a unix system, then a locally
-installed library can be used (say, which is compiled and installed in the user
-home directory). In this case instead of /opt/include and /opt/lib the
-equivalent paths to the local installation should be specified. Of course, in
-that case the pkg-config tool can also be used if we have a complete
-installation (with .pc files).
-
-Last but not least, if the pkg-config tool is not used when compiling, then the
-HAProxy executable may not be able to find the OpenTelemetry C Wrapper library
-at startup. This can be solved in several ways, for example using the
-LD_LIBRARY_PATH environment variable which should be set to the path where the
-library is located before starting the HAProxy.
-
- % LD_LIBRARY_PATH=/opt/lib /path-to/haproxy ...
-
-Another way is to add RUNPATH to HAProxy executable that contains the path to
-the library in question.
-
- % make -j8 USE_OTEL=1 OTEL_RUNPATH=1 OTEL_INC=/opt/include OTEL_LIB=/opt/lib TARGET=linux-glibc
-
-After HAProxy is compiled, we can check if the OTel filter is enabled:
-
- % ./haproxy -vv | grep opentelemetry
- --- command output ----------
- [ OTel] opentelemetry
- --- command output ----------
-
-A summary of all OTel build options:
-
- USE_OTEL - enable the OpenTelemetry filter
- OTEL_DEBUG - compile the filter in debug mode
- OTEL_INC - force path to opentelemetry-c-wrapper include files
- OTEL_LIB - force path to opentelemetry-c-wrapper library
- OTEL_RUNPATH - add opentelemetry-c-wrapper RUNPATH to executable
- OTEL_USE_VARS - enable context propagation via HAProxy variables
-
-
-3. Basic concepts in OpenTelemetry
------------------------------------
-
-Basic concepts of OpenTelemetry can be read on the OpenTelemetry documentation
-website https://opentelemetry.io/docs/concepts/ .
-
-Here we will list only the most important elements of distributed tracing.
-
-A 'trace' is a description of the complete transaction we want to record in the
-tracing system. A 'span' is an operation that represents a unit of work that is
-recorded in a tracing system. A 'span context' is a group of information
-related to a particular span that is passed on to the system (from service to
-service). Using this context, we can add new spans to already open trace (or
-supplement data in already open spans).
-
-An individual span may contain one or more attributes, events, links and baggage
-items.
-
-An 'attribute' is a key-value element that is valid for the entire span.
-Attributes describe properties of the span such as HTTP method, URL, status
-code, and so on.
-
-A span 'event' is a named key-value element that allows you to write some data
-at a certain time within the span's lifetime. It can be used for debugging or
-recording notable occurrences.
-
-A 'link' is a reference to another span (possibly in a different trace) that is
-causally related to the current span. Unlike the parent-child relationship,
-links represent non-hierarchical associations between spans.
-
-A 'baggage' item is a key-value data pair that can be used for the duration of
-an entire trace, from the moment it is added to the span.
-
-A span 'status' indicates the outcome of the operation: unset (default), ok
-(successful) or error (failed). An optional description string can accompany
-the error status.
-
-
-4. OTel configuration
-----------------------
-
-The OTel filter must also be included in the HAProxy configuration, in the
-proxy section (frontend / listen / backend):
-
- frontend otel-test
- ...
- filter opentelemetry [id <id>] config <file>
- ...
-
-If no filter id is specified, 'otel-filter' is used as default. The 'config'
-parameter must be specified and it contains the path of the OTel filter
-configuration file. This file defines the OTel scopes, groups and
-instrumentation sections (see section 4.1). The YAML configuration for the
-OpenTelemetry SDK is a separate file, referenced by the 'config' keyword inside
-the "otel-instrumentation" section (see section 4.2).
-
-
-4.1 OTel scope
----------------
-
-If the filter id is defined for the OTel filter, then the OTel scope with the
-same name should be defined in the configuration file. In the same
-configuration file we can have several defined OTel scopes.
-
-Each OTel scope must have a defined (only one) "otel-instrumentation" section
-that is used to configure the operation of the OTel filter and define the used
-groups and scopes.
-
-OTel scope starts with the id of the filter specified in square brackets and
-ends with the end of the file or when a new OTel scope is defined.
-
-For example, this defines two OTel scopes in the same configuration file:
- [my-first-otel-filter]
- otel-instrumentation instrumentation1
- ...
- otel-group group1
- ...
- otel-scope scope1
- ...
-
- [my-second-otel-filter]
- ...
-
-
-4.2. "otel-instrumentation" section
--------------------------------------
-
-Only one "otel-instrumentation" section must be defined for each OTel scope.
-
-The mandatory 'config' keyword defines the YAML configuration file for the
-OpenTelemetry SDK. This file specifies the telemetry pipeline: exporters,
-processors, samplers, providers and signals.
-
-Through optional keywords can be defined ACLs, logging, rate limit, and groups
-and scopes that define the tracing model.
-
-
-otel-instrumentation <name>
- A new OTel instrumentation with the name <name> is created.
-
- Arguments :
- name - the name of the OpenTelemetry instrumentation section
-
-
- The following keywords are supported in this section:
- - mandatory keywords:
- - config
-
- - optional keywords:
- - acl
- - debug-level
- - groups
- - [no] log
- - [no] option disabled
- - [no] option dontlog-normal
- - [no] option hard-errors
- - rate-limit
- - scopes
-
-
-acl <aclname> <criterion> [flags] [operator] <value> ...
- Declare or complete an access list.
-
- To configure and use the ACL, see section 7 of the HAProxy Configuration
- Manual.
-
-
-config <file>
- The mandatory keyword associated with the OTel instrumentation configuration.
- This keyword sets the path of the YAML configuration file for the
- OpenTelemetry SDK. The YAML file defines the complete telemetry pipeline
- including exporters, samplers, processors, providers and signal routing.
-
- The YAML configuration file supports the following top-level sections:
-
- 'exporters' - defines telemetry data destinations. Supported exporter types
- are:
- - otlp_grpc : export via OTLP over gRPC
- - otlp_http : export via OTLP over HTTP (JSON or Protobuf)
- - otlp_file : export to local files in OTLP format
- - zipkin : export to Zipkin-compatible backends
- - elasticsearch : export to Elasticsearch
- - ostream : write to a file (text output, useful for debugging)
- - memory : in-memory buffer (useful for testing)
-
- 'samplers' - defines trace sampling strategies. Supported types:
- - always_on : sample every trace
- - always_off : sample no traces
- - trace_id_ratio_based : sample a fraction of traces (set by ratio)
- - parent_based : sampling decision based on parent span
-
- 'processors' - defines how telemetry data is processed before export:
- - batch : batch spans before exporting (configurable queue size, export
- interval and batch size)
- - single : export each span individually
-
- 'readers' - defines metric readers with configurable export interval and
- timeout.
-
- 'providers' - defines resource attributes (service name, version, instance ID,
- namespace, etc.) that are attached to all telemetry data.
-
- 'signals' - binds the above components together for each signal type (traces,
- metrics, logs), specifying which exporter, sampler, processor, reader and
- provider to use.
-
- Arguments :
- file - the path of the YAML configuration file
-
-
-debug-level <value>
- This keyword sets the value of the debug level related to the display of debug
- messages in the OTel filter. The 'debug-level' value is a bitmask, ie a
- single value bit enables or disables the display of the corresponding debug
- message that uses that bit. The default value is set via the
- FLT_OTEL_DEBUG_LEVEL macro in the include/config.h file. Debug level value is
- used only if the OTel filter is compiled with the debug mode enabled,
- otherwise it is ignored.
-
- Arguments :
- value - bitmask value (hexadecimal notation, e.g. 0x77f)
-
-
-groups <name> ...
- A list of "otel-group" groups used for the currently defined instrumentation
- is declared. Several groups can be specified in one line.
-
- Arguments :
- name - the name of the OTel group
-
-
-log global
-log <addr> [len <len>] [format <fmt>] <facility> [<level> [<minlevel>]]
-no log
- Enable per-instance logging of events and traffic.
-
- To configure and use the logging system, see section 4.2 of the HAProxy
- Configuration Manual.
-
-
-option disabled
-no option disabled
- Keyword which turns the operation of the OTel filter on or off. By default
- the filter is on.
-
-
-option dontlog-normal
-no option dontlog-normal
- Enable or disable logging of normal, successful processing. By default, this
- option is disabled. For this option to be considered, logging must be turned
- on.
-
- See also: 'log' keyword description.
-
-
-option hard-errors
-no option hard-errors
- During the operation of the filter, some errors may occur, caused by incorrect
- configuration of the instrumentation or some error related to the operation of
- HAProxy. By default, such an error will not interrupt the filter operation
- for the stream in which the error occurred. If the 'hard-errors' option is
- enabled, the operation error prohibits all further processing of events and
- groups in the stream in which the error occurred.
-
-
-rate-limit <value>
- This option allows limiting the use of the OTel filter, ie it can be
- influenced whether the OTel filter is activated for a stream or not.
- Determining whether or not a filter is activated depends on the value of this
- option that is compared to a randomly selected value when attaching the filter
- to the stream. By default, the value of this option is set to 100.0, ie the
- OTel filter is activated for each stream.
-
- Arguments :
- value - floating point value ranging from 0.0 to 100.0
-
-
-scopes <name> ...
- This keyword declares a list of "otel-scope" definitions used for the
- currently defined instrumentation. Multiple scopes can be specified in the
- same line.
-
- Arguments :
- name - the name of the OTel scope
-
-
-4.3. "otel-scope" section
---------------------------
-
-Stream processing begins with filter attachment, then continues with the
-processing of a number of defined events and groups, and ends with filter
-detachment. The "otel-scope" section is used to define actions related to
-individual events. However, this section may be part of a group, so the event
-does not have to be part of the definition.
-
-
-otel-scope <name>
- Creates a new OTel scope definition named <name>.
-
- Arguments :
- name - the name of the OTel scope
-
-
- The following keywords are supported in this section:
- - acl
- - attribute
- - baggage
- - event
- - extract
- - finish
- - idle-timeout
- - inject
- - instrument
- - link
- - log-record
- - otel-event
- - span
- - status
-
-
-acl <aclname> <criterion> [flags] [operator] <value> ...
- Declare or complete an access list.
-
- To configure and use the ACL, see section 7 of the HAProxy Configuration
- Manual.
-
-
-attribute <key> <sample> ...
- This keyword allows setting an attribute for the currently active span. The
- first argument is the name of the attribute (key) and the rest are its value.
- A value can consist of one or more sample expressions. If the value is only
- one sample, then the type of that data depends on the type of the HAProxy
- sample. If the value contains more samples, then the data type is string.
- The data conversion table is below:
-
- HAProxy sample data type | the OpenTelemetry data type
- --------------------------+----------------------------
- NULL | NULL
- BOOL | BOOL
- INT32 | INT64
- UINT32 | UINT64
- INT64 | INT64
- UINT64 | UINT64
- IPV4 | STRING
- IPV6 | STRING
- STRING | STRING
- BINARY | UNSUPPORTED
- --------------------------+----------------------------
-
- Arguments :
- key - key part of a data pair (attribute name)
- sample - sample expression (value part of a data pair), at least
- one sample must be present
-
-
-baggage <key> <sample> ...
- Baggage items allow the propagation of data between spans, ie allow the
- assignment of metadata that is propagated to future children spans. This data
- is formatted in the style of key-value pairs and is part of the context that
- can be transferred between processes that are part of a server architecture.
-
- This keyword allows setting the baggage for the currently active span. The
- data type is always a string, ie any sample type is converted to a string.
- The exception is a binary value that is not supported by the OTel filter.
-
- See the 'attribute' keyword description for the data type conversion table.
-
- Arguments :
- key - key part of a data pair
- sample - sample expression (value part of a data pair), at least one sample
- must be present
-
-
-event <name> <key> <sample> ...
- This keyword allows adding a span event to the currently active span. A span
- event is a named, timestamped annotation with optional attributes. The data
- type is always a string, ie any sample type is converted to a string.
-
- See the 'attribute' keyword description for the data type conversion table.
-
- Arguments :
- name - name of the span event
- key - key part of a data pair (attribute name within the event)
- sample - sample expression (value part of a data pair), at least one sample
- must be present
-
-
-extract <name-prefix> [use-vars | use-headers]
- For a more detailed description of the propagation process of the span
- context, see the description of the keyword 'inject'. Only the process of
- extracting data from the carrier is described here.
-
- The default carrier is HTTP headers. If OTEL_USE_VARS is enabled at compile
- time, the 'use-vars' option can be used instead to extract context from
- HAProxy variables.
-
- Arguments :
- name-prefix - data name prefix (ie key element prefix)
- use-vars - data is extracted from HAProxy variables
- use-headers - data is extracted from the HTTP header
-
-
- Below is an example of using HAProxy variables to transfer span context data:
-
- --- test/ctx/otel.cfg -----------------------------------------------
- ...
- otel-scope client_session_start_2
- extract "otel_ctx_1" use-vars
- span "Client session" parent "otel_ctx_1"
- ...
- ---------------------------------------------------------------------
-
-
-finish <name> ...
- Closing a particular span or span context. Instead of the name of the span,
- there are several specially predefined names with which we can finish certain
- groups of spans. So it can be used as the name '*req*' for all open spans
- related to the request channel, '*res*' for all open spans related to the
- response channel and '*' for all open spans regardless of which channel they
- are related to. Several spans and/or span contexts can be specified in one
- line.
-
- Arguments :
- name - the name of the span or span context
-
-
-inject <name-prefix> [use-vars] [use-headers]
- In OpenTelemetry, the transfer of data related to the tracing process between
- microservices that are part of a larger service is done through the
- propagation of the span context. The basic operations that allow us to access
- and transfer this data are 'inject' and 'extract'.
-
- 'inject' allows us to extract span context so that the obtained data can be
- forwarded to another process (microservice) via the selected carrier. 'inject'
- in the name actually means inject data into carrier. Carrier is an interface
- here (ie a data structure) that allows us to transfer tracing state from one
- process to another.
-
- Data transfer can take place via one of two selected storage methods, the
- first is by adding data to the HTTP header and the second is by using HAProxy
- variables (the latter requires OTEL_USE_VARS=1 at compile time). Only data
- transfer via HTTP header can be used to transfer data to another process (ie
- microservice). All data is organized in the form of key-value data pairs.
-
- No matter which data transfer method you use, we need to specify a prefix for
- the key element. All alphanumerics (lowercase only) and underline character
- can be used to construct the data name prefix. Uppercase letters can actually
- be used, but they will be converted to lowercase when creating the prefix.
- The special prefix '-' can be used to generate the name automatically from the
- scope's event name or the span name.
-
- Arguments :
- name-prefix - data name prefix (ie key element prefix), or '-' for automatic
- naming
- use-vars - HAProxy variables are used to store and transfer data
- (requires OTEL_USE_VARS=1)
- use-headers - HTTP headers are used to store and transfer data
-
-
- Below is an example of using HTTP headers and variables to propagate the span
- context.
-
- --- test/ctx/otel.cfg -----------------------------------------------
- ...
- otel-scope client_session_start_1
- span "HAProxy session" root
- inject "otel_ctx_1" use-headers use-vars
- ...
- ---------------------------------------------------------------------
-
- Because HAProxy does not allow the '-' character in the variable name (which
- is automatically generated by the OpenTelemetry API and on which we have no
- influence), it is converted to the letter 'D'. We can see that there is no
- such conversion in the name of the HTTP header because the '-' sign is allowed
- there. Due to this conversion, initially all uppercase letters are converted
- to lowercase because otherwise we would not be able to distinguish whether the
- disputed sign '-' is used or not.
-
- Thus created HTTP headers and variables are deleted when executing the
- 'finish' keyword or when detaching the stream from the filter.
-
-
-instrument { update <name> [<attr>] | <type> <name> [<aggr>] [<desc>] [<unit>] <value> [<bounds>] }
- This keyword allows creating or updating metric instruments within the scope.
- Metric instruments record numerical measurements that are exported alongside
- traces.
-
- To create a new instrument, specify the instrument type, a name, and a sample
- expression providing the measurement value (preceded by the 'value' keyword).
- Optionally, a human-readable description (preceded by 'desc') and a unit
- string (preceded by 'unit') can be added.
-
- An aggregation type can be specified using the 'aggr' keyword followed by one
- of the supported aggregation types listed below. When specified, a metrics
- view is registered with the given aggregation strategy. If no aggregation
- type is specified, the SDK default is used.
-
- For histogram instruments (hist_int), optional bucket boundaries can be
- specified using the 'bounds' keyword followed by a double-quoted string of
- space-separated integers in strictly ascending order. When bounds are
- specified without an explicit aggregation type, histogram aggregation is
- used automatically.
-
- To update an existing instrument (previously created in another scope), use
- 'update' followed by the name of the instrument. Optional attributes can be
- added using the 'attr' keyword followed by a key and a sample expression
- evaluated at runtime.
-
- Supported instrument types:
- - cnt_int : counter (uint64)
- - hist_int : histogram (uint64)
- - udcnt_int : up-down counter (int64)
- - gauge_int : gauge (int64)
-
- Supported aggregation types:
- - drop : measurements are discarded
- - histogram : explicit bucket histogram
- - last_value : last recorded value
- - sum : sum of recorded values
- - default : SDK default for the instrument type
- - exp_histogram : base-2 exponential histogram
-
- Observable (asynchronous) instruments are not supported. The OpenTelemetry
- SDK invokes their callbacks from an external background thread that is not
- a HAProxy thread. HAProxy sample fetches rely on internal per-thread-group
- state and return incorrect results when called from a non-HAProxy thread.
-
- Double-precision types are not supported because HAProxy sample fetches do
- not return double values.
-
- For example:
- instrument cnt_int "my_counter" desc "Counter" value int(1)
- instrument hist_int "my_hist" aggr exp_histogram desc "Latency" value lat_ns_tot unit "ns"
- instrument hist_int "my_hist2" desc "Latency" value lat_ns_tot unit "ns" bounds "100 1000 10000 100000"
- instrument update "my_counter" attr "key1" str("val1")
-
- Arguments :
- type - the instrument type (see list above)
- name - the name of the instrument
- aggr - optional aggregation type (see list above)
- desc - optional human-readable description of the instrument
- unit - optional unit string for the instrument
- value - sample expression providing the measurement value
- bounds - optional histogram bucket boundaries (hist_int only)
- attr - attribute key and sample expression (update form only)
-
-
-log-record <severity> [id <integer>] [event <name>] [span <span-name>] [attr <key> <sample>] ... <sample> ...
- This keyword emits an OpenTelemetry log record within the scope. The first
- argument is a required severity level. Optional keywords follow in any order
- before the trailing sample expressions that form the log record body:
-
- id <integer> - numeric event identifier
- event <name> - event name string
- span <span-name> - associate the log record with an open span
- attr <key> <sample> - add an attribute evaluated at runtime (repeatable)
-
- The remaining arguments at the end are sample fetch expressions. A single
- sample preserves its native type; multiple samples are concatenated as a
- string.
-
- Supported severity levels follow the OpenTelemetry specification:
- trace, trace2, trace3, trace4, debug, debug2, debug3, debug4,
- info, info2, info3, info4, warn, warn2, warn3, warn4,
- error, error2, error3, error4, fatal, fatal2, fatal3, fatal4
-
- The log record is only emitted when the logger is enabled for the configured
- severity. If a 'span' reference is given but the named span is not found at
- runtime, the log record is emitted without span correlation.
-
- For example:
- log-record info str("heartbeat")
- log-record info id 1001 event "http-request" span "Frontend HTTP request" attr "http.method" method method url
- log-record trace id 1000 event "session-start" span "Client session" attr "src_ip" src src str(":") src_port
- log-record warn event "server-unavailable" str("503 Service Unavailable")
-
- Arguments :
- severity - the log severity level (see list above)
- id - optional numeric event identifier
- event - optional event name
- span - optional name of an open span to associate with
- attr - optional attribute key-value pairs (repeatable)
- sample - sample fetch expression(s) forming the log record body
-
-
-link <span> ...
- This keyword adds span links to the currently active span. A span link
- represents a causal relationship to another span without establishing a
- parent-child hierarchy. Links are useful for connecting spans across
- different traces or for associating related spans within the same trace.
-
- Multiple span names can be specified in one line. Each name is resolved at
- runtime by searching for an active span or an extracted context with that
- name. If a referenced span or context cannot be found, the link is silently
- skipped.
-
- Arguments :
- span - the name of a span or span context to link to
-
-
-otel-event <name> [{ if | unless } <condition>]
- Set the event that triggers the 'otel-scope' to which it is assigned.
- Optionally, it can be followed by an ACL-based condition, in which case it
- will only be evaluated if the condition is true.
-
- ACL-based conditions are executed in the context of a stream that processes
- the client and server connections. To configure and use the ACL, see section
- 7 of the HAProxy Configuration Manual.
-
- Arguments :
- name - the event name
- condition - a standard ACL-based condition
-
- Supported events are (the table gives the names of the events in the OTel
- filter and the corresponding equivalent in the SPOE filter):
-
- -------------------------------------|------------------------------
- the OTel filter | the SPOE filter
- -------------------------------------|------------------------------
- on-stream-start | -
- on-stream-stop | -
- on-idle-timeout | -
- on-backend-set | -
- -------------------------------------|------------------------------
- on-client-session-start | on-client-session
- on-frontend-tcp-request | on-frontend-tcp-request
- on-http-wait-request | -
- on-http-body-request | -
- on-frontend-http-request | on-frontend-http-request
- on-switching-rules-request | -
- on-backend-tcp-request | on-backend-tcp-request
- on-backend-http-request | on-backend-http-request
- on-process-server-rules-request | -
- on-http-process-request | -
- on-tcp-rdp-cookie-request | -
- on-process-sticking-rules-request | -
- on-http-headers-request | -
- on-http-end-request | -
- on-client-session-end | -
- on-server-unavailable | -
- -------------------------------------|------------------------------
- on-server-session-start | on-server-session
- on-tcp-response | on-tcp-response
- on-http-wait-response | -
- on-process-store-rules-response | -
- on-http-response | on-http-response
- on-http-headers-response | -
- on-http-end-response | -
- on-http-reply | -
- on-server-session-end | -
- -------------------------------------|------------------------------
-
- --- Stream lifecycle events (not tied to a channel analyzer) ---
-
- The on-stream-start and on-stream-stop events fire from the stream_start and
- stream_stop filter callbacks respectively, before any channel processing
- begins and after all channel processing ends. No channel is available at
- that point, so context injection/extraction via HTTP headers cannot be used
- in scopes bound to these events. Sample fetches in these scopes are not
- direction-constrained.
-
- The on-idle-timeout event fires periodically when the stream has no data
- transfer activity. It requires the 'idle-timeout' keyword to set the
- interval. This event is useful for heartbeat spans, idle-time metrics, and
- idle-time log records. It fires from the check_timeouts filter callback
- using HAProxy's tick-based timer infrastructure.
-
- The on-backend-set event fires from the stream_set_backend filter callback
- when a backend is assigned to the stream. It is not called if the frontend
- and the backend are the same proxy.
-
-
- --- Request channel events ---
-
- Analyzer events (tied to AN_REQ_* bits):
-
- The on-frontend-tcp-request event fires during frontend TCP content inspection
- (AN_REQ_INSPECT_FE).
-
- The on-http-wait-request event fires after the complete HTTP request has been
- received (AN_REQ_WAIT_HTTP). This is a post-analyzer event.
-
- The on-http-body-request event fires when the HTTP request body is available
- for inspection (AN_REQ_HTTP_BODY).
-
- The on-frontend-http-request event fires during frontend HTTP request
- processing: header rules, monitoring, statistics and redirects
- (AN_REQ_HTTP_PROCESS_FE).
-
- The on-switching-rules-request event fires when backend switching rules are
- evaluated (AN_REQ_SWITCHING_RULES).
-
- The on-backend-tcp-request event fires during backend TCP content inspection
- (AN_REQ_INSPECT_BE).
-
- The on-backend-http-request event fires during backend HTTP request processing
- (AN_REQ_HTTP_PROCESS_BE).
-
- The on-process-server-rules-request event fires when use-server rules are
- evaluated (AN_REQ_SRV_RULES).
-
- The on-http-process-request event fires during inner HTTP request processing
- (AN_REQ_HTTP_INNER).
-
- The on-tcp-rdp-cookie-request event fires when RDP cookie persistence is
- evaluated (AN_REQ_PRST_RDP_COOKIE).
-
- The on-process-sticking-rules-request event fires when stick-table persistence
- matching rules are evaluated (AN_REQ_STICKING_RULES).
-
- Non-analyzer events (not tied to AN_REQ_* bits):
-
- The on-client-session-start event fires when the request channel analysis
- begins. It corresponds to the start of a new client session.
-
- The on-http-headers-request event fires from the http_headers filter callback
- after all HTTP request headers have been parsed and analyzed.
-
- The on-http-end-request event fires from the http_end filter callback when all
- HTTP request data has been processed and forwarded.
-
- The on-client-session-end event fires when the request channel analysis ends.
-
- The on-server-unavailable event fires during request channel end-analysis when
- response analyzers were configured but never executed because the server was
- not reached.
-
-
- --- Response channel events ---
-
- Analyzer events (tied to AN_RES_* bits):
-
- The on-tcp-response event fires during TCP response content inspection
- (AN_RES_INSPECT).
-
- The on-http-wait-response event fires after the complete HTTP response has
- been received (AN_RES_WAIT_HTTP). This is a post-analyzer event.
-
- The on-process-store-rules-response event fires when stick-table store rules
- are evaluated (AN_RES_STORE_RULES).
-
- The on-http-response event fires during backend HTTP response processing
- (AN_RES_HTTP_PROCESS_BE).
-
- Non-analyzer events (not tied to AN_RES_* bits):
-
- The on-server-session-start event fires when the response channel analysis
- begins, after a server connection has been established.
-
- The on-http-headers-response event fires from the http_headers filter callback
- after all HTTP response headers have been parsed and analyzed.
-
- The on-http-end-response event fires from the http_end filter callback when
- all HTTP response data has been processed and forwarded.
-
- The on-http-reply event fires from the http_reply filter callback when HAProxy
- generates an internal reply (error page, deny response, redirect). It always
- fires on the response channel.
-
- The on-server-session-end event fires when the response channel analysis ends.
-
-
-idle-timeout <time>
- Set the idle timeout interval for a scope bound to the 'on-idle-timeout'
- event. The timer fires periodically at the given interval when the stream
- is idle. This keyword is mandatory for scopes using the 'on-idle-timeout'
- event and cannot be used with any other event.
-
- The <time> argument accepts the standard HAProxy time format: a number
- followed by a unit suffix (ms, s, m, h, d). A value of zero is not
- permitted.
-
- Arguments :
- time - the idle timeout interval (e.g. 5s, 500ms, 1m)
-
- Example :
- scopes on_idle_timeout
- ..
- otel-scope on_idle_timeout
- idle-timeout 5s
- span "heartbeat" root
- attribute "idle.elapsed" str("idle-check")
- instrument cnt_int "idle.count" value int(1)
- log-record info str("heartbeat")
- otel-event on-idle-timeout
-
-
-span <name> [<reference>] [<link>] [root]
- Creating a new span (or referencing an already opened one). If a new span is
- created, it can have a parent reference to another span or context, an inline
- link to another span, or be marked as a root span. If no reference is
- specified, the new span will become a root span. We need to pay attention to
- the fact that in one trace there can be only one root span. If a non-existent
- span is specified as a reference, a new span will not be created.
-
- The parent reference is set using the 'parent' keyword followed by the name of
- an existing span or extracted context. An inline link is set using the 'link'
- keyword followed by a span or context name. The 'root' keyword explicitly
- marks the span as a root span.
-
- For example:
- span "HAProxy session" root
- span "Client session" parent "HAProxy session"
- span "HTTP request" parent "TCP request" link "HAProxy session"
- span "Client session" parent "otel_ctx_1"
-
- Only one inline link can be specified per 'span' declaration. For multiple
- links, use the standalone 'link' keyword described above.
-
- Arguments :
- name - the name of the span being created or referenced
- (operation name)
- reference - 'parent <name>' or 'link <name>' or 'root'
-
-
-status <code> [<sample> ...]
- This keyword sets the status for the currently active span. The status
- indicates the outcome of the operation represented by the span.
-
- The status code is one of the following predefined values:
- - ignore : do not set any status (default)
- - unset : explicitly mark status as unset
- - ok : the operation completed successfully
- - error : the operation resulted in an error
-
- An optional description can follow the status code, consisting of one or more
- sample expressions whose values are concatenated as a string. The description
- is typically used with the 'error' status to provide additional context about
- the failure.
-
- For example:
- status "ok"
- status "error" str("http.status_code: ") status
-
- Arguments :
- code - the status code (ignore, unset, ok, error)
- sample - optional sample expression(s) for the status description
-
-
-4.4. "otel-group" section
---------------------------
-
-This section allows us to define a group of OTel scopes, that is not activated
-via an event but is triggered from TCP or HTTP rules. More precisely, these are
-the following rules: 'tcp-request', 'tcp-response', 'http-request',
-'http-response' and 'http-after-response'. These rules can be defined in the
-HAProxy configuration file.
-
-The action keyword used in these rules is 'otel-group', and it takes the filter
-id and the group name as arguments:
-
- http-response otel-group <filter-id> <group-name> [{ if | unless } ...]
-
-
-otel-group <name>
- Creates a new OTel group definition named <name>.
-
- Arguments :
- name - the name of the OTel group
-
-
- The following keywords are supported in this section:
- - scopes
-
-
-scopes <name> ...
- 'otel-scope' sections that are part of the specified group are defined. If
- the mentioned 'otel-scope' sections are used only in some OTel group, they do
- not have to have defined events. Several 'otel-scope' sections can be
- specified in one line.
-
- Arguments :
- name - the name of the 'otel-scope' section
-
-
-5. Examples
-------------
-
-Several examples of the OTel filter configuration can be found in the test
-directory. A brief description of the prepared configurations follows:
-
-cmp - a configuration made for comparison purposes with other tracing
- implementations.
-
-sa - a standalone configuration in which all possible events are used. This
- is the most comprehensive example demonstrating spans, attributes,
- events, links, baggage, status and other features.
-
-ctx - a configuration similar to 'sa', with the difference that the spans are
- opened using extracted span contexts as references instead of direct
- parent span names. This demonstrates the inject/extract context
- propagation mechanism using HAProxy variables.
-
-fe be - a more complex example of the OTel filter configuration that uses two
- cascaded HAProxy services (frontend and backend). The span context
- between HAProxy processes is transmitted via the HTTP header using
- inject/extract.
-
-empty - an empty configuration in which the OTel filter is initialized but no
- event is triggered. It is not very usable, except to check the behavior
- of the OTel filter in the case of a similar configuration.
-
-
-The OTel filter does not use tracer plugins. Instead, telemetry data is
-exported using the OpenTelemetry protocol (OTLP) directly to any compatible
-backend. The backend is configured through the YAML configuration file
-specified by the 'config' keyword.
-
-In order to be able to collect and view trace data we need an OpenTelemetry
-compatible backend. There are many options available, including:
-
- - Jaeger : https://www.jaegertracing.io/
- - Grafana : https://grafana.com/oss/tempo/
- - Zipkin : https://zipkin.io/
- - SigNoz : https://signoz.io/
- - Datadog : https://www.datadoghq.com/
-
-For quick testing, a simple setup using the OpenTelemetry Collector and Jaeger
-can be started with Docker:
-
- # docker run -d --name jaeger -p 4317:4317 -p 4318:4318 -p 16686:16686 jaegertracing/all-in-one:latest
-
-This starts Jaeger with OTLP/gRPC on port 4317, OTLP/HTTP on port 4318 and the
-web UI on port 16686. If we want to use that container later, it can be started
-and stopped using the 'docker container start/stop' commands.
-
-The test configurations use a YAML file that defines an OTLP/HTTP exporter
-sending data to localhost:4318. A typical minimal YAML configuration looks like
-this:
-
- --- otel.yml --------------------------------------------------------
- exporters:
- my_exporter:
- type: otlp_http
- endpoint: "http://localhost:4318/v1/traces"
-
- samplers:
- my_sampler:
- type: always_on
-
- processors:
- my_processor:
- type: batch
-
- providers:
- my_provider:
- resources:
- - service.name: "haproxy"
-
- signals:
- traces:
- scope_name: "HAProxy OTel"
- exporters: my_exporter
- samplers: my_sampler
- processors: my_processor
- providers: my_provider
- ---------------------------------------------------------------------
-
-In order to use any of the configurations from the test directory, we can run
-one of the pre-configured scripts:
-
- % ./run-sa.sh
- % ./run-ctx.sh
- % ./run-cmp.sh
- % ./run-fe-be.sh
-
-
-5.1. Benchmarking results
---------------------------
-
-To check the performance impact of the OTel filter, test configurations located
-in the test directory have been benchmarked. The test results (with the names
-README-speed-xxx, where xxx is the name of the configuration being tested) are
-also in the test directory.
-
-Testing was done with the wrk utility using 8 threads, 8 connections and a
-5-minute test duration. Detailed results and methodology are documented in the
-test/README-test-speed file.
-
-Below is a summary of the 'sa' (standalone) configuration results, which uses
-all possible events and demonstrates the worst-case scenario:
-
- ---------------------------------------------------------------
- rate-limit req/s avg latency overhead
- ---------------------------------------------------------------
- 100.0% 38,202 213.08 us 21.6%
- 50.0% 42,777 190.49 us 12.2%
- 25.0% 45,302 180.46 us 7.0%
- 10.0% 46,879 174.69 us 3.7%
- 2.5% 47,993 170.58 us 1.4%
- disabled 48,788 167.74 us ~0
- off 48,697 168.00 us baseline
- ---------------------------------------------------------------
-
-The 'off' baseline is measured with the 'filter opentelemetry' directive
-commented out, so the filter is not loaded at all. The 'disabled' level has
-the filter loaded but disabled via 'option disabled', so the initialization
-overhead is included but no events are processed.
-
-As the table shows, with the rate limit set to 25% the overhead is about 7%.
-At 10% the overhead drops to 3.7%. In typical production use with a rate limit
-of 10% or less, the performance impact should be negligible.
-
-
-6. OTel CLI
-------------
-
-Via the HAProxy CLI interface we can find out the current status of the OTel
-filter and change several of its settings.
-
-All supported CLI commands can be found in the following way, using the socat
-utility with the assumption that the HAProxy CLI socket path is set to
-/tmp/haproxy.sock (of course, instead of socat, nc or other utility can be used
-with a change in arguments when running the same):
-
- % echo "help" | socat - UNIX-CONNECT:/tmp/haproxy.sock | grep flt-otel
- --- command output ----------
- flt-otel debug [level] : set the OTel filter debug level
- flt-otel disable : disable the OTel filter
- flt-otel enable : enable the OTel filter
- flt-otel soft-errors : turning off hard-errors mode
- flt-otel hard-errors : enabling hard-errors mode
- flt-otel logging [state] : set logging state
- flt-otel rate [value] : set the rate limit
- flt-otel status : show the OTel filter status
- --- command output ----------
-
-'flt-otel debug' can only be used in case the OTel filter is compiled with the
-debug mode enabled. When invoked without arguments, these commands display the
-current value of the respective setting.
-
-
-7. Known bugs and limitations
--------------------------------
-
-The name of the span context definition can contain only letters, numbers and
-characters '_' and '-'. Also, all uppercase letters in the name are converted
-to lowercase. The character '-' is converted internally to the 'D' character,
-and since a HAProxy variable is generated from that name, this should be taken
-into account if we want to use it somewhere in the HAProxy configuration. The
-above mentioned span context is used in the 'inject' and 'extract' keywords.
-
-An inline span link (using the 'link' keyword within a 'span' declaration) is
-limited to a single link per span declaration due to the fixed argument count
-(maximum 7 arguments). For multiple links, use the standalone 'link' keyword
-instead.
-
-Let's look a little at the example test/fe-be (configurations are in the
-test/fe and test/be directories, 'fe' is here the abbreviation for frontend and
-'be' for backend). In case we have the 'rate-limit' set to a value less than
-100.0, then distributed tracing will not be started with each new HTTP request.
-It also means that the span context will not be delivered (via the HTTP header)
-to the backend HAProxy process. The 'rate-limit' on the backend HAProxy must be
-set to 100.0, but because the frontend HAProxy does not send a span context
-every time, all such cases will cause an error to be reported on the backend
-server. Therefore, the 'hard-errors' option must be set on the backend server,
-so that processing on that stream is stopped as soon as the first error occurs.
+++ /dev/null
-OpenTelemetry Filter Configuration Structures
-==============================================================================
-
-1 Overview
-------------------------------------------------------------------------------
-
-The OpenTelemetry filter configuration is a tree of C structures that mirrors
-the hierarchical layout of the filter's configuration file. Each structure type
-carries a common header macro, and its allocation and deallocation are performed
-by macro-generated init/free function pairs defined in conf_funcs.h.
-
-The root of the tree is flt_otel_conf, which owns the instrumentation settings,
-groups, and scopes. Scopes contain the actual tracing and metrics definitions:
-contexts, spans, instruments, and their sample expressions.
-
-Source files:
- include/conf.h Structure definitions and debug macros.
- include/conf_funcs.h Init/free macro templates and declarations.
- src/conf.c Init/free implementations for all types.
-
-
-2 Common Macros
-------------------------------------------------------------------------------
-
-Two macros provide the building blocks embedded in every configuration
-structure.
-
-2.1 FLT_OTEL_CONF_STR(p)
-
-Expands to an anonymous struct containing a string pointer and its cached
-length:
-
- struct {
- char *p;
- size_t p_len;
- };
-
-Used for auxiliary string fields that do not need list linkage (e.g. ref_id and
-ctx_id in flt_otel_conf_span).
-
-2.2 FLT_OTEL_CONF_HDR(p)
-
-Expands to an anonymous struct that extends FLT_OTEL_CONF_STR with a
-configuration file line number and an intrusive list node:
-
- struct {
- char *p;
- size_t p_len;
- int cfg_line;
- struct list list;
- };
-
-Every configuration structure embeds FLT_OTEL_CONF_HDR as its first member.
-The <p> parameter names the identifier field (e.g. "id", "key", "str", "span",
-"fmt_expr"). The list node chains the structure into its parent's list.
-The cfg_line records the source line for error reporting.
-
-
-3 Structure Hierarchy
-------------------------------------------------------------------------------
-
-The complete ownership tree, from root to leaves:
-
- flt_otel_conf
- +-- flt_otel_conf_instr (one, via pointer)
- | +-- flt_otel_conf_ph (ph_groups list)
- | +-- flt_otel_conf_ph (ph_scopes list)
- | +-- struct acl (acls list, HAProxy-owned type)
- | +-- struct logger (proxy_log.loggers, HAProxy type)
- +-- flt_otel_conf_group (groups list)
- | +-- flt_otel_conf_ph (ph_scopes list)
- +-- flt_otel_conf_scope (scopes list)
- +-- flt_otel_conf_context (contexts list)
- +-- flt_otel_conf_span (spans list)
- | +-- flt_otel_conf_link (links list)
- | +-- flt_otel_conf_sample (attributes list)
- | | +-- flt_otel_conf_sample_expr (exprs list)
- | +-- flt_otel_conf_sample (events list)
- | | +-- flt_otel_conf_sample_expr (exprs list)
- | +-- flt_otel_conf_sample (baggages list)
- | | +-- flt_otel_conf_sample_expr (exprs list)
- | +-- flt_otel_conf_sample (statuses list)
- | +-- flt_otel_conf_sample_expr (exprs list)
- +-- flt_otel_conf_str (spans_to_finish list)
- +-- flt_otel_conf_instrument (instruments list)
- | +-- flt_otel_conf_sample (samples list)
- | +-- flt_otel_conf_sample_expr (exprs list)
- +-- flt_otel_conf_log_record (log_records list)
- +-- flt_otel_conf_sample (attributes list)
- | +-- flt_otel_conf_sample_expr (exprs list)
- +-- flt_otel_conf_sample (samples list)
- +-- flt_otel_conf_sample_expr (exprs list)
-
-All child lists use HAProxy's intrusive doubly-linked list (struct list)
-threaded through the FLT_OTEL_CONF_HDR embedded in each child structure.
-
-3.1 Placeholder Structures
-
-The flt_otel_conf_ph structure serves as an indirection node. During parsing,
-placeholder entries record names of groups and scopes. At check time
-(flt_otel_check), these names are resolved to pointers to the actual
-flt_otel_conf_group or flt_otel_conf_scope structures via the ptr field.
-Two type aliases exist for clarity:
-
- #define flt_otel_conf_ph_group flt_otel_conf_ph
- #define flt_otel_conf_ph_scope flt_otel_conf_ph
-
-Corresponding free aliases ensure the FLT_OTEL_LIST_DESTROY macro can locate
-the correct free function:
-
- #define flt_otel_conf_ph_group_free flt_otel_conf_ph_free
- #define flt_otel_conf_ph_scope_free flt_otel_conf_ph_free
-
-
-4 Structure Definitions
-------------------------------------------------------------------------------
-
-4.1 flt_otel_conf (root)
-
- proxy Proxy owning the filter.
- id The OpenTelemetry filter id.
- cfg_file The OpenTelemetry filter configuration file name.
- instr The OpenTelemetry instrumentation settings (pointer).
- groups List of all available groups.
- scopes List of all available scopes.
- cnt Various counters related to filter operation.
- smp_args Deferred sample fetch arguments to resolve at check time.
-
-This structure does not use FLT_OTEL_CONF_HDR because it is not part of any
-list -- it is the unique root, owned by the filter instance.
-
-4.2 flt_otel_conf_instr (instrumentation)
-
- FLT_OTEL_CONF_HDR(id) The OpenTelemetry instrumentation name.
- config The OpenTelemetry configuration file name.
- tracer The OpenTelemetry tracer handle.
- meter The OpenTelemetry meter handle.
- logger The OpenTelemetry logger handle.
- rate_limit Rate limit as uint32 ([0..2^32-1] maps [0..100]%).
- flag_harderr Hard-error mode flag.
- flag_disabled Disabled flag.
- logging Logging mode (0, 1, or 3).
- proxy_log The log server list (HAProxy proxy structure).
- analyzers Defined channel analyzers bitmask.
- idle_timeout Minimum idle timeout across scopes (ms, 0 = off).
- acls ACLs declared on this tracer.
- ph_groups List of all used groups (placeholders).
- ph_scopes List of all used scopes (placeholders).
-
-Exactly one instrumentation block is allowed per filter instance. The parser
-stores a pointer to it in flt_otel_conf.instr.
-
-4.3 flt_otel_conf_group
-
- FLT_OTEL_CONF_HDR(id) The group name.
- flag_used The indication that the group is being used.
- ph_scopes List of all used scopes (placeholders).
-
-Groups bundle scopes for use with the "otel-group" HAProxy action.
-
-4.4 flt_otel_conf_scope
-
- FLT_OTEL_CONF_HDR(id) The scope name.
- flag_used The indication that the scope is being used.
- event FLT_OTEL_EVENT_* identifier.
- idle_timeout Idle timeout interval in milliseconds (0 = off).
- acls ACLs declared on this scope.
- cond ACL condition to meet.
- contexts Declared contexts.
- spans Declared spans.
- spans_to_finish The list of spans scheduled for finishing.
- instruments The list of metric instruments.
- log_records The list of log records.
-
-Each scope binds to a single HAProxy analyzer event (or none, if used only
-through groups).
-
-4.5 flt_otel_conf_span
-
- FLT_OTEL_CONF_HDR(id) The name of the span.
- FLT_OTEL_CONF_STR(ref_id) The reference name, if used.
- FLT_OTEL_CONF_STR(ctx_id) The span context name, if used.
- ctx_flags The type of storage used for the span context.
- flag_root Whether this is a root span.
- links The set of linked span names.
- attributes The set of key:value attributes.
- events The set of events with key-value attributes.
- baggages The set of key:value baggage items.
- statuses Span status code and description.
-
-The ref_id and ctx_id fields use FLT_OTEL_CONF_STR because they are simple name
-strings without list linkage.
-
-4.6 flt_otel_conf_instrument
-
- FLT_OTEL_CONF_HDR(id) The name of the instrument.
- idx Meter instrument index: UNSET (-1) before creation,
- PENDING (-2) while another thread is creating, or >= 0
- for the actual meter index.
- type Instrument type (or UPDATE).
- aggr_type Aggregation type for the view (create only).
- description Instrument description (create only).
- unit Instrument unit (create only).
- samples Sample expressions for the value.
- bounds Histogram bucket boundaries (create only).
- bounds_num Number of histogram bucket boundaries.
- attributes Instrument attributes (update only, flt_otel_conf_sample).
- ref Resolved create-form instrument (update only).
-
-Instruments come in two forms: create-form (defines a new metric with type,
-description, unit, and optional histogram bounds) and update-form (references
-an existing instrument via the ref pointer). Update-form attributes are stored
-as flt_otel_conf_sample entries and evaluated at runtime.
-
-4.7 flt_otel_conf_log_record
-
- FLT_OTEL_CONF_HDR(id) Required by macro; member <id> is not used directly.
- severity The severity level.
- event_id Optional event identifier.
- event_name Optional event name.
- span Optional span reference.
- attributes Log record attributes (flt_otel_conf_sample list).
- samples Sample expressions for the body.
-
-Log records are emitted via the OTel logger at the configured severity. The
-optional span reference associates the log record with an open span at runtime.
-Attributes are stored as flt_otel_conf_sample entries added via the 'attr'
-keyword, which can be repeated. Attribute values are HAProxy sample expressions
-evaluated at runtime.
-
-4.8 flt_otel_conf_context
-
- FLT_OTEL_CONF_HDR(id) The name of the context.
- flags Storage type from which the span context is extracted.
-
-4.9 flt_otel_conf_sample
-
- FLT_OTEL_CONF_HDR(key) The list containing sample names.
- fmt_string Combined sample-expression arguments string.
- extra Optional supplementary data.
- exprs Used to chain sample expressions.
- num_exprs Number of defined expressions.
- lf_expr The log-format expression.
- lf_used Whether lf_expr is used instead of exprs.
-
-The extra field carries type-specific data: event name strings (OTELC_VALUE_DATA)
-for span events, status code integers (OTELC_VALUE_INT32) for span statuses.
-
-When the sample value argument contains the "%[" sequence, the parser treats
-it as a log-format string: the lf_used flag is set and the compiled result is
-stored in lf_expr, while the exprs list remains empty. At runtime, if lf_used
-is true, the log-format expression is evaluated via build_logline() instead of
-the sample expression list.
-
-4.10 flt_otel_conf_sample_expr
-
- FLT_OTEL_CONF_HDR(fmt_expr) The original expression format string.
- expr The sample expression (struct sample_expr).
-
-4.11 Simple Types
-
- flt_otel_conf_hdr Generic header; used for simple named entries.
- flt_otel_conf_str String holder (identical to conf_hdr in layout, but the
- HDR field is named "str" instead of "id"); used for
- spans_to_finish.
- flt_otel_conf_link Span link reference; HDR field named "span".
- flt_otel_conf_ph Placeholder; carries a ptr field resolved at check time.
-
-
-5 Initialization
-------------------------------------------------------------------------------
-
-5.1 Macro-Generated Init Functions
-
-The FLT_OTEL_CONF_FUNC_INIT macro (conf_funcs.h) generates a function with the
-following signature for each configuration type:
-
- struct flt_otel_conf_<type> *flt_otel_conf_<type>_init(const char *id, int line, struct list *head, char **err);
-
-The generated function performs these steps:
-
- 1. Validates that <id> is non-NULL and non-empty.
- 2. Checks the identifier length against FLT_OTEL_ID_MAXLEN (64).
- 3. If <head> is non-NULL, iterates the list to reject duplicate identifiers
- (strcmp match).
- 4. Allocates the structure with OTELC_CALLOC (zeroed memory).
- 5. Records the configuration line number in cfg_line.
- 6. Duplicates the identifier string with OTELC_STRDUP.
- 7. If <head> is non-NULL, appends the structure to the list via LIST_APPEND.
- 8. Executes any custom initialization body provided as the third macro
- argument.
-
-If any step fails, the function sets an error message via FLT_OTEL_ERR and
-returns NULL.
-
-5.2 Custom Initialization Bodies
-
-Several structure types require additional setup beyond what the macro template
-provides. The custom init body runs after the base allocation and list
-insertion succeed:
-
- conf_sample:
- LIST_INIT for exprs. Calls lf_expr_init for lf_expr.
-
- conf_span:
- LIST_INIT for links, attributes, events, baggages, statuses.
-
- conf_scope:
- LIST_INIT for acls, contexts, spans, spans_to_finish, instruments,
- log_records.
-
- conf_group:
- LIST_INIT for ph_scopes.
-
- conf_instrument:
- Sets idx and type to OTELC_METRIC_INSTRUMENT_UNSET, aggr_type to
- OTELC_METRIC_AGGREGATION_UNSET. LIST_INIT for samples.
-
- conf_log_record:
- LIST_INIT for samples.
-
- conf_instr:
- Sets rate_limit to FLT_OTEL_FLOAT_U32(100.0) (100%). Calls init_new_proxy
- for proxy_log. LIST_INIT for acls, ph_groups, ph_scopes.
-
-Types with no custom body (hdr, str, link, ph, sample_expr, context) rely
-entirely on the zeroed OTELC_CALLOC allocation.
-
-5.3 Extended Sample Initialization
-
-The flt_otel_conf_sample_init_ex function (conf.c) provides a higher-level
-initialization for sample structures:
-
- 1. Verifies sufficient arguments in the args[] array.
- 2. Calls flt_otel_conf_sample_init with the sample key.
- 3. Copies extra data (event name string or status code integer).
- 4. Concatenates remaining arguments into the fmt_string via
- flt_otel_args_concat.
- 5. Counts the number of sample expressions.
-
-This function is used by the parser for span attributes, events, baggages,
-statuses, and instrument samples.
-
-5.4 Top-Level Initialization
-
-The flt_otel_conf_init function (conf.c) is hand-written rather than
-macro-generated because the root structure does not follow the standard header
-pattern:
-
- 1. Allocates flt_otel_conf with OTELC_CALLOC.
- 2. Stores the proxy reference.
- 3. Initializes the groups and scopes lists.
-
-
-6 Deallocation
-------------------------------------------------------------------------------
-
-6.1 Macro-Generated Free Functions
-
-The FLT_OTEL_CONF_FUNC_FREE macro (conf_funcs.h) generates a function with the
-following signature:
-
- void flt_otel_conf_<type>_free(struct flt_otel_conf_<type> **ptr);
-
-The generated function performs these steps:
-
- 1. Checks that both <ptr> and <*ptr> are non-NULL.
- 2. Executes any custom cleanup body provided as the third macro argument.
- 3. Frees the identifier string with OTELC_SFREE.
- 4. Removes the structure from its list with FLT_OTEL_LIST_DEL.
- 5. Frees the structure with OTELC_SFREE_CLEAR and sets <*ptr> to NULL.
-
-6.2 Custom Cleanup Bodies
-
-Custom cleanup runs before the base teardown, allowing child structures to be
-freed while the parent is still valid:
-
- conf_sample:
- Frees fmt_string. If extra is OTELC_VALUE_DATA, frees the data pointer.
- Destroys the exprs list (sample_expr entries). Deinitializes lf_expr via
- lf_expr_deinit.
-
- conf_sample_expr:
- Releases the HAProxy sample expression via release_sample_expr.
-
- conf_span:
- Frees ref_id and ctx_id strings.
- Destroys links, attributes, events, baggages, and statuses lists.
-
- conf_instrument:
- Frees description, unit, and bounds. Destroys the samples list.
- Destroys the attr key-value array via otelc_kv_destroy.
-
- conf_log_record:
- Frees event_name and span strings. Destroys the attr key-value array via
- otelc_kv_destroy. Destroys the samples list.
-
- conf_scope:
- Prunes and frees each ACL entry. Frees the ACL condition via free_acl_cond.
- Destroys contexts, spans, spans_to_finish, instruments, and log_records
- lists.
-
- conf_group:
- Destroys the ph_scopes list.
-
- conf_instr:
- Frees the config string. Prunes and frees each ACL entry. Frees each
- logger entry from proxy_log.loggers. Destroys the ph_groups and ph_scopes
- lists.
-
-Types with no custom cleanup (hdr, str, link, ph, context) only run the base
-teardown: free the identifier, unlink, free the structure.
-
-6.3 List Destruction
-
-The FLT_OTEL_LIST_DESTROY(type, head) macro (defined in define.h) iterates a
-list and calls flt_otel_conf_<type>_free for each entry. This macro drives the
-recursive teardown from parent to leaf.
-
-6.4 Top-Level Deallocation
-
-The flt_otel_conf_free function (conf.c) is hand-written:
-
- 1. Frees the id and cfg_file strings.
- 2. Calls flt_otel_conf_instr_free for the instrumentation.
- 3. Destroys the groups list (which recursively frees placeholders).
- 4. Destroys the scopes list (which recursively frees contexts, spans,
- instruments, log records, and all their children).
- 5. Frees the root structure and sets the pointer to NULL.
-
-
-7 Summary of Init/Free Pairs
-------------------------------------------------------------------------------
-
-The following table lists all configuration types and their init/free function
-pairs. Types marked "macro" are generated by the FLT_OTEL_CONF_FUNC_(INIT|FREE)
-macros. The HDR field column shows which member name is used for the common
-header.
-
- Type HDR field Source Custom init body
- --------------- --------- ------ -------------------------
- conf (none) manual groups, scopes
- conf_hdr id macro (none)
- conf_str str macro (none)
- conf_link span macro (none)
- conf_ph id macro (none)
- conf_sample_expr fmt_expr macro (none)
- conf_sample key macro exprs, lf_expr
- conf_sample (ex) key manual extra, fmt_string, exprs
- conf_context id macro (none)
- conf_span id macro 5 sub-lists
- conf_instrument id macro idx, type, samples
- conf_log_record id macro samples
- conf_scope id macro 6 sub-lists
- conf_group id macro ph_scopes
- conf_instr id macro rate_limit, proxy_log, acls, ph_groups, ph_scopes
+++ /dev/null
- -----------------------------------------
- HAProxy OTel filter configuration guide
- Version 1.0
- ( Last update: 2026-03-18 )
- -----------------------------------------
- Author : Miroslav Zagorac
- Contact : mzagorac at haproxy dot com
-
-
-SUMMARY
---------
-
- 1. Overview
- 2. HAProxy filter declaration
- 3. OTel configuration file structure
- 3.1. OTel scope (top-level)
- 3.2. "otel-instrumentation" section
- 3.3. "otel-scope" section
- 3.4. "otel-group" section
- 4. YAML configuration file
- 4.1. Exporters
- 4.2. Samplers
- 4.3. Processors
- 4.4. Readers
- 4.5. Providers
- 4.6. Signals
- 5. HAProxy rule integration
- 6. Complete examples
- 6.1. Standalone example (sa)
- 6.2. Frontend / backend example (fe/be)
- 6.3. Context propagation example (ctx)
- 6.4. Comparison example (cmp)
- 6.5. Empty / minimal example (empty)
-
-
-1. Overview
-------------
-
-The OTel filter configuration consists of two files:
-
- 1) An OTel configuration file (.cfg) that defines the tracing model: scopes,
- groups, spans, attributes, events, instrumentation and log-records.
-
- 2) A YAML configuration file (.yml) that configures the OpenTelemetry SDK
- pipeline: exporters, samplers, processors, readers, providers and signal
- routing.
-
-The OTel configuration file is referenced from the HAProxy configuration using
-the 'filter opentelemetry' directive. The YAML file is in turn referenced from
-the OTel configuration file using the 'config' keyword inside the
-"otel-instrumentation" section.
-
-
-2. HAProxy filter declaration
-------------------------------
-
-The filter is activated by adding a filter directive in the HAProxy
-configuration, in a proxy section (frontend / listen / backend):
-
- frontend my-frontend
- ...
- filter opentelemetry [id <id>] config <otel-cfg-file>
- ...
-
-If no filter id is specified, 'otel-filter' is used as default. The 'config'
-parameter is mandatory and specifies the path to the OTel configuration file.
-
-Example (from test/sa/haproxy.cfg):
-
- frontend otel-test-sa-frontend
- bind *:10080
- default_backend servers-backend
-
- acl acl-http-status-ok status 100:399
-
- filter opentelemetry id otel-test-sa config sa/otel.cfg
-
- http-response otel-group otel-test-sa http_response_group if acl-http-status-ok
- http-after-response otel-group otel-test-sa http_after_response_group if !acl-http-status-ok
-
- backend servers-backend
- server server-1 127.0.0.1:8000
-
-
-3. OTel configuration file structure
---------------------------------------
-
-The OTel configuration file uses a simple section-based format. It contains
-three types of sections: one "otel-instrumentation" section (mandatory), zero
-or more "otel-scope" sections, and zero or more "otel-group" sections.
-
-
-3.1. OTel scope (top-level)
------------------------------
-
-The file is organized into top-level OTel scopes, each identified by a filter
-id enclosed in square brackets. The filter id must match the id specified in
-the HAProxy 'filter opentelemetry' directive.
-
- [<filter-id>]
- otel-instrumentation <name>
- ...
-
- otel-group <name>
- ...
-
- otel-scope <name>
- ...
-
-Multiple OTel scopes (for different filter instances) can coexist in the same
-file:
-
- [my-first-filter]
- otel-instrumentation instr1
- ...
-
- [my-second-filter]
- otel-instrumentation instr2
- ...
-
-
-3.2. "otel-instrumentation" section
--------------------------------------
-
-Exactly one "otel-instrumentation" section must be defined per OTel scope.
-It configures the global behavior of the filter and declares which groups
-and scopes are active.
-
-Syntax:
-
- otel-instrumentation <name>
-
-Keywords (mandatory):
-
- config <file>
- Path to the YAML configuration file for the OpenTelemetry SDK.
-
-Keywords (optional):
-
- acl <aclname> <criterion> [flags] [operator] <value> ...
- Declare an ACL. See section 7 of the HAProxy Configuration Manual.
-
- debug-level <value>
- Set the debug level bitmask (e.g. 0x77f). Only effective when compiled
- with OTEL_DEBUG=1.
-
- groups <name> ...
- Declare one or more "otel-group" sections used by this instrumentation.
- Can be repeated on multiple lines.
-
- log global
- log <addr> [len <len>] [format <fmt>] <facility> [<level> [<minlvl>]]
- no log
- Enable per-instance logging.
-
- option disabled / no option disabled
- Disable or enable the filter. Default: enabled.
-
- option dontlog-normal / no option dontlog-normal
- Suppress logging for normal (successful) operations. Default: disabled.
-
- option hard-errors / no option hard-errors
- Stop all filter processing in a stream after the first error. Default:
- disabled (errors are non-fatal).
-
- rate-limit <value>
- Percentage of streams for which the filter is activated. Floating-point
- value from 0.0 to 100.0. Default: 100.0.
-
- scopes <name> ...
- Declare one or more "otel-scope" sections used by this instrumentation.
- Can be repeated on multiple lines.
-
-
-Example (from test/sa/otel.cfg):
-
- [otel-test-sa]
- otel-instrumentation otel-test-instrumentation
- debug-level 0x77f
- log localhost:514 local7 debug
- config sa/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 client_session_start
- scopes frontend_tcp_request
- ...
- scopes server_session_end
-
-
-3.3. "otel-scope" section
----------------------------
-
-An "otel-scope" section defines the actions that take place when a particular
-event fires or when a group is triggered.
-
-Syntax:
-
- otel-scope <name>
-
-Supported keywords:
-
- span <name> [parent <ref>] [link <ref>] [root]
- Create a new span or reference an already opened one.
-
- - 'root' marks this span as the trace root (only one per trace).
- - 'parent <ref>' sets the parent to an existing span or extracted context
- name.
- - 'link <ref>' adds an inline link to another span or context. Multiple
- inline links can be specified within the argument limit.
- - If no reference is given, the span becomes a root span.
-
- A span declaration opens a "sub-context" within the scope: the keywords
- 'link', 'attribute', 'event', 'baggage', 'status' and 'inject' that follow
- apply to that span until the next 'span' keyword or the end of the scope.
-
- Examples:
- span "HAProxy session" root
- span "Client session" parent "HAProxy session"
- span "HTTP request" parent "TCP request" link "HAProxy session"
- span "Client session" parent "otel_ctx_1"
-
-
- attribute <key> <sample> ...
- Set an attribute on the currently active span. A single sample preserves
- its native type; multiple samples are concatenated as a string.
-
- Examples:
- attribute "http.method" method
- attribute "http.url" url
- attribute "http.version" str("HTTP/") req.ver
-
-
- event <name> <key> <sample> ...
- Add a span event (timestamped annotation) to the currently active span.
- The data type is always string.
-
- Examples:
- event "event_ip" "src" src str(":") src_port
- event "event_be" "be" be_id str(" ") be_name
-
-
- baggage <key> <sample> ...
- Set baggage on the currently active span. Baggage propagates to all child
- spans. The data type is always string.
-
- Example:
- baggage "haproxy_id" var(sess.otel.uuid)
-
-
- status <code> [<sample> ...]
- Set the span status. Valid codes: ignore (default), unset, ok, error.
- An optional description follows the code.
-
- Examples:
- status "ok"
- status "error" str("http.status_code: ") status
-
-
- link <span> ...
- Add non-hierarchical links to the currently active span. Multiple span
- names can be specified. Use this keyword for multiple links (the inline
- 'link' in 'span' is limited to one).
-
- Example:
- link "HAProxy session" "Client session"
-
-
- inject <name-prefix> [use-vars] [use-headers]
- Inject span context into an HTTP header carrier and/or HAProxy variables.
- The prefix names the context; the special prefix '-' generates the name
- automatically. Default storage: use-headers. The 'use-vars' option
- requires OTEL_USE_VARS=1 at compile time.
-
- Example:
- span "HAProxy session" root
- inject "otel_ctx_1" use-headers use-vars
-
-
- extract <name-prefix> [use-vars | use-headers]
- Extract a previously injected span context from an HTTP header or HAProxy
- variables. The extracted context can then be used as a parent reference
- in 'span ... parent <name-prefix>'.
-
- Example:
- extract "otel_ctx_1" use-vars
- span "Client session" parent "otel_ctx_1"
-
-
- finish <name> ...
- Close one or more spans or span contexts. Special names:
- '*' - finish all open spans
- '*req*' - finish all request-channel spans
- '*res*' - finish all response-channel spans
-
- Multiple names can be given on one line. A quoted context name after a
- span name finishes the associated context as well.
-
- Examples:
- finish "Frontend TCP request"
- finish "Client session" "otel_ctx_2"
- finish *
-
-
- instrument <type> <name> [aggr <aggregation>] [desc <description>] [unit <unit>] value <sample> [bounds <bounds>]
- instrument update <name> [attr <key> <sample> ...]
- Create or update a metric instrument.
-
- Supported types:
- cnt_int - counter (uint64)
- hist_int - histogram (uint64)
- udcnt_int - up-down counter (int64)
- gauge_int - gauge (int64)
-
- Supported aggregation types:
- drop - measurements are discarded
- histogram - explicit bucket histogram
- last_value - last recorded value
- sum - sum of recorded values
- default - SDK default for the instrument type
- exp_histogram - base-2 exponential histogram
-
- An aggregation type can be specified using the 'aggr' keyword. When
- specified, a metrics view is registered with the given aggregation
- strategy. If omitted, the SDK default is used.
-
- For histogram instruments (hist_int), optional bucket boundaries can be
- specified using the 'bounds' keyword followed by a double-quoted string
- of space-separated numbers (order does not matter; values are sorted
- internally). When bounds are specified without an explicit aggregation
- type, histogram aggregation is used automatically.
-
- Observable (asynchronous) and double-precision types are not supported.
- Observable instrument callbacks are invoked by the OTel SDK from an
- external background thread; HAProxy sample fetches rely on internal
- per-thread-group state and return incorrect results from a non-HAProxy
- thread. Double-precision types are not supported because HAProxy sample
- fetches do not return double values.
-
- Examples:
- instrument cnt_int "name_cnt_int" desc "Integer Counter" value int(1),add(2) unit "unit"
- instrument hist_int "name_hist" aggr exp_histogram desc "Latency" value lat_ns_tot unit "ns"
- instrument hist_int "name_hist2" desc "Latency" value lat_ns_tot unit "ns" bounds "100 1000 10000"
- instrument update "name_cnt_int" attr "attr_1_key" str("attr_1_value")
-
-
- log-record <severity> [id <integer>] [event <name>] [span <span-name>] [attr <key> <sample>] ... <sample> ...
- Emit an OpenTelemetry log record. The first argument is a required
- severity level. Optional keywords follow in any order:
-
- id <integer> - numeric event identifier
- event <name> - event name string
- span <span-name> - associate the log record with an open span
- attr <key> <sample> - add an attribute evaluated at runtime (repeatable)
-
- The 'attr' keyword takes an attribute name and a single HAProxy sample
- expression. The expression is evaluated at runtime, following the same
- rules as span attributes: a bare sample fetch (e.g. src) or a log-format
- string (e.g. "%[src]:%[src_port]").
-
- The remaining arguments at the end are sample fetch expressions that form
- the log record body. A single sample preserves its native type; multiple
- samples are concatenated as a string.
-
- Supported severity levels follow the OpenTelemetry specification:
- trace, trace2, trace3, trace4
- debug, debug2, debug3, debug4
- info, info2, info3, info4
- warn, warn2, warn3, warn4
- error, error2, error3, error4
- fatal, fatal2, fatal3, fatal4
-
- The log record is only emitted if the logger is enabled for the configured
- severity (controlled by the 'min_severity' option in the YAML logs signal
- configuration). If a 'span' reference is given but the named span is not
- found at runtime, the log record is emitted without span correlation.
-
- Examples:
- log-record info str("heartbeat")
- log-record info id 1001 event "http-request" span "Frontend HTTP request" attr "http.method" method method url
- log-record trace id 1000 event "session-start" span "Client session" attr "src_ip" src attr "src_port" src_port src str(":") src_port
- log-record warn event "server-unavailable" str("503 Service Unavailable")
- log-record info event "session-stop" str("stream stopped")
-
-
- acl <aclname> <criterion> [flags] [operator] <value> ...
- Declare an ACL local to this scope.
-
- Example:
- acl acl-test-src-ip src 127.0.0.1
-
-
- otel-event <name> [{ if | unless } <condition>]
- Bind this scope to a filter event, optionally with an ACL-based condition.
-
- Supported events (stream lifecycle):
- on-stream-start
- on-stream-stop
- on-idle-timeout
- on-backend-set
-
- Supported events (request channel):
- on-client-session-start
- on-frontend-tcp-request
- on-http-wait-request
- on-http-body-request
- on-frontend-http-request
- on-switching-rules-request
- on-backend-tcp-request
- on-backend-http-request
- on-process-server-rules-request
- on-http-process-request
- on-tcp-rdp-cookie-request
- on-process-sticking-rules-request
- on-http-headers-request
- on-http-end-request
- on-client-session-end
- on-server-unavailable
-
- Supported events (response channel):
- on-server-session-start
- on-tcp-response
- on-http-wait-response
- on-process-store-rules-response
- on-http-response
- on-http-headers-response
- on-http-end-response
- on-http-reply
- on-server-session-end
-
- The on-stream-start event fires from the stream_start filter callback,
- before any channel processing begins. The on-stream-stop event fires from
- the stream_stop callback, after all channel processing ends. No channel
- is available at that point, so context injection/extraction via HTTP
- headers cannot be used in scopes bound to these events.
-
- The on-idle-timeout event fires periodically when the stream has no data
- transfer activity. It requires the 'idle-timeout' keyword to set the
- interval. Scopes bound to this event can create heartbeat spans, record
- idle-time metrics, and emit idle-time log records.
-
- The on-backend-set event fires when a backend is assigned to the stream.
- It is not called if the frontend and backend are the same.
-
- The on-http-headers-request and on-http-headers-response events fire after
- all HTTP headers have been parsed and analyzed.
-
- The on-http-end-request and on-http-end-response events fire when all HTTP
- data has been processed and forwarded.
-
- The on-http-reply event fires when HAProxy generates an internal reply
- (error page, deny response, redirect).
-
- Examples:
- otel-event on-stream-start if acl-test-src-ip
- otel-event on-stream-stop
- otel-event on-client-session-start
- otel-event on-client-session-start if acl-test-src-ip
- otel-event on-http-response if !acl-http-status-ok
- otel-event on-idle-timeout
-
-
- idle-timeout <time>
- Set the idle timeout interval for a scope bound to the 'on-idle-timeout'
- event. The timer fires periodically at the given interval when the stream
- has no data transfer activity. This keyword is mandatory for scopes using
- the 'on-idle-timeout' event and cannot be used with any other event.
-
- The <time> argument accepts the standard HAProxy time format: a number
- followed by a unit suffix (ms, s, m, h, d). A value of zero is not
- permitted.
-
- Example:
- scopes on_idle_timeout
- ..
- otel-scope on_idle_timeout
- idle-timeout 5s
- span "heartbeat" root
- attribute "idle.elapsed" str("idle-check")
- instrument cnt_int "idle.count" value int(1)
- log-record info str("heartbeat")
- otel-event on-idle-timeout
-
-
-3.4. "otel-group" section
----------------------------
-
-An "otel-group" section defines a named collection of scopes that can be
-triggered from HAProxy TCP/HTTP rules rather than from filter events.
-
-Syntax:
-
- otel-group <name>
-
-Keywords:
-
- scopes <name> ...
- List the "otel-scope" sections that belong to this group. Multiple names
- can be given on one line. Scopes that are used only in groups do not need
- to define an 'otel-event'.
-
-Example (from test/sa/otel.cfg):
-
- 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")
-
-
-4. YAML configuration file
-----------------------------
-
-The YAML configuration file defines the OpenTelemetry SDK pipeline. It is
-referenced by the 'config' keyword in the "otel-instrumentation" section.
-It contains the following top-level sections: exporters, samplers, processors,
-readers, providers and signals.
-
-
-4.1. Exporters
----------------
-
-Each exporter has a user-chosen name and a 'type' that determines which
-additional options are available. Options marked with (*) are required.
-
-Supported types:
-
- otlp_grpc - Export via OTLP over gRPC.
- type (*) "otlp_grpc"
- thread_name exporter thread name (string)
- endpoint OTLP/gRPC endpoint URL (string)
- use_ssl_credentials enable SSL channel credentials (boolean)
- ssl_credentials_cacert_path CA certificate file path (string)
- ssl_credentials_cacert_as_string CA certificate as inline string (string)
- ssl_client_key_path client private key file path (string)
- ssl_client_key_string client private key as inline string (string)
- ssl_client_cert_path client certificate file path (string)
- ssl_client_cert_string client certificate as inline string (string)
- timeout export timeout in seconds (integer)
- user_agent User-Agent header value (string)
- max_threads maximum exporter threads (integer)
- compression compression algorithm name (string)
- max_concurrent_requests concurrent request limit (integer)
-
-
- otlp_http - Export via OTLP over HTTP (JSON or Protobuf).
- type (*) "otlp_http"
- thread_name exporter thread name (string)
- endpoint OTLP/HTTP endpoint URL (string)
- content_type payload format: "json" or "protobuf"
- json_bytes_mapping binary encoding: "hexid", "utf8" or "base64"
- debug enable debug output (boolean)
- timeout export timeout in seconds (integer)
- http_headers custom HTTP headers (list of key: value)
- max_concurrent_requests concurrent request limit (integer)
- max_requests_per_connection request limit per connection (integer)
- background_thread_wait_for idle timeout for the HTTP background thread
- in milliseconds; 0 means the thread never
- exits on its own (integer, default: 0). If
- this option is set, 'insecure-fork-wanted'
- must be used in the HAProxy configuration,
- otherwise HAProxy may crash while exporting
- OTel data
- ssl_insecure_skip_verify skip TLS certificate verification (boolean)
- ssl_ca_cert_path CA certificate file path (string)
- ssl_ca_cert_string CA certificate as inline string (string)
- ssl_client_key_path client private key file path (string)
- ssl_client_key_string client private key as inline string (string)
- ssl_client_cert_path client certificate file path (string)
- ssl_client_cert_string client certificate as inline string (string)
- ssl_min_tls minimum TLS version (string)
- ssl_max_tls maximum TLS version (string)
- ssl_cipher TLS cipher list (string)
- ssl_cipher_suite TLS 1.3 cipher suite list (string)
- compression compression algorithm name (string)
-
-
- otlp_file - Export to local files in OTLP format.
- type (*) "otlp_file"
- thread_name exporter thread name (string)
- file_pattern output filename pattern (string)
- alias_pattern symlink pattern for latest file (string)
- flush_interval flush interval in microseconds (integer)
- flush_count spans per flush (integer)
- file_size maximum file size in bytes (integer)
- rotate_size number of rotated files to keep (integer)
-
-
- ostream - Write to a file (text output, useful for debugging).
- type (*) "ostream"
- filename output file path (string)
-
-
- memory - In-memory buffer (useful for testing).
- type (*) "memory"
- buffer_size maximum buffered items (integer)
-
-
- zipkin - Export to Zipkin-compatible backends.
- type (*) "zipkin"
- endpoint Zipkin collector URL (string)
- format payload format: "json" or "protobuf"
- service_name service name reported to Zipkin (string)
- ipv4 service IPv4 address (string)
- ipv6 service IPv6 address (string)
-
-
- elasticsearch - Export to Elasticsearch.
- type (*) "elasticsearch"
- host Elasticsearch hostname (string)
- port Elasticsearch port (integer)
- index Elasticsearch index name (string)
- response_timeout response timeout in seconds (integer)
- debug enable debug output (boolean)
- http_headers custom HTTP headers (list of key: value)
-
-
-4.2. Samplers
---------------
-
-Samplers control which traces are recorded. Each sampler has a user-chosen
-name and a 'type' that determines its behavior.
-
-Supported types:
-
- always_on - Sample every trace.
- type (*) "always_on"
-
-
- always_off - Sample no traces.
- type (*) "always_off"
-
-
- trace_id_ratio_based - Sample a fraction of traces.
- type (*) "trace_id_ratio_based"
- ratio sampling ratio, 0.0 to 1.0 (float)
-
-
- parent_based - Inherit sampling decision from parent span.
- type (*) "parent_based"
- delegate fallback sampler name (string)
-
-
-4.3. Processors
-----------------
-
-Processors define how telemetry data is handled before export. Each
-processor has a user-chosen name and a 'type' that determines its behavior.
-
-Supported types:
-
- batch - Batch spans before exporting.
- type (*) "batch"
- thread_name processor thread name (string)
- max_queue_size maximum queued spans (integer)
- schedule_delay export interval in milliseconds (integer)
- max_export_batch_size maximum spans per export call (integer)
-
- When the queue reaches half capacity, a preemptive notification triggers
- an early export.
-
- single - Export each span individually (no batching).
- type (*) "single"
-
-
-4.4. Readers
--------------
-
-Readers define how metrics are collected and exported. Each reader has a
-user-chosen name.
-
- thread_name reader thread name (string)
- export_interval collection interval in milliseconds (integer)
- export_timeout export timeout in milliseconds (integer)
-
-
-4.5. Providers
----------------
-
-Providers define resource attributes attached to all telemetry data. Each
-provider has a user-chosen name.
-
- resources key-value resource attributes (list)
-
-Standard resource attribute keys include service.name, service.version,
-service.instance.id and service.namespace.
-
-
-4.6. Signals
--------------
-
-Signals bind exporters, samplers, processors, readers and providers together
-for each telemetry type. The supported signal names are "traces", "metrics"
-and "logs".
-
- scope_name instrumentation scope name (string)
- exporters exporter name reference (string)
- samplers sampler name reference (string, traces only)
- processors processor name reference (string, traces/logs)
- readers reader name reference (string, metrics only)
- providers provider name reference (string)
- min_severity minimum log severity level (string, logs only)
-
-The "min_severity" option controls which log records are emitted. Only log
-records whose severity is equal to or higher than the configured minimum are
-passed to the exporter. The value is a severity name as listed under the
-"log-record" keyword (e.g. "trace", "debug", "info", "warn", "error", "fatal").
-If omitted, the logger accepts all severity levels.
-
-
-5. HAProxy rule integration
-----------------------------
-
-Groups defined in the OTel configuration file can be triggered from HAProxy
-TCP/HTTP rules using the 'otel-group' action keyword:
-
- http-request otel-group <filter-id> <group> [condition]
- http-response otel-group <filter-id> <group> [condition]
- http-after-response otel-group <filter-id> <group> [condition]
- tcp-request otel-group <filter-id> <group> [condition]
- tcp-response otel-group <filter-id> <group> [condition]
-
-This allows running specific groups of scopes based on ACL conditions defined
-in the HAProxy configuration.
-
-Example (from test/sa/haproxy.cfg):
-
- acl acl-http-status-ok status 100:399
-
- filter opentelemetry id otel-test-sa config sa/otel.cfg
-
- # Run response scopes for successful responses
- http-response otel-group otel-test-sa http_response_group if acl-http-status-ok
-
- # Run after-response scopes for error responses
- http-after-response otel-group otel-test-sa http_after_response_group if !acl-http-status-ok
-
-
-6. Complete examples
----------------------
-
-The test directory contains several complete example configurations. Each
-subdirectory contains an OTel configuration file (otel.cfg), a YAML file
-(otel.yml) and a HAProxy configuration file (haproxy.cfg).
-
-
-6.1. Standalone example (sa)
-------------------------------
-
-The most comprehensive example. All possible events are used, with spans,
-attributes, events, links, baggage, status, metrics and groups demonstrated.
-
---- test/sa/otel.cfg (excerpt) -----------------------------------------
-
-[otel-test-sa]
- otel-instrumentation otel-test-instrumentation
- config sa/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 client_session_start
- scopes frontend_tcp_request
- ...
- 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 on_stream_start
- instrument udcnt_int "haproxy.sessions.active" desc "Active sessions" value int(1) unit "{session}"
- span "HAProxy session" root
- baggage "haproxy_id" var(sess.otel.uuid)
- event "event_ip" "src" 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 *
- otel-event on-stream-stop
-
- otel-scope client_session_start
- span "Client session" parent "HAProxy session"
- otel-event on-client-session-start
-
- otel-scope frontend_http_request
- 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"
- otel-event on-frontend-http-request
-
- otel-scope server_session_start
- span "Server session" parent "HAProxy session"
- link "HAProxy session" "Client session"
- finish "Process sticking rules request"
- otel-event on-server-session-start
-
- otel-scope server_session_end
- finish *
- otel-event on-server-session-end
-
----------------------------------------------------------------------
-
-
-6.2. Frontend / backend example (fe/be)
------------------------------------------
-
-Demonstrates distributed tracing across two cascaded HAProxy instances using
-inject/extract to propagate the span context via HTTP headers.
-
-The frontend HAProxy (test/fe) creates the root trace and injects context:
-
---- test/fe/otel.cfg (excerpt) -----------------------------------------
-
- otel-scope backend_http_request
- span "Backend HTTP request" parent "Backend TCP request"
- finish "Backend TCP request"
- span "HAProxy session"
- inject "otel-ctx" use-headers
- otel-event on-backend-http-request
-
----------------------------------------------------------------------
-
-The backend HAProxy (test/be) extracts the context and continues the trace:
-
---- test/be/otel.cfg (excerpt) -----------------------------------------
-
- otel-scope frontend_http_request
- extract "otel-ctx" use-headers
- span "HAProxy session" parent "otel-ctx" root
- baggage "haproxy_id" var(sess.otel.uuid)
- span "Client session" parent "HAProxy session"
- span "Frontend HTTP request" parent "Client session"
- attribute "http.method" method
- attribute "http.url" url
- attribute "http.version" str("HTTP/") req.ver
- otel-event on-frontend-http-request
-
----------------------------------------------------------------------
-
-
-6.3. Context propagation example (ctx)
-----------------------------------------
-
-Similar to 'sa', but spans are opened using extracted span contexts as parent
-references instead of direct span names. This demonstrates the inject/extract
-mechanism using HAProxy variables.
-
---- test/ctx/otel.cfg (excerpt) ----------------------------------------
-
- otel-scope client_session_start_1
- span "HAProxy session" root
- inject "otel_ctx_1" use-headers use-vars
- baggage "haproxy_id" var(sess.otel.uuid)
- otel-event on-client-session-start
-
- otel-scope client_session_start_2
- extract "otel_ctx_1" use-vars
- span "Client session" parent "otel_ctx_1"
- inject "otel_ctx_2" use-headers use-vars
- otel-event on-client-session-start
-
- otel-scope frontend_tcp_request
- extract "otel_ctx_2" use-vars
- span "Frontend TCP request" parent "otel_ctx_2"
- inject "otel_ctx_3" use-headers use-vars
- otel-event on-frontend-tcp-request
-
- otel-scope http_wait_request
- extract "otel_ctx_3" use-vars
- span "HTTP wait request" parent "otel_ctx_3"
- finish "Frontend TCP request" "otel_ctx_3"
- otel-event on-http-wait-request
-
----------------------------------------------------------------------
-
-
-6.4. Comparison example (cmp)
--------------------------------
-
-A configuration made for comparison purposes with other tracing implementations.
-It uses a simplified span hierarchy without context propagation.
-
---- test/cmp/otel.cfg (excerpt) ----------------------------------------
-
- otel-scope client_session_start
- span "HAProxy session" root
- baggage "haproxy_id" var(sess.otel.uuid)
- span "Client session" parent "HAProxy session"
- otel-event on-client-session-start
-
- otel-scope http_response-error
- span "HTTP response"
- status "error" str("!acl-http-status-ok")
- otel-event on-http-response if !acl-http-status-ok
-
- otel-scope server_session_end
- finish "HTTP response" "Server session"
- otel-event on-http-response
-
- otel-scope client_session_end
- finish "*"
- otel-event on-http-response
-
----------------------------------------------------------------------
-
-
-6.5. Empty / minimal example (empty)
---------------------------------------
-
-The minimal valid OTel configuration. The filter is initialized but no events
-are triggered:
-
---- test/empty/otel.cfg -------------------------------------------------
-
- otel-instrumentation otel-test-instrumentation
- config empty/otel.yml
-
----------------------------------------------------------------------
-
-This is useful for testing the OTel filter initialization behavior without any
-actual telemetry processing.
+++ /dev/null
-OpenTelemetry filter -- design patterns
-==========================================================================
-
-This document describes the cross-cutting design patterns used throughout
-the OTel filter implementation. It complements README-implementation
-(component-by-component architecture) with a pattern-oriented view of the
-mechanisms that span multiple source files.
-
-
-1 X-Macro Code Generation
-----------------------------------------------------------------------
-
-The filter uses X-macro lists extensively to generate enums, keyword tables,
-event metadata and configuration init/free functions from a single definition.
-Each X-macro list is a preprocessor define containing repeated invocations of
-a helper macro whose name is supplied by the expansion context.
-
-1.1 Event Definitions
-
-FLT_OTEL_EVENT_DEFINES (event.h) drives the event system. Each entry has the
-form:
-
- FLT_OTEL_EVENT_DEF(NAME, CHANNEL, REQ_AN, RES_AN, HTTP_INJ, "string")
-
-The same list is expanded twice:
-
- - In enum FLT_OTEL_EVENT_enum (event.h) to produce symbolic constants
- (FLT_OTEL_EVENT_NONE, FLT_OTEL_EVENT_REQ_STREAM_START, etc.) followed by
- the FLT_OTEL_EVENT_MAX sentinel.
-
- - In the flt_otel_event_data[] initializer (event.c) to produce a const table
- of struct flt_otel_event_data entries carrying the analyzer bit, sample
- fetch direction, valid fetch locations, HTTP injection flag and event name
- string.
-
-Adding a new event requires a single line in the X-macro list.
-
-1.2 Parser Keyword Definitions
-
-Three X-macro lists drive the configuration parser:
-
- FLT_OTEL_PARSE_INSTR_DEFINES (parser.h, 9 keywords)
- FLT_OTEL_PARSE_GROUP_DEFINES (parser.h, 2 keywords)
- FLT_OTEL_PARSE_SCOPE_DEFINES (parser.h, 15 keywords)
-
-Each entry has the form:
-
- FLT_OTEL_PARSE_DEF(ENUM, flag, check, min, max, "name", "usage")
-
-Each list is expanded to:
-
- - An enum for keyword indices (FLT_OTEL_PARSE_INSTR_ID, etc.).
- - A static parse_data[] table of struct flt_otel_parse_data entries carrying
- the keyword index, flag_check_id, check_name type, argument count bounds,
- keyword name string and usage string.
-
-The section parsers (flt_otel_parse_cfg_instr, _group, _scope) look up args[0]
-in their parse_data table via flt_otel_parse_cfg_check(), which validates
-argument counts and character constraints before dispatching to
-the keyword-specific handler.
-
-Additional X-macro lists generate:
-
- FLT_OTEL_PARSE_SCOPE_STATUS_DEFINES Span status codes.
- FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEFINES Instrument type keywords.
- FLT_OTEL_HTTP_METH_DEFINES HTTP method name table.
- FLT_OTEL_GROUP_DEFINES Group action metadata.
-
-1.3 Configuration Init/Free Generation
-
-Two macros in conf_funcs.h generate init and free functions for all
-configuration structure types:
-
- FLT_OTEL_CONF_FUNC_INIT(_type_, _id_, _func_)
- FLT_OTEL_CONF_FUNC_FREE(_type_, _id_, _func_)
-
-The _type_ parameter names the structure (e.g. "span"), _id_ names the
-identifier field within the FLT_OTEL_CONF_HDR (e.g. "id", "key", "str"), and
-_func_ is a brace-enclosed code block executed after the boilerplate allocation
-(for init) or before the boilerplate teardown (for free).
-
-The generated init function:
- 1. Validates the identifier is non-NULL and non-empty.
- 2. Checks the length against FLT_OTEL_ID_MAXLEN (64).
- 3. Detects duplicates in the target list.
- 4. Handles auto-generated IDs (FLT_OTEL_CONF_HDR_SPECIAL prefix).
- 5. Allocates with OTELC_CALLOC, duplicates the ID with OTELC_STRDUP.
- 6. Appends to the parent list via LIST_APPEND.
- 7. Executes the custom _func_ body.
-
-The generated free function:
- 1. Executes the custom _func_ body (typically list destruction).
- 2. Frees the identifier string.
- 3. Removes the node from its list.
- 4. Frees the structure with OTELC_SFREE_CLEAR.
-
-conf.c instantiates these macros for all 13 configuration types: hdr, str, ph,
-sample_expr, sample, link, context, span, scope, instrument, log_record, group,
-instr. The more complex types (span, scope, instr) carry non-trivial _func_
-bodies that initialize sub-lists or set default values.
-
-
-2 Type-Safe Generic Macros
-----------------------------------------------------------------------
-
-define.h provides utility macros that use typeof() and statement expressions
-for type-safe generic programming without C++ templates.
-
-2.1 Safe Pointer Dereferencing
-
- FLT_OTEL_PTR_SAFE(a, b)
- Returns 'a' if non-NULL, otherwise the default value 'b'. Uses typeof(*(a))
- to verify type compatibility at compile time.
-
- FLT_OTEL_DEREF(p, m, v)
- Safely dereferences p->m, returning 'v' if 'p' is NULL.
-
- FLT_OTEL_DDEREF(a, m, v)
- Safely double-dereferences *a->m, returning 'v' if either 'a' or '*a' is
- NULL.
-
-These macros eliminate repetitive NULL checks while preserving the correct
-pointer types through typeof().
-
-2.2 String Comparison
-
- FLT_OTEL_STR_CMP(S, s)
- Compares a runtime string 's' against a compile-time string literal 'S'.
- Expands to a call with the literal's address and computed length, avoiding
- repeated strlen() on known constants.
-
- FLT_OTEL_CONF_STR_CMP(s, S)
- Compares two configuration strings using their cached length fields (from
- FLT_OTEL_CONF_STR / FLT_OTEL_CONF_HDR). Falls through to memcmp() only when
- lengths match, providing an early-out fast path.
-
-2.3 List Operations
-
- FLT_OTEL_LIST_ISVALID(a)
- Checks whether a list head has been initialized (both prev and next are
- non-NULL).
-
- FLT_OTEL_LIST_DEL(a)
- Safely deletes a list element only if the list head is valid.
-
- FLT_OTEL_LIST_DESTROY(t, h)
- Destroys all elements in a typed configuration list by iterating with
- list_for_each_entry_safe and calling flt_otel_conf_<t>_free() for each
- entry. The type 't' is used to derive both the structure type and the
- free function name.
-
-2.4 Thread-Local Rotating Buffers
-
- FLT_OTEL_BUFFER_THR(b, m, n, p)
- Declares a thread-local pool of 'n' string buffers, each of size 'm'.
- The pool index rotates on each invocation, avoiding the need for explicit
- allocation in debug formatting functions. Each call returns a pointer to
- the next buffer via the output parameter 'p'.
-
- Used primarily in debug-only functions (flt_otel_analyzer,
- flt_otel_list_dump) where temporary strings must survive until their caller
- finishes with them.
-
-
-3 Error Handling Architecture
-----------------------------------------------------------------------
-
-3.1 Error Accumulation Macros
-
-Three macros in define.h manage error messages:
-
- FLT_OTEL_ERR(f, ...)
- Formats an error message via memprintf() into the caller's char **err
- pointer, but only if *err is still NULL. Prevents overwriting the first
- error with a cascading secondary error.
-
- FLT_OTEL_ERR_APPEND(f, ...)
- Unconditionally appends to the error message regardless of prior content.
- Used when a function must report multiple issues.
-
- FLT_OTEL_ERR_FREE(p)
- Logs the accumulated error message at WARNING level via FLT_OTEL_ALERT,
- then frees the string and NULLs the pointer.
-
-All source functions that can fail accept a char **err parameter. The
-convention is: set *err on error, leave it alone on success. Callers check
-both the return value and *err to detect problems.
-
-3.2 Parse-Time Error Reporting
-
-Configuration parsing (parser.c) uses additional macros:
-
- FLT_OTEL_PARSE_ERR(e, f, ...)
- Sets the error string and ORs ERR_ABORT | ERR_ALERT into the return value.
- Used inside section parser switch statements.
-
- FLT_OTEL_PARSE_ALERT(f, ...)
- Issues a ha_alert() with the current file and line, then sets the error
- flags. Used when the error message should appear immediately.
-
- FLT_OTEL_POST_PARSE_ALERT(f, ...)
- Same as PARSE_ALERT but uses cfg_file instead of file, for post-parse
- validation that runs after the parser has moved on.
-
- FLT_OTEL_PARSE_IFERR_ALERT()
- Checks whether *err was set by a called function, and if so, issues an
- alert and clears the error. This bridges between functions that use the
- char **err convention and the parser's own alert mechanism.
-
-3.3 Runtime Error Modes
-
-Two helper functions in filter.c implement the runtime error policy:
-
- flt_otel_return_int(f, err, retval)
- flt_otel_return_void(f, err)
-
-If an error is detected (retval == FLT_OTEL_RET_ERROR or *err != NULL):
-
- Hard-error mode (rt_ctx->flag_harderr):
- Sets rt_ctx->flag_disabled = 1, disabling the filter for the remainder of
- the stream. Atomically increments the disabled counter. The error is
- logged as "filter hard-error (disabled)".
-
- Soft-error mode (default):
- The error is logged as "filter soft-error" and processing continues. The
- tracing data for the current event may be incomplete but the stream is not
- affected.
-
-In both modes, FLT_OTEL_RET_OK is always returned to HAProxy. A tracing failure
-never interrupts stream processing.
-
-3.4 Disabled State Checking
-
-flt_otel_is_disabled() (filter.c) is called at the top of every filter callback
-that processes events. It checks three conditions:
-
- 1. rt_ctx is NULL (filter was never attached or already detached).
- 2. rt_ctx->flag_disabled is set (hard-error disabled the filter).
- 3. conf->instr->flag_disabled is set (globally disabled via CLI).
-
-All three are loaded via _HA_ATOMIC_LOAD() for thread safety. When disabled,
-the callback returns immediately without processing.
-
-
-4 Thread Safety Model
-----------------------------------------------------------------------
-
-The filter runs within HAProxy's multi-threaded worker model. Each stream is
-bound to a single thread, so per-stream state (the runtime context, spans and
-contexts) is accessed without locking. Cross-stream shared state uses atomic
-operations.
-
-4.1 Atomic Fields
-
-The following fields are accessed atomically across threads:
-
- conf->instr->flag_disabled Toggled by CLI "otel enable/disable". Checked in
- flt_otel_ops_attach() and flt_otel_is_disabled().
-
- conf->instr->flag_harderr Toggled by CLI "otel hard-errors/soft-errors".
- Copied to rt_ctx at attach time.
-
- conf->instr->rate_limit Set by CLI "otel rate".
- Read in flt_otel_ops_attach().
-
- conf->instr->logging Set by CLI "otel logging".
- Copied to rt_ctx at attach time.
-
- flt_otel_drop_cnt Incremented in flt_otel_log_handler_cb().
- Read by CLI "otel status".
-
- conf->cnt.disabled[] Incremented in flt_otel_return_int/void().
- conf->cnt.attached[] Incremented in flt_otel_ops_attach().
-
-All use _HA_ATOMIC_LOAD(), _HA_ATOMIC_STORE(), _HA_ATOMIC_INC() or
-_HA_ATOMIC_ADD() from HAProxy's atomic primitives.
-
-4.2 Metric Instrument Creation (CAS Pattern)
-
-Metric instruments are shared across all threads but created lazily on first
-use. The creation uses a compare-and-swap pattern to ensure exactly one thread
-performs the creation:
-
- int64_t expected = OTELC_METRIC_INSTRUMENT_UNSET; /* -1 */
- if (HA_ATOMIC_CAS(&instr->idx, &expected, OTELC_METRIC_INSTRUMENT_PENDING)) {
- /* This thread won the race -- create the instrument. */
- idx = meter->create_instrument(...);
- HA_ATOMIC_STORE(&instr->idx, idx);
- }
-
-The three states are:
- UNSET (-1) Not yet created. The CAS target.
- PENDING (-2) Creation in progress by another thread.
- >= 0 Valid meter index. Ready for recording.
-
-Threads that lose the CAS skip creation. Update-form instruments check that the
-referenced create-form's idx is >= 0 before recording; if it is still PENDING or
-UNSET, the measurement is silently skipped.
-
-4.3 Per-Thread Initialization Guard
-
-flt_otel_ops_init_per_thread() uses the FLT_CFG_FL_HTX flag on fconf as a
-one-shot guard: the tracer, meter and logger background threads are started only
-on the first call. Subsequent calls from other proxies sharing the same filter
-configuration skip the start sequence.
-
-4.4 CLI Update Propagation
-
-CLI set commands (cli.c) iterate all OTel filter instances across all proxies
-using the FLT_OTEL_PROXIES_LIST_START / FLT_OTEL_PROXIES_LIST_END macros
-(util.h) and atomically update the target field on each instance. This ensures
-that "otel disable" takes effect globally, not just on one proxy.
-
-
-5 Sample Evaluation Pipeline
-----------------------------------------------------------------------
-
-Sample expressions bridge HAProxy's runtime data (headers, variables, connection
-properties) into OTel attributes, events, baggage, status, metric values and log
-record bodies.
-
-5.1 Two Evaluation Paths
-
-The parser detects which path to use based on the presence of "%[" in the sample
-value argument:
-
- Log-format path (sample->lf_used == true):
- The value is parsed via parse_logformat_string() and stored in
- sample->lf_expr. At runtime, build_logline() evaluates the expression into
- a temporary buffer of global.tune.bufsize bytes. The result is always a
- string.
-
- Bare sample path (sample->lf_used == false):
- Each whitespace-separated token is parsed via sample_parse_expr() and
- stored as an flt_otel_conf_sample_expr in sample->exprs. At runtime, each
- expression is evaluated via sample_process(). If there is exactly one
- expression, the native HAProxy sample type is preserved (bool, int, IP
- address, string). If there are multiple expressions, their string
- representations are concatenated.
-
-5.2 Type Conversion Chain
-
-flt_otel_sample_to_value() (util.c) converts HAProxy sample types to OTel value
-types:
-
- SMP_T_BOOL -> OTELC_VALUE_BOOL
- SMP_T_SINT -> OTELC_VALUE_INT64
- Other -> OTELC_VALUE_DATA (string, via flt_otel_sample_to_str)
-
-flt_otel_sample_to_str() handles the string conversion for non-trivial types:
-
- SMP_T_BOOL "0" or "1"
- SMP_T_SINT snprintf decimal
- SMP_T_IPV4 inet_ntop
- SMP_T_IPV6 inet_ntop
- SMP_T_STR direct copy
- SMP_T_METH lookup from static HTTP method table, or raw string for
- HTTP_METH_OTHER
-
-For metric instruments, string values are rejected -- the meter requires
-OTELC_VALUE_INT64. If the sample evaluates to a string, otelc_value_strtonum()
-attempts numeric conversion as a last resort.
-
-5.3 Dispatch to Data Structures
-
-flt_otel_sample_add() (util.c) is the top-level evaluator. After converting the
-sample to an otelc_value, it dispatches based on type:
-
- FLT_OTEL_EVENT_SAMPLE_ATTRIBUTE
- -> flt_otel_sample_add_kv(&data->attributes, key, &value)
-
- FLT_OTEL_EVENT_SAMPLE_BAGGAGE
- -> flt_otel_sample_add_kv(&data->baggage, key, &value)
-
- FLT_OTEL_EVENT_SAMPLE_EVENT
- -> flt_otel_sample_add_event(&data->events, sample, &value)
- Groups attributes by event name; creates a new flt_otel_scope_data_event
- node if the event name is not yet present.
-
- FLT_OTEL_EVENT_SAMPLE_STATUS
- -> flt_otel_sample_set_status(&data->status, sample, &value, err)
- Sets the status code from sample->extra.num and the description from the
- evaluated value. Rejects duplicate status settings.
-
-5.4 Growable Key-Value Arrays
-
-Attribute and baggage arrays use a lazy-allocation / grow-on-demand pattern via
-flt_otel_sample_add_kv() (util.c):
-
- - Initial allocation: FLT_OTEL_ATTR_INIT_SIZE (8) elements via otelc_kv_new().
- - Growth: FLT_OTEL_ATTR_INC_SIZE (4) additional elements via otelc_kv_resize()
- when the array is full.
- - The cnt field tracks used elements; size tracks total capacity.
-
-Event attribute arrays follow the same pattern within each
-flt_otel_scope_data_event node.
-
-
-6 Rate Limiting and Filtering
-----------------------------------------------------------------------
-
-6.1 Rate Limit Representation
-
-The rate limit is configured as a floating-point percentage (0.0 to 100.0) but
-stored internally as a uint32_t for lock-free comparison:
-
- FLT_OTEL_FLOAT_U32(a) Converts a percentage to a uint32 value:
- (uint32_t)((a / 100.0) * UINT32_MAX)
-
- FLT_OTEL_U32_FLOAT(a) Converts back for display:
- ((double)(a) / UINT32_MAX) * 100.0
-
-At attach time, the filter compares a fresh ha_random32() value against the
-stored rate_limit. If the random value exceeds the threshold, the filter
-returns FLT_OTEL_RET_IGNORE and does not attach to the stream. This provides
-uniform sampling without floating-point arithmetic on the hot path.
-
-6.2 ACL-Based Filtering
-
-Each scope may carry an ACL condition (if/unless) evaluated at scope
-execution time via acl_exec_cond(). ACL references are resolved through
-a priority chain in flt_otel_parse_acl() (parser.c):
-
- 1. Scope-local ACLs (declared within the otel-scope section).
- 2. Instrumentation ACLs (declared in the otel-instrumentation section).
- 3. Proxy ACLs (declared in the HAProxy frontend/backend section).
-
-The first list that produces a successful build_acl_cond() result is used.
-If the condition fails at runtime:
-
- - If the scope contains a root span, the entire stream is disabled
- (rt_ctx->flag_disabled = 1) to avoid orphaned child spans.
- - Otherwise, the scope is simply skipped.
-
-6.3 Global Disable
-
-The filter can be disabled globally via the CLI ("otel disable") or the
-configuration ("option disabled"). The flag_disabled field on the
-instrumentation configuration is checked atomically in flt_otel_ops_attach()
-before any per-stream allocation occurs.
-
-
-7 Memory Management Strategy
-----------------------------------------------------------------------
-
-7.1 Pool-Based Allocation
-
-Four HAProxy memory pools are registered for frequently allocated structures
-(pool.c):
-
- pool_head_otel_scope_span Per-stream span entries.
- pool_head_otel_scope_context Per-stream context entries.
- pool_head_otel_runtime_context Per-stream runtime context.
- pool_head_otel_span_context OTel SDK objects (via C wrapper allocator
- callback).
-
-Pool registration is conditional on compile-time flags (USE_POOL_OTEL_* in
-config.h). flt_otel_pool_alloc() attempts pool allocation first and falls back
-to heap allocation (OTELC_MALLOC) when the pool is NULL or the requested size
-exceeds the pool's element size.
-
-The C wrapper library's memory allocations are redirected through
-flt_otel_mem_malloc() / flt_otel_mem_free() (filter.c), which use the
-otel_span_context pool via otelc_ext_init(). This keeps OTel SDK objects in
-HAProxy's pool allocator for cache-friendly allocation.
-
-7.2 Trash Buffers
-
-flt_otel_trash_alloc() (pool.c) provides temporary scratch buffers of
-global.tune.bufsize bytes. When USE_TRASH_CHUNK is defined, it uses HAProxy's
-alloc_trash_chunk(); otherwise it manually allocates a buffer structure and data
-area. Trash buffers are used for:
-
- - Log-format evaluation in metric recording and log record emission.
- - HTTP header name construction in flt_otel_http_header_set().
- - Temporary string assembly in sample evaluation.
-
-7.3 Scope Data Lifecycle
-
-flt_otel_scope_data (scope.h) is initialized and freed within a single span
-processing block in flt_otel_scope_run(). It is stack-conceptual data:
-allocated at the start of each span's processing, populated during sample
-evaluation, consumed by flt_otel_scope_run_span(), and freed immediately after.
-The key-value arrays within it are heap-allocated via otelc_kv_new() and freed
-via otelc_kv_destroy().
-
-
-8 Context Propagation Mechanism
-----------------------------------------------------------------------
-
-8.1 Carrier Abstraction
-
-The OTel C wrapper uses a carrier pattern for context propagation. Two carrier
-types exist, each with writer and reader variants:
-
- otelc_text_map_writer / otelc_text_map_reader
- otelc_http_headers_writer / otelc_http_headers_reader
-
-otelc.c provides callback functions that the C wrapper invokes during
-inject/extract operations:
-
- Injection (writer callbacks):
- flt_otel_text_map_writer_set_cb()
- flt_otel_http_headers_writer_set_cb()
- Both append key-value pairs to the carrier's text_map via
- OTELC_TEXT_MAP_ADD.
-
- Extraction (reader callbacks):
- flt_otel_text_map_reader_foreach_key_cb()
- flt_otel_http_headers_reader_foreach_key_cb()
- Both iterate the text_map's key-value pairs, invoking a handler function
- for each entry. Iteration stops early if the handler returns -1.
-
-8.2 HTTP Header Storage
-
-flt_otel_http_headers_get() (http.c) reads headers from the HTX buffer with
-prefix matching. The prefix is stripped from header names in the returned
-text_map. A special prefix character ('-') matches all headers without prefix
-filtering.
-
-flt_otel_http_header_set() constructs a "prefix-name" header, removes all
-existing occurrences via an http_find_header / http_remove_header loop, then
-adds the new value via http_add_header.
-
-8.3 Variable Storage
-
-When USE_OTEL_VARS is enabled, span context can also be stored in HAProxy
-transaction-scoped variables. Variable names are normalized
-(flt_otel_normalize_name in vars.c):
-
- Dashes -> 'D' (FLT_OTEL_VAR_CHAR_DASH)
- Spaces -> 'S' (FLT_OTEL_VAR_CHAR_SPACE)
- Uppercase -> lowercase
-
-Full variable names are constructed as "scope.prefix.name" with dots as
-component separators.
-
-Two implementation paths exist based on the USE_OTEL_VARS_NAME compile flag:
-
- With USE_OTEL_VARS_NAME:
- A binary tracking buffer (stored as a HAProxy variable) records the names
- of all context variables. flt_otel_ctx_loop() iterates length-prefixed
- entries in this buffer, calling a callback for each. This enables efficient
- enumeration without scanning the full variable store.
-
- Without USE_OTEL_VARS_NAME:
- Direct CEB tree traversal on the HAProxy variable store with prefix
- matching. This is simpler but potentially slower for large variable sets.
-
-The choice is auto-detected at build time by checking whether struct var has
-a 'name' member.
-
-
-9 Debug Infrastructure
-----------------------------------------------------------------------
-
-9.1 Conditional Compilation
-
-When OTEL_DEBUG=1 is set at build time, -DDEBUG_OTEL is added to the compiler
-flags. This enables:
-
- - Additional flt_ops callbacks: deinit_per_thread, http_payload, http_reset,
- tcp_payload. In non-debug builds these are NULL.
-
- - FLT_OTEL_USE_COUNTERS (config.h), which activates the per-event counters in
- flt_otel_counters (attached[], disabled[] arrays).
-
- - Debug-only functions throughout the codebase, marked with [D] in
- README-func: flt_otel_scope_data_dump, flt_otel_http_headers_dump,
- flt_otel_vars_dump, flt_otel_args_dump, flt_otel_filters_dump, etc.
-
- - The OTELC_DBG() macro for level-gated debug output.
-
-9.2 Debug Level Bitmask
-
-The debug level is a uint stored in conf->instr and controllable at runtime via
-"otel debug <level>". The default value is FLT_OTEL_DEBUG_LEVEL (0b11101111111
-in config.h). Each bit enables a category of debug output.
-
-9.3 Logging Integration
-
-The FLT_OTEL_LOG macro (debug.h) integrates with HAProxy's send_log() system.
-Its behavior depends on the logging flags:
-
- FLT_OTEL_LOGGING_OFF (0):
- No log messages are emitted.
-
- FLT_OTEL_LOGGING_ON (1 << 0):
- Log messages are sent to the log servers configured in the instrumentation
- block via parse_logger().
-
- FLT_OTEL_LOGGING_NOLOGNORM (1 << 1):
- Combined with ON, suppresses normal-level messages (only warnings and above
- are emitted).
-
-These flags are set via the "option dontlog-normal" configuration keyword or the
-"otel logging" CLI command.
-
-9.4 Counter System
-
-When FLT_OTEL_USE_COUNTERS is defined, the flt_otel_counters structure (conf.h)
-maintains:
-
- attached[4] Counters for attach outcomes, incremented atomically in
- flt_otel_ops_attach().
- disabled[2] Counters for hard-error disables, incremented atomically in
- flt_otel_return_int() / flt_otel_return_void().
-
-The counters are reported by the "otel status" CLI command and dumped at deinit
-time in debug builds.
-
-
-10 Idle Timeout Mechanism
-----------------------------------------------------------------------
-
-The idle timeout fires periodic events on idle streams, enabling heartbeat-style
-span updates or metric recordings.
-
-10.1 Configuration
-
-Each otel-scope bound to the "on-idle-timeout" event must declare an
-idle-timeout interval. At check time (flt_otel_ops_check), the minimum idle
-timeout across all scopes is stored in conf->instr->idle_timeout.
-
-10.2 Initialization
-
-In flt_otel_ops_stream_start(), the runtime context's idle_timeout and idle_exp
-fields are initialized from the precomputed minimum. The expiration tick is
-merged into the request channel's analyse_exp to ensure the stream task wakes
-at the right time.
-
-10.3 Firing
-
-flt_otel_ops_check_timeouts() checks tick_is_expired(rt_ctx->idle_exp).
-When expired:
- - The on-idle-timeout event fires via flt_otel_event_run().
- - The timer is rescheduled for the next interval.
- - If analyse_exp itself has expired (which would cause a tight loop), it is
- reset before the new idle_exp is merged.
- - STRM_EVT_MSG is set on stream->pending_events to ensure the stream is
- re-evaluated.
-
-
-11 Group Action Integration Pattern
-----------------------------------------------------------------------
-
-The "otel-group" action integrates OTel scopes with HAProxy's rule system
-(http-request, http-response, http-after-response, tcp-request, tcp-response).
-
-11.1 Registration
-
-group.c registers action keywords via INITCALL1 macros for all five action
-contexts. Each registration points to flt_otel_group_parse() as the parse
-function.
-
-11.2 Parse-Check-Execute Cycle
-
- Parse (flt_otel_group_parse):
- Stores the filter ID and group ID as string duplicates in rule->arg.act.p[].
- Sets the check, action and release callbacks.
-
- Check (flt_otel_group_check):
- Resolves the string IDs to configuration pointers by scanning the proxy's
- filter list. Replaces the string pointers with resolved flt_conf,
- flt_otel_conf and flt_otel_conf_ph pointers. Frees the original string
- duplicates.
-
- Execute (flt_otel_group_action):
- Finds the filter instance in the stream's filter list. Validates rule->from
- against the flt_otel_group_data[] table to determine the sample fetch
- direction and valid fetch locations. Iterates all scopes in the group and
- calls flt_otel_scope_run() for each. Always returns ACT_RET_CONT -- a group
- action never interrupts rule processing.
-
-11.3 Group Data Table
-
-The flt_otel_group_data[] table (group.c) maps each HAProxy action context
-(ACT_F_*) to:
-
- act_from The action context enum value.
- smp_val The valid sample fetch location (FE or BE).
- smp_opt_dir The sample fetch direction (REQ or RES).
-
-This table is generated from the FLT_OTEL_GROUP_DEFINES X-macro list (group.h).
-
-
-12 CLI Runtime Control Architecture
-----------------------------------------------------------------------
-
-cli.c registers commands under the "otel" prefix. The command table maps
-keyword sequences to handler functions with access level requirements.
-
-12.1 Command Table
-
- "otel status" No access restriction. Read-only status report.
- "otel enable" ACCESS_LVL_ADMIN. Clears flag_disabled.
- "otel disable" ACCESS_LVL_ADMIN. Sets flag_disabled.
- "otel hard-errors" ACCESS_LVL_ADMIN. Sets flag_harderr.
- "otel soft-errors" ACCESS_LVL_ADMIN. Clears flag_harderr.
- "otel logging" ACCESS_LVL_ADMIN for setting; any for reading.
- "otel rate" ACCESS_LVL_ADMIN for setting; any for reading.
- "otel debug" ACCESS_LVL_ADMIN. DEBUG_OTEL only.
-
-The "otel enable" and "otel disable" commands share the same handler
-(flt_otel_cli_parse_disabled) with the private argument distinguishing the
-operation (1 for disable, 0 for enable). The same pattern is used for
-hard-errors / soft-errors.
-
-12.2 Global Propagation
-
-All set operations iterate every OTel filter instance across all proxies using
-FLT_OTEL_PROXIES_LIST_START / FLT_OTEL_PROXIES_LIST_END macros and atomically
-update the target field. A single "otel disable" command disables the filter
-in every proxy that has it configured.
-
-12.3 Status Report
-
-The "otel status" command assembles a dynamic string via memprintf() containing:
-
- - Library versions (C++ SDK and C wrapper).
- - Debug level (DEBUG_OTEL only).
- - Dropped diagnostic message count.
- - Per-proxy: config file, group/scope counts, instrumentation details, rate
- limit, error mode, disabled state, logging state, analyzer bitmask, and
- counters (if FLT_OTEL_USE_COUNTERS).
+++ /dev/null
-OpenTelemetry filter -- function reference
-==========================================================================
-
-Functions are grouped by source file. Functions marked with [D] are only
-compiled when DEBUG_OTEL is defined.
-
-
-src/filter.c
-----------------------------------------------------------------------
-
-Filter lifecycle callbacks and helpers registered in flt_otel_ops.
-
- flt_otel_mem_malloc
- Allocator callback for the OTel C wrapper library. Uses the HAProxy
- pool_head_otel_span_context pool.
-
- flt_otel_mem_free
- Deallocator callback for the OTel C wrapper library.
-
- flt_otel_log_handler_cb
- Diagnostic callback for the OTel C wrapper library. Counts SDK internal
- diagnostic messages.
-
- flt_otel_thread_id
- Returns the current HAProxy thread ID (tid).
-
- flt_otel_lib_init
- Initializes the OTel C wrapper library: verifies the library version,
- constructs the configuration path, calls otelc_init(), and creates the
- tracer, meter and logger instances.
-
- flt_otel_is_disabled
- Checks whether the filter instance is disabled for the current stream.
- Logs the event name when DEBUG_OTEL is enabled.
-
- flt_otel_return_int
- Error handler for callbacks returning int. In hard-error mode, disables
- the filter; in soft-error mode, clears the error and returns OK.
-
- flt_otel_return_void
- Error handler for callbacks returning void. Same logic as
- flt_otel_return_int but without a return value.
-
- flt_otel_ops_init
- Filter init callback (flt_ops.init). Called once per proxy to initialize
- the OTel library via flt_otel_lib_init() and register CLI keywords.
-
- flt_otel_ops_deinit
- Filter deinit callback (flt_ops.deinit). Destroys the tracer, meter and
- logger, frees the configuration, and calls otelc_deinit().
-
- flt_otel_ops_check
- Filter check callback (flt_ops.check). Validates the parsed
- configuration: checks for duplicate filter IDs, resolves group/scope
- placeholder references, verifies root span count, and sets analyzer bits.
-
- flt_otel_ops_init_per_thread
- Per-thread init callback (flt_ops.init_per_thread). Starts the OTel
- tracer thread and enables HTX filtering.
-
- flt_otel_ops_deinit_per_thread [D]
- Per-thread deinit callback (flt_ops.deinit_per_thread).
-
- flt_otel_ops_attach
- Filter attach callback (flt_ops.attach). Called when a filter instance is
- attached to a stream. Applies rate limiting, creates the runtime context,
- and sets analyzer bits.
-
- flt_otel_ops_stream_start
- Stream start callback (flt_ops.stream_start). Fires the
- on-stream-start event before any channel processing begins. The channel
- argument is NULL. After the event, initializes the idle timer in the
- runtime context from the precomputed minimum idle_timeout in the
- instrumentation configuration.
-
- flt_otel_ops_stream_set_backend
- Stream set-backend callback (flt_ops.stream_set_backend). Fires the
- on-backend-set event when a backend is assigned to the stream.
-
- flt_otel_ops_stream_stop
- Stream stop callback (flt_ops.stream_stop). Fires the
- on-stream-stop event after all channel processing ends. The channel
- argument is NULL.
-
- flt_otel_ops_detach
- Filter detach callback (flt_ops.detach). Frees the runtime context when
- the filter is detached from a stream.
-
- flt_otel_ops_check_timeouts
- Timeout callback (flt_ops.check_timeouts). When the idle-timeout timer
- has expired, fires the on-idle-timeout event and reschedules the timer
- for the next interval. Sets the STRM_EVT_MSG pending event flag on the
- stream.
-
- flt_otel_ops_channel_start_analyze
- Channel start-analyze callback. Registers analyzers on the channel and
- runs the client/server session start event. Propagates the idle-timeout
- expiry to the channel's analyse_exp so the stream task keeps waking.
-
- flt_otel_ops_channel_pre_analyze
- Channel pre-analyze callback. Maps the analyzer bit to an event index and
- runs the corresponding event.
-
- flt_otel_ops_channel_post_analyze
- Channel post-analyze callback. Non-resumable; called once when a
- filterable analyzer finishes.
-
- flt_otel_ops_channel_end_analyze
- Channel end-analyze callback. Runs the client/server session end event.
- For the request channel, also fires the server-unavailable event if no
- response was processed.
-
- flt_otel_ops_http_headers
- HTTP headers callback (flt_ops.http_headers). Fires
- on-http-headers-request or on-http-headers-response depending on the
- channel direction.
-
- flt_otel_ops_http_payload [D]
- HTTP payload callback (flt_ops.http_payload).
-
- flt_otel_ops_http_end
- HTTP end callback (flt_ops.http_end). Fires on-http-end-request or
- on-http-end-response depending on the channel direction.
-
- flt_otel_ops_http_reset [D]
- HTTP reset callback (flt_ops.http_reset).
-
- flt_otel_ops_http_reply
- HTTP reply callback (flt_ops.http_reply). Fires the on-http-reply event
- when HAProxy generates an internal reply.
-
- flt_otel_ops_tcp_payload [D]
- TCP payload callback (flt_ops.tcp_payload).
-
-
-src/event.c
-----------------------------------------------------------------------
-
-Event dispatching, metrics recording and scope/span execution engine.
-
- flt_otel_scope_run_instrument_record
- Records a measurement for a synchronous metric instrument. Evaluates
- update-form attributes via flt_otel_sample_eval() and
- flt_otel_sample_add_kv(), evaluates the sample expression from the
- create-form instrument (instr_ref), and submits the value to the meter
- via update_instrument_kv_n().
-
- flt_otel_scope_run_instrument
- Processes all metric instruments for a scope. Runs in two passes: the
- first lazily creates create-form instruments via the meter, using
- HA_ATOMIC_CAS to guarantee thread-safe one-time initialization; the second
- iterates update-form instruments and records measurements via
- flt_otel_scope_run_instrument_record(). Instruments whose index is still
- negative (UNUSED or PENDING) are skipped.
-
- flt_otel_scope_run_log_record
- Emits log records for a scope. Iterates over the configured log-record
- list, skipping entries whose severity is below the logger threshold.
- Evaluates the body from sample fetch expressions or a log-format string,
- optionally resolves a span reference against the runtime context, and
- emits the record via the logger. A missing span is non-fatal -- the
- record is emitted without span correlation.
-
- flt_otel_scope_run_span
- Executes a single span: creates the OTel span on first call, adds links,
- baggage, attributes, events and status, then injects the context into HTTP
- headers or HAProxy variables.
-
- flt_otel_scope_run
- Executes a complete scope: evaluates ACL conditions, extracts contexts,
- iterates over configured spans (resolving links, evaluating sample
- expressions), calls flt_otel_scope_run_span for each, processes metric
- instruments via flt_otel_scope_run_instrument(), emits log records via
- flt_otel_scope_run_log_record(), then marks and finishes completed spans.
-
- flt_otel_event_run
- Top-level event dispatcher. Called from filter callbacks, iterates over
- all scopes matching the event index and calls flt_otel_scope_run() for
- each.
-
-
-src/scope.c
-----------------------------------------------------------------------
-
-Runtime context, span and context lifecycle management.
-
- flt_otel_pools_info [D]
- Logs the sizes of all registered HAProxy memory pools used by the OTel
- filter.
-
- flt_otel_runtime_context_init
- Allocates and initializes the per-stream runtime context. Generates a
- UUID and stores it in the sess.otel.uuid HAProxy variable.
-
- flt_otel_runtime_context_free
- Frees the runtime context: ends all active spans, destroys all extracted
- contexts, and releases pool memory.
-
- flt_otel_scope_span_init
- Finds an existing scope span by name or creates a new one. Resolves the
- parent reference (span or extracted context).
-
- flt_otel_scope_span_free
- Frees a scope span entry if its OTel span has been ended. Refuses to free
- an active (non-NULL) span.
-
- flt_otel_scope_context_init
- Finds an existing scope context by name or creates a new one by extracting
- the span context from a text map.
-
- flt_otel_scope_context_free
- Frees a scope context entry and destroys the underlying OTel span context.
-
- flt_otel_scope_data_dump [D]
- Dumps scope data contents (baggage, attributes, events, links, status) for
- debugging.
-
- flt_otel_scope_data_init
- Zero-initializes a scope data structure and its event/link lists.
-
- flt_otel_scope_data_free
- Frees all scope data contents: key-value arrays, event entries, link
- entries, and status description.
-
- flt_otel_scope_finish_mark
- Marks spans and contexts for finishing. Supports wildcard ("*"),
- channel-specific ("req"/"res"), and named targets.
-
- flt_otel_scope_finish_marked
- Ends all spans and destroys all contexts that have been marked for
- finishing by flt_otel_scope_finish_mark().
-
- flt_otel_scope_free_unused
- Removes scope spans with NULL OTel span and scope contexts with NULL OTel
- context. Cleans up associated HTTP headers and variables.
-
-
-src/parser.c
-----------------------------------------------------------------------
-
-Configuration file parsing for otel-instrumentation, otel-group and otel-scope
-sections.
-
- flt_otel_parse_strdup
- Duplicates a string with error handling; optionally stores the string
- length.
-
- flt_otel_parse_keyword
- Parses a single keyword argument: checks for duplicates and missing
- values, then stores via flt_otel_parse_strdup().
-
- flt_otel_parse_invalid_char
- Validates characters in a name according to the specified type
- (identifier, domain, context prefix, variable).
-
- flt_otel_parse_cfg_check
- Common validation for config keywords: looks up the keyword, checks
- argument count and character validity, verifies that the parent section ID
- is set.
-
- flt_otel_parse_cfg_sample_expr
- Parses a single HAProxy sample expression within a sample definition.
- Calls sample_parse_expr().
-
- flt_otel_parse_cfg_sample
- Parses a complete sample definition (key plus one or more sample
- expressions).
-
- flt_otel_parse_cfg_str
- Parses one or more string arguments into a conf_str list (used for the
- "finish" keyword).
-
- flt_otel_parse_cfg_file
- Parses and validates a file path argument; checks that the file exists and
- is readable.
-
- flt_otel_parse_check_scope
- Checks whether the current config parsing is within the correct HAProxy
- configuration scope (cfg_scope filtering).
-
- flt_otel_parse_cfg_instr
- Section parser for the otel-instrumentation block. Handles keywords:
- otel-instrumentation ID, log, config, groups, scopes, acl, rate-limit,
- option, debug-level.
-
- flt_otel_post_parse_cfg_instr
- Post-parse callback for otel-instrumentation. Links the instrumentation
- to the config and checks that a config file is specified.
-
- flt_otel_parse_cfg_group
- Section parser for the otel-group block. Handles keywords: otel-group ID,
- scopes.
-
- flt_otel_post_parse_cfg_group
- Post-parse callback for otel-group. Checks that at least one scope is
- defined.
-
- flt_otel_parse_cfg_scope_ctx
- Parses the context storage type argument ("use-headers" or "use-vars") for
- inject/extract keywords.
-
- flt_otel_parse_acl
- Builds an ACL condition by trying multiple ACL lists in order
- (scope-local, instrumentation, proxy).
-
- flt_otel_parse_bounds
- Parses a space-separated string of numbers into a dynamically allocated
- array of doubles for histogram bucket boundaries. Sorts the values
- internally.
-
- flt_otel_parse_cfg_instrument
- Parses the "instrument" keyword inside an otel-scope section. Supports
- both "update" form (referencing an existing instrument) and "create" form
- (defining a new metric instrument with type, name, optional aggregation
- type, description, unit, value, and optional histogram bounds).
-
- flt_otel_parse_cfg_scope
- Section parser for the otel-scope block. Handles keywords: otel-scope ID,
- span, link, attribute, event, baggage, status, inject, extract, finish,
- instrument, log-record, acl, otel-event.
-
- flt_otel_post_parse_cfg_scope
- Post-parse callback for otel-scope. Checks that HTTP header injection is
- only used on events that support it.
-
- flt_otel_parse_cfg
- Parses the OTel filter configuration file. Backs up current sections,
- registers temporary otel-instrumentation/group/scope section parsers,
- loads and parses the file, then restores the original sections.
-
- flt_otel_parse
- Main filter parser entry point, registered for the "otel" filter keyword.
- Parses the filter ID and configuration file path from the HAProxy config
- line.
-
-
-src/conf.c
-----------------------------------------------------------------------
-
-Configuration structure allocation and deallocation. Most init/free pairs are
-generated by the FLT_OTEL_CONF_FUNC_INIT and FLT_OTEL_CONF_FUNC_FREE macros.
-
- flt_otel_conf_hdr_init
- Allocates and initializes a conf_hdr structure.
-
- flt_otel_conf_hdr_free
- Frees a conf_hdr structure and removes it from its list.
-
- flt_otel_conf_str_init
- Allocates and initializes a conf_str structure.
-
- flt_otel_conf_str_free
- Frees a conf_str structure and removes it from its list.
-
- flt_otel_conf_link_init
- Allocates and initializes a conf_link structure (span link).
-
- flt_otel_conf_link_free
- Frees a conf_link structure and removes it from its list.
-
- flt_otel_conf_ph_init
- Allocates and initializes a conf_ph (placeholder) structure.
-
- flt_otel_conf_ph_free
- Frees a conf_ph structure and removes it from its list.
-
- flt_otel_conf_sample_expr_init
- Allocates and initializes a conf_sample_expr structure.
-
- flt_otel_conf_sample_expr_free
- Frees a conf_sample_expr structure and releases the parsed sample
- expression.
-
- flt_otel_conf_sample_init
- Allocates and initializes a conf_sample structure.
-
- flt_otel_conf_sample_init_ex
- Extended sample initialization: sets the key, extra data (event name or
- status code), concatenated value string, and expression count.
-
- flt_otel_conf_sample_free
- Frees a conf_sample structure including its value, extra data, and all
- sample expressions.
-
- flt_otel_conf_context_init
- Allocates and initializes a conf_context structure.
-
- flt_otel_conf_context_free
- Frees a conf_context structure and removes it from its list.
-
- flt_otel_conf_span_init
- Allocates and initializes a conf_span structure with empty lists for
- links, attributes, events, baggages and statuses.
-
- flt_otel_conf_span_free
- Frees a conf_span structure and all its child lists.
-
- flt_otel_conf_instrument_init
- Allocates and initializes a conf_instrument structure.
-
- flt_otel_conf_instrument_free
- Frees a conf_instrument structure and removes it from its list.
-
- flt_otel_conf_log_record_init
- Allocates and initializes a conf_log_record structure with empty
- attributes and samples lists.
-
- flt_otel_conf_log_record_free
- Frees a conf_log_record structure: event_name, span, attributes and
- samples list.
-
- flt_otel_conf_scope_init
- Allocates and initializes a conf_scope structure with empty lists for
- ACLs, contexts, spans, spans_to_finish and instruments.
-
- flt_otel_conf_scope_free
- Frees a conf_scope structure, ACLs, condition, and all child lists.
-
- flt_otel_conf_group_init
- Allocates and initializes a conf_group structure with an empty placeholder
- scope list.
-
- flt_otel_conf_group_free
- Frees a conf_group structure and its placeholder scope list.
-
- flt_otel_conf_instr_init
- Allocates and initializes a conf_instr structure. Sets the default rate
- limit to 100%, initializes the proxy_log, and creates empty ACL and
- placeholder lists.
-
- flt_otel_conf_instr_free
- Frees a conf_instr structure including ACLs, loggers, config path, and
- placeholder lists.
-
- flt_otel_conf_init
- Allocates and initializes the top-level flt_otel_conf structure with empty
- group and scope lists.
-
- flt_otel_conf_free
- Frees the top-level flt_otel_conf structure and all of its children
- (instrumentation, groups, scopes).
-
-
-src/cli.c
-----------------------------------------------------------------------
-
-HAProxy CLI command handlers for runtime filter management.
-
- cmn_cli_set_msg
- Sets the CLI appctx response message and state.
-
- flt_otel_cli_parse_debug [D]
- CLI handler for "otel debug [level]". Gets or sets the debug level.
-
- flt_otel_cli_parse_disabled
- CLI handler for "otel enable" and "otel disable".
-
- flt_otel_cli_parse_option
- CLI handler for "otel soft-errors" and "otel hard-errors".
-
- flt_otel_cli_parse_logging
- CLI handler for "otel logging [state]". Gets or sets the logging state
- (off/on/dontlog-normal).
-
- flt_otel_cli_parse_rate
- CLI handler for "otel rate [value]". Gets or sets the rate limit
- percentage.
-
- flt_otel_cli_parse_status
- CLI handler for "otel status". Displays filter configuration and runtime
- state for all OTel filter instances.
-
- flt_otel_cli_init
- Registers the OTel CLI keywords with HAProxy.
-
-
-src/otelc.c
-----------------------------------------------------------------------
-
-OpenTelemetry context propagation bridge (inject/extract) between HAProxy and
-the OTel C wrapper library.
-
- flt_otel_text_map_writer_set_cb
- Writer callback for text map injection. Appends a key-value pair to the
- text map.
-
- flt_otel_http_headers_writer_set_cb
- Writer callback for HTTP headers injection. Appends a key-value pair to
- the text map.
-
- flt_otel_inject_text_map
- Injects span context into a text map carrier.
-
- flt_otel_inject_http_headers
- Injects span context into an HTTP headers carrier.
-
- flt_otel_text_map_reader_foreach_key_cb
- Reader callback for text map extraction. Iterates over all key-value
- pairs in the text map.
-
- flt_otel_http_headers_reader_foreach_key_cb
- Reader callback for HTTP headers extraction. Iterates over all key-value
- pairs in the text map.
-
- flt_otel_extract_text_map
- Extracts a span context from a text map carrier via the tracer.
-
- flt_otel_extract_http_headers
- Extracts a span context from an HTTP headers carrier via the tracer.
-
-
-src/http.c
-----------------------------------------------------------------------
-
-HTTP header manipulation for context propagation.
-
- flt_otel_http_headers_dump [D]
- Dumps all HTTP headers from the channel's HTX buffer.
-
- flt_otel_http_headers_get
- Extracts HTTP headers matching a prefix into a text map. Used by the
- "extract" keyword to read span context from incoming request headers.
-
- flt_otel_http_header_set
- Sets or removes an HTTP header. Combines prefix and name into the full
- header name, removes all existing occurrences, then adds the new value
- (if non-NULL).
-
- flt_otel_http_headers_remove
- Removes all HTTP headers matching a prefix. Wrapper around
- flt_otel_http_header_set() with NULL name and value.
-
-
-src/vars.c
-----------------------------------------------------------------------
-
-HAProxy variable integration for context propagation and storage. Only compiled
-when USE_OTEL_VARS is defined.
-
- flt_otel_vars_scope_dump [D]
- Dumps all variables for a single HAProxy variable scope.
-
- flt_otel_vars_dump [D]
- Dumps all variables across all scopes (PROC, SESS, TXN, REQ/RES).
-
- flt_otel_smp_init
- Initializes a sample structure with stream ownership and optional string
- data.
-
- flt_otel_smp_add
- Appends a context variable name to the binary sample data buffer used for
- tracking registered context variables.
-
- flt_otel_normalize_name
- Normalizes a variable name: replaces dashes with 'D' and spaces with 'S',
- converts to lowercase.
-
- flt_otel_denormalize_name
- Reverses the normalization applied by flt_otel_normalize_name(). Restores
- dashes from 'D' and spaces from 'S'.
-
- flt_otel_var_name
- Constructs a full variable name from scope, prefix and name components,
- separated by dots.
-
- flt_otel_ctx_loop
- Iterates over all context variable names stored in the binary sample data,
- calling a callback for each.
-
- flt_otel_ctx_set_cb
- Callback for flt_otel_ctx_loop() that checks whether a context variable
- name already exists.
-
- flt_otel_ctx_set
- Registers a context variable name in the binary tracking buffer if it is
- not already present.
-
- flt_otel_var_register
- Registers a HAProxy variable via vars_check_arg() so it can be used at
- runtime.
-
- flt_otel_var_set
- Sets a HAProxy variable value. For context-scope variables, also
- registers the name in the context tracking buffer.
-
- flt_otel_vars_unset_cb
- Callback for flt_otel_ctx_loop() that unsets each context variable.
-
- flt_otel_vars_unset
- Unsets all context variables for a given prefix and removes the tracking
- variable itself.
-
- flt_otel_vars_get_scope
- Resolves a scope name string ("proc", "sess", "txn", "req", "res") to the
- corresponding HAProxy variable store.
-
- flt_otel_vars_get_cb
- Callback for flt_otel_ctx_loop() that reads each context variable value
- and adds it to a text map.
-
- flt_otel_vars_get
- Reads all context variables for a prefix into a text map. Used by the
- "extract" keyword with variable storage.
-
-
-src/pool.c
-----------------------------------------------------------------------
-
-Memory pool and trash buffer helpers.
-
- flt_otel_pool_alloc
- Allocates memory from a HAProxy pool (if available) or from the heap.
- Optionally zero-fills the allocated block.
-
- flt_otel_pool_strndup
- Duplicates a string using a HAProxy pool (if available) or the heap.
-
- flt_otel_pool_free
- Returns memory to a HAProxy pool or frees it from the heap.
-
- flt_otel_trash_alloc
- Allocates a trash buffer chunk, optionally zero-filled.
-
- flt_otel_trash_free
- Frees a trash buffer chunk.
-
-
-src/util.c
-----------------------------------------------------------------------
-
-Utility and conversion functions.
-
- flt_otel_args_dump [D]
- Dumps configuration arguments array to stderr.
-
- flt_otel_filters_dump [D]
- Dumps all OTel filter instances across all proxies.
-
- flt_otel_chn_label [D]
- Returns "REQuest" or "RESponse" based on channel flags.
-
- flt_otel_pr_mode [D]
- Returns "HTTP" or "TCP" based on proxy mode.
-
- flt_otel_stream_pos [D]
- Returns "frontend" or "backend" based on stream flags.
-
- flt_otel_type [D]
- Returns "frontend" or "backend" based on filter flags.
-
- flt_otel_analyzer [D]
- Returns the analyzer name string for a given analyzer bit.
-
- flt_otel_list_dump [D]
- Returns a summary string for a list (empty, single, count).
-
- flt_otel_args_count
- Counts the number of valid (non-NULL) arguments in an args array, handling
- gaps from blank arguments.
-
- flt_otel_args_concat
- Concatenates arguments starting from a given index into a single
- space-separated string.
-
- flt_otel_strtod
- Parses a string to double with range validation.
-
- flt_otel_strtoll
- Parses a string to int64 with range validation.
-
- flt_otel_sample_to_str
- Converts sample data to its string representation. Handles bool, sint,
- IPv4, IPv6, str, and HTTP method types.
-
- flt_otel_sample_to_value
- Converts sample data to an otelc_value. Preserves native types (bool,
- int64) where possible; falls back to string.
-
- flt_otel_sample_add_event
- Adds a sample value as a span event attribute. Groups attributes by event
- name; dynamically grows the attribute array.
-
- flt_otel_sample_set_status
- Sets the span status code and description from sample data.
-
- flt_otel_sample_add_kv
- Adds a sample value as a key-value attribute or baggage entry.
- Dynamically grows the key-value array.
-
- flt_otel_sample_eval
- Evaluates all sample expressions for a configured sample definition and
- stores the result in an otelc_value. Supports both log-format and bare
- sample expression paths. When flag_native is true and the sample has
- exactly one expression, the native HAProxy sample type is preserved;
- otherwise results are concatenated into a string.
-
- flt_otel_sample_add
- Top-level sample evaluator and dispatcher. Calls flt_otel_sample_eval()
- to evaluate the sample, then dispatches the result to the appropriate
- handler (attribute, event, baggage, status).
-
-
-src/group.c
-----------------------------------------------------------------------
-
-Group action support for http-response / http-after-response / tcp-request /
-tcp-response rules.
-
- flt_otel_group_action
- Action callback (action_ptr) for the otel-group rule. Finds the filter
- instance on the current stream and runs all scopes defined in the group.
-
- flt_otel_group_check
- Check callback (check_ptr) for the otel-group rule. Resolves filter ID
- and group ID references against the proxy's filter configuration.
-
- flt_otel_group_release
- Release callback (release_ptr) for the otel-group rule.
-
- flt_otel_group_parse
- Parses the "otel-group" action keyword from HAProxy config rules.
- Registered for tcp-request, tcp-response, http-request, http-response and
- http-after-response action contexts.
+++ /dev/null
-OpenTelemetry Filter Implementation Review
-======================================================================
-
-1 Overview
-----------------------------------------------------------------------
-
-The OpenTelemetry (OTel) filter for HAProxy provides distributed tracing,
-metrics and logging capabilities. It creates, propagates and exports spans,
-metric instruments and log records that follow the OpenTelemetry specification.
-The filter hooks into the HAProxy stream processing pipeline through the
-filter API and maps HAProxy channel analyzer events to OpenTelemetry span
-lifecycle operations, metric recordings and log-record emissions.
-
-The implementation is located entirely under addons/otel/ and consists of
-header files, C source files, a Makefile, and a set of test configurations
-with runner scripts.
-
-
-2 Directory Structure
-----------------------------------------------------------------------
-
- addons/otel/
- |-- Makefile Build integration (USE_OTEL option)
- |-- include/
- | |-- include.h Master include (pulls all headers)
- | |-- config.h Build-time tunables (pool sizes, limits)
- | |-- define.h Utility macros (memory, strings, lists)
- | |-- debug.h Debug/logging infrastructure
- | |-- filter.h Filter return codes, alert macros
- | |-- parser.h Configuration keyword definitions
- | |-- conf.h Configuration data structures
- | |-- conf_funcs.h Generated init/free function macros
- | |-- event.h Event enumeration and data table
- | |-- scope.h Runtime span/context structures
- | |-- pool.h Memory pool helpers
- | |-- http.h HTTP header manipulation
- | |-- otelc.h Span context inject/extract wrappers
- | |-- vars.h HAProxy variable integration
- | |-- util.h String conversion, sample helpers
- | |-- group.h Group action (HAProxy rule integration)
- | `-- cli.h CLI command interface
- |-- src/
- | |-- filter.c Filter lifecycle and channel callbacks
- | |-- parser.c Configuration file parser
- | |-- conf.c Configuration structure init/free
- | |-- event.c Scope/span execution engine
- | |-- scope.c Runtime context and span management
- | |-- http.c HTTP header get/set/remove
- | |-- otelc.c C wrapper inject/extract bridge
- | |-- vars.c HAProxy variable read/write
- | |-- pool.c Pool alloc/free, trash buffers
- | |-- util.c Argument handling, sample conversion
- | |-- group.c Group action parsing and execution
- | `-- cli.c CLI command handlers
- `-- test/
- |-- copy-yml.sh YAML configuration transformer
- |-- test-speed.sh Performance benchmarking runner
- |-- run-sa.sh Standalone test runner
- |-- run-fe-be.sh Frontend-backend chain runner
- |-- run-ctx.sh Context propagation test runner
- |-- run-cmp.sh Comparison test runner
- |-- sa/ Standalone test configs
- |-- fe/ Frontend-only test configs
- |-- be/ Backend-only test configs
- |-- ctx/ Context propagation test configs
- |-- cmp/ Comparison test configs
- `-- empty/ Minimal/empty configuration test
-
-
-3 Build System
-----------------------------------------------------------------------
-
-The Makefile is included from the main HAProxy build when USE_OTEL is set.
-It detects the opentelemetry-c-wrapper library via pkg-config or manual
-OTEL_INC/OTEL_LIB paths.
-
-Build options:
-
- USE_OTEL=1 Enable the filter (required).
- OTEL_DEBUG=1 Compile with DEBUG_OTEL; links the _dbg variant of the
- wrapper library and enables additional debug callbacks in
- filter.c (stream_set_backend, http_headers, http_payload,
- tcp_payload, etc.).
- OTEL_USE_VARS=1 Compile vars.c; enables USE_OTEL_VARS which allows span
- context propagation via HAProxy transaction variables in
- addition to HTTP headers.
- OTEL_INC=<path> Manual include path for the C wrapper.
- OTEL_LIB=<path> Manual library path for the C wrapper.
- OTEL_RUNPATH=1 Embed RPATH to the wrapper library.
-
-Compiled objects (11 always, 12 with OTEL_USE_VARS):
-
- cli.o conf.o event.o filter.o group.o http.o opentelemetry.o parser.o
- pool.o scope.o util.o [vars.o]
-
-
-4 Configuration Parsing
-----------------------------------------------------------------------
-
-Configuration parsing is driven by parser.c. The filter is declared in the
-HAProxy configuration with:
-
- filter opentelemetry [id <name>] config <file>
-
-The flt_otel_parse() function (parser.c) handles the "filter" line, creates an
-flt_otel_conf structure, and delegates to parse_cfg() which loads the referenced
-YAML/CFG file. That file is parsed using temporary section registrations for
-three section types:
-
- otel-instrumentation -> flt_otel_parse_cfg_instr()
- otel-group -> flt_otel_parse_cfg_group()
- otel-scope -> flt_otel_parse_cfg_scope()
-
-After each section is fully parsed, a post-parse function validates the section
-(e.g., flt_otel_post_parse_cfg_scope() checks that context injection is only
-used on events that support it).
-
-4.1 Instrumentation Section
-
- otel-instrumentation <name>
- config <file>
- log <target>
- debug-level <value>
- rate-limit <value>
- option { disabled | hard-errors | dontlog-normal }
- groups <name> ...
- scopes <name> ...
- acl <name> <criterion> ...
-
-The instrumentation block defines global filter parameters: the YAML exporter
-configuration file, logging, rate limiting, and references to groups and scopes.
-Exactly one instrumentation block is allowed per filter instance.
-
-4.2 Group Section
-
- otel-group <name>
- scopes <name> ...
-
-Groups bundle multiple scopes under a single name for use with HAProxy
-http-request/http-response rules via the "otel-group" action. The group action
-(group.c) parses the rule, resolves the scope references at check time, and
-executes all referenced scopes when the rule fires.
-
-4.3 Scope Section
-
- otel-scope <name>
- otel-event <event-name> [if|unless <condition>]
- extract <name-prefix> [use-headers|use-vars]
- span <name> [parent <ref>] [link <ref>] [root]
- link <span> ...
- attribute <key> <sample> ...
- event <name> <key> <sample> ...
- baggage <key> <sample> ...
- status <code> [<sample> ...]
- inject <name-prefix> [use-headers] [use-vars]
- finish <name> ...
- instrument <type> <name> ... / instrument update <name> ...
- log-record <severity> [id <int>] [event <name>] [span <ref>] [attr <key> <sample>] ... <sample> ...
- acl <name> <criterion> ...
-
-Each scope ties to a single HAProxy analyzer event (or none, if used only
-through groups). Scopes contain context extraction directives, span
-definitions, metric instruments, log records, and finish directives.
-
-A span may specify:
- - A parent reference (another span or extracted context).
- - One or more links to other spans/contexts. Inline link syntax allows one
- link on the span line; the standalone "link" keyword allows multiple.
- - The "root" flag marking it as the trace root.
- - Attributes, events, baggages and status evaluated from HAProxy sample
- expressions at runtime.
- - An inject directive to propagate the span context via HTTP headers and/or
- HAProxy variables.
-
-4.4 Configuration Structure Initialization
-
-All configuration structures are allocated and freed using macro-generated
-functions from conf_funcs.h:
-
- FLT_OTEL_CONF_FUNC_INIT(type, id_field, extra_init)
- FLT_OTEL_CONF_FUNC_FREE(type, id_field, extra_free)
-
-These macros produce flt_otel_conf_<type>_init() and _free() functions.
-The init function:
- - Checks the identifier length against FLT_OTEL_ID_MAXLEN (64).
- - Checks for duplicate identifiers in the target list.
- - Allocates the structure with OTELC_CALLOC.
- - Copies the identifier with OTELC_STRDUP.
- - Appends to the head list.
- - Executes any extra initialization (e.g., LIST_INIT for sub-lists in the
- span structure).
-
-The free function:
- - Executes any extra cleanup (e.g., destroying sub-lists).
- - Frees the identifier string.
- - Removes the node from its list.
- - Frees the structure.
-
-The full init/free chain for all structures:
-
- flt_otel_conf flt_otel_conf_init() / flt_otel_conf_free()
- flt_otel_conf_instr generated via macro
- flt_otel_conf_ph generated (for ph_groups, ph_scopes)
- flt_otel_conf_group generated
- flt_otel_conf_ph generated (for ph_scopes)
- flt_otel_conf_scope generated
- flt_otel_conf_context generated
- flt_otel_conf_span generated
- flt_otel_conf_link generated
- flt_otel_conf_sample generated + _init_ex()
- flt_otel_conf_sample_expr generated
- flt_otel_conf_instrument generated
- flt_otel_conf_log_record generated
- flt_otel_conf_sample generated + _init_ex()
- flt_otel_conf_sample_expr generated
-
-
-5 Filter Lifecycle
-----------------------------------------------------------------------
-
-The filter registers its operations in the flt_otel_ops structure (filter.c)
-and the keyword parser via INITCALL1 (parser.c).
-
-5.1 Proxy-Level Initialization
-
- flt_otel_ops_init():
- - Registers CLI commands via flt_otel_cli_init().
- - Initializes the OpenTelemetry library via flt_otel_lib_init(): verifies
- the C wrapper version, resolves the absolute path of the YAML
- configuration file, calls otelc_init() to set up exporters, creates the
- tracer, meter and logger objects, and registers custom memory allocation
- and thread-id callbacks with the wrapper via otelc_ext_init().
-
- flt_otel_ops_check():
- - Validates that filter IDs are unique across all proxies.
- - Resolves group->scope and instrumentation->scope/group placeholder
- references to actual configuration structures (setting the ptr field
- and flag_used).
- - Warns about unused scopes, missing root spans, or multiple root spans.
- - Validates metric instruments: resolves update-form references to their
- matching create-form definitions, and rejects duplicate create-form names
- across scopes.
- - Computes the aggregated analyzer bitmask from all used scopes.
-
- flt_otel_ops_init_per_thread():
- - Starts the tracer, meter and logger background threads on first call.
- - Sets the FLT_CFG_FL_HTX flag to enable HTX stream filtering.
-
- flt_otel_ops_deinit():
- - Destroys the tracer, meter and logger.
- - Frees the entire configuration tree.
- - Calls otelc_deinit() to shut down the wrapper library.
-
-5.2 Stream-Level Callbacks
-
- flt_otel_ops_attach():
- - Checks if the filter is globally disabled; returns IGNORE.
- - Applies rate limiting via ha_random32(); returns IGNORE if the random
- value exceeds the configured rate_limit.
- - Creates the runtime context (flt_otel_runtime_context_init) with a
- generated UUID and initialized span/context lists.
- - Sets pre_analyzers and post_analyzers bitmasks from the instrumentation's
- aggregated analyzer flags. AN_REQ_WAIT_HTTP and AN_RES_WAIT_HTTP are
- placed in post_analyzers because those analyzers can only be used in the
- post_analyze callback. AN_REQ_HTTP_TARPIT is excluded from pre_analyzers.
-
- flt_otel_ops_detach():
- - Frees the runtime context, which finishes all remaining active spans and
- destroys all remaining contexts.
-
- flt_otel_ops_check_timeouts():
- - Checks whether the idle-timeout timer has expired; if so, fires the
- on-idle-timeout event and reschedules the timer for the next interval.
- - Sets STRM_EVT_MSG on the stream's pending_events to ensure the filter is
- re-evaluated after a timeout.
-
-5.3 Error Handling
-
-Two helper functions manage errors:
-
- flt_otel_return_int() / flt_otel_return_void():
- - If the result indicates an error or an error string is set: in hard-error
- mode, the filter is disabled for the current stream (flag_disabled = 1)
- and the disabled counter is incremented atomically. In soft-error mode,
- the error is merely logged.
- - The error string is always freed.
- - For int returns, FLT_OTEL_RET_OK is returned regardless, so the stream
- continues processing even after an error.
-
-
-6 Event Processing (Channel Analyzers)
-----------------------------------------------------------------------
-
-The filter maps HAProxy channel analyzer callbacks to a table of named events
-defined in event.h (FLT_OTEL_EVENT_DEFINES).
-
-6.1 Event Table
-
-Each event entry carries:
- - an_bit: the HAProxy analyzer bit (AN_REQ_*, AN_RES_*)
- - an_name: the analyzer bit name (e.g. "AN_REQ_FLT_HTTP_HDRS")
- - smp_opt_dir: sample fetch direction (REQ or RES)
- - smp_val_fe/be: valid sample fetch locations
- - flag_http_inject: whether span context can be injected into HTTP headers
- at this point
- - name: configuration event name (e.g. "on-frontend-http-request")
-
-Events with an_bit == 0 are pseudo-events not tied to any channel
-analyzer. Two of them fire from stream lifecycle callbacks:
- - on-stream-start (flt_otel_ops_stream_start, before channel processing)
- - on-stream-stop (flt_otel_ops_stream_stop, after channel processing)
-
-One fires periodically from the check_timeouts callback:
- - on-idle-timeout (flt_otel_ops_check_timeouts, when stream is idle)
-
-One fires from the stream_set_backend callback:
- - on-backend-set (flt_otel_ops_stream_set_backend, when backend is assigned)
-
-Four fire from HTTP lifecycle callbacks:
- - on-http-headers-request / on-http-headers-response (flt_otel_ops_http_headers)
- - on-http-end-request / on-http-end-response (flt_otel_ops_http_end)
- - on-http-reply (flt_otel_ops_http_reply)
-
-The remaining pseudo-events fire from channel start/end callbacks:
- - on-client-session-start / on-client-session-end
- - on-server-session-start / on-server-session-end
- - on-server-unavailable
-
-The stream lifecycle events pass NULL for the channel argument, so
-context injection/extraction via HTTP headers cannot be used. Their
-sample fetch direction is unconstrained (0xff), allowing both request
-and response fetches.
-
-6.2 Callback Flow
-
- stream_start(s, f):
- - Fires on-stream-start with chn=NULL.
- - Called when a new stream begins, before any channel processing.
- - Initializes the idle timer from the precomputed minimum idle_timeout in
- the instrumentation configuration.
-
- stream_set_backend(s, f, be):
- - Fires on-backend-set with chn=&s->req.
- - Called when a backend is assigned (skipped if frontend == backend).
-
- stream_stop(s, f):
- - Fires on-stream-stop with chn=NULL.
- - Called when a stream is destroyed, after all channel processing.
-
- check_timeouts(s, f):
- - Checks whether the idle-timeout timer has expired.
- - If expired, fires on-idle-timeout and reschedules the timer.
-
- channel_start_analyze(chn):
- - Enables the per-channel analyzers from pre_analyzers.
- - Fires on-client-session-start (request) or on-server-session-start
- (response).
- - Propagates the idle-timeout expiry to the channel's analyse_exp.
-
- channel_pre_analyze(chn, an_bit):
- - Looks up the event by an_bit in the event table.
- - Calls flt_otel_event_run() for the matching event.
-
- channel_post_analyze(chn, an_bit):
- - Same as pre_analyze but for post-analyzers (AN_REQ_WAIT_HTTP,
- AN_RES_WAIT_HTTP).
-
- channel_end_analyze(chn):
- - Fires on-client-session-end (request) or on-server-session-end (response).
- - For the request channel: if response analyzers were configured but
- none executed (server was unreachable), fires on-server-unavailable.
-
- http_headers(s, f, msg):
- - Fires on-http-headers-request or on-http-headers-response depending on
- msg->chn direction.
-
- http_end(s, f, msg):
- - Fires on-http-end-request or on-http-end-response depending on
- msg->chn direction.
-
- http_reply(s, f, status, msg):
- - Fires on-http-reply with chn=&s->res.
-
-6.3 Scope Execution
-
- flt_otel_event_run() (event.c):
- - Captures timestamps (CLOCK_MONOTONIC + CLOCK_REALTIME).
- - Updates the runtime context's executed-analyzers bitmask.
- - Iterates all scopes matching the event; calls flt_otel_scope_run() for
- each used scope.
-
- flt_otel_scope_run() (event.c):
- 1. Evaluates the scope's ACL condition; if it fails:
- - If the scope contains a root span, disables the stream.
- - Returns without processing.
- 2. Extracts contexts: for each configured extract directive, reads
- the span context from HTTP headers or HAProxy variables via
- flt_otel_scope_context_init().
- 3. Processes spans: for each configured span:
- a. Calls flt_otel_scope_span_init() which either returns an existing
- scope_span (by name) or creates a new one with resolved parent
- reference.
- b. Resolves span links against the runtime context -- first searching
- active spans, then extracted contexts. Unresolved links produce a
- NOTICE-level warning and are skipped.
- c. Evaluates attributes, events, baggages, and status from sample
- expressions via flt_otel_sample_add().
- d. Calls flt_otel_scope_run_span() which:
- - Creates the OTel span via tracer->start_span_with_options()
- (if not already started).
- - Adds all resolved links via span->add_link().
- - Sets baggage, attributes, events, and status.
- - Optionally injects the span context into HTTP headers and/or
- HAProxy variables.
- 4. Processes metric instruments via flt_otel_scope_run_instrument(), which
- runs two passes: the first lazily creates create-form instruments using
- HA_ATOMIC_CAS for thread-safe one-time initialization; the second records
- measurements for update-form instruments, skipping any whose index is
- still negative (creation pending or not yet attempted).
- 5. Emits log records via flt_otel_scope_run_log_record(), which iterates
- the scope's log-record list, skips entries below the logger's severity
- threshold, evaluates sample expressions into a body string, resolves
- the optional span reference, and emits the record via the logger.
- 6. Marks spans listed in "finish" directives.
- 7. Calls flt_otel_scope_finish_marked() to end marked spans/contexts.
- 8. Calls flt_otel_scope_free_unused() to remove finished and destroyed
- scope_span/scope_context entries from the runtime lists.
-
-
-7 Runtime Data Structures
-----------------------------------------------------------------------
-
-7.1 Runtime Context (per stream)
-
- flt_otel_runtime_context:
- stream Owning stream pointer.
- filter Owning filter pointer.
- uuid[40] Generated UUID v4 for the session.
- flag_harderr Copied from instrumentation config.
- flag_disabled Set when the filter encounters a hard error or ACL disables
- processing.
- logging Logging flags.
- analyzers Bitmask of analyzers that have actually executed.
- idle_timeout Idle timeout interval in milliseconds (0 = off).
- idle_exp Tick at which the next idle timeout fires.
- spans Linked list of flt_otel_scope_span.
- contexts Linked list of flt_otel_scope_context.
-
-7.2 Scope Span
-
- flt_otel_scope_span:
- id / id_len Span operation name (borrowed from config).
- smp_opt_dir Direction in which the span was created.
- flag_finish Set by finish directives, cleared after ending.
- span The OTel span object (NULL before start, NULL after
- end_with_options).
- ref_span Parent span pointer (resolved at init).
- ref_ctx Parent context pointer (resolved at init).
- list Chain in runtime_context.spans.
-
- flt_otel_scope_span_init() performs memoization: if a span with the same name
- already exists in rt_ctx->spans, it returns the existing entry. This allows
- multiple scopes to contribute attributes/events to the same logical span.
-
-7.3 Scope Context
-
- flt_otel_scope_context:
- id / id_len Context name (borrowed from config).
- smp_opt_dir Direction in which the context was extracted.
- flag_finish Marks the context for destruction.
- context The OTel span_context object.
- list Chain in runtime_context.contexts.
-
- Similarly memoized: duplicate extraction of the same context name returns the
- existing entry.
-
-7.4 Scope Data (per span per scope run, stack-allocated)
-
- flt_otel_scope_data:
- baggage Key-value array for baggage items.
- attributes Key-value array for span attributes.
- events Linked list of flt_otel_scope_data_event (each with name
- + key-value array).
- links Linked list of flt_otel_scope_data_link (each with span
- and/or context pointer).
- status Status code and description string.
-
- Initialized at the start of each span processing block and freed at the end.
- The link entries hold borrowed pointers to OTel objects owned by the runtime
- context, so only the link nodes themselves are freed.
-
-7.5 Span Finishing
-
- finish <name> / finish * / finish *req* / finish *res*
-
- The "finish" directive marks spans and contexts for completion:
- - "*" marks all.
- - "*req*" / "*res*" marks those created in the request/response direction
- respectively.
- - Otherwise, marks by exact name.
-
- flt_otel_scope_finish_marked() iterates all marked entries:
- - Spans are ended via span->end_with_options() which NULLs the span pointer.
- - Contexts are destroyed via context->destroy() which NULLs the context
- pointer.
-
- flt_otel_scope_free_unused() then removes entries with NULL span/context
- pointers from the runtime lists. For contexts, associated HTTP headers
- and variables are also cleaned up.
-
- On stream detach (flt_otel_runtime_context_free), any remaining active spans
- are force-ended and all entries are freed.
-
-
-8 Span Links
-----------------------------------------------------------------------
-
-Span links associate a span with other spans or contexts without establishing
-a parent-child relationship.
-
-8.1 Configuration
-
-Two syntaxes are supported:
-
- Inline (one link per span declaration):
- span <name> [parent <ref>] link <linked-span> [root]
-
- Standalone (multiple links, requires a preceding span):
- link <span-name> [<span-name> ...]
-
-The flt_otel_conf_link structure stores each link target name. Duplicate link
-names within the same span are rejected by the init macro's duplicate check.
-The links list is initialized in flt_otel_conf_span_init() and destroyed in
-flt_otel_conf_span_free().
-
-8.2 Runtime Resolution
-
-At scope execution time (event.c, flt_otel_scope_run), for each configured link:
- 1. The name is searched in rt_ctx->spans (active scope_span entries).
- If found, the OTel span pointer is captured.
- 2. If not found in spans, the name is searched in rt_ctx->contexts (extracted
- scope_context entries). If found, the OTel span_context pointer is
- captured.
- 3. If neither found, a NOTICE warning is logged and the link is skipped.
- 4. A flt_otel_scope_data_link node is allocated and appended to the scope
- data's links list.
-
-In flt_otel_scope_run_span(), all resolved links are applied via
-span->add_link(span, link_span, link_context, NULL, 0). The last two arguments
-(attributes array and count) are NULL/0, meaning links carry no additional
-attributes.
-
-
-9 Context Propagation
-----------------------------------------------------------------------
-
-9.1 Extraction
-
- extract <name-prefix> [use-headers|use-vars]
-
-Extracts an incoming trace context. The prefix identifies the header name
-pattern (for HTTP) or variable name pattern (for vars).
-
- - use-headers (default): flt_otel_http_headers_get() iterates HTX headers
- matching the prefix and builds an otelc_text_map.
- - use-vars: flt_otel_vars_get() reads HAProxy variables matching the prefix
- pattern.
-
-The text map is passed to flt_otel_extract_http_headers() which uses the
-C wrapper to reconstruct an otelc_span_context.
-
-9.2 Injection
-
- inject <name-prefix> [use-headers] [use-vars]
-
-Injects the current span's context into outgoing data. Both storage types can
-be used simultaneously.
-
- flt_otel_inject_http_headers() serializes the span context into an
- otelc_http_headers_writer which produces a text_map. For each key-value pair:
- - use-headers: flt_otel_http_header_set() adds/replaces the header with the
- prefixed name.
- - use-vars: flt_otel_var_register() + flt_otel_var_set() stores the value
- in a HAProxy transaction variable with normalized name (dashes replaced
- with 'D', spaces with 'S', uppercase lowered; dots serve as component
- separators).
-
-
-10 HTTP Header Manipulation
-----------------------------------------------------------------------
-
- http.c provides three operations:
-
- flt_otel_http_headers_get(chn, prefix, prefix_len, err):
- Iterates the HTX message headers. Headers whose name starts with the given
- prefix are collected into an otelc_text_map. The prefix is stripped from
- the names in the returned map.
-
- flt_otel_http_header_set(chn, prefix, name, value, err):
- Removes any existing header matching "prefix" + "name", then adds a new
- header with the given value. If name is NULL, all headers with the prefix
- are removed (bulk delete).
-
- flt_otel_http_headers_remove(chn, prefix, err):
- Convenience wrapper; removes all headers matching the prefix.
-
-
-11 HAProxy Variable Integration
-----------------------------------------------------------------------
-
-Enabled with OTEL_USE_VARS=1. Provides an alternative propagation mechanism
-using HAProxy transaction-scoped variables.
-
-Variable names are normalized: dashes and spaces are replaced with special
-characters to comply with HAProxy variable naming rules. A meta-variable
-tracks the list of context variable names so they can be enumerated for
-extraction.
-
-Key functions:
- flt_otel_var_register() Registers a variable with HAProxy.
- flt_otel_var_set() Sets a variable value.
- flt_otel_vars_get() Reads all context variables into a text_map for
- extraction.
- flt_otel_vars_unset() Removes all context variables.
-
-
-12 Group Action Integration
-----------------------------------------------------------------------
-
-The "otel-group" HAProxy action allows triggering trace scopes from
-tcp-request, tcp-response, http-request, http-response and
-http-after-response rules:
-
- tcp-request otel-group <filter-id> <group-name>
- tcp-response otel-group <filter-id> <group-name>
- http-request otel-group <filter-id> <group-name>
- http-response otel-group <filter-id> <group-name>
- http-after-response otel-group <filter-id> <group-name>
-
- group.c implements:
- flt_otel_group_parse(): Parses the action arguments.
- flt_otel_group_check(): Resolves group and scope references.
- flt_otel_group_action(): At runtime, finds the OTel filter in the stream,
- iterates all scopes in the group, and calls
- flt_otel_scope_run() for each.
-
-
-13 Memory Management
-----------------------------------------------------------------------
-
- pool.c provides wrappers around HAProxy memory pools and standard
- allocation:
-
- flt_otel_pool_alloc() Allocates from a pool (if non-NULL and the requested
- size fits) or via calloc.
- flt_otel_pool_free() Returns memory to the pool or frees it.
- flt_otel_pool_strndup() Duplicates a string via pool allocation.
- flt_otel_trash_alloc() Acquires a trash buffer chunk.
- flt_otel_trash_free() Releases a trash buffer chunk.
-
-Four pool heads are registered for hot-path structures:
- - otel_scope_span (scope.c)
- - otel_scope_context (scope.c)
- - otel_runtime_context (scope.c)
- - otel_span_context (filter.c, used by the C wrapper via otelc_ext_init
- callback)
-
-The wrapper library's memory allocations are redirected through
-flt_otel_mem_malloc() / flt_otel_mem_free() which use the otel_span_context
-pool. This ensures OTel objects benefit from HAProxy's pool allocator.
-
-
-14 CLI Interface
-----------------------------------------------------------------------
-
- cli.c registers commands under "flt-otel" for runtime control:
- - Setting the debug level.
- - Enabling/disabling the filter on the fly.
-
-Logging can be independently controlled via the instrumentation's logging
-flags (ON, NOLOGNORM). Log output goes to the log servers configured in the
-instrumentation block.
-
-
-15 Debug Infrastructure
-----------------------------------------------------------------------
-
-When compiled with OTEL_DEBUG=1 (DEBUG_OTEL defined), the filter enables:
-
- - Additional flt_ops callbacks: stream_set_backend, deinit_per_thread,
- http_headers, http_payload, http_end, http_reset, http_reply, tcp_payload.
- In non-debug builds these are set to NULL. (Note: stream_start and
- stream_stop are always registered because they fire the on-stream-start
- and on-stream-stop events.)
-
- - The OTELC_DBG() macro produces debug output at various levels.
-
- - flt_otel_scope_data_dump() dumps the complete scope data (baggage,
- attributes, events, links, status) for inspection.
-
- - Event usage counters (per-event htx_is_empty statistics) are maintained and
- printed at deinit.
-
- - Pool size information is printed at startup.
-
-The debug level is a bitmask that can be adjusted at runtime via the CLI.
-
-
-16 Test Infrastructure
-----------------------------------------------------------------------
-
-16.1 Test Scenarios
-
- sa Standalone: comprehensive test exercising all request and response
- events, span links (both inline and standalone syntax), events with data
- capture, baggage, and the full span hierarchy from client session start
- to server session end.
-
- fe Frontend-only: tests the request-side span chain with context injection
- into HTTP headers.
-
- be Backend-only: tests context extraction from HTTP headers and
- response-side processing. Designed to run as the backend of
- the fe/ test.
-
- ctx Context propagation: deep nesting test that verifies context propagation
- via both HTTP headers and HAProxy variables.
-
- cmp Comparison: simplified configuration made for comparison with other
- tracing implementations.
-
- empty Minimal: validates that an empty configuration (only the
- instrumentation block, no scopes) does not crash.
-
-16.2 Test Runners
-
-All runners are POSIX shell scripts (/bin/sh). They accept an optional HAProxy
-binary path and log to test/_logs/.
-
- run-sa.sh Runs a single HAProxy instance with sa/ config.
- run-cmp.sh Runs a single HAProxy instance with cmp/ config.
- test-speed.sh Runs performance benchmarks for one or all configurations.
- run-ctx.sh Runs a single HAProxy instance with ctx/ config.
- run-fe-be.sh Launches two HAProxy instances (frontend on port 10080, backend
- on port 11080) forming a trace propagation chain. Handles
- graceful shutdown via SIGUSR1.
-
- copy-yml.sh Transforms a template YAML configuration by replacing
- placeholders with test-specific values (service names, file
- suffixes, etc.).
-
-16.3 Exporter Configuration
-
-Each test directory contains an otel.yml file configuring three exporter types:
- - OTLP file exporter (writes traces to local files).
- - OTLP gRPC exporter (sends to localhost:4317).
- - OTLP HTTP exporter (sends to localhost:4318 in JSON format).
-
-
-17 Notable Design Decisions
-----------------------------------------------------------------------
-
- - Span memoization: flt_otel_scope_span_init() and
- flt_otel_scope_context_init() return existing entries if one with the
- same name already exists. This allows multiple scopes to contribute data
- (attributes, events) to the same logical span across different analyzer
- events.
-
- - Lazy span creation: the OTel span object is created on first use in
- flt_otel_scope_run_span(), not at scope_span_init time. This separates
- the span identity (name, parent reference) from the actual OTel resource.
-
- - Soft/hard error modes: in soft mode, errors are logged but the stream
- continues with tracing effectively abandoned for that span. In hard mode,
- the filter disables itself for the rest of the stream. Either way, stream
- processing is never interrupted by a tracing failure (FLT_OTEL_RET_OK is
- always returned).
-
- - Rate limiting uses a uint32 representation of a percentage
- (FLT_OTEL_FLOAT_U32), compared against ha_random32() for uniform
- distribution without floating-point at runtime.
-
- - Server-unavailable fallback: if the backend was never reached (no response
- analyzers executed), the on-server-unavailable event is fired at client
- session end to ensure all spans are properly closed.
-
- - Custom memory allocator: the C wrapper's allocations are routed through
- HAProxy memory pools via otelc_ext_init(), keeping OTel objects in the
- same allocation domain as the rest of the filter.
-
- - Thread integration: flt_otel_thread_id() returns the HAProxy tid, ensuring
- the wrapper's thread-local operations map to HAProxy worker threads.
-
-
-18 Tracer, Span and Metrics Internals
-----------------------------------------------------------------------
-
-This chapter describes the end-to-end lifecycle of the tracer and meter
-objects, the runtime span management model, and the metric instrument
-recording pipeline.
-
-18.1 Tracer Provider Initialization
-
-The tracer provider is set up during the proxy-level flt_otel_ops_init()
-callback, which delegates to flt_otel_lib_init() (filter.c).
-The initialization sequence is as follows:
-
- 1. Version check: OTELC_IS_VALID_VERSION() verifies that the
- OpenTelemetry C wrapper library version matches the header files.
-
- 2. Configuration path: the relative path from the "config" keyword in
- the instrumentation section is resolved to an absolute path using
- getcwd() + snprintf().
-
- 3. SDK initialization: otelc_init(path, err) loads the YAML
- configuration file and sets up the SDK exporters, samplers,
- processors and metric readers.
-
- 4. Tracer creation: otelc_tracer_create(err) allocates the tracer
- handle and stores it in instr->tracer.
-
- 5. Meter creation: otelc_meter_create(err) allocates the meter handle
- and stores it in instr->meter.
-
- 6. Logger creation: otelc_logger_create(err) allocates the logger
- handle and stores it in instr->logger.
-
- 7. Extension callbacks: on success, otelc_ext_init() registers custom
- memory allocation (flt_otel_mem_malloc / flt_otel_mem_free) and
- thread-id (flt_otel_thread_id) callbacks so that OTel SDK objects
- use HAProxy memory pools and thread numbering.
-
- 8. Log handler: otelc_log_set_handler() installs a callback that
- counts SDK diagnostic messages via the flt_otel_drop_cnt counter.
-
-All three handles are stored in the flt_otel_conf_instr structure
-(conf.h):
-
- struct flt_otel_conf_instr {
- ...
- struct otelc_tracer *tracer; /* The OpenTelemetry tracer handle. */
- struct otelc_meter *meter; /* The OpenTelemetry meter handle. */
- struct otelc_logger *logger; /* The OpenTelemetry logger handle. */
- ...
- };
-
-18.2 Per-Thread Tracer, Meter and Logger Startup
-
-The flt_otel_ops_init_per_thread() callback (filter.c) starts the
-tracer, meter and logger background threads on the first call:
-
- if (!(fconf->flags & FLT_CFG_FL_HTX)) {
- retval = OTELC_OPS(conf->instr->tracer, start);
- if (retval != OTELC_RET_ERROR) {
- retval = OTELC_OPS(conf->instr->meter, start);
- ...
- }
- if (retval != OTELC_RET_ERROR) {
- retval = OTELC_OPS(conf->instr->logger, start);
- ...
- }
- fconf->flags |= FLT_CFG_FL_HTX;
- }
-
-The FLT_CFG_FL_HTX flag ensures that start is called only once, even
-when multiple proxies share the same filter configuration. If any
-start operation fails, the error string from the failing handle is
-forwarded via FLT_OTEL_ALERT.
-
-18.3 Tracer, Meter and Logger Shutdown
-
-At proxy deinit (flt_otel_ops_deinit, filter.c), the tracer, meter
-and logger are destroyed in a single call:
-
- otelc_deinit(&((*conf)->instr->tracer), &((*conf)->instr->meter), &((*conf)->instr->logger));
-
-This flushes any pending spans, metric data and log records to the
-configured exporters, then releases the SDK resources. The full
-configuration tree is freed immediately after via flt_otel_conf_free().
-
-18.4 Span Lifecycle
-
-Spans progress through four phases: identity allocation, OTel span
-creation, data population, and completion.
-
-18.4.1 Span Identity Allocation
-
-When a scope containing a span definition executes for the first time,
-flt_otel_scope_span_init() (scope.c) allocates a scope_span
-entry from the otel_scope_span pool and inserts it into the runtime
-context's spans list:
-
- retptr = flt_otel_pool_alloc(pool_head_otel_scope_span, ...);
- retptr->id = id; /* Borrowed from config. */
- retptr->id_len = id_len;
- retptr->smp_opt_dir = dir;
- retptr->ref_span = ref_span; /* Resolved parent span. */
- retptr->ref_ctx = ref_ctx; /* Resolved parent context. */
- LIST_INSERT(&(rt_ctx->spans), &(retptr->list));
-
-The parent reference (ref_id) is resolved at this point by searching the
-runtime context's spans list first, then the contexts list. If the
-parent name cannot be found in either list, an error is returned and the
-span is not created.
-
-Memoization: if a span with the same name already exists in
-rt_ctx->spans, the existing entry is returned without allocation. This
-allows multiple scopes (across different analyzer events) to contribute
-attributes, events and other data to the same logical span.
-
-18.4.2 OTel Span Creation (Lazy)
-
-The actual OTel span object is created lazily on first use in
-flt_otel_scope_run_span() (event.c):
-
- if (span->span == NULL) {
- span->span = OTELC_OPS(conf->instr->tracer,
- start_span_with_options, span->id,
- span->ref_span, span->ref_ctx,
- ts_steady, ts_system, OTELC_SPAN_KIND_SERVER);
- }
-
-The arguments are:
-
- span->id The operation name (string identifier from config).
- span->ref_span The parent span pointer (NULL if root or no parent).
- span->ref_ctx The parent span context (from extracted context).
- ts_steady Monotonic timestamp (CLOCK_MONOTONIC) for duration.
- ts_system Wall-clock timestamp (CLOCK_REALTIME) for events.
- OTELC_SPAN_KIND_SERVER Fixed span kind for all HAProxy spans.
-
-This separation between identity allocation and OTel creation means the
-span name, parent references and pool entry exist before the OTel
-resource is allocated. Subsequent scope executions that reference the
-same span name find the existing entry (via memoization) and add their
-data to the already-created OTel span.
-
-18.4.3 Span Data Population
-
-After creation, flt_otel_scope_run_span() (event.c) populates
-the span with data collected during scope execution:
-
- Links (event.c):
- Each resolved link is added via span->add_link(span, link_span,
- link_context, NULL, 0). Links associate the span with other spans
- or contexts without establishing a parent-child relationship. The
- last two arguments (attributes array and count) are always NULL/0.
-
- Baggage (event.c):
- span->set_baggage_kv_n(data->baggage.attr, data->baggage.cnt)
- sets key-value baggage items propagated across service boundaries.
-
- Attributes (event.c):
- span->set_attribute_kv_n(data->attributes.attr, data->attributes.cnt)
- sets key-value span attributes evaluated from HAProxy sample
- expressions.
-
- Events (event.c):
- For each event in data->events (iterated in reverse insertion order):
- span->add_event_kv_n(event->name, ts_system, event->attr, event->cnt)
- adds a named event with a wall-clock timestamp and key-value
- attributes.
-
- Status (event.c):
- span->set_status(data->status.code, data->status.description)
- sets the span's status code and description string. Only one status
- per event is allowed.
-
-18.4.4 Span Context Injection
-
-After populating the span, if the configuration contains an "inject"
-directive (conf_span->ctx_id is non-NULL), the span context is
-serialized for downstream propagation (event.c).
-
- flt_otel_inject_http_headers() serializes the span context into an
- otelc_http_headers_writer, producing a text_map of key-value pairs.
- For each pair, depending on the ctx_flags:
-
- FLT_OTEL_CTX_USE_HEADERS:
- flt_otel_http_header_set() writes the header into the HTX message.
-
- FLT_OTEL_CTX_USE_VARS (requires OTEL_USE_VARS=1):
- flt_otel_var_register() + flt_otel_var_set() store the value
- in a HAProxy transaction variable.
-
- Both storage types can be used simultaneously on the same span.
-
-18.4.5 Span Completion
-
-Spans are ended through the marking mechanism described in chapter 7.5.
-The actual end call in flt_otel_scope_finish_marked() (scope.c) is:
-
- OTELC_OPSR(span->span, end_with_options,
- ts_finish, OTELC_SPAN_STATUS_IGNORE, NULL);
-
-The arguments are the monotonic timestamp, a status hint (IGNORE means
-"do not override the status already set on the span"), and NULL for
-error string. After end_with_options returns, the OTELC_OPSR macro
-NULLs the span pointer, making the entry eligible for removal by
-flt_otel_scope_free_unused().
-
-On stream detach, flt_otel_runtime_context_free() (scope.c)
-force-ends any remaining active spans with the current monotonic
-timestamp and frees all pool entries.
-
-18.5 Metric Instruments
-
-The filter supports the full set of OpenTelemetry metric instrument
-types through a two-form configuration model: "create" form instruments
-define the instrument, and "update" form instruments record measurements
-against it.
-
-18.5.1 Instrument Types
-
-The following instrument types are available (parser.h):
-
- cnt_int Counter (uint64)
- hist_int Histogram (uint64)
- udcnt_int UpDownCounter (int64)
- gauge_int Gauge (int64)
-
-Observable (asynchronous) instruments are not supported. The OTel SDK invokes
-their callbacks from an external background thread that is not a HAProxy
-thread. HAProxy sample fetches rely on internal per-thread-group state and
-return incorrect results when called from a non-HAProxy thread.
-
-Double-precision types are not supported because HAProxy sample fetches do not
-return double values.
-
- Special:
- update Update-form instrument (records measurements)
-
-Each create-form instrument carries a description, unit, aggregation type,
-sample expression list, and optional histogram bucket boundaries. Each
-update-form instrument carries a reference to its create-form counterpart
-and an attribute key-value array for per-scope dimensions.
-
-18.5.2 Instrument Configuration Structure
-
-The flt_otel_conf_instrument structure (conf.h) holds:
-
- idx Meter instrument index. Initially set to
- OTELC_METRIC_INSTRUMENT_UNSET (-1). Transitions to
- OTELC_METRIC_INSTRUMENT_PENDING (-2) during creation,
- then to the positive meter index on success.
- type The otelc_metric_instrument_t type constant, or
- OTELC_METRIC_INSTRUMENT_UPDATE (0xff) for update-form.
- aggr_type The otelc_metric_aggregation_type_t constant.
- Initially OTELC_METRIC_AGGREGATION_UNSET (-1).
- description Instrument description string (create-form only).
- unit Instrument unit string (create-form only).
- samples List of sample expressions for the instrument value.
- bounds Histogram bucket boundaries array (create-form only).
- bounds_num Number of histogram bucket boundaries.
- attributes List of flt_otel_conf_sample entries (update-form only).
- ref Pointer to the create-form instrument (update-form only).
-
-18.5.3 Meter Initialization and Startup
-
-The meter handle is created alongside the tracer in flt_otel_lib_init()
-(filter.c) via otelc_meter_create(err) and started per-thread
-in flt_otel_ops_init_per_thread() (filter.c) via
-OTELC_OPS(conf->instr->meter, start). The meter background thread
-handles periodic collection and export of metric data.
-
-18.5.4 Instrument Creation and Recording
-
-Metric instrument processing is performed by
-flt_otel_scope_run_instrument() (event.c), which runs in two
-passes during scope execution.
-
- Pass 1 -- Create-form instruments (event.c):
-
- Iterates all instruments in the scope. For each create-form
- instrument whose idx is OTELC_METRIC_INSTRUMENT_UNSET:
-
- a. Thread-safe one-time creation: HA_ATOMIC_CAS transitions the idx
- from UNSET to PENDING. If the CAS fails (another thread is
- already creating this instrument), the current thread skips it.
-
- b. Instrument creation: meter->create_instrument() is called with
- the instrument name, description, unit, type and callback data.
- On success, the returned index is stored atomically; on failure,
- the idx is reset to UNSET. If the instrument has an explicit
- aggregation type or histogram bucket boundaries, meter->add_view()
- is called before instrument creation to register a view with the
- configured aggregation strategy and optional bounds. When bounds
- are present without an explicit aggregation type, histogram
- aggregation is used automatically for backward compatibility.
-
- Pass 2 -- Update-form instruments (event.c):
-
- Iterates all instruments again. For each update-form instrument:
-
- a. Reference validation: the ref pointer must be non-NULL (resolved
- at check time to the create-form instrument).
-
- b. Index check: if the create-form instrument's idx is still
- negative (UNUSED or PENDING), the measurement is skipped.
-
- c. Recording: flt_otel_scope_run_instrument_record() evaluates the
- sample expression, converts it to an otelc_value, and calls
- meter->update_instrument_kv_n(idx, &value, attr, attr_len).
-
-18.5.5 Sample Evaluation for Metrics
-
-The recording function flt_otel_scope_run_instrument_record()
-(event.c) supports two evaluation paths:
-
- Standard path: evaluates sample_process() on the first expression in
- the create-form instrument's samples list, using the stream's backend,
- session and direction context.
-
- Log-format path: if sample->lf_used is set, allocates a temporary
- buffer of global.tune.bufsize, calls build_logline() to evaluate the
- log-format expression, and presents the result as an SMP_T_STR sample.
-
-Both paths converge on flt_otel_sample_to_value(), which converts the
-HAProxy sample data to an otelc_value. Metric instruments require
-numeric values (INT64); if the conversion produces
-OTELC_VALUE_DATA (string), a warning is logged and the measurement is
-rejected.
-
-
-18.5.6 Instrument Lifecycle Summary
-
- Configuration time:
- idx = OTELC_METRIC_INSTRUMENT_UNSET (-1)
- type = instrument type constant
-
- First scope execution (any thread):
- idx transitions: UNSET -> PENDING -> meter_index (success)
- UNSET -> PENDING -> UNSET (failure)
-
- Subsequent scope executions:
- Create-form: skipped (idx is already a valid meter index).
- Update-form: evaluates samples and records via meter API.
-
- Shutdown:
- otelc_deinit() flushes and destroys tracer, meter and logger,
- including all registered instruments and their callbacks.
-
-
-18.6 Log Records
-
-The filter supports OpenTelemetry log records via the "log-record"
-keyword inside otel-scope sections. Each log record is emitted through
-the OTel logger at a configured severity level, with an evaluated body,
-optional span correlation and optional key-value attributes.
-
-18.6.1 Log Record Configuration Structure
-
-The flt_otel_conf_log_record structure (conf.h) holds:
-
- severity The otelc_log_severity_t severity level.
- event_id Optional numeric event identifier (int64).
- event_name Optional event name string.
- span Optional span reference name (resolved at runtime).
- attributes List of flt_otel_conf_sample entries for attributes.
- samples List of sample expressions for the body.
-
-The attributes list contains flt_otel_conf_sample entries, one per "attr"
-keyword. Each entry's key field holds the attribute name and its sample
-expressions are evaluated at runtime, following the same two-path model
-(bare sample or log-format) as span attributes.
-
-The samples list contains exactly one flt_otel_conf_sample entry, which
-in turn holds either a list of bare sample expressions or a single
-log-format expression (when the value contains "%[").
-
-18.6.2 Log Record Emission
-
-Log record processing is performed by flt_otel_scope_run_log_record()
-(event.c), called from flt_otel_scope_run() after metric instrument
-processing and before span finishing.
-
-For each configured log record the function performs:
-
- 1. Severity check: OTELC_OPS(logger, enabled, severity) tests whether
- the logger accepts records at this severity. If not, the entry is
- skipped. The threshold is controlled by the "min_severity" option
- in the YAML logs signal configuration.
-
- 2. Attribute evaluation: each entry in the attributes list is evaluated via
- flt_otel_sample_add() into a temporary flt_otel_scope_data structure.
- The evaluated key-value array is passed to logger->log_span() and freed
- after emission.
-
- 3. Body evaluation: the single sample entry is evaluated using one of
- two paths:
-
- Log-format path (sample->lf_used is true):
- A temporary buffer of global.tune.bufsize is allocated and
- build_logline() evaluates the log-format expression into it.
-
- Bare sample expression path:
- Each expression in sample->exprs is evaluated via
- sample_process() and converted to a string via
- flt_otel_sample_to_str(). Results are concatenated into a
- single buffer.
-
- 4. Span resolution: if conf_log->span is non-NULL, the runtime
- context's spans list is searched for a scope_span with a matching
- name. If found, the OTel span pointer is captured for correlation.
- A missing span is non-fatal -- a NOTICE warning is logged and the
- record is emitted without span correlation.
-
- 5. Emission: logger->log_span() is called with the severity, event_id,
- event_name, resolved span (or NULL), wall-clock timestamp, the evaluated
- attributes and the evaluated body string.
-
-18.6.3 Logger Lifecycle Summary
-
- Proxy init (flt_otel_lib_init):
- otelc_logger_create() allocates the logger handle.
-
- Per-thread init (flt_otel_ops_init_per_thread):
- logger->start() launches the logger background thread.
-
- Scope execution (flt_otel_scope_run):
- flt_otel_scope_run_log_record() emits records via logger->log_span().
-
- Shutdown (flt_otel_ops_deinit):
- otelc_deinit() flushes pending log records and destroys the logger.
+++ /dev/null
-OpenTelemetry filter -- miscellaneous notes
-==============================================================================
-
-1 Parsing sample expressions in HAProxy
-------------------------------------------------------------------------------
-
-HAProxy provides two entry points for turning a configuration string into an
-evaluable sample expression.
-
-
-1.1 sample_parse_expr()
-..............................................................................
-
-Parses a bare sample-fetch name with an optional converter chain. The input is
-the raw expression without any surrounding syntax.
-
- Declared in: include/haproxy/sample.h
- Defined in: src/sample.c
-
- struct sample_expr *sample_parse_expr(char **str, int *idx, const char *file, int line, char **err_msg, struct arg_list *al, char **endptr);
-
-The function reads from str[*idx] and advances *idx past the consumed tokens.
-
-Configuration example (otel-scope instrument keyword):
-
- instrument my_counter "name" desc req.hdr(host),lower ...
-
-Here "req.hdr(host),lower" is a single configuration token that
-sample_parse_expr() receives directly. It recognises the fetch "req.hdr(host)"
-and the converter "lower" separated by a comma.
-
-
-1.2 parse_logformat_string()
-..............................................................................
-
-Parses a log-format string that may contain literal text mixed with sample
-expressions wrapped in %[...] delimiters.
-
- Declared in: include/haproxy/log.h
- Defined in: src/log.c
-
- int parse_logformat_string(const char *fmt, struct proxy *curproxy, struct lf_expr *lf_expr, int options, int cap, char **err);
-
-Configuration example (HAProxy log-format directive):
-
- log-format "host=%[req.hdr(host),lower] status=%[status]"
-
-The %[...] wrapper tells parse_logformat_string() where each embedded sample
-expression begins and ends. The text outside the brackets ("host=", " status=")
-is emitted as-is.
-
-
-1.3 Which one to use
-..............................................................................
-
-Use sample_parse_expr() when the configuration token is a single, standalone
-sample expression (no surrounding text). This is the case for the otel filter
-keywords such as "attribute", "event", "baggage", "status", "value", and
-similar.
-
-Use parse_logformat_string() when the value is a free-form string that may mix
-literal text with zero or more embedded expressions.
-
-
-2 Signal keywords
-------------------------------------------------------------------------------
-
-The OTel filter configuration uses one keyword per signal to create or update
-signal-specific objects. The keyword names follow the OpenTelemetry
-specification's own terminology rather than using informal synonyms.
-
- Signal Keyword Creates / updates
- -------- ----------- ------------------------------------------
- Tracing span A trace span.
- Metrics instrument A metric instrument (counter, gauge, ...).
- Logging log-record A log record.
-
-The tracing keyword follows the same logic. A "trace" is the complete
-end-to-end path of a request through a distributed system, composed of one or
-more "spans". Each span represents a single unit of work within that trace.
-The configuration operates at the span level: it creates individual spans, sets
-their parent-child relationships, and attaches attributes and events. Using
-"trace" as the keyword would be imprecise because one does not configure a trace
-directly; one configures the spans that collectively form a trace.
-
-The metrics keyword is analogous. In the OpenTelemetry data model the
-terminology is layered: a "metric" is the aggregated output that the SDK
-produces after processing recorded measurements, while an "instrument" is the
-concrete object through which those measurements are recorded -- a counter,
-histogram, gauge, or up-down counter. The configuration operates at the
-instrument level: it creates an instrument of a specific type and records values
-through it. Using "metric" as the keyword would be imprecise because one does
-not configure a metric directly; one configures an instrument that yields
-metrics.
-
-The logging keyword follows the same pattern. A "log" is the broad signal
-category, while a "log record" is a single discrete entry within that signal.
-The configuration operates at the log-record level: it creates individual log
-records with a severity, a body, and optional attributes and span context.
-Using "log" as the keyword would be imprecise because one does not configure a
-log stream directly; one configures the individual log records that comprise it.
+++ /dev/null
-## HAProxy OpenTelemetry Filter (OTel)
-
-The OTel filter enables HAProxy to emit telemetry data -- traces, metrics and
-logs -- to any OpenTelemetry-compatible backend via the OpenTelemetry protocol
-(OTLP).
-
-It is the successor to the OpenTracing (OT) filter, built on the OpenTelemetry
-standard which unifies distributed tracing, metrics and logging into a single
-observability framework.
-
-### Features
-
-- **Distributed tracing** -- spans with parent-child relationships, context
- propagation via HTTP headers or HAProxy variables, links, baggage and status.
-- **Metrics** -- counter, histogram, up-down counter and gauge instruments with
- configurable aggregation and bucket boundaries.
-- **Logging** -- log records with severity levels, optional span correlation and
- runtime-evaluated attributes.
-- **Rate limiting** -- percentage-based sampling (0.0--100.0) for controlling
- overhead.
-- **ACL integration** -- fine-grained conditional execution at instrumentation,
- scope and event levels.
-- **CLI management** -- runtime enable/disable, rate adjustment, error mode
- switching and status inspection.
-- **Context propagation** -- inject/extract span contexts between cascaded
- HAProxy instances or external services.
-
-### Dependencies
-
-The filter requires the
-[OpenTelemetry C Wrapper](https://github.com/haproxytech/opentelemetry-c-wrapper)
-library, which wraps the OpenTelemetry C++ SDK.
-
-### Building
-
-The OTel filter is compiled together with HAProxy by adding `USE_OTEL=1` to the
-make command.
-
-#### Using pkg-config
-
-```
-PKG_CONFIG_PATH=/opt/lib/pkgconfig make -j8 USE_OTEL=1 TARGET=linux-glibc
-```
-
-#### Explicit paths
-
-```
-make -j8 USE_OTEL=1 OTEL_INC=/opt/include OTEL_LIB=/opt/lib TARGET=linux-glibc
-```
-
-#### Build options
-
-| Variable | Description |
-|-----------------|-----------------------------------------------------|
-| `USE_OTEL` | Enable the OpenTelemetry filter |
-| `OTEL_DEBUG` | Compile in debug mode |
-| `OTEL_INC` | Force path to opentelemetry-c-wrapper include files |
-| `OTEL_LIB` | Force path to opentelemetry-c-wrapper library |
-| `OTEL_RUNPATH` | Add opentelemetry-c-wrapper RUNPATH to executable |
-| `OTEL_USE_VARS` | Enable context propagation via HAProxy variables |
-
-#### Debug mode
-
-```
-PKG_CONFIG_PATH=/opt/lib/pkgconfig make -j8 USE_OTEL=1 OTEL_DEBUG=1 TARGET=linux-glibc
-```
-
-#### Variable-based context propagation
-
-```
-PKG_CONFIG_PATH=/opt/lib/pkgconfig make -j8 USE_OTEL=1 OTEL_USE_VARS=1 TARGET=linux-glibc
-```
-
-#### Verifying the build
-
-```
-./haproxy -vv | grep -i opentelemetry
-```
-
-If the filter is built in, the output contains:
-
-```
-Built with OpenTelemetry support (C++ version 1.26.0, C Wrapper version 1.0.0-842).
- [OTEL] opentelemetry
-```
-
-#### Library path at runtime
-
-When pkg-config is not used, the executable may not find the library at startup.
-Use `LD_LIBRARY_PATH` or build with `OTEL_RUNPATH=1`:
-
-```
-LD_LIBRARY_PATH=/opt/lib ./haproxy ...
-```
-
-```
-make -j8 USE_OTEL=1 OTEL_RUNPATH=1 OTEL_INC=/opt/include OTEL_LIB=/opt/lib TARGET=linux-glibc
-```
-
-### Configuration
-
-The filter uses a two-file configuration model:
-
-1. **OTel configuration file** (`.cfg`) -- defines the telemetry model:
- instrumentation settings, scopes and groups.
-2. **YAML configuration file** (`.yml`) -- defines the OpenTelemetry SDK
- pipeline: exporters, samplers, processors, providers and signal routing.
-
-#### Activating the filter
-
-Add the filter to a HAProxy proxy section (frontend/listen/backend):
-
-```
-frontend my-frontend
- ...
- filter opentelemetry [id <id>] config <file>
- ...
-```
-
-If no filter id is specified, `otel-filter` is used as default.
-
-#### OTel configuration file structure
-
-The OTel configuration file contains three section types:
-
-- `otel-instrumentation` -- mandatory; references the YAML file, sets rate
- limits, error modes, logging and declares groups and scopes.
-- `otel-scope` -- defines actions (spans, attributes, metrics, logs) triggered
- by stream events or from groups.
-- `otel-group` -- a named collection of scopes triggered from HAProxy TCP/HTTP
- rules.
-
-#### Minimal YAML configuration
-
-```yaml
-exporters:
- my_exporter:
- type: otlp_http
- endpoint: "http://localhost:4318/v1/traces"
-
-samplers:
- my_sampler:
- type: always_on
-
-processors:
- my_processor:
- type: batch
-
-providers:
- my_provider:
- resources:
- - service.name: "haproxy"
-
-signals:
- traces:
- scope_name: "HAProxy OTel"
- exporters: my_exporter
- samplers: my_sampler
- processors: my_processor
- providers: my_provider
-```
-
-#### Supported YAML exporters
-
-| Type | Description |
-|-----------------|---------------------------------------|
-| `otlp_grpc` | OTLP over gRPC |
-| `otlp_http` | OTLP over HTTP (JSON or Protobuf) |
-| `otlp_file` | Local files in OTLP format |
-| `zipkin` | Zipkin-compatible backends |
-| `elasticsearch` | Elasticsearch |
-| `ostream` | Text output to a file (for debugging) |
-| `memory` | In-memory buffer (for testing) |
-
-### Scope keywords
-
-| Keyword | Description |
-|----------------|---------------------------------------------------------|
-| `span` | Create or reference a span |
-| `attribute` | Set key-value span attributes |
-| `event` | Add timestamped span events |
-| `baggage` | Set context propagation data |
-| `status` | Set span status (ok/error/ignore/unset) |
-| `link` | Add span links to related spans |
-| `inject` | Inject context into headers or variables |
-| `extract` | Extract context from headers or variables |
-| `finish` | Close spans (supports wildcards: `*`, `*req*`, `*res*`) |
-| `instrument` | Create or update metric instruments |
-| `log-record` | Emit a log record with severity |
-| `otel-event` | Bind scope to a filter event with optional ACL |
-| `idle-timeout` | Set periodic event interval for idle streams |
-
-### CLI commands
-
-Available via the HAProxy CLI socket (prefix: `flt-otel`):
-
-| Command | Description |
-|----------------------------|------------------------------------|
-| `flt-otel status` | Show filter status |
-| `flt-otel enable` | Enable the filter |
-| `flt-otel disable` | Disable the filter |
-| `flt-otel hard-errors` | Enable hard-errors mode |
-| `flt-otel soft-errors` | Disable hard-errors mode |
-| `flt-otel logging [state]` | Set logging state |
-| `flt-otel rate [value]` | Set or show the rate limit |
-| `flt-otel debug [level]` | Set debug level (debug build only) |
-
-When invoked without arguments, `rate`, `logging` and `debug` display the
-current value.
-
-### Performance
-
-Benchmark results from the standalone (`sa`) configuration, which exercises all
-events (worst-case scenario):
-
-| Rate limit | Req/s | Avg latency | Overhead |
-|------------|--------|-------------|----------|
-| 100.0% | 38,202 | 213.08 us | 21.6% |
-| 50.0% | 42,777 | 190.49 us | 12.2% |
-| 25.0% | 45,302 | 180.46 us | 7.0% |
-| 10.0% | 46,879 | 174.69 us | 3.7% |
-| 2.5% | 47,993 | 170.58 us | 1.4% |
-| disabled | 48,788 | 167.74 us | ~0 |
-| off | 48,697 | 168.00 us | baseline |
-
-With a rate limit of 10% or less, the performance impact is negligible.
-Detailed methodology and additional results are in the `test/` directory.
-
-### Test configurations
-
-The `test/` directory contains ready-to-run example configurations:
-
-- **sa** -- standalone; the most comprehensive example, demonstrating spans,
- attributes, events, links, baggage, status, metrics, log records, ACL
- conditions and idle-timeout events.
-- **fe/be** -- distributed tracing across two cascaded HAProxy instances using
- HTTP header-based context propagation.
-- **ctx** -- context propagation via HAProxy variables using the inject/extract
- mechanism.
-- **cmp** -- minimal configuration for benchmarking comparison.
-- **empty** -- filter initialized with no active telemetry.
-
-#### Quick start with Jaeger
-
-Start a Jaeger all-in-one container:
-
-```
-docker run -d --name jaeger -p 4317:4317 -p 4318:4318 -p 16686:16686 jaegertracing/all-in-one:latest
-```
-
-Run one of the test configurations:
-
-```
-./test/run-sa.sh
-```
-
-Open the Jaeger UI at `http://localhost:16686` to view traces.
-
-### Documentation
-
-Detailed documentation is available in the following files:
-
-- [README](README) -- complete reference documentation
-- [README-configuration](README-configuration) -- configuration guide
-- [README-conf](README-conf) -- configuration details
-- [README-design](README-design) -- cross-cutting design patterns
-- [README-implementation](README-implementation) -- component architecture
-- [README-func](README-func) -- function reference
-- [README-misc](README-misc) -- miscellaneous notes
-
-### Copyright
-
-Copyright 2026 HAProxy Technologies
-
-### Author
-
-Miroslav Zagorac <mzagorac@haproxy.com>
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef _OTEL_CLI_H_
-#define _OTEL_CLI_H_
-
-#define FLT_OTEL_CLI_CMD "flt-otel"
-
-#define FLT_OTEL_CLI_LOGGING_OFF "off"
-#define FLT_OTEL_CLI_LOGGING_ON "on"
-#define FLT_OTEL_CLI_LOGGING_NOLOGNORM "dontlog-normal"
-#define FLT_OTEL_CLI_LOGGING_STATE(a) (((a) & FLT_OTEL_LOGGING_ON) ? (((a) & FLT_OTEL_LOGGING_NOLOGNORM) ? "enabled, " FLT_OTEL_CLI_LOGGING_NOLOGNORM : "enabled") : "disabled")
-
-#define FLT_OTEL_CLI_MSG_CAT(a) (((a) == NULL) ? "" : (a)), (((a) == NULL) ? "" : "\n")
-
-
-/* Register CLI keywords for the OTel filter. */
-void flt_otel_cli_init(void);
-
-#endif /* _OTEL_CLI_H_ */
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef _OTEL_CONF_H_
-#define _OTEL_CONF_H_
-
-/* Extract the OTel filter configuration from a filter instance. */
-#define FLT_OTEL_CONF(f) ((struct flt_otel_conf *)FLT_CONF(f))
-
-/* Expand to a string pointer and its length for a named member. */
-#define FLT_OTEL_STR_HDR_ARGS(p,m) (p)->m, (p)->m##_len
-/***
- * It should be noted that the macro FLT_OTEL_CONF_HDR_ARGS() does not have
- * all the parameters defined that would correspond to the format found in
- * the FLT_OTEL_CONF_HDR_FMT macro (first pointer is missing).
- *
- * This is because during the expansion of the OTELC_DBG_STRUCT() macro, an
- * incorrect conversion is performed and instead of the first correct code,
- * a second incorrect code is generated:
- *
- * do {
- * if ((p) == NULL)
- * ..
- * } while (0)
- *
- * do {
- * if ((p), (int) (p)->id_len, (p)->id, (p)->id_len, (p)->cfg_line == NULL)
- * ..
- * } while (0)
- *
- */
-#define FLT_OTEL_CONF_HDR_FMT "%p:{ { '%.*s' %zu %d } "
-#define FLT_OTEL_CONF_HDR_ARGS(p,m) (int)(p)->m##_len, (p)->m, (p)->m##_len, (p)->cfg_line
-
-/*
- * Special two-byte prefix that triggers automatic id generation in
- * FLT_OTEL_CONF_FUNC_INIT(): the text after the prefix is combined
- * with the configuration line number to form a unique identifier.
- */
-#define FLT_OTEL_CONF_HDR_SPECIAL "\x1e\x1f"
-
-#define FLT_OTEL_CONF_STR_CMP(s,S) ((s##_len == S##_len) && (memcmp(s, S, S##_len) == 0))
-
-#define FLT_OTEL_DBG_CONF_SAMPLE_EXPR(h,p) \
- OTELC_DBG(DEBUG, h "%p:{ '%s' %p }", (p), (p)->fmt_expr, (p)->expr)
-
-#define FLT_OTEL_DBG_CONF_SAMPLE(h,p) \
- OTELC_DBG(DEBUG, h "%p:{ '%s' '%s' %s %s %d %p %hhu }", (p), \
- (p)->key, (p)->fmt_string, otelc_value_dump(&((p)->extra), ""), \
- flt_otel_list_dump(&((p)->exprs)), (p)->num_exprs, &((p)->lf_expr), (p)->lf_used)
-
-#define FLT_OTEL_DBG_CONF_HDR(h,p,i) \
- OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "}", (p), FLT_OTEL_CONF_HDR_ARGS(p, i))
-
-#define FLT_OTEL_DBG_CONF_CONTEXT(h,p) \
- OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "0x%02hhx }", (p), FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->flags)
-
-#define FLT_OTEL_DBG_CONF_SPAN(h,p) \
- OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "'%s' %zu %s' %zu %hhu 0x%02hhx %s %s %s %s %s }", \
- (p), FLT_OTEL_CONF_HDR_ARGS(p, id), FLT_OTEL_STR_HDR_ARGS(p, ref_id), \
- FLT_OTEL_STR_HDR_ARGS(p, ctx_id), (p)->flag_root, (p)->ctx_flags, \
- flt_otel_list_dump(&((p)->links)), flt_otel_list_dump(&((p)->attributes)), \
- flt_otel_list_dump(&((p)->events)), flt_otel_list_dump(&((p)->baggages)), \
- flt_otel_list_dump(&((p)->statuses)))
-
-#define FLT_OTEL_DBG_CONF_SCOPE(h,p) \
- OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%hhu %d %u %s %p %s %s %s %s %s }", (p), \
- FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->flag_used, (p)->event, (p)->idle_timeout, \
- flt_otel_list_dump(&((p)->acls)), (p)->cond, flt_otel_list_dump(&((p)->contexts)), \
- flt_otel_list_dump(&((p)->spans)), flt_otel_list_dump(&((p)->spans_to_finish)), \
- flt_otel_list_dump(&((p)->instruments)), flt_otel_list_dump(&((p)->log_records)))
-
-#define FLT_OTEL_DBG_CONF_GROUP(h,p) \
- OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%hhu %s }", (p), \
- FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->flag_used, flt_otel_list_dump(&((p)->ph_scopes)))
-
-#define FLT_OTEL_DBG_CONF_PH(h,p) \
- OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%p }", (p), FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->ptr)
-
-#define FLT_OTEL_DBG_CONF_INSTR(h,p) \
- OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "'%s' %p %p %p %u %hhu %hhu 0x%02hhx %p:%s 0x%08x %u %s %s %s }", \
- (p), FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->config, (p)->tracer, (p)->meter, (p)->logger, \
- (p)->rate_limit, (p)->flag_harderr, (p)->flag_disabled, (p)->logging, &((p)->proxy_log), \
- flt_otel_list_dump(&((p)->proxy_log.loggers)), (p)->analyzers, (p)->idle_timeout, \
- flt_otel_list_dump(&((p)->acls)), flt_otel_list_dump(&((p)->ph_groups)), \
- flt_otel_list_dump(&((p)->ph_scopes)))
-
-#define FLT_OTEL_DBG_CONF_INSTRUMENT(h,p) \
- OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%" PRId64 " %d %d '%s' '%s' %s %s %p %zu %p }", (p), \
- FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->idx, (p)->type, (p)->aggr_type, OTELC_STR_ARG((p)->description), \
- OTELC_STR_ARG((p)->unit), flt_otel_list_dump(&((p)->samples)), flt_otel_list_dump(&((p)->attributes)), \
- (p)->ref, (p)->bounds_num, (p)->bounds)
-
-#define FLT_OTEL_DBG_CONF_LOG_RECORD(h,p) \
- OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%d %" PRId64 " '%s' '%s' %s %s }", (p), \
- FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->severity, (p)->event_id, OTELC_STR_ARG((p)->event_name), \
- OTELC_STR_ARG((p)->span), flt_otel_list_dump(&((p)->attributes)), flt_otel_list_dump(&((p)->samples)))
-
-#define FLT_OTEL_DBG_CONF(h,p) \
- OTELC_DBG(DEBUG, h "%p:{ %p '%s' '%s' %p %s %s }", (p), \
- (p)->proxy, (p)->id, (p)->cfg_file, (p)->instr, \
- flt_otel_list_dump(&((p)->groups)), flt_otel_list_dump(&((p)->scopes)))
-
-/* Anonymous struct containing a string pointer and its length. */
-#define FLT_OTEL_CONF_STR(p) \
- struct { \
- char *p; \
- size_t p##_len; \
- }
-
-/* Common header embedded in all configuration structures. */
-#define FLT_OTEL_CONF_HDR(p) \
- struct { \
- FLT_OTEL_CONF_STR(p); \
- int cfg_line; \
- struct list list; \
- }
-
-
-/* Generic configuration header used for simple named list entries. */
-struct flt_otel_conf_hdr {
- FLT_OTEL_CONF_HDR(id); /* A list containing header names. */
-};
-
-/* flt_otel_conf_sample->exprs */
-struct flt_otel_conf_sample_expr {
- FLT_OTEL_CONF_HDR(fmt_expr); /* The original sample expression format string. */
- struct sample_expr *expr; /* The sample expression. */
-};
-
-/*
- * flt_otel_conf_span->attributes
- * flt_otel_conf_span->events (event_name -> OTELC_VALUE_STR(&extra))
- * flt_otel_conf_span->baggages
- * flt_otel_conf_span->statuses (status_code -> extra.u.value_int32)
- * flt_otel_conf_instrument->samples
- * flt_otel_conf_log_record->samples
- */
-struct flt_otel_conf_sample {
- FLT_OTEL_CONF_HDR(key); /* The list containing sample names. */
- char *fmt_string; /* All sample-expression arguments are combined into a single string. */
- struct otelc_value extra; /* Optional supplementary data. */
- struct list exprs; /* Used to chain sample expressions. */
- int num_exprs; /* Number of defined expressions. */
- struct lf_expr lf_expr; /* The log-format expression. */
- bool lf_used; /* Whether lf_expr is used instead of exprs. */
-};
-
-/*
- * flt_otel_conf_scope->spans_to_finish
- *
- * It can be seen that this structure is actually identical to the structure
- * flt_otel_conf_hdr.
- */
-struct flt_otel_conf_str {
- FLT_OTEL_CONF_HDR(str); /* A list containing character strings. */
-};
-
-/* flt_otel_conf_scope->contexts */
-struct flt_otel_conf_context {
- FLT_OTEL_CONF_HDR(id); /* The name of the context. */
- uint8_t flags; /* The type of storage from which the span context is extracted. */
-};
-
-/* flt_otel_conf_span->links */
-struct flt_otel_conf_link {
- FLT_OTEL_CONF_HDR(span); /* The list containing link names. */
-};
-
-/*
- * Span configuration within a scope.
- * flt_otel_conf_scope->spans
- */
-struct flt_otel_conf_span {
- FLT_OTEL_CONF_HDR(id); /* The name of the span. */
- FLT_OTEL_CONF_STR(ref_id); /* The reference name, if used. */
- FLT_OTEL_CONF_STR(ctx_id); /* The span context name, if used. */
- uint8_t ctx_flags; /* The type of storage used for the span context. */
- bool flag_root; /* Whether this is a root span. */
- struct list links; /* The set of linked span names. */
- struct list attributes; /* The set of key:value attributes. */
- struct list events; /* The set of events with key-value attributes. */
- struct list baggages; /* The set of key:value baggage items. */
- struct list statuses; /* Span status code and description (only one per list). */
-};
-
-/*
- * Metric instrument configuration within a scope.
- * flt_otel_conf_scope->instruments
- */
-struct flt_otel_conf_instrument {
- FLT_OTEL_CONF_HDR(id); /* The name of the instrument. */
- int64_t idx; /* Meter instrument index (-1 if not yet created). */
- otelc_metric_instrument_t type; /* Instrument type (or UPDATE). */
- otelc_metric_aggregation_type_t aggr_type; /* Aggregation type for the view (create only). */
- char *description; /* Instrument description (create only). */
- char *unit; /* Instrument unit (create only). */
- struct list samples; /* Sample expressions for the value. */
- double *bounds; /* Histogram bucket boundaries (create only). */
- size_t bounds_num; /* Number of histogram bucket boundaries. */
- struct list attributes; /* Instrument attributes (update only, flt_otel_conf_sample). */
- struct flt_otel_conf_instrument *ref; /* Resolved create-form instrument (update only). */
-};
-
-/*
- * Log record configuration within a scope.
- * flt_otel_conf_scope->log_records
- */
-struct flt_otel_conf_log_record {
- FLT_OTEL_CONF_HDR(id); /* Required by macro; member <id> is not used directly. */
- otelc_log_severity_t severity; /* The severity level. */
- int64_t event_id; /* Optional event identifier. */
- char *event_name; /* Optional event name. */
- char *span; /* Optional span reference. */
- struct list attributes; /* Log record attributes (flt_otel_conf_sample). */
- struct list samples; /* Sample expressions for the body. */
-};
-
-/* Configuration for a single event scope. */
-struct flt_otel_conf_scope {
- FLT_OTEL_CONF_HDR(id); /* The scope name. */
- bool flag_used; /* The indication that the scope is being used. */
- int event; /* FLT_OTEL_EVENT_* */
- uint idle_timeout; /* Idle timeout interval in milliseconds (0 = off). */
- struct list acls; /* ACLs declared on this scope. */
- struct acl_cond *cond; /* ACL condition to meet. */
- struct list contexts; /* Declared contexts. */
- struct list spans; /* Declared spans. */
- struct list spans_to_finish; /* The list of spans scheduled for finishing. */
- struct list instruments; /* The list of metric instruments. */
- struct list log_records; /* The list of log records. */
-};
-
-/* Configuration for a named group of scopes. */
-struct flt_otel_conf_group {
- FLT_OTEL_CONF_HDR(id); /* The group name. */
- bool flag_used; /* The indication that the group is being used. */
- struct list ph_scopes; /* List of all used scopes. */
-};
-
-/* Placeholder referencing a scope or group by name. */
-struct flt_otel_conf_ph {
- FLT_OTEL_CONF_HDR(id); /* The scope/group name. */
- void *ptr; /* Pointer to real placeholder structure. */
-};
-#define flt_otel_conf_ph_group flt_otel_conf_ph
-#define flt_otel_conf_ph_scope flt_otel_conf_ph
-
-/* Top-level OTel instrumentation settings (tracer, meter, options). */
-struct flt_otel_conf_instr {
- FLT_OTEL_CONF_HDR(id); /* The OpenTelemetry instrumentation name. */
- char *config; /* The OpenTelemetry configuration file name. */
- struct otelc_tracer *tracer; /* The OpenTelemetry tracer handle. */
- struct otelc_meter *meter; /* The OpenTelemetry meter handle. */
- struct otelc_logger *logger; /* The OpenTelemetry logger handle. */
- uint32_t rate_limit; /* [0 2^32-1] <-> [0.0 100.0] */
- bool flag_harderr; /* [0 1] */
- bool flag_disabled; /* [0 1] */
- uint8_t logging; /* [0 1 3] */
- struct proxy proxy_log; /* The log server list. */
- uint analyzers; /* Defined channel analyzers. */
- uint idle_timeout; /* Minimum idle timeout across scopes (ms, 0 = off). */
- struct list acls; /* ACLs declared on this tracer. */
- struct list ph_groups; /* List of all used groups. */
- struct list ph_scopes; /* List of all used scopes. */
-};
-
-/* Runtime counters for filter diagnostics. */
-struct flt_otel_counters {
-#ifdef DEBUG_OTEL
- struct {
- bool flag_used; /* Whether this event is used. */
- uint64_t htx[2]; /* htx_is_empty() function result counter. */
- } event[FLT_OTEL_EVENT_MAX];
-#endif
-
-#ifdef FLT_OTEL_USE_COUNTERS
- uint64_t attached[4]; /* [run rate-limit disabled error] */
- uint64_t disabled[2]; /* How many times stream processing is disabled. */
-#endif
-};
-
-/* The OpenTelemetry filter configuration. */
-struct flt_otel_conf {
- struct proxy *proxy; /* Proxy owning the filter. */
- char *id; /* The OpenTelemetry filter id. */
- char *cfg_file; /* The OpenTelemetry filter configuration file name. */
- struct flt_otel_conf_instr *instr; /* The OpenTelemetry instrumentation settings. */
- struct list groups; /* List of all available groups. */
- struct list scopes; /* List of all available scopes. */
- struct flt_otel_counters cnt; /* Various counters related to filter operation. */
- struct list smp_args; /* Deferred OTEL sample fetch args to resolve. */
-};
-
-
-/* Allocate and initialize a sample from parsed arguments. */
-struct flt_otel_conf_sample *flt_otel_conf_sample_init_ex(const char **args, int idx, int n, const struct otelc_value *extra, int line, struct list *head, char **err);
-
-/* Allocate and initialize the top-level OTel filter configuration. */
-struct flt_otel_conf *flt_otel_conf_init(struct proxy *px);
-
-/* Free the top-level OTel filter configuration. */
-void flt_otel_conf_free(struct flt_otel_conf **ptr);
-
-#endif /* _OTEL_CONF_H_ */
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef _OTEL_CONF_FUNCS_H_
-#define _OTEL_CONF_FUNCS_H_
-
-/*
- * Macro that generates a flt_otel_conf_<type>_init() function. The generated
- * function allocates and initializes a configuration structure of the given
- * type, checks for duplicate names in the list, and optionally runs a custom
- * initializer body.
- */
-#define FLT_OTEL_CONF_FUNC_INIT(_type_, _id_, _func_) \
- struct flt_otel_conf_##_type_ *flt_otel_conf_##_type_##_init(const char *id, int line, struct list *head, char **err) \
- { \
- struct flt_otel_conf_##_type_ *retptr = NULL; \
- struct flt_otel_conf_##_type_ *ptr; \
- char id_buffer[FLT_OTEL_ID_MAXLEN + 16]; \
- size_t _id_##_len; \
- \
- OTELC_FUNC("\"%s\", %d, %p, %p:%p", OTELC_STR_ARG(id), line, head, OTELC_DPTR_ARGS(err)); \
- \
- if ((id == NULL) || (*id == '\0')) { \
- FLT_OTEL_ERR("name not set"); \
- \
- OTELC_RETURN_PTR(retptr); \
- } \
- else if ((id[0] == FLT_OTEL_CONF_HDR_SPECIAL[0]) && (id[1] == FLT_OTEL_CONF_HDR_SPECIAL[1])) { \
- (void)snprintf(id_buffer, sizeof(id_buffer), "%s:%d", id + 2, line); \
- \
- id = id_buffer; \
- } \
- \
- _id_##_len = strlen(id); \
- if (_id_##_len >= FLT_OTEL_ID_MAXLEN) { \
- FLT_OTEL_ERR("'%s' : name too long", id); \
- \
- OTELC_RETURN_PTR(retptr); \
- } \
- \
- if (head != NULL) \
- list_for_each_entry(ptr, head, list) \
- if (strcmp(ptr->_id_, id) == 0) { \
- FLT_OTEL_ERR("'%s' : already defined", id); \
- \
- OTELC_RETURN_PTR(retptr); \
- } \
- \
- retptr = OTELC_CALLOC(1, sizeof(*retptr)); \
- if (retptr != NULL) { \
- retptr->cfg_line = line; \
- retptr->_id_##_len = _id_##_len; \
- retptr->_id_ = OTELC_STRDUP(id); \
- if (retptr->_id_ != NULL) { \
- if (head != NULL) \
- LIST_APPEND(head, &(retptr->list)); \
- \
- FLT_OTEL_DBG_CONF_HDR("- conf_" #_type_ " init ", retptr, _id_); \
- } \
- else \
- OTELC_SFREE_CLEAR(retptr); \
- } \
- \
- if (retptr != NULL) { \
- _func_ \
- } \
- \
- if (retptr == NULL) \
- FLT_OTEL_ERR("out of memory"); \
- \
- OTELC_RETURN_PTR(retptr); \
- }
-
-/*
- * Macro that generates a flt_otel_conf_<type>_free() function. The generated
- * function runs a custom cleanup body, then frees the name string, removes the
- * structure from its list, and frees the structure.
- */
-#define FLT_OTEL_CONF_FUNC_FREE(_type_, _id_, _func_) \
- void flt_otel_conf_##_type_##_free(struct flt_otel_conf_##_type_ **ptr) \
- { \
- OTELC_FUNC("%p:%p", OTELC_DPTR_ARGS(ptr)); \
- \
- if ((ptr == NULL) || (*ptr == NULL)) \
- OTELC_RETURN(); \
- \
- { _func_ } \
- \
- OTELC_SFREE((*ptr)->_id_); \
- FLT_OTEL_LIST_DEL(&((*ptr)->list)); \
- OTELC_SFREE_CLEAR(*ptr); \
- \
- OTELC_RETURN(); \
- }
-
-
-/* The FLT_OTEL_LIST_DESTROY() macro uses the following two definitions. */
-#define flt_otel_conf_ph_group_free flt_otel_conf_ph_free
-#define flt_otel_conf_ph_scope_free flt_otel_conf_ph_free
-
-/* Declare init/free function prototypes for a configuration type. */
-#define FLT_OTEL_CONF_FUNC_DECL(_type_) \
- struct flt_otel_conf_##_type_ *flt_otel_conf_##_type_##_init(const char *id, int line, struct list *head, char **err); \
- void flt_otel_conf_##_type_##_free(struct flt_otel_conf_##_type_ **ptr);
-
-FLT_OTEL_CONF_FUNC_DECL(hdr)
-FLT_OTEL_CONF_FUNC_DECL(str)
-FLT_OTEL_CONF_FUNC_DECL(ph)
-FLT_OTEL_CONF_FUNC_DECL(sample_expr)
-FLT_OTEL_CONF_FUNC_DECL(sample)
-FLT_OTEL_CONF_FUNC_DECL(link)
-FLT_OTEL_CONF_FUNC_DECL(context)
-FLT_OTEL_CONF_FUNC_DECL(span)
-FLT_OTEL_CONF_FUNC_DECL(scope)
-FLT_OTEL_CONF_FUNC_DECL(instrument)
-FLT_OTEL_CONF_FUNC_DECL(log_record)
-FLT_OTEL_CONF_FUNC_DECL(group)
-FLT_OTEL_CONF_FUNC_DECL(instr)
-
-#endif /* _OTEL_CONF_FUNCS_H_ */
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef _OTEL_CONFIG_H_
-#define _OTEL_CONFIG_H_
-
-/* Memory pool selection flags. */
-#define USE_POOL_BUFFER
-#define USE_POOL_OTEL_SPAN_CONTEXT
-#define USE_POOL_OTEL_SCOPE_SPAN
-#define USE_POOL_OTEL_SCOPE_CONTEXT
-#define USE_POOL_OTEL_RUNTIME_CONTEXT
-#define USE_TRASH_CHUNK
-
-/* Enable per-event and per-stream diagnostic counters in debug builds. */
-#if defined(DEBUG_OTEL) && !defined(FLT_OTEL_USE_COUNTERS)
-# define FLT_OTEL_USE_COUNTERS
-#endif
-
-#define FLT_OTEL_ID_MAXLEN 64 /* Maximum identifier length. */
-#define FLT_OTEL_DEBUG_LEVEL 0b11101111111 /* Default debug bitmask. */
-
-#define FLT_OTEL_ATTR_INIT_SIZE 8 /* Initial attribute array capacity. */
-#define FLT_OTEL_ATTR_INC_SIZE 4 /* Attribute array growth increment. */
-
-#endif /* _OTEL_CONFIG_H_ */
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef _OTEL_DEBUG_H_
-#define _OTEL_DEBUG_H_
-
-#ifdef DEBUG_FULL
-# define DEBUG_OTEL
-#endif
-
-/*
- * FLT_OTEL_DBG_ARGS - include extra debug-only function parameters.
- * FLT_OTEL_DBG_BUF - dump a buffer structure for debugging.
- *
- * When DEBUG_OTEL is not defined, these expand to nothing.
- */
-#ifdef DEBUG_OTEL
-# define FLT_OTEL_DBG_ARGS(a, ...) a, ##__VA_ARGS__
-# define FLT_OTEL_DBG_BUF(l,a) OTELC_DBG(l, "%p:{ %zu %p %zu %zu }", (a), (a)->size, (a)->area, (a)->data, (a)->head)
-#else
-# define FLT_OTEL_DBG_ARGS(...)
-# define FLT_OTEL_DBG_BUF(...) while (0)
-#endif /* DEBUG_OTEL */
-
-/*
- * ON | NOLOGNORM |
- * -----+-----------+-------------
- * 0 | 0 | no log
- * 0 | 1 | no log
- * 1 | 0 | log all
- * 1 | 1 | log errors
- * -----+-----------+-------------
- */
-#define FLT_OTEL_LOG(l,f, ...) \
- do { \
- if (!(conf->instr->logging & FLT_OTEL_LOGGING_ON)) \
- OTELC_DBG(DEBUG, "NOLOG[%d]: [" FLT_OTEL_SCOPE "]: [%s] " f, (l), conf->id, ##__VA_ARGS__); \
- else if ((conf->instr->logging & FLT_OTEL_LOGGING_NOLOGNORM) && ((l) > LOG_ERR)) \
- OTELC_DBG(NOTICE, "NOLOG[%d]: [" FLT_OTEL_SCOPE "]: [%s] " f, (l), conf->id, ##__VA_ARGS__); \
- else { \
- send_log(&(conf->instr->proxy_log), (l), "[" FLT_OTEL_SCOPE "]: [%s] " f "\n", conf->id, ##__VA_ARGS__); \
- \
- OTELC_DBG(INFO, "LOG[%d]: %s", (l), logline); \
- } \
- } while (0)
-
-#endif /* _OTEL_DEBUG_H_ */
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef _OTEL_DEFINE_H_
-#define _OTEL_DEFINE_H_
-
-/* Safe pointer dereference with default value. */
-#define FLT_OTEL_DEREF(p,m,v) (((p) != NULL) ? (p)->m : (v))
-
-/* Check whether argument at index n is in range, non-NULL and non-empty. */
-#define FLT_OTEL_ARG_ISVALID(n) ({ typeof(n) _n = (n); OTELC_IN_RANGE(_n, 0, MAX_LINE_ARGS - 1) && (args[_n] != NULL) && (*args[_n] != '\0'); })
-
-/* Convert a uint32_t rate value to a floating-point percentage. */
-#define FLT_OTEL_U32_FLOAT(a) ((a) * 100.0 / UINT32_MAX)
-
-/* Convert a floating-point percentage to a uint32_t rate value. */
-#define FLT_OTEL_FLOAT_U32(a) ((uint32_t)((a) / 100.0 * UINT32_MAX + 0.5))
-
-#define FLT_OTEL_STR_DASH_72 "------------------------------------------------------------------------"
-#define FLT_OTEL_STR_DASH_78 FLT_OTEL_STR_DASH_72 "------"
-#define FLT_OTEL_STR_FLAG_YN(a) ((a) ? "yes" : "no")
-
-/* Compile-time string length excluding the null terminator. */
-#define FLT_OTEL_STR_SIZE(a) (sizeof(a) - 1)
-
-/* Expand to address and length pair for a string literal. */
-#define FLT_OTEL_STR_ADDRSIZE(a) (a), FLT_OTEL_STR_SIZE(a)
-
-/* Compare a runtime string against a compile-time string literal. */
-#define FLT_OTEL_STR_CMP(S,s) ((s##_len == FLT_OTEL_STR_SIZE(S)) && (memcmp((s), FLT_OTEL_STR_ADDRSIZE(S)) == 0))
-
-/* Tolerance for double comparison in flt_otel_qsort_compar_double(). */
-#define FLT_OTEL_DBL_EPSILON 1e-9
-
-/* Execute a statement exactly once across all invocations. */
-#define FLT_OTEL_RUN_ONCE(f) do { static bool _f = 1; if (_f) { _f = 0; { f; } } } while (0)
-
-/* Check whether a list head has been initialized. */
-#define FLT_OTEL_LIST_ISVALID(a) ({ typeof(a) _a = (a); (_a != NULL) && (_a->n != NULL) && (_a->p != NULL); })
-
-/* Safely delete a list element if its list head is valid. */
-#define FLT_OTEL_LIST_DEL(a) do { if (FLT_OTEL_LIST_ISVALID(a)) LIST_DELETE(a); } while (0)
-
-/* Destroy all elements in a typed configuration list. */
-#define FLT_OTEL_LIST_DESTROY(t,h) \
- do { \
- struct flt_otel_conf_##t *_ptr, *_back; \
- \
- if (!FLT_OTEL_LIST_ISVALID(h) || LIST_ISEMPTY(h)) \
- break; \
- \
- OTELC_DBG(NOTICE, "- deleting " #t " list %s", flt_otel_list_dump(h)); \
- \
- list_for_each_entry_safe(_ptr, _back, (h), list) \
- flt_otel_conf_##t##_free(&_ptr); \
- } while (0)
-
-/* Declare a rotating thread-local string buffer pool. */
-#define FLT_OTEL_BUFFER_THR(b,m,n,p) \
- static THREAD_LOCAL char b[m][n]; \
- static THREAD_LOCAL size_t __idx = 0; \
- char *p = b[__idx]; \
- __idx = (__idx + 1) % (m)
-
-/* Format an error message if none has been set yet. */
-#define FLT_OTEL_ERR(f, ...) \
- do { \
- if ((err != NULL) && (*err == NULL)) { \
- (void)memprintf(err, f, ##__VA_ARGS__); \
- \
- OTELC_DBG(DEBUG, "err: '%s'", *err); \
- } \
- } while (0)
-/* Append to an existing error message unconditionally. */
-#define FLT_OTEL_ERR_APPEND(f, ...) \
- do { \
- if (err != NULL) \
- (void)memprintf(err, f, ##__VA_ARGS__); \
- } while (0)
-/* Log an error message and free its memory. */
-#define FLT_OTEL_ERR_FREE(p) \
- do { \
- if ((p) == NULL) \
- break; \
- \
- OTELC_DBG(LOG, "%s:%d: ERROR: %s", __func__, __LINE__, (p)); \
- OTELC_SFREE_CLEAR(p); \
- } while (0)
-
-#endif /* _OTEL_DEFINE_H_ */
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef _OTEL_EVENT_H_
-#define _OTEL_EVENT_H_
-
-/*
- * This must be defined in order for macro FLT_OTEL_EVENT_DEFINES
- * and structure flt_otel_event_data to have the correct contents.
- */
-#define AN__NONE 0
-#define AN__STREAM_START 0 /* on-stream-start */
-#define AN__STREAM_STOP 0 /* on-stream-stop */
-#define AN__IDLE_TIMEOUT 0 /* on-idle-timeout */
-#define AN__BACKEND_SET 0 /* on-backend-set */
-#define AN_REQ_HTTP_HEADERS 0 /* on-http-headers-request */
-#define AN_RES_HTTP_HEADERS 0 /* on-http-headers-response */
-#define AN_REQ_HTTP_END 0 /* on-http-end-request */
-#define AN_RES_HTTP_END 0 /* on-http-end-response */
-#define AN_RES_HTTP_REPLY 0 /* on-http-reply */
-#define AN_REQ_CLIENT_SESS_START 0
-#define AN_REQ_SERVER_UNAVAILABLE 0
-#define AN_REQ_CLIENT_SESS_END 0
-#define AN_RES_SERVER_SESS_START 0
-#define AN_RES_SERVER_SESS_END 0
-#define SMP_VAL_FE_ 0
-#define SMP_VAL_BE_ 0
-#define SMP_OPT_DIR_ 0xff
-
-/*
- * Event names are selected to be somewhat compatible with the SPOE filter,
- * from which the following names are taken:
- * - on-client-session -> on-client-session-start
- * - on-frontend-tcp-request
- * - on-frontend-http-request
- * - on-backend-tcp-request
- * - on-backend-http-request
- * - on-server-session -> on-server-session-start
- * - on-tcp-response
- * - on-http-response
- *
- * FLT_OTEL_EVENT_NONE is used as an index for 'otel-scope' sections that do not
- * have an event defined. The 'otel-scope' sections thus defined can be used
- * within the 'otel-group' section.
- *
- * A description of the macro arguments can be found in the structure
- * flt_otel_event_data definition.
- *
- * The following table is derived from the definitions AN_REQ_* and AN_RES_*
- * found in the HAProxy include file include/haproxy/channel-t.h.
- */
-#define FLT_OTEL_EVENT_DEFINES \
- /* Stream lifecycle pseudo-events (an_bit = 0, not tied to a channel analyzer) */ \
- FLT_OTEL_EVENT_DEF( NONE, , , , 0, "") \
- FLT_OTEL_EVENT_DEF( STREAM_START, , , , 0, "on-stream-start") \
- FLT_OTEL_EVENT_DEF( STREAM_STOP, , , , 0, "on-stream-stop") \
- FLT_OTEL_EVENT_DEF( CLIENT_SESS_START, REQ, CON_ACC, , 1, "on-client-session-start") \
- FLT_OTEL_EVENT_DEF( IDLE_TIMEOUT, , , , 0, "on-idle-timeout") \
- FLT_OTEL_EVENT_DEF( BACKEND_SET, , , , 0, "on-backend-set") \
- \
- /* Request analyzers */ \
-/* FLT_OTEL_EVENT_DEF( FLT_START_FE, REQ, , , , "on-filter-start") */ \
- FLT_OTEL_EVENT_DEF( INSPECT_FE, REQ, REQ_CNT, , 1, "on-frontend-tcp-request") \
- FLT_OTEL_EVENT_DEF( WAIT_HTTP, REQ, , , 1, "on-http-wait-request") \
- FLT_OTEL_EVENT_DEF( HTTP_BODY, REQ, , , 1, "on-http-body-request") \
- FLT_OTEL_EVENT_DEF( HTTP_PROCESS_FE, REQ, HRQ_HDR, , 1, "on-frontend-http-request") \
- FLT_OTEL_EVENT_DEF( SWITCHING_RULES, REQ, , , 1, "on-switching-rules-request") \
-/* FLT_OTEL_EVENT_DEF( FLT_START_BE, REQ, , , , "") */ \
- FLT_OTEL_EVENT_DEF( INSPECT_BE, REQ, REQ_CNT, REQ_CNT, 1, "on-backend-tcp-request") \
- FLT_OTEL_EVENT_DEF( HTTP_PROCESS_BE, REQ, HRQ_HDR, HRQ_HDR, 1, "on-backend-http-request") \
-/* FLT_OTEL_EVENT_DEF( HTTP_TARPIT, REQ, , , 1, "on-http-tarpit-request") */ \
- FLT_OTEL_EVENT_DEF( SRV_RULES, REQ, , , 1, "on-process-server-rules-request") \
- FLT_OTEL_EVENT_DEF( HTTP_INNER, REQ, , , 1, "on-http-process-request") \
- FLT_OTEL_EVENT_DEF( PRST_RDP_COOKIE, REQ, , , 1, "on-tcp-rdp-cookie-request") \
- FLT_OTEL_EVENT_DEF( STICKING_RULES, REQ, , , 1, "on-process-sticking-rules-request") \
-/* FLT_OTEL_EVENT_DEF( FLT_HTTP_HDRS, REQ, , , , "") */ \
-/* FLT_OTEL_EVENT_DEF( HTTP_XFER_BODY, REQ, , , , "") */ \
-/* FLT_OTEL_EVENT_DEF( WAIT_CLI, REQ, , , , "") */ \
-/* FLT_OTEL_EVENT_DEF( FLT_XFER_DATA, REQ, , , , "") */ \
-/* FLT_OTEL_EVENT_DEF( FLT_END, REQ, , , , "") */ \
- FLT_OTEL_EVENT_DEF( HTTP_HEADERS, REQ, HRQ_HDR, HRQ_HDR, 1, "on-http-headers-request") \
- FLT_OTEL_EVENT_DEF( HTTP_END, REQ, , , 1, "on-http-end-request") \
- FLT_OTEL_EVENT_DEF( CLIENT_SESS_END, REQ, , , 0, "on-client-session-end") \
- FLT_OTEL_EVENT_DEF(SERVER_UNAVAILABLE, REQ, , , 0, "on-server-unavailable") \
- \
- /* Response analyzers */ \
- FLT_OTEL_EVENT_DEF( SERVER_SESS_START, RES, , SRV_CON, 0, "on-server-session-start") \
-/* FLT_OTEL_EVENT_DEF( FLT_START_FE, RES, , , , "") */ \
-/* FLT_OTEL_EVENT_DEF( FLT_START_BE, RES, , , , "") */ \
- FLT_OTEL_EVENT_DEF( INSPECT, RES, RES_CNT, RES_CNT, 0, "on-tcp-response") \
- FLT_OTEL_EVENT_DEF( WAIT_HTTP, RES, , , 1, "on-http-wait-response") \
- FLT_OTEL_EVENT_DEF( STORE_RULES, RES, , , 1, "on-process-store-rules-response") \
- FLT_OTEL_EVENT_DEF( HTTP_PROCESS_BE, RES, HRS_HDR, HRS_HDR, 1, "on-http-response") \
- FLT_OTEL_EVENT_DEF( HTTP_HEADERS, RES, HRS_HDR, HRS_HDR, 1, "on-http-headers-response") \
- FLT_OTEL_EVENT_DEF( HTTP_END, RES, , , 0, "on-http-end-response") \
- FLT_OTEL_EVENT_DEF( HTTP_REPLY, RES, , , 0, "on-http-reply") \
-/* FLT_OTEL_EVENT_DEF( HTTP_PROCESS_FE, RES, , , , "") */ \
-/* FLT_OTEL_EVENT_DEF( FLT_HTTP_HDRS, RES, , , , "") */ \
-/* FLT_OTEL_EVENT_DEF( HTTP_XFER_BODY, RES, , , , "") */ \
-/* FLT_OTEL_EVENT_DEF( WAIT_CLI, RES, , , , "") */ \
-/* FLT_OTEL_EVENT_DEF( FLT_XFER_DATA, RES, , , , "") */ \
-/* FLT_OTEL_EVENT_DEF( FLT_END, RES, , , , "") */ \
- FLT_OTEL_EVENT_DEF( SERVER_SESS_END, RES, , , 0, "on-server-session-end")
-
-enum FLT_OTEL_EVENT_enum {
-#define FLT_OTEL_EVENT_DEF(a,b,c,d,e,f) FLT_OTEL_EVENT_##b##_##a,
- FLT_OTEL_EVENT_DEFINES
- FLT_OTEL_EVENT_MAX
-#undef FLT_OTEL_EVENT_DEF
-};
-
-/* Sample data types associated with a scope event. */
-enum FLT_OTEL_EVENT_SAMPLE_enum {
- FLT_OTEL_EVENT_SAMPLE_ATTRIBUTE = 0,
- FLT_OTEL_EVENT_SAMPLE_EVENT,
- FLT_OTEL_EVENT_SAMPLE_BAGGAGE,
- FLT_OTEL_EVENT_SAMPLE_STATUS,
-};
-
-/* Per-event metadata mapping analyzer bits to filter event names. */
-struct flt_otel_event_data {
- uint an_bit; /* Used channel analyser. */
- const char *an_name; /* Channel analyser name. */
- uint smp_opt_dir; /* Fetch direction (request/response). */
- uint smp_val_fe; /* Valid FE fetch location. */
- uint smp_val_be; /* Valid BE fetch location. */
- bool flag_http_inject; /* Span context injection allowed. */
- const char *name; /* Filter event name. */
-};
-
-struct flt_otel_conf_scope;
-
-
-/* Per-event metadata table indexed by FLT_OTEL_EVENT_* constants. */
-extern const struct flt_otel_event_data flt_otel_event_data[FLT_OTEL_EVENT_MAX];
-
-
-/* Execute a single scope: create spans, record instruments, evaluate samples. */
-int flt_otel_scope_run(struct stream *s, struct filter *f, struct channel *chn, struct flt_otel_conf_scope *conf_scope, const struct timespec *ts_steady, const struct timespec *ts_system, uint dir, char **err);
-
-/* Run all scopes matching a filter event on the given stream and channel. */
-int flt_otel_event_run(struct stream *s, struct filter *f, struct channel *chn, int event, char **err);
-
-#endif /* _OTEL_EVENT_H_ */
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef _OTEL_FILTER_H_
-#define _OTEL_FILTER_H_
-
-#define FLT_OTEL_FMT_NAME "'" FLT_OTEL_OPT_NAME "' : "
-#define FLT_OTEL_FMT_TYPE "'filter' : "
-#define FLT_OTEL_VAR_UUID "sess", "otel", "uuid"
-#define FLT_OTEL_ALERT(f, ...) ha_alert(FLT_OTEL_FMT_TYPE FLT_OTEL_FMT_NAME f "\n", ##__VA_ARGS__)
-
-#define FLT_OTEL_CONDITION_IF "if"
-#define FLT_OTEL_CONDITION_UNLESS "unless"
-
-/* Return codes for OTel filter operations. */
-enum FLT_OTEL_RET_enum {
- FLT_OTEL_RET_ERROR = -1,
- FLT_OTEL_RET_WAIT = 0,
- FLT_OTEL_RET_IGNORE = 0,
- FLT_OTEL_RET_OK = 1,
-};
-
-/* Dump or iterate a named configuration list for debugging. */
-#define FLT_OTEL_DBG_LIST(d,m,p,t,v,f) \
- do { \
- if (LIST_ISEMPTY(&((d)->m##s))) { \
- OTELC_DBG(DEBUG, p "- no " #m "s " t); \
- } else { \
- const struct flt_otel_conf_##m *v; \
- \
- OTELC_DBG(DEBUG, p "- " t " " #m "s: %s", \
- flt_otel_list_dump(&((d)->m##s))); \
- list_for_each_entry(v, &((d)->m##s), list) \
- do { f; } while (0); \
- } \
- } while (0)
-
-
-extern const char *otel_flt_id;
-extern uint64_t flt_otel_drop_cnt;
-extern struct flt_ops flt_otel_ops;
-
-
-/* Check whether the OTel filter is disabled for a stream. */
-bool flt_otel_is_disabled(const struct filter *f FLT_OTEL_DBG_ARGS(, int event));
-
-#endif /* _OTEL_FILTER_H_ */
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef _OTEL_GROUP_H_
-#define _OTEL_GROUP_H_
-
-#define FLT_OTEL_ACTION_GROUP "otel-group"
-
-/* Argument indices for the otel-group action rule. */
-enum FLT_OTEL_ARG_enum {
- FLT_OTEL_ARG_FILTER_ID = 0,
- FLT_OTEL_ARG_GROUP_ID,
-
- FLT_OTEL_ARG_FLT_CONF = 0,
- FLT_OTEL_ARG_CONF,
- FLT_OTEL_ARG_GROUP,
-};
-
-/*
- * A description of the macro arguments can be found in the structure
- * flt_otel_group_data definition
- */
-#define FLT_OTEL_GROUP_DEFINES \
- FLT_OTEL_GROUP_DEF(ACT_F_TCP_REQ_CON, SMP_VAL_FE_CON_ACC, SMP_OPT_DIR_REQ) \
- FLT_OTEL_GROUP_DEF(ACT_F_TCP_REQ_SES, SMP_VAL_FE_SES_ACC, SMP_OPT_DIR_REQ) \
- FLT_OTEL_GROUP_DEF(ACT_F_TCP_REQ_CNT, SMP_VAL_FE_REQ_CNT, SMP_OPT_DIR_REQ) \
- FLT_OTEL_GROUP_DEF(ACT_F_TCP_RES_CNT, SMP_VAL_BE_RES_CNT, SMP_OPT_DIR_RES) \
- FLT_OTEL_GROUP_DEF(ACT_F_HTTP_REQ, SMP_VAL_FE_HRQ_HDR, SMP_OPT_DIR_REQ) \
- FLT_OTEL_GROUP_DEF(ACT_F_HTTP_RES, SMP_VAL_BE_HRS_HDR, SMP_OPT_DIR_RES)
-
-/* Per-action-from metadata mapping action types to fetch directions. */
-struct flt_otel_group_data {
- enum act_from act_from; /* ACT_F_* */
- uint smp_val; /* Valid FE/BE fetch location. */
- uint smp_opt_dir; /* Fetch direction (request/response). */
-};
-
-#endif /* _OTEL_GROUP_H_ */
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef _OTEL_HTTP_H_
-#define _OTEL_HTTP_H_
-
-#ifndef DEBUG_OTEL
-# define flt_otel_http_headers_dump(...) while (0)
-#else
-/* Dump all HTTP headers from a channel for debugging. */
-void flt_otel_http_headers_dump(const struct channel *chn);
-#endif
-
-/* Extract HTTP headers matching a prefix into a text map. */
-struct otelc_text_map *flt_otel_http_headers_get(struct channel *chn, const char *prefix, size_t len, char **err);
-
-/* Set or replace an HTTP header in a channel. */
-int flt_otel_http_header_set(struct channel *chn, const char *prefix, const char *name, const char *value, char **err);
-
-/* Remove all HTTP headers matching a prefix from a channel. */
-int flt_otel_http_headers_remove(struct channel *chn, const char *prefix, char **err);
-
-#endif /* _OTEL_HTTP_H_ */
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef _OTEL_INCLUDE_H_
-#define _OTEL_INCLUDE_H_
-
-#include <errno.h>
-#include <stdbool.h>
-#include <math.h>
-#include <values.h>
-#ifdef USE_THREAD
-# include <pthread.h>
-#endif
-
-#include <haproxy/api.h>
-#include <haproxy/cfgparse.h>
-#include <haproxy/acl.h>
-#include <haproxy/cli.h>
-#include <haproxy/clock.h>
-#include <haproxy/filters.h>
-#include <haproxy/http_htx.h>
-#include <haproxy/http_rules.h>
-#include <haproxy/log.h>
-#include <haproxy/proxy.h>
-#include <haproxy/sample.h>
-#include <haproxy/tcp_rules.h>
-#include <haproxy/tools.h>
-#include <haproxy/vars.h>
-
-#include <opentelemetry-c-wrapper/include.h>
-
-#include "config.h"
-#include "debug.h"
-#include "define.h"
-#include "cli.h"
-#include "event.h"
-#include "conf.h"
-#include "conf_funcs.h"
-#include "filter.h"
-#include "group.h"
-#include "http.h"
-#include "otelc.h"
-#include "parser.h"
-#include "pool.h"
-#include "scope.h"
-#include "util.h"
-#include "vars.h"
-
-#endif /* _OTEL_INCLUDE_H_ */
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef _OTEL_OTELC_H_
-#define _OTEL_OTELC_H_
-
-/* Inject span context into a text map carrier. */
-int flt_otel_inject_text_map(const struct otelc_span *span, struct otelc_text_map_writer *carrier);
-
-/* Inject span context into an HTTP headers carrier. */
-int flt_otel_inject_http_headers(const struct otelc_span *span, struct otelc_http_headers_writer *carrier);
-
-/* Extract span context from a text map carrier. */
-struct otelc_span_context *flt_otel_extract_text_map(struct otelc_tracer *tracer, struct otelc_text_map_reader *carrier, const struct otelc_text_map *text_map);
-
-/* Extract span context from an HTTP headers carrier. */
-struct otelc_span_context *flt_otel_extract_http_headers(struct otelc_tracer *tracer, struct otelc_http_headers_reader *carrier, const struct otelc_text_map *text_map);
-
-#endif /* _OTEL_OTELC_H_ */
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef _OTEL_PARSER_H_
-#define _OTEL_PARSER_H_
-
-#define FLT_OTEL_SCOPE "OTEL"
-
-/*
- * filter FLT_OTEL_OPT_NAME FLT_OTEL_OPT_FILTER_ID <FLT_OTEL_OPT_FILTER_ID_DEFAULT> FLT_OTEL_OPT_CONFIG <file>
- */
-#define FLT_OTEL_OPT_NAME "opentelemetry"
-#define FLT_OTEL_OPT_FILTER_ID "id"
-#define FLT_OTEL_OPT_FILTER_ID_DEFAULT "otel-filter"
-#define FLT_OTEL_OPT_CONFIG "config"
-
-#define FLT_OTEL_PARSE_SECTION_INSTR_ID "otel-instrumentation"
-#define FLT_OTEL_PARSE_SECTION_GROUP_ID "otel-group"
-#define FLT_OTEL_PARSE_SECTION_SCOPE_ID "otel-scope"
-
-#define FLT_OTEL_PARSE_SPAN_ROOT "root"
-#define FLT_OTEL_PARSE_SPAN_PARENT "parent"
-#define FLT_OTEL_PARSE_SPAN_LINK "link"
-#define FLT_OTEL_PARSE_INSTRUMENT_DESC "desc"
-#define FLT_OTEL_PARSE_INSTRUMENT_VALUE "value"
-#define FLT_OTEL_PARSE_INSTRUMENT_ATTR "attr"
-#define FLT_OTEL_PARSE_INSTRUMENT_UNIT "unit"
-#define FLT_OTEL_PARSE_INSTRUMENT_BOUNDS "bounds"
-#define FLT_OTEL_PARSE_INSTRUMENT_AGGR "aggr"
-#define FLT_OTEL_PARSE_LOG_RECORD_ID "id"
-#define FLT_OTEL_PARSE_LOG_RECORD_EVENT "event"
-#define FLT_OTEL_PARSE_LOG_RECORD_SPAN "span"
-#define FLT_OTEL_PARSE_LOG_RECORD_ATTR "attr"
-#define FLT_OTEL_PARSE_CTX_AUTONAME "-"
-#define FLT_OTEL_PARSE_CTX_IGNORE_NAME '-'
-#define FLT_OTEL_PARSE_CTX_USE_HEADERS "use-headers"
-#define FLT_OTEL_PARSE_CTX_USE_VARS "use-vars"
-#define FLT_OTEL_PARSE_OPTION_HARDERR "hard-errors"
-#define FLT_OTEL_PARSE_OPTION_DISABLED "disabled"
-#define FLT_OTEL_PARSE_OPTION_NOLOGNORM "dontlog-normal"
-
-/*
- * A description of the macro arguments can be found in the structure
- * flt_otel_parse_data definition
- */
-#define FLT_OTEL_PARSE_INSTR_DEFINES \
- FLT_OTEL_PARSE_INSTR_DEF( ID, 0, CHAR, 2, 2, "otel-instrumentation", " <name>") \
- FLT_OTEL_PARSE_INSTR_DEF( ACL, 0, CHAR, 3, 0, "acl", " <name> <criterion> [flags] [operator] <value> ...") \
- FLT_OTEL_PARSE_INSTR_DEF( LOG, 0, CHAR, 2, 0, "log", " { global | <addr> [len <len>] [format <fmt>] <facility> [<level> [<minlevel>]] }") \
- FLT_OTEL_PARSE_INSTR_DEF( CONFIG, 0, NONE, 2, 2, "config", " <file>") \
- FLT_OTEL_PARSE_INSTR_DEF( GROUPS, 0, NONE, 2, 0, "groups", " <name> ...") \
- FLT_OTEL_PARSE_INSTR_DEF( SCOPES, 0, NONE, 2, 0, "scopes", " <name> ...") \
- FLT_OTEL_PARSE_INSTR_DEF( RATE_LIMIT, 0, NONE, 2, 2, "rate-limit", " <value>") \
- FLT_OTEL_PARSE_INSTR_DEF( OPTION, 0, NONE, 2, 2, "option", " { disabled | dontlog-normal | hard-errors }") \
- FLT_OTEL_PARSE_INSTR_DEF(DEBUG_LEVEL, 0, NONE, 2, 2, "debug-level", " <value>")
-
-#define FLT_OTEL_PARSE_GROUP_DEFINES \
- FLT_OTEL_PARSE_GROUP_DEF( ID, 0, CHAR, 2, 2, "otel-group", " <name>") \
- FLT_OTEL_PARSE_GROUP_DEF(SCOPES, 0, NONE, 2, 0, "scopes", " <name> ...")
-
-#ifdef USE_OTEL_VARS
-# define FLT_OTEL_PARSE_SCOPE_INJECT_HELP " <name-prefix> [use-vars] [use-headers]"
-# define FLT_OTEL_PARSE_SCOPE_EXTRACT_HELP " <name-prefix> [use-vars | use-headers]"
-#else
-# define FLT_OTEL_PARSE_SCOPE_INJECT_HELP " <name-prefix> [use-headers]"
-# define FLT_OTEL_PARSE_SCOPE_EXTRACT_HELP " <name-prefix> [use-headers]"
-#endif
-
-/*
- * The first argument of the FLT_OTEL_PARSE_SCOPE_STATUS_DEF() macro is defined
- * as otelc_span_status_t in <opentelemetry-c-wrapper/span.h> .
- */
-#define FLT_OTEL_PARSE_SCOPE_STATUS_DEFINES \
- FLT_OTEL_PARSE_SCOPE_STATUS_DEF(IGNORE, "ignore") \
- FLT_OTEL_PARSE_SCOPE_STATUS_DEF( UNSET, "unset" ) \
- FLT_OTEL_PARSE_SCOPE_STATUS_DEF( OK, "ok" ) \
- FLT_OTEL_PARSE_SCOPE_STATUS_DEF( ERROR, "error" )
-
-/* Sentinel: instrument has not been created yet. */
-#define OTELC_METRIC_INSTRUMENT_UNSET -1
-
-/* Sentinel: instrument creation is in progress by another thread. */
-#define OTELC_METRIC_INSTRUMENT_PENDING -2
-
-/* Sentinel: update-form instrument (re-evaluates an existing one). */
-#define OTELC_METRIC_INSTRUMENT_UPDATE 0xff
-
-#define OTELC_METRIC_AGGREGATION_UNSET -1
-
-/*
- * Observable (asynchronous) instruments are not supported. The OTel SDK
- * invokes their callbacks from an external background thread that is not a
- * HAProxy thread. HAProxy sample fetches rely on internal per-thread-group
- * state and return incorrect results when called from a non-HAProxy thread.
- *
- * Double-precision instruments are not supported because HAProxy sample fetches
- * do not return double values.
- */
-#define FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEFINES \
- FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEF(UPDATE, "update" ) \
- FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEF(COUNTER_UINT64, "cnt_int" ) \
- FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEF(HISTOGRAM_UINT64, "hist_int" ) \
- FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEF(UDCOUNTER_INT64, "udcnt_int") \
- FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEF(GAUGE_INT64, "gauge_int")
-
-/*
- * In case the possibility of working with OpenTelemetry context via HAProxy
- * variables is not used, args_max member of the structure flt_otel_parse_data
- * should be reduced for 'inject' keyword. However, this is not critical
- * because in this case the 'use-vars' argument cannot be entered anyway,
- * so I will not complicate it here with additional definitions.
- */
-#define FLT_OTEL_PARSE_SCOPE_DEFINES \
- FLT_OTEL_PARSE_SCOPE_DEF( ID, 0, CHAR, 2, 2, "otel-scope", " <name>") \
- FLT_OTEL_PARSE_SCOPE_DEF( SPAN, 0, NONE, 2, 7, "span", " <name> [<reference>] [<link>] [root]") \
- FLT_OTEL_PARSE_SCOPE_DEF( LINK, 1, NONE, 2, 0, "link", " <span> ...") \
- FLT_OTEL_PARSE_SCOPE_DEF( ATTRIBUTE, 1, NONE, 3, 0, "attribute", " <key> <sample> ...") \
- FLT_OTEL_PARSE_SCOPE_DEF( EVENT, 1, NONE, 4, 0, "event", " <name> <key> <sample> ...") \
- FLT_OTEL_PARSE_SCOPE_DEF( BAGGAGE, 1, VAR, 3, 0, "baggage", " <key> <sample> ...") \
- FLT_OTEL_PARSE_SCOPE_DEF( INJECT, 1, CTX, 2, 4, "inject", FLT_OTEL_PARSE_SCOPE_INJECT_HELP) \
- FLT_OTEL_PARSE_SCOPE_DEF( EXTRACT, 0, CTX, 2, 3, "extract", FLT_OTEL_PARSE_SCOPE_EXTRACT_HELP) \
- FLT_OTEL_PARSE_SCOPE_DEF( STATUS, 1, NONE, 2, 0, "status", " <code> [<sample> ...]") \
- FLT_OTEL_PARSE_SCOPE_DEF( FINISH, 0, NONE, 2, 0, "finish", " <name> ...") \
- FLT_OTEL_PARSE_SCOPE_DEF( INSTRUMENT, 0, NONE, 3, 0, "instrument", " { update <name> [<attr> ...] | <type> <name> [<aggr>] [<desc>] [<unit>] <value> [<bounds>] }") \
- FLT_OTEL_PARSE_SCOPE_DEF( LOG_RECORD, 0, NONE, 3, 0, "log-record", " <severity> [<id>] [<event>] [<span>] [<attr>] <sample> ...") \
- FLT_OTEL_PARSE_SCOPE_DEF(IDLE_TIMEOUT, 0, NONE, 2, 2, "idle-timeout", " <time>") \
- FLT_OTEL_PARSE_SCOPE_DEF( ACL, 0, CHAR, 3, 0, "acl", " <name> <criterion> [flags] [operator] <value> ...") \
- FLT_OTEL_PARSE_SCOPE_DEF( ON_EVENT, 0, NONE, 2, 0, "otel-event", " <name> [{ if | unless } <condition>]")
-
-/* Invalid character check modes for identifier validation. */
-enum FLT_OTEL_PARSE_INVCHAR_enum {
- FLT_OTEL_PARSE_INVALID_NONE,
- FLT_OTEL_PARSE_INVALID_CHAR,
- FLT_OTEL_PARSE_INVALID_DOM,
- FLT_OTEL_PARSE_INVALID_CTX,
- FLT_OTEL_PARSE_INVALID_VAR,
-};
-
-enum FLT_OTEL_PARSE_INSTR_enum {
-#define FLT_OTEL_PARSE_INSTR_DEF(a,b,c,d,e,f,g) FLT_OTEL_PARSE_INSTR_##a,
- FLT_OTEL_PARSE_INSTR_DEFINES
-#undef FLT_OTEL_PARSE_INSTR_DEF
-};
-
-enum FLT_OTEL_PARSE_GROUP_enum {
-#define FLT_OTEL_PARSE_GROUP_DEF(a,b,c,d,e,f,g) FLT_OTEL_PARSE_GROUP_##a,
- FLT_OTEL_PARSE_GROUP_DEFINES
-#undef FLT_OTEL_PARSE_GROUP_DEF
-};
-
-enum FLT_OTEL_PARSE_SCOPE_enum {
-#define FLT_OTEL_PARSE_SCOPE_DEF(a,b,c,d,e,f,g) FLT_OTEL_PARSE_SCOPE_##a,
- FLT_OTEL_PARSE_SCOPE_DEFINES
-#undef FLT_OTEL_PARSE_SCOPE_DEF
-};
-
-/* Context storage type flags for inject/extract operations. */
-enum FLT_OTEL_CTX_USE_enum {
- FLT_OTEL_CTX_USE_VARS = 1 << 0,
- FLT_OTEL_CTX_USE_HEADERS = 1 << 1,
-};
-
-/* Logging state flags for the OTel filter. */
-enum FLT_OTEL_LOGGING_enum {
- FLT_OTEL_LOGGING_OFF = 0,
- FLT_OTEL_LOGGING_ON = 1 << 0,
- FLT_OTEL_LOGGING_NOLOGNORM = 1 << 1,
-};
-
-/* Keyword metadata used by the configuration section parsers. */
-struct flt_otel_parse_data {
- int keyword; /* Keyword index. */
- bool flag_check_id; /* Whether the group ID must be defined for the keyword. */
- int check_name; /* Checking allowed characters in the name. */
- int args_min; /* The minimum number of arguments required. */
- int args_max; /* The maximum number of arguments allowed. */
- const char *name; /* Keyword name. */
- const char *usage; /* Usage text to be printed in case of an error. */
-};
-
-#define FLT_OTEL_PARSE_KEYWORD(n,s) (strcmp(args[n], (s)) == 0)
-
-#define FLT_OTEL_PARSE_WARNING(f, ...) \
- ha_warning("parsing [%s:%d] : " FLT_OTEL_FMT_TYPE FLT_OTEL_FMT_NAME "'" f "'\n", ##__VA_ARGS__);
-
-#define FLT_OTEL_PARSE_ALERT(f, ...) \
- do { \
- ha_alert("parsing [%s:%d] : " FLT_OTEL_FMT_TYPE FLT_OTEL_FMT_NAME "'" f "'\n", ##__VA_ARGS__); \
- \
- retval |= ERR_ABORT | ERR_ALERT; \
- } while (0)
-
-#define FLT_OTEL_POST_PARSE_ALERT(f, ...) \
- FLT_OTEL_PARSE_ALERT(f, flt_otel_current_config->cfg_file, ##__VA_ARGS__)
-
-#define FLT_OTEL_PARSE_ERR(e,f, ...) \
- do { \
- if (*(e) == NULL) \
- (void)memprintf((e), f, ##__VA_ARGS__); \
- \
- retval |= ERR_ABORT | ERR_ALERT; \
- } while (0)
-
-#define FLT_OTEL_PARSE_IFERR_ALERT() \
- do { \
- if (err == NULL) \
- break; \
- \
- FLT_OTEL_PARSE_ALERT("%s", file, line, err); \
- FLT_OTEL_ERR_FREE(err); \
- } while (0)
-
-#endif /* _OTEL_PARSER_H_ */
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef _OTEL_POOL_H_
-#define _OTEL_POOL_H_
-
-#define FLT_OTEL_POOL_INIT(p,n,s,r) \
- do { \
- if (((r) == FLT_OTEL_RET_OK) && ((p) == NULL)) { \
- (p) = create_pool(n, (s), MEM_F_SHARED); \
- if ((p) == NULL) \
- (r) = FLT_OTEL_RET_ERROR; \
- \
- OTELC_DBG(DEBUG, #p " %p %u", (p), FLT_OTEL_DEREF((p), size, 0)); \
- } \
- } while (0)
-
-#define FLT_OTEL_POOL_DESTROY(p) \
- do { \
- if ((p) != NULL) { \
- OTELC_DBG(DEBUG, #p " %p %u", (p), (p)->size); \
- \
- pool_destroy(p); \
- (p) = NULL; \
- } \
- } while (0)
-
-
-extern struct pool_head *pool_head_otel_scope_span __read_mostly;
-extern struct pool_head *pool_head_otel_scope_context __read_mostly;
-extern struct pool_head *pool_head_otel_runtime_context __read_mostly;
-extern struct pool_head *pool_head_otel_span_context __read_mostly;
-
-
-/* Allocate memory from a pool with optional zeroing. */
-void *flt_otel_pool_alloc(struct pool_head *pool, size_t size, bool flag_clear, char **err);
-
-/* Duplicate a string into pool-allocated memory. */
-void *flt_otel_pool_strndup(struct pool_head *pool, const char *s, size_t size, char **err);
-
-/* Release pool-allocated memory and clear the pointer. */
-void flt_otel_pool_free(struct pool_head *pool, void **ptr);
-
-/* Initialize OTel filter memory pools. */
-int flt_otel_pool_init(void);
-
-/* Destroy OTel filter memory pools. */
-void flt_otel_pool_destroy(void);
-
-/* Log debug information about OTel filter memory pools. */
-#ifndef DEBUG_OTEL
-# define flt_otel_pool_info() while (0)
-#else
-void flt_otel_pool_info(void);
-#endif
-
-/* Allocate a trash buffer with optional zeroing. */
-struct buffer *flt_otel_trash_alloc(bool flag_clear, char **err);
-
-/* Release a trash buffer and clear the pointer. */
-void flt_otel_trash_free(struct buffer **ptr);
-
-#endif /* _OTEL_POOL_H_ */
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef _OTEL_SCOPE_H_
-#define _OTEL_SCOPE_H_
-
-#define FLT_OTEL_SCOPE_SPAN_FINISH_REQ "*req*"
-#define FLT_OTEL_SCOPE_SPAN_FINISH_RES "*res*"
-#define FLT_OTEL_SCOPE_SPAN_FINISH_ALL "*"
-
-#define FLT_OTEL_RT_CTX(p) ((struct flt_otel_runtime_context *)(p))
-
-#define FLT_OTEL_DBG_SCOPE_SPAN(h,p) \
- OTELC_DBG(DEBUG, h "%p:{ '%s' %zu %u %hhu %p %p %p }", (p), \
- FLT_OTEL_STR_HDR_ARGS(p, id), (p)->smp_opt_dir, \
- (p)->flag_finish, (p)->span, (p)->ref_span, (p)->ref_ctx)
-
-#define FLT_OTEL_DBG_SCOPE_CONTEXT(h,p) \
- OTELC_DBG(DEBUG, h "%p:{ '%s' %zu %u %hhu %p }", (p), \
- FLT_OTEL_STR_HDR_ARGS(p, id), (p)->smp_opt_dir, \
- (p)->flag_finish, (p)->context)
-
-#define FLT_OTEL_DBG_SCOPE_DATA_EVENT(h,p) \
- OTELC_DBG(DEBUG, h "%p:{ '%s' %p %zu %zu %s }", &(p), \
- (p).name, (p).attr, (p).cnt, (p).size, \
- flt_otel_list_dump(&((p).list)))
-
-#define FLT_OTEL_DBG_SCOPE_DATA_STATUS(h,p) \
- OTELC_DBG(DEBUG, h "%p:{ %d '%s' }", (p), (p)->code, OTELC_STR_ARG((p)->description))
-
-#define FLT_OTEL_DBG_SCOPE_DATA_KV_FMT "%p:{ %p %zu %zu }"
-#define FLT_OTEL_DBG_SCOPE_DATA_KV_ARGS(p) &(p), (p).attr, (p).cnt, (p).size
-#define FLT_OTEL_DBG_SCOPE_DATA(h,p) \
- OTELC_DBG(DEBUG, h "%p:{ " FLT_OTEL_DBG_SCOPE_DATA_KV_FMT " " FLT_OTEL_DBG_SCOPE_DATA_KV_FMT " %s %s }", (p), \
- FLT_OTEL_DBG_SCOPE_DATA_KV_ARGS((p)->baggage), FLT_OTEL_DBG_SCOPE_DATA_KV_ARGS((p)->attributes), \
- flt_otel_list_dump(&((p)->events)), flt_otel_list_dump(&((p)->links)))
-
-#define FLT_OTEL_DBG_RUNTIME_CONTEXT(h,p) \
- OTELC_DBG(DEBUG, h "%p:{ %p %p '%s' %hhu %hhu 0x%02hhx 0x%08x %u %d %s %s }", (p), \
- (p)->stream, (p)->filter, (p)->uuid, (p)->flag_harderr, (p)->flag_disabled, \
- (p)->logging, (p)->analyzers, (p)->idle_timeout, (p)->idle_exp, \
- flt_otel_list_dump(&((p)->spans)), flt_otel_list_dump(&((p)->contexts)))
-
-/* Anonymous struct containing a const string pointer and its length. */
-#define FLT_OTEL_CONST_STR_HDR(p) \
- struct { \
- const char *p; \
- size_t p##_len; \
- }
-
-
-/* Growable key-value array for span attributes or baggage. */
-struct flt_otel_scope_data_kv {
- struct otelc_kv *attr; /* Key-value array for storing attributes. */
- size_t cnt; /* Number of currently used array elements. */
- size_t size; /* Total number of array elements. */
-};
-
-/* Named event with its own key-value attribute array. */
-struct flt_otel_scope_data_event {
- char *name; /* Event name, not used for other data types. */
- struct otelc_kv *attr; /* Key-value array for storing attributes. */
- size_t cnt; /* Number of currently used array elements. */
- size_t size; /* Total number of array elements. */
- struct list list; /* Used to chain this structure. */
-};
-
-/* Span link referencing another span or span context. */
-struct flt_otel_scope_data_link {
- struct otelc_span *span; /* Linked span, or NULL. */
- struct otelc_span_context *context; /* Linked span context, or NULL. */
- struct list list; /* Used to chain this structure. */
-};
-
-/* Span status code and description. */
-struct flt_otel_scope_data_status {
- int code; /* OTELC_SPAN_STATUS_* value. */
- char *description; /* Span status description string. */
-};
-
-/* Aggregated runtime data collected during scope execution. */
-struct flt_otel_scope_data {
- struct flt_otel_scope_data_kv baggage; /* Defined scope baggage. */
- struct flt_otel_scope_data_kv attributes; /* Defined scope attributes. */
- struct list events; /* Defined scope events. */
- struct list links; /* Defined scope links. */
- struct flt_otel_scope_data_status status; /* Defined scope status. */
-};
-
-/* flt_otel_runtime_context->spans */
-struct flt_otel_scope_span {
- FLT_OTEL_CONST_STR_HDR(id); /* The span operation name/len. */
- uint smp_opt_dir; /* SMP_OPT_DIR_RE(Q|S) */
- bool flag_finish; /* Whether the span is marked for completion. */
- struct otelc_span *span; /* The current span. */
- struct otelc_span *ref_span; /* Span to which the current span refers. */
- struct otelc_span_context *ref_ctx; /* Span context to which the current span refers. */
- struct list list; /* Used to chain this structure. */
-};
-
-/* flt_otel_runtime_context->contexts */
-struct flt_otel_scope_context {
- FLT_OTEL_CONST_STR_HDR(id); /* The span context name/len. */
- uint smp_opt_dir; /* SMP_OPT_DIR_RE(Q|S) */
- bool flag_finish; /* Whether the span context is marked for completion. */
- struct otelc_span_context *context; /* The current span context. */
- struct list list; /* Used to chain this structure. */
-};
-
-/* The runtime filter context attached to a stream. */
-struct flt_otel_runtime_context {
- struct stream *stream; /* The stream to which the filter is attached. */
- struct filter *filter; /* The OpenTelemetry filter. */
- char uuid[40]; /* Randomly generated UUID. */
- bool flag_harderr; /* [0 1] */
- bool flag_disabled; /* [0 1] */
- uint8_t logging; /* [0 1 3] */
- uint analyzers; /* Executed channel analyzers. */
- uint idle_timeout; /* Idle timeout interval in milliseconds (0 = off). */
- int idle_exp; /* Tick at which the next idle timeout fires. */
- struct list spans; /* The scope spans. */
- struct list contexts; /* The scope contexts. */
-};
-
-
-#ifndef DEBUG_OTEL
-# define flt_otel_scope_data_dump(...) while (0)
-#else
-/* Dump scope data contents for debugging. */
-void flt_otel_scope_data_dump(const struct flt_otel_scope_data *data);
-#endif
-
-/* Allocate and initialize a runtime context for a stream. */
-struct flt_otel_runtime_context *flt_otel_runtime_context_init(struct stream *s, struct filter *f, char **err);
-
-/* Free the runtime context attached to a filter. */
-void flt_otel_runtime_context_free(struct filter *f);
-
-/* Allocate and initialize a scope span in the runtime context. */
-struct flt_otel_scope_span *flt_otel_scope_span_init(struct flt_otel_runtime_context *rt_ctx, const char *id, size_t id_len, const char *ref_id, size_t ref_id_len, uint dir, char **err);
-
-/* Free a scope span and remove it from the runtime context. */
-void flt_otel_scope_span_free(struct flt_otel_scope_span **ptr);
-
-/* Allocate and initialize a scope context in the runtime context. */
-struct flt_otel_scope_context *flt_otel_scope_context_init(struct flt_otel_runtime_context *rt_ctx, struct otelc_tracer *tracer, const char *id, size_t id_len, const struct otelc_text_map *text_map, uint dir, char **err);
-
-/* Free a scope context and remove it from the runtime context. */
-void flt_otel_scope_context_free(struct flt_otel_scope_context **ptr);
-
-/* Initialize scope data arrays and lists. */
-void flt_otel_scope_data_init(struct flt_otel_scope_data *ptr);
-
-/* Free all scope data contents. */
-void flt_otel_scope_data_free(struct flt_otel_scope_data *ptr);
-
-/* Mark a span for finishing by name in the runtime context. */
-int flt_otel_scope_finish_mark(const struct flt_otel_runtime_context *rt_ctx, const char *id, size_t id_len);
-
-/* End all spans that have been marked for finishing. */
-void flt_otel_scope_finish_marked(const struct flt_otel_runtime_context *rt_ctx, const struct timespec *ts_finish);
-
-/* Free scope spans and contexts no longer needed by a channel. */
-void flt_otel_scope_free_unused(struct flt_otel_runtime_context *rt_ctx, struct channel *chn);
-
-#endif /* _OTEL_SCOPE_H_ */
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef _OTEL_UTIL_H_
-#define _OTEL_UTIL_H_
-
-#define FLT_OTEL_HTTP_METH_DEFINES \
- FLT_OTEL_HTTP_METH_DEF(OPTIONS) \
- FLT_OTEL_HTTP_METH_DEF(GET) \
- FLT_OTEL_HTTP_METH_DEF(HEAD) \
- FLT_OTEL_HTTP_METH_DEF(POST) \
- FLT_OTEL_HTTP_METH_DEF(PUT) \
- FLT_OTEL_HTTP_METH_DEF(DELETE) \
- FLT_OTEL_HTTP_METH_DEF(TRACE) \
- FLT_OTEL_HTTP_METH_DEF(CONNECT)
-
-/* Iterate over all OTel filter configurations across all proxies. */
-#define FLT_OTEL_PROXIES_LIST_START() \
- do { \
- struct flt_conf *fconf; \
- struct proxy *px; \
- \
- for (px = proxies_list; px != NULL; px = px->next) \
- list_for_each_entry(fconf, &(px->filter_configs), list) \
- if (fconf->id == otel_flt_id) { \
- struct flt_otel_conf *conf = fconf->conf;
-#define FLT_OTEL_PROXIES_LIST_END() \
- } \
- } while (0)
-
-#ifdef DEBUG_OTEL
-# define FLT_OTEL_ARGS_DUMP() do { if (otelc_dbg_level & (1 << OTELC_DBG_LEVEL_LOG)) flt_otel_args_dump((const char **)args); } while (0)
-#else
-# define FLT_OTEL_ARGS_DUMP() while (0)
-#endif
-
-
-#ifndef DEBUG_OTEL
-# define flt_otel_filters_dump() while (0)
-#else
-/* Dump configuration arguments for debugging. */
-void flt_otel_args_dump(const char **args);
-
-/* Dump all OTel filter configurations across all proxies. */
-void flt_otel_filters_dump(void);
-
-/* Return a label string identifying a channel direction. */
-const char *flt_otel_chn_label(const struct channel *chn);
-
-/* Return the proxy mode string for a stream. */
-const char *flt_otel_pr_mode(const struct stream *s);
-
-/* Return the stream processing position as a string. */
-const char *flt_otel_stream_pos(const struct stream *s);
-
-/* Return the filter type string for a filter instance. */
-const char *flt_otel_type(const struct filter *f);
-
-/* Return the analyzer name string for an analyzer bit. */
-const char *flt_otel_analyzer(uint an_bit);
-
-/* Dump a linked list of configuration items as a string. */
-const char *flt_otel_list_dump(const struct list *head);
-#endif
-
-/* Count the number of non-NULL arguments in an argument array. */
-int flt_otel_args_count(const char **args);
-
-/* Concatenate argument array elements into a single string. */
-int flt_otel_args_concat(const char **args, int idx, int n, char **str);
-
-/* Comparator for qsort: ascending order of doubles with epsilon tolerance. */
-int flt_otel_qsort_compar_double(const void *a, const void *b);
-
-/* Parse a string to double with range validation. */
-bool flt_otel_strtod(const char *nptr, double *value, double limit_min, double limit_max, char **err);
-
-/* Parse a string to int64_t with range validation. */
-bool flt_otel_strtoll(const char *nptr, int64_t *value, int64_t limit_min, int64_t limit_max, char **err);
-
-/* Convert sample data to a string representation. */
-int flt_otel_sample_to_str(const struct sample_data *data, char *value, size_t size, char **err);
-
-/* Convert sample data to an OTel value. */
-int flt_otel_sample_to_value(const char *key, const struct sample_data *data, struct otelc_value *value, char **err);
-
-/* Add a key-value pair to a growable key-value array. */
-int flt_otel_sample_add_kv(struct flt_otel_scope_data_kv *kv, const char *key, const struct otelc_value *value);
-
-/* Evaluate a sample definition into an OTel value. */
-int flt_otel_sample_eval(struct stream *s, uint dir, struct flt_otel_conf_sample *sample, bool flag_native, struct otelc_value *value, char **err);
-
-/* Evaluate a sample expression and add the result to scope data. */
-int flt_otel_sample_add(struct stream *s, uint dir, struct flt_otel_conf_sample *sample, struct flt_otel_scope_data *data, int type, char **err);
-
-#endif /* _OTEL_UTIL_H_ */
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#ifndef _OTEL_VARS_H_
-#define _OTEL_VARS_H_
-
-#define FLT_OTEL_VARS_SCOPE "txn"
-#define FLT_OTEL_VAR_CHAR_DASH 'D'
-#define FLT_OTEL_VAR_CHAR_SPACE 'S'
-
-#ifndef USE_OTEL_VARS_NAME
-# define FLT_OTEL_VAR_CTX_SIZE int8_t
-
-/* Context buffer for storing a single variable value during iteration. */
-struct flt_otel_ctx {
- char value[BUFSIZ]; /* Variable value string. */
- int value_len; /* Length of the value string. */
-};
-
-/* Callback type invoked for each context variable during iteration. */
-typedef int (*flt_otel_ctx_loop_cb)(struct sample *, size_t, const char *, const char *, const char *, FLT_OTEL_VAR_CTX_SIZE, char **, void *);
-#endif /* !USE_OTEL_VARS_NAME */
-
-
-#ifndef DEBUG_OTEL
-# define flt_otel_vars_dump(...) while (0)
-#else
-/* Dump all OTel-related variables for a stream. */
-void flt_otel_vars_dump(struct stream *s);
-#endif
-
-/* Register a HAProxy variable for OTel context storage. */
-int flt_otel_var_register(const char *scope, const char *prefix, const char *name, char **err);
-
-/* Set an OTel context variable on a stream. */
-int flt_otel_var_set(struct stream *s, const char *scope, const char *prefix, const char *name, const char *value, uint opt, char **err);
-
-/* Unset all OTel context variables matching a prefix on a stream. */
-int flt_otel_vars_unset(struct stream *s, const char *scope, const char *prefix, uint opt, char **err);
-
-/* Retrieve all OTel context variables matching a prefix into a text map. */
-struct otelc_text_map *flt_otel_vars_get(struct stream *s, const char *scope, const char *prefix, uint opt, char **err);
-
-#endif /* _OTEL_VARS_H_ */
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "../include/include.h"
-
-
-/***
- * NAME
- * flt_otel_cli_set_msg - CLI response message setter
- *
- * SYNOPSIS
- * static int flt_otel_cli_set_msg(struct appctx *appctx, char *err, char *msg)
- *
- * ARGUMENTS
- * appctx - CLI application context
- * err - error message string (or NULL)
- * msg - informational message string (or NULL)
- *
- * DESCRIPTION
- * Sets the CLI response message and state for the given <appctx>. If <err>
- * is non-NULL, it is passed to cli_dynerr() and <msg> is freed; otherwise
- * <msg> is passed to cli_dynmsg() at LOG_INFO severity. When neither message
- * is available, the function returns 0 without changing state.
- *
- * RETURN VALUE
- * Returns 1 when a message was set, or 0 when both pointers were NULL.
- */
-static int flt_otel_cli_set_msg(struct appctx *appctx, char *err, char *msg)
-{
- OTELC_FUNC("%p, %p, %p", appctx, err, msg);
-
- if ((appctx == NULL) || ((err == NULL) && (msg == NULL)))
- OTELC_RETURN_INT(0);
-
- if (err != NULL) {
- OTELC_DBG(INFO, "err(%d): \"%s\"", appctx->st0, err);
-
- OTELC_SFREE(msg);
- OTELC_RETURN_INT(cli_dynerr(appctx, err));
- }
-
- OTELC_DBG(INFO, "msg(%d): \"%s\"", appctx->st0, msg);
-
- OTELC_RETURN_INT(cli_dynmsg(appctx, LOG_INFO, msg));
-}
-
-
-#ifdef DEBUG_OTEL
-
-/***
- * NAME
- * flt_otel_cli_parse_debug - CLI debug level handler
- *
- * SYNOPSIS
- * static int flt_otel_cli_parse_debug(char **args, char *payload, struct appctx *appctx, void *private)
- *
- * ARGUMENTS
- * args - CLI command arguments array
- * payload - CLI command payload string
- * appctx - CLI application context
- * private - unused private data pointer
- *
- * DESCRIPTION
- * Handles the "otel debug [level]" CLI command. When a level argument is
- * provided in <args[2]>, parses it as an integer in the range
- * [0, OTELC_DBG_LEVEL_MASK] and atomically stores it as the global debug
- * level. Setting a level requires admin access level. When no argument is
- * given, reports the current debug level. The response message includes the
- * debug level in both decimal and hexadecimal format.
- *
- * RETURN VALUE
- * Returns 1, or 0 on memory allocation failure.
- */
-static int flt_otel_cli_parse_debug(char **args, char *payload, struct appctx *appctx, void *private)
-{
- char *err = NULL, *msg = NULL;
-
- OTELC_FUNC("%p, \"%s\", %p, %p", args, OTELC_STR_ARG(payload), appctx, private);
-
- FLT_OTEL_ARGS_DUMP();
-
- if (FLT_OTEL_ARG_ISVALID(2)) {
- int64_t value;
-
- if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
- OTELC_RETURN_INT(1);
-
- if (flt_otel_strtoll(args[2], &value, 0, OTELC_DBG_LEVEL_MASK, &err)) {
- _HA_ATOMIC_STORE(&otelc_dbg_level, (int)value);
-
- (void)memprintf(&msg, FLT_OTEL_CLI_CMD " : debug level set to %d (0x%04x)", (int)value, (int)value);
- }
- } else {
- int value = _HA_ATOMIC_LOAD(&otelc_dbg_level);
-
- (void)memprintf(&msg, FLT_OTEL_CLI_CMD " : current debug level is %d (0x%04x)", value, value);
- }
-
- OTELC_RETURN_INT(flt_otel_cli_set_msg(appctx, err, msg));
-}
-
-#endif /* DEBUG_OTEL */
-
-
-/***
- * NAME
- * flt_otel_cli_parse_disabled - CLI enable/disable handler
- *
- * SYNOPSIS
- * static int flt_otel_cli_parse_disabled(char **args, char *payload, struct appctx *appctx, void *private)
- *
- * ARGUMENTS
- * args - CLI command arguments array
- * payload - CLI command payload string
- * appctx - CLI application context
- * private - boolean flag cast to pointer (1 = disable, 0 = enable)
- *
- * DESCRIPTION
- * Handles the "otel enable" and "otel disable" CLI commands. The <private>
- * parameter determines the action: a value of 1 disables the filter, 0
- * enables it. Requires admin access level. The flag_disabled field is
- * atomically updated for all OTel filter instances across all proxies.
- *
- * RETURN VALUE
- * Returns 1, or 0 if no OTel filter instances are configured or on memory
- * allocation failure.
- */
-static int flt_otel_cli_parse_disabled(char **args, char *payload, struct appctx *appctx, void *private)
-{
- char *msg = NULL;
- bool value = (uintptr_t)private;
-
- OTELC_FUNC("%p, \"%s\", %p, %p", args, OTELC_STR_ARG(payload), appctx, private);
-
- FLT_OTEL_ARGS_DUMP();
-
- if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
- OTELC_RETURN_INT(1);
-
- FLT_OTEL_PROXIES_LIST_START() {
- _HA_ATOMIC_STORE(&(conf->instr->flag_disabled), value);
-
- (void)memprintf(&msg, "%s%s" FLT_OTEL_CLI_CMD " : filter %sabled", FLT_OTEL_CLI_MSG_CAT(msg), value ? "dis" : "en");
- } FLT_OTEL_PROXIES_LIST_END();
-
- OTELC_RETURN_INT(flt_otel_cli_set_msg(appctx, NULL, msg));
-}
-
-
-/***
- * NAME
- * flt_otel_cli_parse_option - CLI error mode handler
- *
- * SYNOPSIS
- * static int flt_otel_cli_parse_option(char **args, char *payload, struct appctx *appctx, void *private)
- *
- * ARGUMENTS
- * args - CLI command arguments array
- * payload - CLI command payload string
- * appctx - CLI application context
- * private - boolean flag cast to pointer (1 = hard-errors, 0 = soft-errors)
- *
- * DESCRIPTION
- * Handles the "otel hard-errors" and "otel soft-errors" CLI commands. The
- * <private> parameter determines the error mode: a value of 1 enables
- * hard-error mode (filter failure aborts the stream), 0 enables soft-error
- * mode (failures are silently ignored). Requires admin access level. The
- * flag_harderr field is atomically updated for all OTel filter instances
- * across all proxies.
- *
- * RETURN VALUE
- * Returns 1, or 0 if no OTel filter instances are configured or on memory
- * allocation failure.
- */
-static int flt_otel_cli_parse_option(char **args, char *payload, struct appctx *appctx, void *private)
-{
- char *msg = NULL;
- bool value = (uintptr_t)private;
-
- OTELC_FUNC("%p, \"%s\", %p, %p", args, OTELC_STR_ARG(payload), appctx, private);
-
- FLT_OTEL_ARGS_DUMP();
-
- if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
- OTELC_RETURN_INT(1);
-
- FLT_OTEL_PROXIES_LIST_START() {
- _HA_ATOMIC_STORE(&(conf->instr->flag_harderr), value);
-
- (void)memprintf(&msg, "%s%s" FLT_OTEL_CLI_CMD " : filter set %s-errors", FLT_OTEL_CLI_MSG_CAT(msg), value ? "hard" : "soft");
- } FLT_OTEL_PROXIES_LIST_END();
-
- OTELC_RETURN_INT(flt_otel_cli_set_msg(appctx, NULL, msg));
-}
-
-
-/***
- * NAME
- * flt_otel_cli_parse_logging - CLI logging state handler
- *
- * SYNOPSIS
- * static int flt_otel_cli_parse_logging(char **args, char *payload, struct appctx *appctx, void *private)
- *
- * ARGUMENTS
- * args - CLI command arguments array
- * payload - CLI command payload string
- * appctx - CLI application context
- * private - unused private data pointer
- *
- * DESCRIPTION
- * Handles the "otel logging [state]" CLI command. When a state argument is
- * provided in <args[2]>, it is matched against "off", "on", or "dontlog-normal"
- * and the logging field is atomically updated for all OTel filter instances.
- * Setting a value requires admin access level. When no argument is given,
- * reports the current logging state for all instances. Invalid values
- * produce an error with the accepted options listed.
- *
- * RETURN VALUE
- * Returns 1, or 0 if no OTel filter instances are configured (and no error
- * occurred) or on memory allocation failure.
- */
-static int flt_otel_cli_parse_logging(char **args, char *payload, struct appctx *appctx, void *private)
-{
- char *err = NULL, *msg = NULL;
- bool flag_set = false;
- uint8_t value;
-
- OTELC_FUNC("%p, \"%s\", %p, %p", args, OTELC_STR_ARG(payload), appctx, private);
-
- FLT_OTEL_ARGS_DUMP();
-
- if (FLT_OTEL_ARG_ISVALID(2)) {
- if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
- OTELC_RETURN_INT(1);
-
- if (strcasecmp(args[2], FLT_OTEL_CLI_LOGGING_OFF) == 0) {
- flag_set = true;
- value = FLT_OTEL_LOGGING_OFF;
- }
- else if (strcasecmp(args[2], FLT_OTEL_CLI_LOGGING_ON) == 0) {
- flag_set = true;
- value = FLT_OTEL_LOGGING_ON;
- }
- else if (strcasecmp(args[2], FLT_OTEL_CLI_LOGGING_NOLOGNORM) == 0) {
- flag_set = true;
- value = FLT_OTEL_LOGGING_ON | FLT_OTEL_LOGGING_NOLOGNORM;
- }
- else {
- (void)memprintf(&err, "'%s' : invalid value, use <" FLT_OTEL_CLI_LOGGING_OFF " | " FLT_OTEL_CLI_LOGGING_ON " | " FLT_OTEL_CLI_LOGGING_NOLOGNORM ">", args[2]);
- }
-
- if (flag_set) {
- FLT_OTEL_PROXIES_LIST_START() {
- _HA_ATOMIC_STORE(&(conf->instr->logging), value);
-
- (void)memprintf(&msg, "%s%s" FLT_OTEL_CLI_CMD " : logging is %s", FLT_OTEL_CLI_MSG_CAT(msg), FLT_OTEL_CLI_LOGGING_STATE(value));
- } FLT_OTEL_PROXIES_LIST_END();
- }
- } else {
- FLT_OTEL_PROXIES_LIST_START() {
- value = _HA_ATOMIC_LOAD(&(conf->instr->logging));
-
- (void)memprintf(&msg, "%s%s" FLT_OTEL_CLI_CMD " : logging is currently %s", FLT_OTEL_CLI_MSG_CAT(msg), FLT_OTEL_CLI_LOGGING_STATE(value));
- } FLT_OTEL_PROXIES_LIST_END();
- }
-
- OTELC_RETURN_INT(flt_otel_cli_set_msg(appctx, err, msg));
-}
-
-
-/***
- * NAME
- * flt_otel_cli_parse_rate - CLI rate limit handler
- *
- * SYNOPSIS
- * static int flt_otel_cli_parse_rate(char **args, char *payload, struct appctx *appctx, void *private)
- *
- * ARGUMENTS
- * args - CLI command arguments array
- * payload - CLI command payload string
- * appctx - CLI application context
- * private - unused private data pointer
- *
- * DESCRIPTION
- * Handles the "otel rate [value]" CLI command. When a value argument is
- * provided in <args[2]>, it is parsed as a floating-point number in the
- * range [0.0, 100.0], converted to a fixed-point uint32_t representation,
- * and atomically stored as the rate limit for all OTel filter instances.
- * Setting a value requires admin access level. When no argument is given,
- * reports the current rate limit percentage for all instances.
- *
- * RETURN VALUE
- * Returns 1, or 0 if no OTel filter instances are configured (and no error
- * occurred) or on memory allocation failure.
- */
-static int flt_otel_cli_parse_rate(char **args, char *payload, struct appctx *appctx, void *private)
-{
- char *err = NULL, *msg = NULL;
-
- OTELC_FUNC("%p, \"%s\", %p, %p", args, OTELC_STR_ARG(payload), appctx, private);
-
- FLT_OTEL_ARGS_DUMP();
-
- if (FLT_OTEL_ARG_ISVALID(2)) {
- double value;
-
- if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
- OTELC_RETURN_INT(1);
-
- if (flt_otel_strtod(args[2], &value, 0.0, 100.0, &err)) {
- FLT_OTEL_PROXIES_LIST_START() {
- _HA_ATOMIC_STORE(&(conf->instr->rate_limit), FLT_OTEL_FLOAT_U32(value));
-
- (void)memprintf(&msg, "%s%s" FLT_OTEL_CLI_CMD " : rate limit set to %.2f", FLT_OTEL_CLI_MSG_CAT(msg), value);
- } FLT_OTEL_PROXIES_LIST_END();
- }
- } else {
- FLT_OTEL_PROXIES_LIST_START() {
- uint32_t value = _HA_ATOMIC_LOAD(&(conf->instr->rate_limit));
-
- (void)memprintf(&msg, "%s%s" FLT_OTEL_CLI_CMD " : current rate limit is %.2f", FLT_OTEL_CLI_MSG_CAT(msg), FLT_OTEL_U32_FLOAT(value));
- } FLT_OTEL_PROXIES_LIST_END();
- }
-
- OTELC_RETURN_INT(flt_otel_cli_set_msg(appctx, err, msg));
-}
-
-
-/***
- * NAME
- * flt_otel_cli_parse_status - CLI status display handler
- *
- * SYNOPSIS
- * static int flt_otel_cli_parse_status(char **args, char *payload, struct appctx *appctx, void *private)
- *
- * ARGUMENTS
- * args - CLI command arguments array
- * payload - CLI command payload string
- * appctx - CLI application context
- * private - unused private data pointer
- *
- * DESCRIPTION
- * Handles the "otel status" CLI command. Builds a formatted status report
- * for all OTel filter instances across all proxies. The report includes
- * the library version, proxy name, configuration file path, group and scope
- * counts, disable counts, instrumentation ID, tracer and meter state, rate
- * limit, error mode, disabled state, logging state, and analyzer bits. When
- * DEBUG_OTEL is enabled, the current debug level is also included.
- *
- * RETURN VALUE
- * Returns 1, or 0 on memory allocation failure.
- */
-static int flt_otel_cli_parse_status(char **args, char *payload, struct appctx *appctx, void *private)
-{
- const char *nl = "";
- char *msg = NULL;
-
- OTELC_FUNC("%p, \"%s\", %p, %p", args, OTELC_STR_ARG(payload), appctx, private);
-
- FLT_OTEL_ARGS_DUMP();
- flt_otel_filters_dump();
-
- (void)memprintf(&msg, " " FLT_OTEL_OPT_NAME " filter status\n" FLT_OTEL_STR_DASH_78 "\n");
- (void)memprintf(&msg, "%s library: C++ " OTELCPP_VERSION ", C wrapper %s\n", msg, otelc_version());
-#ifdef DEBUG_OTEL
- (void)memprintf(&msg, "%s debug level: 0x%02hhx\n", msg, otelc_dbg_level);
-#endif
- (void)memprintf(&msg, "%s dropped count: %" PRId64 "/%" PRId64 " %" PRIu64 "\n", msg, otelc_processor_dropped_count(0), otelc_processor_dropped_count(1), _HA_ATOMIC_LOAD(&flt_otel_drop_cnt));
-
- FLT_OTEL_PROXIES_LIST_START() {
- struct flt_otel_conf_group *grp;
- struct flt_otel_conf_scope *scp;
- int n_groups = 0, n_scopes = 0;
-
- list_for_each_entry(grp, &(conf->groups), list)
- n_groups++;
- list_for_each_entry(scp, &(conf->scopes), list)
- n_scopes++;
-
- (void)memprintf(&msg, "%s\n%s proxy %s, filter %s\n", msg, nl, px->id, conf->id);
- (void)memprintf(&msg, "%s configuration: %s\n", msg, conf->cfg_file);
- (void)memprintf(&msg, "%s groups/scopes: %d/%d\n\n", msg, n_groups, n_scopes);
- (void)memprintf(&msg, "%s instrumentation %s\n", msg, conf->instr->id);
- (void)memprintf(&msg, "%s configuration: %s\n", msg, conf->instr->config);
- (void)memprintf(&msg, "%s tracer: %s\n", msg, (conf->instr->tracer != NULL) ? "active" : "not initialized");
- (void)memprintf(&msg, "%s meter: %s\n", msg, (conf->instr->meter != NULL) ? "active" : "not initialized");
- (void)memprintf(&msg, "%s logger: %s\n", msg, (conf->instr->logger != NULL) ? "active" : "not initialized");
- (void)memprintf(&msg, "%s rate limit: %.2f %%\n", msg, FLT_OTEL_U32_FLOAT(_HA_ATOMIC_LOAD(&(conf->instr->rate_limit))));
- (void)memprintf(&msg, "%s hard errors: %s\n", msg, FLT_OTEL_STR_FLAG_YN(_HA_ATOMIC_LOAD(&(conf->instr->flag_harderr))));
- (void)memprintf(&msg, "%s disabled: %s\n", msg, FLT_OTEL_STR_FLAG_YN(_HA_ATOMIC_LOAD(&(conf->instr->flag_disabled))));
- (void)memprintf(&msg, "%s logging: %s\n", msg, FLT_OTEL_CLI_LOGGING_STATE(_HA_ATOMIC_LOAD(&(conf->instr->logging))));
- (void)memprintf(&msg, "%s analyzers: %08x", msg, conf->instr->analyzers);
-#ifdef FLT_OTEL_USE_COUNTERS
- (void)memprintf(&msg, "%s\n\n counters\n", msg);
- (void)memprintf(&msg, "%s attached: %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", msg, conf->cnt.attached[0], conf->cnt.attached[1], conf->cnt.attached[2], conf->cnt.attached[3]);
- (void)memprintf(&msg, "%s disabled: %" PRIu64 " %" PRIu64, msg, conf->cnt.disabled[0], conf->cnt.disabled[1]);
-#endif
-
- nl = "\n";
- } FLT_OTEL_PROXIES_LIST_END();
-
- OTELC_RETURN_INT(flt_otel_cli_set_msg(appctx, NULL, msg));
-}
-
-
-/* CLI command table for the OTel filter. */
-static struct cli_kw_list cli_kws = { { }, {
-#ifdef DEBUG_OTEL
- { { FLT_OTEL_CLI_CMD, "debug", NULL }, FLT_OTEL_CLI_CMD " debug [level] : set the OTEL filter debug level (default: get current debug level)", flt_otel_cli_parse_debug, NULL, NULL, NULL, ACCESS_LVL_ADMIN },
-#endif
- { { FLT_OTEL_CLI_CMD, "disable", NULL }, FLT_OTEL_CLI_CMD " disable : disable the OTEL filter", flt_otel_cli_parse_disabled, NULL, NULL, (void *)1, ACCESS_LVL_ADMIN },
- { { FLT_OTEL_CLI_CMD, "enable", NULL }, FLT_OTEL_CLI_CMD " enable : enable the OTEL filter", flt_otel_cli_parse_disabled, NULL, NULL, (void *)0, ACCESS_LVL_ADMIN },
- { { FLT_OTEL_CLI_CMD, "soft-errors", NULL }, FLT_OTEL_CLI_CMD " soft-errors : disable hard-errors mode", flt_otel_cli_parse_option, NULL, NULL, (void *)0, ACCESS_LVL_ADMIN },
- { { FLT_OTEL_CLI_CMD, "hard-errors", NULL }, FLT_OTEL_CLI_CMD " hard-errors : enable hard-errors mode", flt_otel_cli_parse_option, NULL, NULL, (void *)1, ACCESS_LVL_ADMIN },
- { { FLT_OTEL_CLI_CMD, "logging", NULL }, FLT_OTEL_CLI_CMD " logging [state] : set logging state (default: get current logging state)", flt_otel_cli_parse_logging, NULL, NULL, NULL, ACCESS_LVL_ADMIN },
- { { FLT_OTEL_CLI_CMD, "rate", NULL }, FLT_OTEL_CLI_CMD " rate [value] : set the rate limit (default: get current rate value)", flt_otel_cli_parse_rate, NULL, NULL, NULL, ACCESS_LVL_ADMIN },
- { { FLT_OTEL_CLI_CMD, "status", NULL }, FLT_OTEL_CLI_CMD " status : show the OTEL filter status", flt_otel_cli_parse_status, NULL, NULL, NULL, 0 },
- { /* END */ }
-}};
-
-
-/***
- * NAME
- * flt_otel_cli_init - CLI keyword registration
- *
- * SYNOPSIS
- * void flt_otel_cli_init(void)
- *
- * ARGUMENTS
- * This function takes no arguments.
- *
- * DESCRIPTION
- * Registers the OTel filter CLI keywords with the HAProxy CLI subsystem.
- * The keywords include commands for enable/disable, error mode, logging,
- * rate limit, status display, and (when DEBUG_OTEL is defined) debug level
- * management.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_cli_init(void)
-{
- OTELC_FUNC("");
-
- /* Register CLI keywords. */
- cli_register_kw(&cli_kws);
-
- OTELC_RETURN();
-}
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "../include/include.h"
-
-
-/***
- * NAME
- * flt_otel_conf_hdr_init - conf_hdr structure allocation
- *
- * SYNOPSIS
- * struct flt_otel_conf_hdr *flt_otel_conf_hdr_init(const char *id, int line, struct list *head, char **err)
- *
- * ARGUMENTS
- * id - identifier string to duplicate
- * line - configuration file line number
- * head - list to append to (or NULL)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Allocates and initializes a conf_hdr structure. The <id> string is
- * duplicated and stored as the header identifier. If <head> is non-NULL,
- * the structure is appended to the list.
- *
- * RETURN VALUE
- * Returns a pointer to the initialized structure, or NULL on failure.
- */
-FLT_OTEL_CONF_FUNC_INIT(hdr, id, )
-
-
-/***
- * NAME
- * flt_otel_conf_hdr_free - conf_hdr structure deallocation
- *
- * SYNOPSIS
- * void flt_otel_conf_hdr_free(struct flt_otel_conf_hdr **ptr)
- *
- * ARGUMENTS
- * ptr - a pointer to the address of a structure
- *
- * DESCRIPTION
- * Deallocates memory used by the flt_otel_conf_hdr structure and its
- * contents, then removes it from the list of structures of that type.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-FLT_OTEL_CONF_FUNC_FREE(hdr, id,
- FLT_OTEL_DBG_CONF_HDR("- conf_hdr free ", *ptr, id);
-)
-
-
-/***
- * NAME
- * flt_otel_conf_str_init - conf_str structure allocation
- *
- * SYNOPSIS
- * struct flt_otel_conf_str *flt_otel_conf_str_init(const char *id, int line, struct list *head, char **err)
- *
- * ARGUMENTS
- * id - identifier string to duplicate
- * line - configuration file line number
- * head - list to append to (or NULL)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Allocates and initializes a conf_str structure. The <id> string is
- * duplicated and stored as the string value. If <head> is non-NULL, the
- * structure is appended to the list.
- *
- * RETURN VALUE
- * Returns a pointer to the initialized structure, or NULL on failure.
- */
-FLT_OTEL_CONF_FUNC_INIT(str, str, )
-
-
-/***
- * NAME
- * flt_otel_conf_str_free - conf_str structure deallocation
- *
- * SYNOPSIS
- * void flt_otel_conf_str_free(struct flt_otel_conf_str **ptr)
- *
- * ARGUMENTS
- * ptr - a pointer to the address of a structure
- *
- * DESCRIPTION
- * Deallocates memory used by the flt_otel_conf_str structure and its
- * contents, then removes it from the list of structures of that type.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-FLT_OTEL_CONF_FUNC_FREE(str, str,
- FLT_OTEL_DBG_CONF_HDR("- conf_str free ", *ptr, str);
-)
-
-
-/***
- * NAME
- * flt_otel_conf_link_init - conf_link structure allocation
- *
- * SYNOPSIS
- * struct flt_otel_conf_link *flt_otel_conf_link_init(const char *id, int line, struct list *head, char **err)
- *
- * ARGUMENTS
- * id - identifier string to duplicate
- * line - configuration file line number
- * head - list to append to (or NULL)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Allocates and initializes a conf_link structure for a span link
- * reference. The <id> string is duplicated and stored as the linked
- * span name. If <head> is non-NULL, the structure is appended to
- * the list.
- *
- * RETURN VALUE
- * Returns a pointer to the initialized structure, or NULL on failure.
- */
-FLT_OTEL_CONF_FUNC_INIT(link, span, )
-
-
-/***
- * NAME
- * flt_otel_conf_link_free - conf_link structure deallocation
- *
- * SYNOPSIS
- * void flt_otel_conf_link_free(struct flt_otel_conf_link **ptr)
- *
- * ARGUMENTS
- * ptr - a pointer to the address of a structure
- *
- * DESCRIPTION
- * Deallocates memory used by the flt_otel_conf_link structure and its
- * contents, then removes it from the list of structures of that type.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-FLT_OTEL_CONF_FUNC_FREE(link, span,
- FLT_OTEL_DBG_CONF_HDR("- conf_link free ", *ptr, span);
-)
-
-
-/***
- * NAME
- * flt_otel_conf_ph_init - conf_ph placeholder structure allocation
- *
- * SYNOPSIS
- * struct flt_otel_conf_ph *flt_otel_conf_ph_init(const char *id, int line, struct list *head, char **err)
- *
- * ARGUMENTS
- * id - identifier string to duplicate
- * line - configuration file line number
- * head - list to append to (or NULL)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Allocates and initializes a conf_ph (placeholder) structure. The <id>
- * string is duplicated and stored as the placeholder identifier. If <head>
- * is non-NULL, the structure is appended to the list.
- *
- * RETURN VALUE
- * Returns a pointer to the initialized structure, or NULL on failure.
- */
-FLT_OTEL_CONF_FUNC_INIT(ph, id, )
-
-
-/***
- * NAME
- * flt_otel_conf_ph_free - conf_ph structure deallocation
- *
- * SYNOPSIS
- * void flt_otel_conf_ph_free(struct flt_otel_conf_ph **ptr)
- *
- * ARGUMENTS
- * ptr - a pointer to the address of a structure
- *
- * DESCRIPTION
- * Deallocates memory used by the flt_otel_conf_ph structure and its contents,
- * then removes it from the list of structures of that type.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-FLT_OTEL_CONF_FUNC_FREE(ph, id,
- FLT_OTEL_DBG_CONF_HDR("- conf_ph free ", *ptr, id);
-)
-
-
-/***
- * NAME
- * flt_otel_conf_sample_expr_init - conf_sample_expr structure allocation
- *
- * SYNOPSIS
- * struct flt_otel_conf_sample_expr *flt_otel_conf_sample_expr_init(const char *id, int line, struct list *head, char **err)
- *
- * ARGUMENTS
- * id - identifier string to duplicate
- * line - configuration file line number
- * head - list to append to (or NULL)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Allocates and initializes a conf_sample_expr structure. The <id> string is
- * duplicated and stored as the expression value. If <head> is non-NULL, the
- * structure is appended to the list.
- *
- * RETURN VALUE
- * Returns a pointer to the initialized structure, or NULL on failure.
- */
-FLT_OTEL_CONF_FUNC_INIT(sample_expr, fmt_expr, )
-
-
-/***
- * NAME
- * flt_otel_conf_sample_expr_free - conf_sample_expr structure deallocation
- *
- * SYNOPSIS
- * void flt_otel_conf_sample_expr_free(struct flt_otel_conf_sample_expr **ptr)
- *
- * ARGUMENTS
- * ptr - a pointer to the address of a structure
- *
- * DESCRIPTION
- * Deallocates memory used by the flt_otel_conf_sample_expr structure and its
- * contents, then removes it from the list of structures of that type.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-FLT_OTEL_CONF_FUNC_FREE(sample_expr, fmt_expr,
- FLT_OTEL_DBG_CONF_SAMPLE_EXPR("- conf_sample_expr free ", *ptr);
-
- release_sample_expr((*ptr)->expr);
-)
-
-
-/***
- * NAME
- * flt_otel_conf_sample_init - conf_sample structure allocation
- *
- * SYNOPSIS
- * struct flt_otel_conf_sample *flt_otel_conf_sample_init(const char *id, int line, struct list *head, char **err)
- *
- * ARGUMENTS
- * id - identifier string to duplicate
- * line - configuration file line number
- * head - list to append to (or NULL)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Allocates and initializes a conf_sample structure. The <id> string is
- * duplicated and stored as the sample key. If <head> is non-NULL, the
- * structure is appended to the list.
- *
- * RETURN VALUE
- * Returns a pointer to the initialized structure, or NULL on failure.
- */
-FLT_OTEL_CONF_FUNC_INIT(sample, key,
- LIST_INIT(&(retptr->exprs));
- lf_expr_init(&(retptr->lf_expr));
-)
-
-
-/***
- * NAME
- * flt_otel_conf_sample_init_ex - extended sample initialization
- *
- * SYNOPSIS
- * struct flt_otel_conf_sample *flt_otel_conf_sample_init_ex(const char **args, int idx, int n, const struct otelc_value *extra, int line, struct list *head, char **err)
- *
- * ARGUMENTS
- * args - configuration line arguments array
- * idx - position where sample value starts
- * n - maximum number of arguments to concatenate (0 means all)
- * extra - optional extra data (event name or status code)
- * line - configuration file line number
- * head - list to append to (or NULL)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Creates and initializes a conf_sample structure with extended data. Calls
- * flt_otel_conf_sample_init() with <args[idx - 1]> as the sample key to
- * create the base structure, copies <extra> data (event name string or status
- * code integer), concatenates the remaining arguments into the sample value
- * string, and counts the number of sample expressions.
- *
- * RETURN VALUE
- * Returns a pointer to the initialized structure, or NULL on failure.
- */
-struct flt_otel_conf_sample *flt_otel_conf_sample_init_ex(const char **args, int idx, int n, const struct otelc_value *extra, int line, struct list *head, char **err)
-{
- struct flt_otel_conf_sample *retptr = NULL;
-
- OTELC_FUNC("%p, %d, %d, %p, %d, %p, %p:%p", args, idx, n, extra, line, head, OTELC_DPTR_ARGS(err));
-
- OTELC_DBG_VALUE(DEBUG, "extra ", extra);
-
- /* Ensure the sample value is present in the args[] array. */
- if (flt_otel_args_count(args) <= idx) {
- FLT_OTEL_ERR("'%s' : too few arguments", args[0]);
-
- OTELC_RETURN_PTR(retptr);
- }
-
- /* The sample key is located at the (idx - 1) location of the args[] field. */
- retptr = flt_otel_conf_sample_init(args[idx - 1], line, head, err);
- if (retptr == NULL)
- OTELC_RETURN_PTR(retptr);
-
- if ((extra == NULL) || (extra->u_type == OTELC_VALUE_NULL)) {
- /*
- * Do nothing - sample extra data is not set or initialized,
- * which means it is not used.
- */
- }
- else if (extra->u_type == OTELC_VALUE_STRING) {
- retptr->extra.u_type = OTELC_VALUE_DATA;
- retptr->extra.u.value_data = OTELC_STRDUP(extra->u.value_string);
- if (retptr->extra.u.value_data == NULL) {
- FLT_OTEL_ERR("out of memory");
- flt_otel_conf_sample_free(&retptr);
-
- OTELC_RETURN_PTR(retptr);
- }
- }
- else if (extra->u_type == OTELC_VALUE_INT32) {
- retptr->extra.u_type = extra->u_type;
- retptr->extra.u.value_int32 = extra->u.value_int32;
- }
- else {
- FLT_OTEL_ERR("invalid sample extra data type: %d", extra->u_type);
- flt_otel_conf_sample_free(&retptr);
-
- OTELC_RETURN_PTR(retptr);
- }
-
- /* The sample value starts in the args[] array after the key. */
- retptr->num_exprs = flt_otel_args_concat(args, idx, n, &(retptr->fmt_string));
- if (retptr->num_exprs == FLT_OTEL_RET_ERROR) {
- FLT_OTEL_ERR("out of memory");
- flt_otel_conf_sample_free(&retptr);
-
- OTELC_RETURN_PTR(retptr);
- }
-
- FLT_OTEL_DBG_CONF_SAMPLE("- conf_sample init ", retptr);
-
- OTELC_RETURN_PTR(retptr);
-}
-
-
-/***
- * NAME
- * flt_otel_conf_sample_free - conf_sample structure deallocation
- *
- * SYNOPSIS
- * void flt_otel_conf_sample_free(struct flt_otel_conf_sample **ptr)
- *
- * ARGUMENTS
- * ptr - a pointer to the address of a structure
- *
- * DESCRIPTION
- * Deallocates memory used by the flt_otel_conf_sample structure and its
- * contents, then removes it from the list of structures of that type.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-FLT_OTEL_CONF_FUNC_FREE(sample, key,
- FLT_OTEL_DBG_CONF_SAMPLE("- conf_sample free ", *ptr);
-
- OTELC_SFREE((*ptr)->fmt_string);
- if ((*ptr)->extra.u_type == OTELC_VALUE_DATA)
- OTELC_SFREE((*ptr)->extra.u.value_data);
- FLT_OTEL_LIST_DESTROY(sample_expr, &((*ptr)->exprs));
- lf_expr_deinit(&((*ptr)->lf_expr));
-)
-
-
-/***
- * NAME
- * flt_otel_conf_context_init - conf_context structure allocation
- *
- * SYNOPSIS
- * struct flt_otel_conf_context *flt_otel_conf_context_init(const char *id, int line, struct list *head, char **err)
- *
- * ARGUMENTS
- * id - identifier string to duplicate
- * line - configuration file line number
- * head - list to append to (or NULL)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Allocates and initializes a conf_context structure. The <id> string is
- * duplicated and stored as the context identifier. If <head> is non-NULL,
- * the structure is appended to the list.
- *
- * RETURN VALUE
- * Returns a pointer to the initialized structure, or NULL on failure.
- */
-FLT_OTEL_CONF_FUNC_INIT(context, id, )
-
-
-/***
- * NAME
- * flt_otel_conf_context_free - conf_context structure deallocation
- *
- * SYNOPSIS
- * void flt_otel_conf_context_free(struct flt_otel_conf_context **ptr)
- *
- * ARGUMENTS
- * ptr - a pointer to the address of a structure
- *
- * DESCRIPTION
- * Deallocates memory used by the flt_otel_conf_context structure and its
- * contents, then removes it from the list of structures of that type.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-FLT_OTEL_CONF_FUNC_FREE(context, id,
- FLT_OTEL_DBG_CONF_HDR("- conf_context free ", *ptr, id);
-)
-
-
-/***
- * NAME
- * flt_otel_conf_span_init - conf_span structure allocation
- *
- * SYNOPSIS
- * struct flt_otel_conf_span *flt_otel_conf_span_init(const char *id, int line, struct list *head, char **err)
- *
- * ARGUMENTS
- * id - identifier string to duplicate
- * line - configuration file line number
- * head - list to append to (or NULL)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Allocates and initializes a conf_span structure with empty lists for
- * attributes, events, baggages, and statuses. The <id> string is duplicated
- * and stored as the span name. If <head> is non-NULL, the structure is
- * appended to the list.
- *
- * RETURN VALUE
- * Returns a pointer to the initialized structure, or NULL on failure.
- */
-FLT_OTEL_CONF_FUNC_INIT(span, id,
- LIST_INIT(&(retptr->links));
- LIST_INIT(&(retptr->attributes));
- LIST_INIT(&(retptr->events));
- LIST_INIT(&(retptr->baggages));
- LIST_INIT(&(retptr->statuses));
-)
-
-
-/***
- * NAME
- * flt_otel_conf_span_free - conf_span structure deallocation
- *
- * SYNOPSIS
- * void flt_otel_conf_span_free(struct flt_otel_conf_span **ptr)
- *
- * ARGUMENTS
- * ptr - a pointer to the address of a structure
- *
- * DESCRIPTION
- * Deallocates memory used by the flt_otel_conf_span structure and its
- * contents, then removes it from the list of structures of that type.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-FLT_OTEL_CONF_FUNC_FREE(span, id,
- FLT_OTEL_DBG_CONF_HDR("- conf_span free ", *ptr, id);
-
- OTELC_SFREE((*ptr)->ref_id);
- OTELC_SFREE((*ptr)->ctx_id);
- FLT_OTEL_LIST_DESTROY(link, &((*ptr)->links));
- FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->attributes));
- FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->events));
- FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->baggages));
- FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->statuses));
-)
-
-
-/***
- * NAME
- * flt_otel_conf_instrument_init - conf_instrument structure allocation
- *
- * SYNOPSIS
- * struct flt_otel_conf_instrument *flt_otel_conf_instrument_init(const char *id, int line, struct list *head, char **err)
- *
- * ARGUMENTS
- * id - identifier string to duplicate
- * line - configuration file line number
- * head - list to append to (or NULL)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Allocates and initializes a conf_instrument structure. Sets the instrument
- * type and meter index to OTELC_METRIC_INSTRUMENT_UNSET and initializes the
- * samples and attributes lists. The <id> string is duplicated and stored as
- * the instrument name. If <head> is non-NULL, the structure is appended to
- * the list.
- *
- * RETURN VALUE
- * Returns a pointer to the initialized structure, or NULL on failure.
- */
-FLT_OTEL_CONF_FUNC_INIT(instrument, id,
- retptr->idx = OTELC_METRIC_INSTRUMENT_UNSET;
- retptr->type = OTELC_METRIC_INSTRUMENT_UNSET;
- retptr->aggr_type = OTELC_METRIC_AGGREGATION_UNSET;
- LIST_INIT(&(retptr->samples));
- LIST_INIT(&(retptr->attributes));
-)
-
-
-/***
- * NAME
- * flt_otel_conf_instrument_free - conf_instrument structure deallocation
- *
- * SYNOPSIS
- * void flt_otel_conf_instrument_free(struct flt_otel_conf_instrument **ptr)
- *
- * ARGUMENTS
- * ptr - a pointer to the address of a structure
- *
- * DESCRIPTION
- * Deallocates memory used by the flt_otel_conf_instrument structure and its
- * contents, then removes it from the list of structures of that type.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-FLT_OTEL_CONF_FUNC_FREE(instrument, id,
- FLT_OTEL_DBG_CONF_INSTRUMENT("- conf_instrument free ", *ptr);
-
- OTELC_SFREE((*ptr)->description);
- OTELC_SFREE((*ptr)->unit);
- FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->samples));
- OTELC_SFREE((*ptr)->bounds);
- FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->attributes));
-)
-
-
-/***
- * NAME
- * flt_otel_conf_log_record_init - conf_log_record structure allocation
- *
- * SYNOPSIS
- * struct flt_otel_conf_log_record *flt_otel_conf_log_record_init(const char *id, int line, struct list *head, char **err)
- *
- * ARGUMENTS
- * id - identifier string to duplicate
- * line - configuration file line number
- * head - list to append to (or NULL)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Allocates and initializes a conf_log_record structure. Initializes the
- * attributes and sample expressions lists. The <id> string is required by
- * the macro but is not used directly; the severity level is stored
- * separately. If <head> is non-NULL, the structure is appended to the list.
- *
- * RETURN VALUE
- * Returns a pointer to the initialized structure, or NULL on failure.
- */
-FLT_OTEL_CONF_FUNC_INIT(log_record, id,
- LIST_INIT(&(retptr->attributes));
- LIST_INIT(&(retptr->samples));
-)
-
-
-/***
- * NAME
- * flt_otel_conf_log_record_free - conf_log_record structure deallocation
- *
- * SYNOPSIS
- * void flt_otel_conf_log_record_free(struct flt_otel_conf_log_record **ptr)
- *
- * ARGUMENTS
- * ptr - a pointer to the address of a structure
- *
- * DESCRIPTION
- * Deallocates memory used by the flt_otel_conf_log_record structure and its
- * contents, then removes it from the list of structures of that type.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-FLT_OTEL_CONF_FUNC_FREE(log_record, id,
- FLT_OTEL_DBG_CONF_LOG_RECORD("- conf_log_record free ", *ptr);
-
- OTELC_SFREE((*ptr)->event_name);
- OTELC_SFREE((*ptr)->span);
- FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->attributes));
- FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->samples));
-)
-
-
-/***
- * NAME
- * flt_otel_conf_scope_init - conf_scope structure allocation
- *
- * SYNOPSIS
- * struct flt_otel_conf_scope *flt_otel_conf_scope_init(const char *id, int line, struct list *head, char **err)
- *
- * ARGUMENTS
- * id - identifier string to duplicate
- * line - configuration file line number
- * head - list to append to (or NULL)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Allocates and initializes a conf_scope structure with empty lists for ACLs,
- * contexts, spans, and spans_to_finish. The <id> string is
- * duplicated and stored as the scope name. If <head> is non-NULL, the
- * structure is appended to the list.
- *
- * RETURN VALUE
- * Returns a pointer to the initialized structure, or NULL on failure.
- */
-FLT_OTEL_CONF_FUNC_INIT(scope, id,
- LIST_INIT(&(retptr->acls));
- LIST_INIT(&(retptr->contexts));
- LIST_INIT(&(retptr->spans));
- LIST_INIT(&(retptr->spans_to_finish));
- LIST_INIT(&(retptr->instruments));
- LIST_INIT(&(retptr->log_records));
-)
-
-
-/***
- * NAME
- * flt_otel_conf_scope_free - conf_scope structure deallocation
- *
- * SYNOPSIS
- * void flt_otel_conf_scope_free(struct flt_otel_conf_scope **ptr)
- *
- * ARGUMENTS
- * ptr - a pointer to the address of a structure
- *
- * DESCRIPTION
- * Deallocates memory used by the flt_otel_conf_scope structure and its
- * contents, then removes it from the list of structures of that type.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-FLT_OTEL_CONF_FUNC_FREE(scope, id,
- struct acl *acl;
- struct acl *aclback;
-
- FLT_OTEL_DBG_CONF_SCOPE("- conf_scope free ", *ptr);
-
- list_for_each_entry_safe(acl, aclback, &((*ptr)->acls), list) {
- prune_acl(acl);
- FLT_OTEL_LIST_DEL(&(acl->list));
- OTELC_SFREE(acl);
- }
- free_acl_cond((*ptr)->cond);
- FLT_OTEL_LIST_DESTROY(context, &((*ptr)->contexts));
- FLT_OTEL_LIST_DESTROY(span, &((*ptr)->spans));
- FLT_OTEL_LIST_DESTROY(str, &((*ptr)->spans_to_finish));
- FLT_OTEL_LIST_DESTROY(instrument, &((*ptr)->instruments));
- FLT_OTEL_LIST_DESTROY(log_record, &((*ptr)->log_records));
-)
-
-
-/***
- * NAME
- * flt_otel_conf_group_init - conf_group structure allocation
- *
- * SYNOPSIS
- * struct flt_otel_conf_group *flt_otel_conf_group_init(const char *id, int line, struct list *head, char **err)
- *
- * ARGUMENTS
- * id - identifier string to duplicate
- * line - configuration file line number
- * head - list to append to (or NULL)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Allocates and initializes a conf_group structure with an empty placeholder
- * scope list. The <id> string is duplicated and stored as the group name.
- * If <head> is non-NULL, the structure is appended to the list.
- *
- * RETURN VALUE
- * Returns a pointer to the initialized structure, or NULL on failure.
- */
-FLT_OTEL_CONF_FUNC_INIT(group, id,
- LIST_INIT(&(retptr->ph_scopes));
-)
-
-
-/***
- * NAME
- * flt_otel_conf_group_free - conf_group structure deallocation
- *
- * SYNOPSIS
- * void flt_otel_conf_group_free(struct flt_otel_conf_group **ptr)
- *
- * ARGUMENTS
- * ptr - a pointer to the address of a structure
- *
- * DESCRIPTION
- * Deallocates memory used by the flt_otel_conf_group structure and its
- * contents, then removes it from the list of structures of that type.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-FLT_OTEL_CONF_FUNC_FREE(group, id,
- FLT_OTEL_DBG_CONF_GROUP("- conf_group free ", *ptr);
-
- FLT_OTEL_LIST_DESTROY(ph_scope, &((*ptr)->ph_scopes));
-)
-
-
-/***
- * NAME
- * flt_otel_conf_instr_init - conf_instr structure allocation
- *
- * SYNOPSIS
- * struct flt_otel_conf_instr *flt_otel_conf_instr_init(const char *id, int line, struct list *head, char **err)
- *
- * ARGUMENTS
- * id - identifier string to duplicate
- * line - configuration file line number
- * head - list to append to (or NULL)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Allocates and initializes a conf_instr (instrumentation) structure. Sets
- * the default rate limit to 100%, initializes the proxy_log for logger
- * support, and creates empty lists for ACLs, placeholder groups, and
- * placeholder scopes. The <id> string is duplicated and stored as the
- * instrumentation name. If <head> is non-NULL, the structure is appended
- * to the list.
- *
- * RETURN VALUE
- * Returns a pointer to the initialized structure, or NULL on failure.
- */
-FLT_OTEL_CONF_FUNC_INIT(instr, id,
- retptr->rate_limit = FLT_OTEL_FLOAT_U32(100.0);
- init_new_proxy(&(retptr->proxy_log));
- LIST_INIT(&(retptr->acls));
- LIST_INIT(&(retptr->ph_groups));
- LIST_INIT(&(retptr->ph_scopes));
-)
-
-
-/***
- * NAME
- * flt_otel_conf_instr_free - conf_instr structure deallocation
- *
- * SYNOPSIS
- * void flt_otel_conf_instr_free(struct flt_otel_conf_instr **ptr)
- *
- * ARGUMENTS
- * ptr - a pointer to the address of a structure
- *
- * DESCRIPTION
- * Deallocates memory used by the flt_otel_conf_instr structure and its
- * contents, then removes it from the list of structures of that type.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-FLT_OTEL_CONF_FUNC_FREE(instr, id,
- struct acl *acl;
- struct acl *aclback;
- struct logger *logger;
- struct logger *loggerback;
-
- FLT_OTEL_DBG_CONF_INSTR("- conf_instr free ", *ptr);
-
- OTELC_SFREE((*ptr)->config);
- OTELC_DBG(NOTICE, "- deleting acls list %s", flt_otel_list_dump(&((*ptr)->acls)));
- list_for_each_entry_safe(acl, aclback, &((*ptr)->acls), list) {
- prune_acl(acl);
- FLT_OTEL_LIST_DEL(&(acl->list));
- OTELC_SFREE(acl);
- }
- OTELC_DBG(NOTICE, "- deleting proxy_log.loggers list %s", flt_otel_list_dump(&((*ptr)->proxy_log.loggers)));
- list_for_each_entry_safe(logger, loggerback, &((*ptr)->proxy_log.loggers), list) {
- LIST_DELETE(&(logger->list));
- ha_free(&logger);
- }
- FLT_OTEL_LIST_DESTROY(ph_group, &((*ptr)->ph_groups));
- FLT_OTEL_LIST_DESTROY(ph_scope, &((*ptr)->ph_scopes));
-)
-
-
-/***
- * NAME
- * flt_otel_conf_init - top-level filter configuration allocation
- *
- * SYNOPSIS
- * struct flt_otel_conf *flt_otel_conf_init(struct proxy *px)
- *
- * ARGUMENTS
- * px - proxy instance to associate with
- *
- * DESCRIPTION
- * Allocates and initializes the top-level flt_otel_conf structure. Stores
- * the <px> proxy reference and creates empty group and scope lists.
- *
- * RETURN VALUE
- * Returns a pointer to the initialized structure, or NULL on failure.
- */
-struct flt_otel_conf *flt_otel_conf_init(struct proxy *px)
-{
- struct flt_otel_conf *retptr;
-
- OTELC_FUNC("%p", px);
-
- retptr = OTELC_CALLOC(1, sizeof(*retptr));
- if (retptr == NULL)
- OTELC_RETURN_PTR(retptr);
-
- retptr->proxy = px;
- LIST_INIT(&(retptr->groups));
- LIST_INIT(&(retptr->scopes));
- LIST_INIT(&(retptr->smp_args));
-
- FLT_OTEL_DBG_CONF("- conf init ", retptr);
-
- OTELC_RETURN_PTR(retptr);
-}
-
-
-/***
- * NAME
- * flt_otel_conf_free - top-level filter configuration deallocation
- *
- * SYNOPSIS
- * void flt_otel_conf_free(struct flt_otel_conf **ptr)
- *
- * ARGUMENTS
- * ptr - a pointer to the address of a structure
- *
- * DESCRIPTION
- * Deallocates memory used by the flt_otel_conf structure and its contents.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_conf_free(struct flt_otel_conf **ptr)
-{
- struct arg_list *cur, *back;
-
- OTELC_FUNC("%p:%p", OTELC_DPTR_ARGS(ptr));
-
- if ((ptr == NULL) || (*ptr == NULL))
- OTELC_RETURN();
-
- FLT_OTEL_DBG_CONF("- conf free ", *ptr);
-
- OTELC_SFREE((*ptr)->id);
- OTELC_SFREE((*ptr)->cfg_file);
- flt_otel_conf_instr_free(&((*ptr)->instr));
- FLT_OTEL_LIST_DESTROY(group, &((*ptr)->groups));
- FLT_OTEL_LIST_DESTROY(scope, &((*ptr)->scopes));
- /* Free any unresolved OTEL sample fetch args (error path). */
- list_for_each_entry_safe(cur, back, &((*ptr)->smp_args), list) {
- LIST_DELETE(&(cur->list));
- ha_free(&cur);
- }
- OTELC_SFREE_CLEAR(*ptr);
-
- OTELC_RETURN();
-}
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "../include/include.h"
-
-
-/* Event data table built from the X-macro list. */
-#define FLT_OTEL_EVENT_DEF(a,b,c,d,e,f) { AN_##b##_##a, OTELC_STRINGIFY_ARG(AN_##b##_##a), SMP_OPT_DIR_##b, SMP_VAL_FE_##c, SMP_VAL_BE_##d, e, f },
-const struct flt_otel_event_data flt_otel_event_data[FLT_OTEL_EVENT_MAX] = { FLT_OTEL_EVENT_DEFINES };
-#undef FLT_OTEL_EVENT_DEF
-
-
-/***
- * NAME
- * flt_otel_scope_run_instrument_record - metric instrument value recorder
- *
- * SYNOPSIS
- * static int flt_otel_scope_run_instrument_record(struct stream *s, uint dir, struct otelc_meter *meter, struct flt_otel_conf_instrument *instr_ref, struct flt_otel_conf_instrument *instr, char **err)
- *
- * ARGUMENTS
- * s - the stream providing the sample context
- * dir - the sample fetch direction (SMP_OPT_DIR_REQ/RES)
- * meter - the OTel meter instance
- * instr_ref - the create-form instrument providing samples and meter index
- * instr - the update-form instrument providing per-scope attributes
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Evaluates sample expressions from a create-form instrument and records
- * the resulting value via the <meter> API. Each expression is evaluated
- * with sample_process(), converted to an otelc_value via
- * flt_otel_sample_to_value(), and recorded via
- * <meter>->update_instrument_kv_n().
- *
- * RETURN VALUE
- * Returns FLT_OTEL_RET_OK on success, FLT_OTEL_RET_ERROR on failure.
- */
-static int flt_otel_scope_run_instrument_record(struct stream *s, uint dir, struct otelc_meter *meter, struct flt_otel_conf_instrument *instr_ref, struct flt_otel_conf_instrument *instr, char **err)
-{
- struct flt_otel_conf_sample *sample;
- struct flt_otel_conf_sample_expr *expr;
- struct sample smp;
- struct otelc_value value;
- struct flt_otel_scope_data_kv instr_attr;
- int retval = FLT_OTEL_RET_OK;
-
- OTELC_FUNC("%p, %u, %p, %p, %p, %p:%p", s, dir, meter, instr_ref, instr, OTELC_DPTR_ARGS(err));
-
- /* Evaluate instrument attributes from sample expressions. */
- (void)memset(&instr_attr, 0, sizeof(instr_attr));
-
- list_for_each_entry(sample, &(instr->attributes), list) {
- struct otelc_value attr_value;
-
- OTELC_DBG(DEBUG, "adding instrument attribute '%s' -> '%s'", sample->key, sample->fmt_string);
-
- if (flt_otel_sample_eval(s, dir, sample, true, &attr_value, err) == FLT_OTEL_RET_ERROR) {
- retval = FLT_OTEL_RET_ERROR;
-
- continue;
- }
-
- if (flt_otel_sample_add_kv(&instr_attr, sample->key, &attr_value) == FLT_OTEL_RET_ERROR) {
- if (attr_value.u_type == OTELC_VALUE_DATA)
- OTELC_SFREE(attr_value.u.value_data);
-
- retval = FLT_OTEL_RET_ERROR;
- }
- }
-
- /* The samples list always contains exactly one entry. */
- sample = LIST_NEXT(&(instr_ref->samples), struct flt_otel_conf_sample *, list);
-
- (void)memset(&smp, 0, sizeof(smp));
-
- if (sample->lf_used) {
- /*
- * Log-format path: evaluate into a temporary buffer and present
- * the result as a string sample.
- */
- smp.data.u.str.area = OTELC_CALLOC(1, global.tune.bufsize);
- if (smp.data.u.str.area == NULL) {
- FLT_OTEL_ERR("out of memory");
-
- otelc_kv_destroy(&(instr_attr.attr), instr_attr.cnt);
-
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
- }
-
- smp.data.type = SMP_T_STR;
- smp.data.u.str.data = build_logline(s, smp.data.u.str.area, global.tune.bufsize, &(sample->lf_expr));
- } else {
- /* The expressions list always contains exactly one entry. */
- expr = LIST_NEXT(&(sample->exprs), struct flt_otel_conf_sample_expr *, list);
-
- FLT_OTEL_DBG_CONF_SAMPLE_EXPR("sample expression ", expr);
-
- if (sample_process(s->be, s->sess, s, dir | SMP_OPT_FINAL, expr->expr, &smp) == NULL) {
- OTELC_DBG(NOTICE, "WARNING: failed to fetch '%s'", expr->fmt_expr);
-
- retval = FLT_OTEL_RET_ERROR;
- }
- }
-
- if (retval == FLT_OTEL_RET_ERROR) {
- /* Do nothing. */
- }
- else if (flt_otel_sample_to_value(sample->key, &(smp.data), &value, err) == FLT_OTEL_RET_ERROR) {
- if (value.u_type == OTELC_VALUE_DATA)
- OTELC_SFREE(value.u.value_data);
-
- retval = FLT_OTEL_RET_ERROR;
- }
- else {
- OTELC_DBG_VALUE(DEBUG, "value ", &value);
-
- /*
- * Metric instruments expect numeric values (INT64 or DOUBLE).
- * Reject OTELC_VALUE_DATA since the meter cannot interpret
- * arbitrary string data as a numeric measurement.
- */
- if (value.u_type == OTELC_VALUE_DATA) {
- OTELC_DBG(NOTICE, "WARNING: non-numeric value type for instrument '%s'", instr_ref->id);
-
- if (otelc_value_strtonum(&value, OTELC_VALUE_INT64) == OTELC_RET_ERROR) {
- OTELC_SFREE(value.u.value_data);
-
- retval = FLT_OTEL_RET_ERROR;
- }
- }
-
- if (retval != FLT_OTEL_RET_ERROR)
- if (OTELC_OPS(meter, update_instrument_kv_n, HA_ATOMIC_LOAD(&(instr_ref->idx)), &value, instr_attr.attr, instr_attr.cnt) == OTELC_RET_ERROR)
- retval = FLT_OTEL_RET_ERROR;
- }
-
- otelc_kv_destroy(&(instr_attr.attr), instr_attr.cnt);
-
- if (sample->lf_used)
- OTELC_SFREE(smp.data.u.str.area);
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_scope_run_instrument - metric instrument processor
- *
- * SYNOPSIS
- * static int flt_otel_scope_run_instrument(struct stream *s, uint dir, struct flt_otel_conf_scope *scope, struct otelc_meter *meter, char **err)
- *
- * ARGUMENTS
- * s - the stream providing the sample context
- * dir - the sample fetch direction (SMP_OPT_DIR_REQ/RES)
- * scope - the scope configuration containing the instrument list
- * meter - the OTel meter instance
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Processes all metric instruments configured in <scope>. Runs in two
- * passes: the first pass lazily creates create-form instruments via <meter>
- * on first use, using HA_ATOMIC_CAS on the instrument index to guarantee
- * thread-safe one-time initialization. The second pass iterates over
- * update-form instruments and records measurements via
- * flt_otel_scope_run_instrument_record(). Instruments whose index is still
- * negative (UNUSED or PENDING) are skipped, so that a concurrent creation by
- * another thread does not cause an invalid <meter> access.
- *
- * RETURN VALUE
- * Returns FLT_OTEL_RET_OK on success, FLT_OTEL_RET_ERROR on failure.
- */
-static int flt_otel_scope_run_instrument(struct stream *s, uint dir, struct flt_otel_conf_scope *scope, struct otelc_meter *meter, char **err)
-{
- struct flt_otel_conf_instrument *conf_instr;
- int retval = FLT_OTEL_RET_OK;
-
- OTELC_FUNC("%p, %u, %p, %p, %p:%p", s, dir, scope, meter, OTELC_DPTR_ARGS(err));
-
- list_for_each_entry(conf_instr, &(scope->instruments), list) {
- if (conf_instr->type == OTELC_METRIC_INSTRUMENT_UPDATE) {
- /* Do nothing. */
- }
- else if (HA_ATOMIC_LOAD(&(conf_instr->idx)) == OTELC_METRIC_INSTRUMENT_UNSET) {
- int64_t expected = OTELC_METRIC_INSTRUMENT_UNSET;
- int rc;
-
- OTELC_DBG(DEBUG, "run instrument '%s' -> '%s'", scope->id, conf_instr->id);
- FLT_OTEL_DBG_CONF_INSTRUMENT("", conf_instr);
-
- /*
- * Create form: use this instrument directly. Lazily
- * create the instrument on first use. Use CAS to
- * ensure only one thread performs the creation in a
- * multi-threaded environment.
- */
- if (!HA_ATOMIC_CAS(&(conf_instr->idx), &expected, OTELC_METRIC_INSTRUMENT_PENDING))
- continue;
-
- /*
- * The view must be created before the instrument,
- * otherwise bucket boundaries cannot be set.
- */
- if ((conf_instr->bounds != NULL) && (conf_instr->bounds_num > 0))
- if (OTELC_OPS(meter, add_view, conf_instr->id, conf_instr->description, conf_instr->id, conf_instr->unit, conf_instr->type, conf_instr->aggr_type, conf_instr->bounds, conf_instr->bounds_num) == OTELC_RET_ERROR)
- OTELC_DBG(NOTICE, "WARNING: failed to add view for instrument '%s'", conf_instr->id);
-
- rc = OTELC_OPS(meter, create_instrument, conf_instr->id, conf_instr->description, conf_instr->unit, conf_instr->type, NULL);
- if (rc == OTELC_RET_ERROR) {
- OTELC_DBG(NOTICE, "WARNING: failed to create instrument '%s'", conf_instr->id);
-
- HA_ATOMIC_STORE(&(conf_instr->idx), OTELC_METRIC_INSTRUMENT_UNSET);
-
- retval = FLT_OTEL_RET_ERROR;
-
- continue;
- } else {
- HA_ATOMIC_STORE(&(conf_instr->idx), rc);
- }
- }
- }
-
- list_for_each_entry(conf_instr, &(scope->instruments), list)
- if (conf_instr->type == OTELC_METRIC_INSTRUMENT_UPDATE) {
- struct flt_otel_conf_instrument *instr = conf_instr->ref;
-
- OTELC_DBG(DEBUG, "run instrument '%s' -> '%s'", scope->id, conf_instr->id);
- FLT_OTEL_DBG_CONF_INSTRUMENT("", conf_instr);
-
- /*
- * Update form: record a measurement using an existing
- * create-form instrument.
- */
- if (instr == NULL) {
- OTELC_DBG(NOTICE, "WARNING: invalid reference instrument '%s'", conf_instr->id);
-
- retval = FLT_OTEL_RET_ERROR;
- }
- else if (HA_ATOMIC_LOAD(&(instr->idx)) < 0) {
- OTELC_DBG(NOTICE, "WARNING: instrument '%s' not yet created, skipping", instr->id);
- }
- else if (flt_otel_scope_run_instrument_record(s, dir, meter, instr, conf_instr, err) == FLT_OTEL_RET_ERROR) {
- retval = FLT_OTEL_RET_ERROR;
- }
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_scope_run_log_record - log record emitter
- *
- * SYNOPSIS
- * static int flt_otel_scope_run_log_record(struct stream *s, struct filter *f, uint dir, struct flt_otel_conf_scope *scope, struct otelc_logger *logger, const struct timespec *ts, char **err)
- *
- * ARGUMENTS
- * s - the stream providing the sample context
- * f - the filter instance
- * dir - the sample fetch direction (SMP_OPT_DIR_REQ/RES)
- * scope - the scope configuration containing the log record list
- * logger - the OTel logger instance
- * ts - the wall-clock timestamp for the log record
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Processes all log records configured in <scope>. For each record, checks
- * whether the logger is enabled for the configured severity, evaluates the
- * sample expressions into a body string, resolves the optional span reference
- * against the runtime context, and emits the log record via the logger's
- * log_span operation.
- *
- * RETURN VALUE
- * Returns FLT_OTEL_RET_OK on success, FLT_OTEL_RET_ERROR on failure.
- */
-static int flt_otel_scope_run_log_record(struct stream *s, struct filter *f, uint dir, struct flt_otel_conf_scope *scope, struct otelc_logger *logger, const struct timespec *ts, char **err)
-{
- struct flt_otel_conf_log_record *conf_log;
- int retval = FLT_OTEL_RET_OK;
-
- OTELC_FUNC("%p, %p, %u, %p, %p, %p, %p:%p", s, f, dir, scope, logger, ts, OTELC_DPTR_ARGS(err));
-
- list_for_each_entry(conf_log, &(scope->log_records), list) {
- struct flt_otel_conf_sample *sample;
- struct flt_otel_conf_sample_expr *expr;
- struct sample smp;
- struct otelc_span *otel_span = NULL;
- struct flt_otel_scope_data_kv log_attr;
- struct buffer buffer;
- int rc;
-
- OTELC_DBG(DEBUG, "run log-record '%s' -> '%s'", scope->id, conf_log->id);
-
- /* Skip if the logger is not enabled for this severity. */
- if (OTELC_OPS(logger, enabled, conf_log->severity) == 0)
- continue;
-
- /* Evaluate log record attributes from sample expressions. */
- (void)memset(&log_attr, 0, sizeof(log_attr));
-
- list_for_each_entry(sample, &(conf_log->attributes), list) {
- struct otelc_value attr_value;
-
- OTELC_DBG(DEBUG, "adding log-record attribute '%s' -> '%s'", sample->key, sample->fmt_string);
-
- if (flt_otel_sample_eval(s, dir, sample, true, &attr_value, err) == FLT_OTEL_RET_ERROR) {
- retval = FLT_OTEL_RET_ERROR;
-
- continue;
- }
-
- if (flt_otel_sample_add_kv(&log_attr, sample->key, &attr_value) == FLT_OTEL_RET_ERROR) {
- if (attr_value.u_type == OTELC_VALUE_DATA)
- OTELC_SFREE(attr_value.u.value_data);
-
- retval = FLT_OTEL_RET_ERROR;
- }
- }
-
- /* The samples list has exactly one entry. */
- sample = LIST_NEXT(&(conf_log->samples), typeof(sample), list);
-
- (void)memset(&buffer, 0, sizeof(buffer));
-
- if (sample->lf_used) {
- /*
- * Log-format path: evaluate the log-format expression
- * into a dynamically allocated buffer.
- */
- chunk_init(&buffer, OTELC_CALLOC(1, global.tune.bufsize), global.tune.bufsize);
- if (buffer.area != NULL)
- buffer.data = build_logline(s, buffer.area, buffer.size, &(sample->lf_expr));
- } else {
- /*
- * Bare sample expression path: evaluate each expression
- * and concatenate the results.
- */
- list_for_each_entry(expr, &(sample->exprs), list) {
- (void)memset(&smp, 0, sizeof(smp));
-
- if (sample_process(s->be, s->sess, s, dir | SMP_OPT_FINAL, expr->expr, &smp) == NULL) {
- OTELC_DBG(NOTICE, "WARNING: failed to fetch '%s'", expr->fmt_expr);
-
- retval = FLT_OTEL_RET_ERROR;
-
- break;
- }
-
- if (buffer.area == NULL) {
- chunk_init(&buffer, OTELC_CALLOC(1, global.tune.bufsize), global.tune.bufsize);
- if (buffer.area == NULL)
- break;
- }
-
- rc = flt_otel_sample_to_str(&(smp.data), buffer.area + buffer.data, buffer.size - buffer.data, err);
- if (rc == FLT_OTEL_RET_ERROR) {
- retval = FLT_OTEL_RET_ERROR;
-
- break;
- }
-
- buffer.data += rc;
- }
- }
-
- if (buffer.area == NULL) {
- FLT_OTEL_ERR("out of memory");
-
- retval = FLT_OTEL_RET_ERROR;
-
- otelc_kv_destroy(&(log_attr.attr), log_attr.cnt);
-
- continue;
- }
-
- /*
- * If the log record references a span, resolve it against the
- * runtime context. A missing span is not fatal -- the log
- * record is emitted without span correlation.
- */
- if (conf_log->span != NULL) {
- struct flt_otel_runtime_context *rt_ctx = FLT_OTEL_RT_CTX(f->ctx);
- struct flt_otel_scope_span *sc_span;
-
- list_for_each_entry(sc_span, &(rt_ctx->spans), list)
- if (strcmp(sc_span->id, conf_log->span) == 0) {
- otel_span = sc_span->span;
-
- break;
- }
-
- if (otel_span == NULL)
- OTELC_DBG(NOTICE, "WARNING: cannot find span '%s' for log-record", conf_log->span);
- }
-
- if (OTELC_OPS(logger, log_span, conf_log->severity, conf_log->event_id, conf_log->event_name, otel_span, ts, log_attr.attr, log_attr.cnt, "%s", buffer.area) == OTELC_RET_ERROR)
- retval = FLT_OTEL_RET_ERROR;
-
- otelc_kv_destroy(&(log_attr.attr), log_attr.cnt);
- OTELC_SFREE(buffer.area);
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_scope_run_span - single span execution
- *
- * SYNOPSIS
- * static int flt_otel_scope_run_span(struct stream *s, struct filter *f, struct channel *chn, uint dir, struct flt_otel_scope_span *span, struct flt_otel_scope_data *data, const struct flt_otel_conf_span *conf_span, const struct timespec *ts_steady, const struct timespec *ts_system, char **err)
- *
- * ARGUMENTS
- * s - the stream being processed
- * f - the filter instance
- * chn - the channel used for HTTP header injection
- * dir - the sample fetch direction (SMP_OPT_DIR_REQ/RES)
- * span - the runtime scope span to execute
- * data - the evaluated scope data (attributes, events, links, status)
- * conf_span - the span configuration
- * ts_steady - the monotonic timestamp for span creation
- * ts_system - the wall-clock timestamp for span events
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Executes a single span: creates the OTel span on first call via the tracer,
- * adds links, baggage, attributes, events and status from <data>, then
- * injects the span context into HTTP headers or HAProxy variables if
- * configured in <conf_span>.
- *
- * RETURN VALUE
- * Returns FLT_OTEL_RET_OK on success, FLT_OTEL_RET_ERROR on failure.
- */
-static int flt_otel_scope_run_span(struct stream *s, struct filter *f, struct channel *chn, uint dir, struct flt_otel_scope_span *span, struct flt_otel_scope_data *data, const struct flt_otel_conf_span *conf_span, const struct timespec *ts_steady, const struct timespec *ts_system, char **err)
-{
- struct flt_otel_conf *conf = FLT_OTEL_CONF(f);
- int retval = FLT_OTEL_RET_OK;
-
- OTELC_FUNC("%p, %p, %p, %u, %p, %p, %p, %p, %p, %p:%p", s, f, chn, dir, span, data, conf_span, ts_steady, ts_system, OTELC_DPTR_ARGS(err));
-
- if (span == NULL)
- OTELC_RETURN_INT(retval);
-
- /* Create the OTel span on first invocation. */
- if (span->span == NULL) {
- span->span = OTELC_OPS(conf->instr->tracer, start_span_with_options, span->id, span->ref_span, span->ref_ctx, ts_steady, ts_system, OTELC_SPAN_KIND_SERVER, NULL, 0);
- if (span->span == NULL)
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
- }
-
- /* Add all resolved span links to the current span. */
- if (!LIST_ISEMPTY(&(data->links))) {
- struct flt_otel_scope_data_link *link;
-
- list_for_each_entry(link, &(data->links), list) {
- OTELC_DBG(DEBUG, "adding link %p %p", link->span, link->context);
-
- if (OTELC_OPS(span->span, add_link, link->span, link->context, NULL, 0) == -1)
- retval = FLT_OTEL_RET_ERROR;
- }
- }
-
- /* Set baggage key-value pairs on the span. */
- if (data->baggage.attr != NULL)
- if (OTELC_OPS(span->span, set_baggage_kv_n, data->baggage.attr, data->baggage.cnt) == -1)
- retval = FLT_OTEL_RET_ERROR;
-
- /* Set span attributes. */
- if (data->attributes.attr != NULL)
- if (OTELC_OPS(span->span, set_attribute_kv_n, data->attributes.attr, data->attributes.cnt) == -1)
- retval = FLT_OTEL_RET_ERROR;
-
- /* Add span events in reverse order. */
- if (!LIST_ISEMPTY(&(data->events))) {
- struct flt_otel_scope_data_event *event;
-
- list_for_each_entry_rev(event, &(data->events), list)
- if (OTELC_OPS(span->span, add_event_kv_n, event->name, ts_system, event->attr, event->cnt) == -1)
- retval = FLT_OTEL_RET_ERROR;
- }
-
- /* Set span status code and description. */
- if (data->status.description != NULL)
- if (OTELC_OPS(span->span, set_status, data->status.code, data->status.description) == -1)
- retval = FLT_OTEL_RET_ERROR;
-
- /* Inject span context into HTTP headers and variables. */
- if (conf_span->ctx_id != NULL) {
- struct otelc_http_headers_writer writer;
- struct otelc_text_map *text_map = NULL;
-
- if (flt_otel_inject_http_headers(span->span, &writer) != FLT_OTEL_RET_ERROR) {
- int i = 0;
-
- if (conf_span->ctx_flags & (FLT_OTEL_CTX_USE_VARS | FLT_OTEL_CTX_USE_HEADERS)) {
- for (text_map = &(writer.text_map); i < text_map->count; i++) {
-#ifdef USE_OTEL_VARS
- if (!(conf_span->ctx_flags & FLT_OTEL_CTX_USE_VARS))
- /* Do nothing. */;
- else if (flt_otel_var_register(FLT_OTEL_VARS_SCOPE, conf_span->ctx_id, text_map->key[i], err) == FLT_OTEL_RET_ERROR)
- retval = FLT_OTEL_RET_ERROR;
- else if (flt_otel_var_set(s, FLT_OTEL_VARS_SCOPE, conf_span->ctx_id, text_map->key[i], text_map->value[i], dir, err) == FLT_OTEL_RET_ERROR)
- retval = FLT_OTEL_RET_ERROR;
-#endif
-
- if (!(conf_span->ctx_flags & FLT_OTEL_CTX_USE_HEADERS))
- /* Do nothing. */;
- else if (flt_otel_http_header_set(chn, conf_span->ctx_id, text_map->key[i], text_map->value[i], err) == FLT_OTEL_RET_ERROR)
- retval = FLT_OTEL_RET_ERROR;
- }
- }
-
- otelc_text_map_destroy(&text_map);
- }
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_scope_run - scope execution engine
- *
- * SYNOPSIS
- * int flt_otel_scope_run(struct stream *s, struct filter *f, struct channel *chn, struct flt_otel_conf_scope *conf_scope, const struct timespec *ts_steady, const struct timespec *ts_system, uint dir, char **err)
- *
- * ARGUMENTS
- * s - the stream being processed
- * f - the filter instance
- * chn - the channel for context extraction and injection
- * conf_scope - the scope configuration to execute
- * ts_steady - the monotonic timestamp, or NULL to use current time
- * ts_system - the wall-clock timestamp, or NULL to use current time
- * dir - the sample fetch direction (SMP_OPT_DIR_REQ/RES)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Executes a complete scope: evaluates ACL conditions, extracts contexts
- * from HTTP headers or HAProxy variables, iterates over configured spans
- * (resolving links, evaluating sample expressions for attributes, events,
- * baggage and status), calls flt_otel_scope_run_span() for each, processes
- * metric instruments, emits log records, then marks and finishes completed
- * spans.
- *
- * RETURN VALUE
- * Returns FLT_OTEL_RET_OK on success, FLT_OTEL_RET_ERROR on failure.
- */
-int flt_otel_scope_run(struct stream *s, struct filter *f, struct channel *chn, struct flt_otel_conf_scope *conf_scope, const struct timespec *ts_steady, const struct timespec *ts_system, uint dir, char **err)
-{
- struct flt_otel_conf *conf = FLT_OTEL_CONF(f);
- struct flt_otel_conf_context *conf_ctx;
- struct flt_otel_conf_span *conf_span;
- struct flt_otel_conf_str *span_to_finish;
- struct timespec ts_now_steady, ts_now_system;
- int retval = FLT_OTEL_RET_OK;
-
- OTELC_FUNC("%p, %p, %p, %p, %p, %p, %u, %p:%p", s, f, chn, conf_scope, ts_steady, ts_system, dir, OTELC_DPTR_ARGS(err));
-
- OTELC_DBG(DEBUG, "channel: %s, mode: %s (%s)", flt_otel_chn_label(chn), flt_otel_pr_mode(s), flt_otel_stream_pos(s));
- OTELC_DBG(DEBUG, "run scope '%s' %d", conf_scope->id, conf_scope->event);
- FLT_OTEL_DBG_CONF_SCOPE("run scope ", conf_scope);
-
- if (ts_steady == NULL) {
- (void)clock_gettime(CLOCK_MONOTONIC, &ts_now_steady);
-
- ts_steady = &ts_now_steady;
- }
- if (ts_system == NULL) {
- (void)clock_gettime(CLOCK_REALTIME, &ts_now_system);
-
- ts_system = &ts_now_system;
- }
-
- /* Evaluate the scope's ACL condition; skip this scope on mismatch. */
- if (conf_scope->cond != NULL) {
- enum acl_test_res res;
- int rc;
-
- res = acl_exec_cond(conf_scope->cond, s->be, s->sess, s, dir | SMP_OPT_FINAL);
- rc = acl_pass(res);
- if (conf_scope->cond->pol == ACL_COND_UNLESS)
- rc = !rc;
-
- OTELC_DBG(DEBUG, "the ACL rule %s", rc ? "matches" : "does not match");
-
- /*
- * If the rule does not match, the current scope is skipped.
- *
- * If it is a root span, further processing of the session is
- * disabled. As soon as the first span is encountered which
- * is marked as root, further search is interrupted.
- */
- if (rc == 0) {
- list_for_each_entry(conf_span, &(conf_scope->spans), list)
- if (conf_span->flag_root) {
- OTELC_DBG(LOG, "session disabled");
-
- FLT_OTEL_RT_CTX(f->ctx)->flag_disabled = 1;
-
-#ifdef FLT_OTEL_USE_COUNTERS
- _HA_ATOMIC_ADD(conf->cnt.disabled + 0, 1);
-#endif
-
- break;
- }
-
- OTELC_RETURN_INT(retval);
- }
- }
-
- /* Extract and initialize OpenTelemetry propagation contexts. */
- list_for_each_entry(conf_ctx, &(conf_scope->contexts), list) {
- struct otelc_text_map *text_map = NULL;
-
- OTELC_DBG(DEBUG, "run context '%s' -> '%s'", conf_scope->id, conf_ctx->id);
- FLT_OTEL_DBG_CONF_CONTEXT("run context ", conf_ctx);
-
- /*
- * The OpenTelemetry context is read from the HTTP header
- * or from HAProxy variables.
- */
- if (conf_ctx->flags & FLT_OTEL_CTX_USE_HEADERS)
- text_map = flt_otel_http_headers_get(chn, conf_ctx->id, conf_ctx->id_len, err);
-#ifdef USE_OTEL_VARS
- else
- text_map = flt_otel_vars_get(s, FLT_OTEL_VARS_SCOPE, conf_ctx->id, dir, err);
-#endif
-
- if (text_map != NULL) {
- if (flt_otel_scope_context_init(f->ctx, conf->instr->tracer, conf_ctx->id, conf_ctx->id_len, text_map, dir, err) == NULL)
- retval = FLT_OTEL_RET_ERROR;
-
- otelc_text_map_destroy(&text_map);
- } else {
- retval = FLT_OTEL_RET_ERROR;
- }
- }
-
- /* Process configured spans: resolve links and collect samples. */
- list_for_each_entry(conf_span, &(conf_scope->spans), list) {
- struct flt_otel_scope_data data;
- struct flt_otel_scope_span *span;
- struct flt_otel_conf_sample *sample;
-
- OTELC_DBG(DEBUG, "run span '%s' -> '%s'", conf_scope->id, conf_span->id);
- FLT_OTEL_DBG_CONF_SPAN("run span ", conf_span);
-
- flt_otel_scope_data_init(&data);
-
- span = flt_otel_scope_span_init(f->ctx, conf_span->id, conf_span->id_len, conf_span->ref_id, conf_span->ref_id_len, dir, err);
- if (span == NULL)
- retval = FLT_OTEL_RET_ERROR;
-
- /*
- * Resolve configured span links against the runtime context.
- * Each link name is looked up first in the active spans, then
- * in the extracted contexts.
- */
- if (!LIST_ISEMPTY(&(conf_span->links))) {
- struct flt_otel_runtime_context *rt_ctx = FLT_OTEL_RT_CTX(f->ctx);
- struct flt_otel_conf_link *conf_link;
-
- list_for_each_entry(conf_link, &(conf_span->links), list) {
- struct flt_otel_scope_data_link *data_link;
- struct otelc_span *link_span = NULL;
- struct otelc_span_context *link_ctx = NULL;
- struct flt_otel_scope_span *sc_span;
- struct flt_otel_scope_context *sc_ctx;
-
- /* Try to find a matching span first. */
- list_for_each_entry(sc_span, &(rt_ctx->spans), list)
- if (FLT_OTEL_CONF_STR_CMP(sc_span->id, conf_link->span)) {
- link_span = sc_span->span;
-
- break;
- }
-
- /* If no span found, try to find a matching context. */
- if (link_span == NULL) {
- list_for_each_entry(sc_ctx, &(rt_ctx->contexts), list)
- if (FLT_OTEL_CONF_STR_CMP(sc_ctx->id, conf_link->span)) {
- link_ctx = sc_ctx->context;
-
- break;
- }
- }
-
- if ((link_span == NULL) && (link_ctx == NULL)) {
- OTELC_DBG(NOTICE, "WARNING: cannot find linked span/context '%s'", conf_link->span);
-
- continue;
- }
-
- data_link = OTELC_CALLOC(1, sizeof(*data_link));
- if (data_link == NULL) {
- retval = FLT_OTEL_RET_ERROR;
-
- break;
- }
-
- data_link->span = link_span;
- data_link->context = link_ctx;
- LIST_APPEND(&(data.links), &(data_link->list));
-
- OTELC_DBG(DEBUG, "resolved link '%s' -> %p %p", conf_link->span, link_span, link_ctx);
- }
- }
-
- list_for_each_entry(sample, &(conf_span->attributes), list) {
- OTELC_DBG(DEBUG, "adding attribute '%s' -> '%s'", sample->key, sample->fmt_string);
-
- if (flt_otel_sample_add(s, dir, sample, &data, FLT_OTEL_EVENT_SAMPLE_ATTRIBUTE, err) == FLT_OTEL_RET_ERROR)
- retval = FLT_OTEL_RET_ERROR;
- }
-
- list_for_each_entry(sample, &(conf_span->events), list) {
- OTELC_DBG(DEBUG, "adding event '%s' -> '%s'", sample->key, sample->fmt_string);
-
- if (flt_otel_sample_add(s, dir, sample, &data, FLT_OTEL_EVENT_SAMPLE_EVENT, err) == FLT_OTEL_RET_ERROR)
- retval = FLT_OTEL_RET_ERROR;
- }
-
- list_for_each_entry(sample, &(conf_span->baggages), list) {
- OTELC_DBG(DEBUG, "adding baggage '%s' -> '%s'", sample->key, sample->fmt_string);
-
- if (flt_otel_sample_add(s, dir, sample, &data, FLT_OTEL_EVENT_SAMPLE_BAGGAGE, err) == FLT_OTEL_RET_ERROR)
- retval = FLT_OTEL_RET_ERROR;
- }
-
- /*
- * Regardless of the use of the list, only one status per event
- * is allowed.
- */
- list_for_each_entry(sample, &(conf_span->statuses), list) {
- OTELC_DBG(DEBUG, "adding status '%s' -> '%s'", sample->key, sample->fmt_string);
-
- if (flt_otel_sample_add(s, dir, sample, &data, FLT_OTEL_EVENT_SAMPLE_STATUS, err) == FLT_OTEL_RET_ERROR)
- retval = FLT_OTEL_RET_ERROR;
- }
-
- /* Attempt to run the span regardless of earlier errors. */
- if (span != NULL)
- if (flt_otel_scope_run_span(s, f, chn, dir, span, &data, conf_span, ts_steady, ts_system, err) == FLT_OTEL_RET_ERROR)
- retval = FLT_OTEL_RET_ERROR;
-
- flt_otel_scope_data_free(&data);
- }
-
- /* Process metric instruments. */
- if (!LIST_ISEMPTY(&(conf_scope->instruments)))
- if (flt_otel_scope_run_instrument(s, dir, conf_scope, conf->instr->meter, err) == FLT_OTEL_RET_ERROR)
- retval = FLT_OTEL_RET_ERROR;
-
- /* Emit log records. */
- if (!LIST_ISEMPTY(&(conf_scope->log_records)))
- if (flt_otel_scope_run_log_record(s, f, dir, conf_scope, conf->instr->logger, ts_system, err) == FLT_OTEL_RET_ERROR)
- retval = FLT_OTEL_RET_ERROR;
-
- /* Mark the configured spans for finishing and clean up. */
- list_for_each_entry(span_to_finish, &(conf_scope->spans_to_finish), list)
- if (flt_otel_scope_finish_mark(f->ctx, span_to_finish->str, span_to_finish->str_len) == FLT_OTEL_RET_ERROR)
- retval = FLT_OTEL_RET_ERROR;
-
- flt_otel_scope_finish_marked(f->ctx, ts_steady);
- flt_otel_scope_free_unused(f->ctx, chn);
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_event_run - top-level event dispatcher
- *
- * SYNOPSIS
- * int flt_otel_event_run(struct stream *s, struct filter *f, struct channel *chn, int event, char **err)
- *
- * ARGUMENTS
- * s - the stream being processed
- * f - the filter instance
- * chn - the channel being analyzed
- * event - the event index (FLT_OTEL_EVENT_*)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Top-level event dispatcher called from filter callbacks. It iterates over
- * all scopes matching the <event> index and calls flt_otel_scope_run() for
- * each. All spans within a single event share the same monotonic and
- * wall-clock timestamps.
- *
- * RETURN VALUE
- * Returns FLT_OTEL_RET_OK on success, FLT_OTEL_RET_ERROR on failure.
- */
-int flt_otel_event_run(struct stream *s, struct filter *f, struct channel *chn, int event, char **err)
-{
- struct flt_otel_conf *conf = FLT_OTEL_CONF(f);
- struct flt_otel_conf_scope *conf_scope;
- struct timespec ts_steady, ts_system;
- int retval = FLT_OTEL_RET_OK;
-
- OTELC_FUNC("%p, %p, %p, %d, %p:%p", s, f, chn, event, OTELC_DPTR_ARGS(err));
-
- OTELC_DBG(DEBUG, "channel: %s, mode: %s (%s)", flt_otel_chn_label(chn), flt_otel_pr_mode(s), flt_otel_stream_pos(s));
- OTELC_DBG(DEBUG, "run event '%s' %d %s", flt_otel_event_data[event].name, event, flt_otel_event_data[event].an_name);
-
-#ifdef DEBUG_OTEL
- _HA_ATOMIC_ADD(conf->cnt.event[event].htx + ((chn == NULL) ? 1 : (htx_is_empty(htxbuf(&(chn->buf))) ? 1 : 0)), 1);
-#endif
-
- FLT_OTEL_RT_CTX(f->ctx)->analyzers |= flt_otel_event_data[event].an_bit;
-
- /* All spans should be created/completed at the same time. */
- (void)clock_gettime(CLOCK_MONOTONIC, &ts_steady);
- (void)clock_gettime(CLOCK_REALTIME, &ts_system);
-
- /*
- * It is possible that there are defined multiple scopes that use the
- * same event. Therefore, there must not be a 'break' here, ie an exit
- * from the 'for' loop.
- */
- list_for_each_entry(conf_scope, &(conf->scopes), list) {
- if (conf_scope->event != event)
- /* Do nothing. */;
- else if (!conf_scope->flag_used)
- OTELC_DBG(DEBUG, "scope '%s' %d not used", conf_scope->id, conf_scope->event);
- else if (flt_otel_scope_run(s, f, chn, conf_scope, &ts_steady, &ts_system, flt_otel_event_data[event].smp_opt_dir, err) == FLT_OTEL_RET_ERROR)
- retval = FLT_OTEL_RET_ERROR;
- }
-
-#ifdef USE_OTEL_VARS
- flt_otel_vars_dump(s);
-#endif
- flt_otel_http_headers_dump(chn);
-
- OTELC_DBG(DEBUG, "event = %d %s, chn = %p, s->req = %p, s->res = %p", event, flt_otel_event_data[event].an_name, chn, &(s->req), &(s->res));
-
- OTELC_RETURN_INT(retval);
-}
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "../include/include.h"
-
-
-/*
- * OpenTelemetry filter id, used to identify OpenTelemetry filters. The name
- * of this variable is consistent with the other filter names declared in
- * include/haproxy/filters.h .
- */
-const char *otel_flt_id = "the OpenTelemetry filter";
-
-/* Counter of OTel SDK internal diagnostic messages. */
-uint64_t flt_otel_drop_cnt = 0;
-
-#if defined(USE_THREAD) && defined(DEBUG_OTEL)
-/* Counter for assigning unique IDs to threads not registered as workers. */
-static int flt_otel_thread_id_offset = -1;
-
-/* Per-thread registration data for HAProxy worker threads. */
-static struct {
- pthread_t id; /* POSIX thread ID. */
- bool registered; /* Entry is valid. */
-} flt_otel_tid[MAX_THREADS + 1];
-#endif
-
-
-/***
- * NAME
- * flt_otel_mem_malloc - OTel library memory allocator callback
- *
- * SYNOPSIS
- * static void *flt_otel_mem_malloc(const char *func, int line, size_t size)
- *
- * ARGUMENTS
- * func - caller function name (debug only)
- * line - caller source line number (debug only)
- * size - number of bytes to allocate
- *
- * DESCRIPTION
- * Allocator callback for the OpenTelemetry C wrapper library. It allocates
- * the requested <size> bytes from the HAProxy pool_head_otel_span_context
- * pool. This function is registered via otelc_ext_init().
- *
- * RETURN VALUE
- * Returns a pointer to the allocated memory, or NULL on failure.
- */
-static void *flt_otel_mem_malloc(FLT_OTEL_DBG_ARGS(const char *func, int line, ) size_t size)
-{
- return flt_otel_pool_alloc(pool_head_otel_span_context, size, 1, NULL);
-}
-
-
-/***
- * NAME
- * flt_otel_mem_free - OTel library memory deallocator callback
- *
- * SYNOPSIS
- * static void flt_otel_mem_free(const char *func, int line, void *ptr)
- *
- * ARGUMENTS
- * func - caller function name (debug only)
- * line - caller source line number (debug only)
- * ptr - pointer to the memory to free
- *
- * DESCRIPTION
- * Deallocator callback for the OpenTelemetry C wrapper library. It returns
- * the memory pointed to by <ptr> back to the HAProxy
- * pool_head_otel_span_context pool. This function is registered via
- * otelc_ext_init().
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-static void flt_otel_mem_free(FLT_OTEL_DBG_ARGS(const char *func, int line, ) void *ptr)
-{
- flt_otel_pool_free(pool_head_otel_span_context, &ptr);
-}
-
-
-/***
- * NAME
- * flt_otel_thread_id - OTel library thread ID callback
- *
- * SYNOPSIS
- * static int flt_otel_thread_id(void)
- *
- * ARGUMENTS
- * This function takes no arguments.
- *
- * DESCRIPTION
- * Thread ID callback for the OpenTelemetry C wrapper library. For registered
- * HAProxy worker threads it returns the HAProxy thread identifier (tid). For
- * unregistered threads, such as those created internally by the OTel SDK, it
- * assigns and returns a unique ID from the atomic offset counter. This
- * function is registered via otelc_ext_init().
- *
- * RETURN VALUE
- * Returns the HAProxy thread ID for worker threads, a unique offset-based ID
- * for unregistered threads, or -1 if the thread index is out of range or the
- * offset counter has not yet been initialized.
- */
-static int flt_otel_thread_id(void)
-{
-#if defined(USE_THREAD) && defined(DEBUG_OTEL)
- static THREAD_LOCAL int retval = -1;
-
- if (!OTELC_IN_RANGE(tid, 0, OTELC_TABLESIZE(flt_otel_tid)))
- return -1;
- else if (!flt_otel_tid[tid].registered)
- return tid;
- else if (pthread_equal(flt_otel_tid[tid].id, pthread_self()))
- return tid;
-
- if ((retval == -1) && (HA_ATOMIC_LOAD(&flt_otel_thread_id_offset) != -1))
- retval = HA_ATOMIC_FETCH_ADD(&flt_otel_thread_id_offset, 1);
-
- return retval;
-
-#else
-
- return tid;
-#endif /* USE_THREAD && DEBUG_OTEL */
-}
-
-
-/***
- * NAME
- * flt_otel_log_handler_cb - counts SDK internal diagnostic messages
- *
- * SYNOPSIS
- * static void flt_otel_log_handler_cb(otelc_log_level_t level, const char *file, int line, const char *msg, const struct otelc_kv *attr, size_t attr_len, void *ctx)
- *
- * ARGUMENTS
- * level - severity of the OTel SDK diagnostic message
- * file - source file that emitted the message
- * line - source line number
- * msg - formatted diagnostic message text
- * attr - array of key-value attributes associated with the message
- * attr_len - number of entries in the attr array
- * ctx - opaque context pointer (unused)
- *
- * DESCRIPTION
- * Custom OTel SDK internal log handler registered via otelc_log_set_handler().
- * Each invocation atomically increments the flt_otel_drop_cnt counter so the
- * HAProxy OTel filter can verify how many OTel SDK diagnostic messages were
- * emitted. The message content is intentionally ignored.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-static void flt_otel_log_handler_cb(otelc_log_level_t level __maybe_unused, const char *file __maybe_unused, int line __maybe_unused, const char *msg __maybe_unused, const struct otelc_kv *attr __maybe_unused, size_t attr_len __maybe_unused, void *ctx __maybe_unused)
-{
- OTELC_FUNC("%d, \"%s\", %d, \"%s\", %p, %zu, %p", level, OTELC_STR_ARG(file), line, OTELC_STR_ARG(msg), attr, attr_len, ctx);
-
- _HA_ATOMIC_INC(&flt_otel_drop_cnt);
-
- OTELC_RETURN();
-}
-
-
-/***
- * NAME
- * flt_otel_lib_init - OTel library initialization
- *
- * SYNOPSIS
- * static int flt_otel_lib_init(struct flt_otel_conf_instr *instr, char **err)
- *
- * ARGUMENTS
- * instr - pointer to the instrumentation configuration
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Initializes the OpenTelemetry C wrapper library for the instrumentation
- * specified by <instr>. It verifies the library version, constructs the
- * absolute configuration path from <instr>->config, calls otelc_init(), and
- * creates the tracer and meter instances. On success, it registers the
- * memory and thread ID callbacks via otelc_ext_init().
- *
- * RETURN VALUE
- * Returns 0 on success, or FLT_OTEL_RET_ERROR on failure.
- */
-static int flt_otel_lib_init(struct flt_otel_conf_instr *instr, char **err)
-{
- char cwd[PATH_MAX], path[PATH_MAX];
- int rc, retval = FLT_OTEL_RET_ERROR;
-
- OTELC_FUNC("%p, %p:%p", instr, OTELC_DPTR_ARGS(err));
-
- if (!OTELC_IS_VALID_VERSION()) {
- FLT_OTEL_ERR("OpenTelemetry C Wrapper version mismatch: library (%s) does not match header files (%s). Please ensure both are the same version.", otelc_version(), OTELC_VERSION);
-
- OTELC_RETURN_INT(retval);
- }
-
- if (flt_otel_pool_init() == FLT_OTEL_RET_ERROR) {
- FLT_OTEL_ERR("failed to initialize memory pools");
-
- OTELC_RETURN_INT(retval);
- }
-
- flt_otel_pool_info();
-
- if (getcwd(cwd, sizeof(cwd)) == NULL) {
- FLT_OTEL_ERR("failed to get current working directory");
-
- OTELC_RETURN_INT(retval);
- }
-
- rc = snprintf(path, sizeof(path), "%s/%s", cwd, instr->config);
- if ((rc == -1) || (rc >= sizeof(path))) {
- FLT_OTEL_ERR("failed to construct the OpenTelemetry configuration path");
-
- OTELC_RETURN_INT(retval);
- }
-
- if (otelc_init(path, err) == OTELC_RET_ERROR) {
- if (*err == NULL)
- FLT_OTEL_ERR("%s", "failed to initialize tracing library");
-
- OTELC_RETURN_INT(retval);
- }
-
- instr->tracer = otelc_tracer_create(err);
- if (instr->tracer == NULL) {
- if (*err == NULL)
- FLT_OTEL_ERR("%s", "failed to initialize OpenTelemetry tracer");
-
- OTELC_RETURN_INT(retval);
- }
-
- instr->meter = otelc_meter_create(err);
- if (instr->meter == NULL) {
- if (*err == NULL)
- FLT_OTEL_ERR("%s", "failed to initialize OpenTelemetry meter");
-
- OTELC_RETURN_INT(retval);
- }
-
- instr->logger = otelc_logger_create(err);
- if (instr->logger == NULL) {
- if (*err == NULL)
- FLT_OTEL_ERR("%s", "failed to initialize OpenTelemetry logger");
- } else {
-#if defined(USE_THREAD) && defined(DEBUG_OTEL)
- flt_otel_tid[tid].id = pthread_self();
- flt_otel_tid[tid].registered = true;
- HA_ATOMIC_STORE(&flt_otel_thread_id_offset, 1000);
-#endif
- otelc_ext_init(flt_otel_mem_malloc, flt_otel_mem_free, flt_otel_thread_id);
- otelc_log_set_handler(flt_otel_log_handler_cb, NULL, false);
-
- retval = 0;
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_is_disabled - filter disabled check
- *
- * SYNOPSIS
- * bool flt_otel_is_disabled(const struct filter *f, int event)
- *
- * ARGUMENTS
- * f - the filter instance to check
- * event - the event identifier, or -1 (debug only)
- *
- * DESCRIPTION
- * Checks whether the filter instance is disabled for the current stream by
- * examining the runtime context's flag_disabled field. When DEBUG_OTEL is
- * enabled, it also logs the filter name, type and the <event> name.
- *
- * RETURN VALUE
- * Returns true if the filter is disabled, false otherwise.
- */
-bool flt_otel_is_disabled(const struct filter *f FLT_OTEL_DBG_ARGS(, int event))
-{
-#ifdef DEBUG_OTEL
- const struct flt_otel_conf *conf = FLT_OTEL_CONF(f);
- const char *msg;
-#endif
- bool retval;
-
- retval = FLT_OTEL_RT_CTX(f->ctx)->flag_disabled ? 1 : 0;
-
-#ifdef DEBUG_OTEL
- msg = retval ? " (disabled)" : "";
-
- if (OTELC_IN_RANGE(event, 0, FLT_OTEL_EVENT_MAX - 1))
- OTELC_DBG(NOTICE, "filter '%s', type: %s, event: '%s' %d%s", conf->id, flt_otel_type(f), flt_otel_event_data[event].name, event, msg);
- else
- OTELC_DBG(NOTICE, "filter '%s', type: %s%s", conf->id, flt_otel_type(f), msg);
-#endif
-
- return retval;
-}
-
-
-/***
- * NAME
- * flt_otel_return_int - error handler for int-returning callbacks
- *
- * SYNOPSIS
- * static int flt_otel_return_int(const struct filter *f, char **err, int retval)
- *
- * ARGUMENTS
- * f - the filter instance
- * err - indirect pointer to error message string
- * retval - the return value from the caller
- *
- * DESCRIPTION
- * Error handler for filter callbacks that return an integer value. If
- * <retval> indicates an error or <err> contains a message, the filter is
- * disabled when hard-error mode is enabled; in soft-error mode, the error
- * is silently cleared. The error message is always freed before returning.
- *
- * RETURN VALUE
- * Returns FLT_OTEL_RET_OK if an error was handled, or the original <retval>.
- */
-static int flt_otel_return_int(const struct filter *f, char **err, int retval)
-{
- struct flt_otel_runtime_context *rt_ctx = f->ctx;
-
- /* Disable the filter on hard errors; ignore on soft errors. */
- if ((retval == FLT_OTEL_RET_ERROR) || ((err != NULL) && (*err != NULL))) {
- if (rt_ctx->flag_harderr) {
- OTELC_DBG(INFO, "WARNING: filter hard-error (disabled)");
-
- rt_ctx->flag_disabled = 1;
-
-#ifdef FLT_OTEL_USE_COUNTERS
- _HA_ATOMIC_ADD(FLT_OTEL_CONF(f)->cnt.disabled + 1, 1);
-#endif
- } else {
- OTELC_DBG(INFO, "WARNING: filter soft-error");
- }
-
- retval = FLT_OTEL_RET_OK;
- }
-
- FLT_OTEL_ERR_FREE(*err);
-
- return retval;
-}
-
-
-/***
- * NAME
- * flt_otel_return_void - error handler for void-returning callbacks
- *
- * SYNOPSIS
- * static void flt_otel_return_void(const struct filter *f, char **err)
- *
- * ARGUMENTS
- * f - the filter instance
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Error handler for filter callbacks that return void. If <err> contains
- * a message, the filter is disabled when hard-error mode is enabled; in
- * soft-error mode, the error is silently cleared. The error message is
- * always freed before returning.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-static void flt_otel_return_void(const struct filter *f, char **err)
-{
- struct flt_otel_runtime_context *rt_ctx = f->ctx;
-
- /* Disable the filter on hard errors; ignore on soft errors. */
- if ((err != NULL) && (*err != NULL)) {
- if (rt_ctx->flag_harderr) {
- OTELC_DBG(INFO, "WARNING: filter hard-error (disabled)");
-
- rt_ctx->flag_disabled = 1;
-
-#ifdef FLT_OTEL_USE_COUNTERS
- _HA_ATOMIC_ADD(FLT_OTEL_CONF(f)->cnt.disabled + 1, 1);
-#endif
- } else {
- OTELC_DBG(INFO, "WARNING: filter soft-error");
- }
- }
-
- FLT_OTEL_ERR_FREE(*err);
-}
-
-
-/***
- * NAME
- * flt_otel_ops_init - filter init callback (flt_ops.init)
- *
- * SYNOPSIS
- * static int flt_otel_ops_init(struct proxy *p, struct flt_conf *fconf)
- *
- * ARGUMENTS
- * p - the proxy to which the filter is attached
- * fconf - the filter configuration
- *
- * DESCRIPTION
- * It initializes the filter for a proxy. You may define this callback if you
- * need to complete your filter configuration.
- *
- * RETURN VALUE
- * Returns a negative value if an error occurs, any other value otherwise.
- */
-static int flt_otel_ops_init(struct proxy *p, struct flt_conf *fconf)
-{
- struct flt_otel_conf *conf = FLT_OTEL_DEREF(fconf, conf, NULL);
- char *err = NULL;
- int retval = FLT_OTEL_RET_ERROR;
-
- OTELC_FUNC("%p, %p", p, fconf);
-
- if (conf == NULL)
- OTELC_RETURN_INT(retval);
-
- flt_otel_cli_init();
-
- /*
- * Initialize the OpenTelemetry library.
- */
- retval = flt_otel_lib_init(conf->instr, &err);
- if (retval != FLT_OTEL_RET_ERROR)
- /* Do nothing. */;
- else if (err != NULL) {
- FLT_OTEL_ALERT("%s", err);
-
- FLT_OTEL_ERR_FREE(err);
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_ops_deinit - filter deinit callback (flt_ops.deinit)
- *
- * SYNOPSIS
- * static void flt_otel_ops_deinit(struct proxy *p, struct flt_conf *fconf)
- *
- * ARGUMENTS
- * p - the proxy to which the filter is attached
- * fconf - the filter configuration
- *
- * DESCRIPTION
- * It cleans up what the parsing function and the init callback have done.
- * This callback is useful to release memory allocated for the filter
- * configuration.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-static void flt_otel_ops_deinit(struct proxy *p, struct flt_conf *fconf)
-{
- struct flt_otel_conf **conf = (fconf == NULL) ? NULL : (typeof(conf))&(fconf->conf);
- struct otelc_tracer *otel_tracer = NULL;
- struct otelc_meter *otel_meter = NULL;
- struct otelc_logger *otel_logger = NULL;
-#ifdef DEBUG_OTEL
- char buffer[BUFSIZ];
- int i;
-#endif
-
- OTELC_FUNC("%p, %p", p, fconf);
-
- if (conf == NULL)
- OTELC_RETURN();
-
-#ifdef DEBUG_OTEL
- otelc_statistics(buffer, sizeof(buffer));
- OTELC_DBG(LOG, "%s", buffer);
-
-# ifdef FLT_OTEL_USE_COUNTERS
- OTELC_DBG(LOG, "attach counters: %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, (*conf)->cnt.attached[0], (*conf)->cnt.attached[1], (*conf)->cnt.attached[2], (*conf)->cnt.attached[3]);
-# endif
-
- OTELC_DBG(LOG, "--- used events ----------");
- for (i = 0; i < OTELC_TABLESIZE((*conf)->cnt.event); i++)
- if ((*conf)->cnt.event[i].flag_used)
- OTELC_DBG(LOG, " %02d %25s: %" PRIu64 " / %" PRIu64, i, flt_otel_event_data[i].an_name, (*conf)->cnt.event[i].htx[0], (*conf)->cnt.event[i].htx[1]);
-#endif /* DEBUG_OTEL */
-
- /*
- * Save the OTel handles before freeing the configuration.
- * flt_otel_conf_free() must run while the wrapper's ext callbacks
- * still point to the HAProxy pool allocator; otelc_deinit() resets
- * those callbacks, so it runs last.
- */
- if ((*conf)->instr != NULL) {
- otel_tracer = (*conf)->instr->tracer;
- otel_meter = (*conf)->instr->meter;
- otel_logger = (*conf)->instr->logger;
- }
-
- flt_otel_conf_free(conf);
- OTELC_MEMINFO();
- flt_otel_pool_destroy();
- otelc_deinit(&otel_tracer, &otel_meter, &otel_logger);
-
- OTELC_RETURN();
-}
-
-
-/***
- * NAME
- * flt_otel_ops_check - filter check callback (flt_ops.check)
- *
- * SYNOPSIS
- * static int flt_otel_ops_check(struct proxy *p, struct flt_conf *fconf)
- *
- * ARGUMENTS
- * p - the proxy to which the filter is attached
- * fconf - the filter configuration
- *
- * DESCRIPTION
- * Validates the internal configuration of the OTel filter after the parsing
- * phase, when the HAProxy configuration is fully defined. The following
- * checks are performed: duplicate filter IDs across all proxies, presence of
- * an instrumentation section and its configuration file, duplicate group and
- * scope names, empty groups, group-to-scope and instrumentation-to-group/scope
- * cross-references, unused scopes, root span count, analyzer bits, and
- * create-form instrument name uniqueness and update-form instrument
- * resolution.
- *
- * RETURN VALUE
- * Returns the number of encountered errors.
- */
-static int flt_otel_ops_check(struct proxy *p, struct flt_conf *fconf)
-{
- struct proxy *px;
- struct flt_otel_conf *conf = FLT_OTEL_DEREF(fconf, conf, NULL);
- struct flt_otel_conf_group *conf_group;
- struct flt_otel_conf_scope *conf_scope;
- struct flt_otel_conf_ph *ph_group, *ph_scope;
- int retval = 0, scope_unused_cnt = 0, span_root_cnt = 0;
-
- OTELC_FUNC("%p, %p", p, fconf);
-
- if (conf == NULL)
- OTELC_RETURN_INT(++retval);
-
- /*
- * Resolve deferred OTEL sample fetch arguments.
- *
- * These were kept out of the proxy's arg list during parsing to avoid
- * the global smp_resolve_args() call, which would reject backend-only
- * fetches on a frontend proxy. All backends and servers are now
- * available, so resolve under full FE+BE capabilities.
- */
- if (!LIST_ISEMPTY(&(conf->smp_args))) {
- char *err = NULL;
- uint saved_cap = p->cap;
-
- LIST_SPLICE(&(p->conf.args.list), &(conf->smp_args));
- LIST_INIT(&(conf->smp_args));
- p->cap |= PR_CAP_LISTEN;
-
- if (smp_resolve_args(p, &err) != 0) {
- FLT_OTEL_ALERT("%s", err);
- ha_free(&err);
-
- retval++;
- }
-
- p->cap = saved_cap;
- }
-
- /*
- * If only the proxy specified with the <p> parameter is checked, then
- * no duplicate filters can be found that are not defined in the same
- * configuration sections.
- */
- for (px = proxies_list; px != NULL; px = px->next) {
- struct flt_conf *fconf_tmp;
-
- OTELC_DBG(NOTICE, "proxy '%s'", px->id);
-
- /*
- * The names of all OTEL filters (filter ID) should be checked,
- * they must be unique.
- */
- list_for_each_entry(fconf_tmp, &(px->filter_configs), list)
- if ((fconf_tmp != fconf) && (fconf_tmp->id == otel_flt_id)) {
- struct flt_otel_conf *conf_tmp = fconf_tmp->conf;
-
- OTELC_DBG(NOTICE, " OTEL filter '%s'", conf_tmp->id);
-
- if (strcmp(conf_tmp->id, conf->id) == 0) {
- FLT_OTEL_ALERT("''%s' : duplicated filter ID'", conf_tmp->id);
-
- retval++;
- }
- }
- }
-
- if (FLT_OTEL_DEREF(conf->instr, id, NULL) == NULL) {
- FLT_OTEL_ALERT("''%s' : no instrumentation found'", conf->id);
-
- retval++;
- }
-
- if ((conf->instr != NULL) && (conf->instr->config == NULL)) {
- FLT_OTEL_ALERT("''%s' : no configuration file specified'", conf->instr->id);
-
- retval++;
- }
-
- /*
- * Checking that defined 'otel-group' section names are unique.
- */
- list_for_each_entry(conf_group, &(conf->groups), list) {
- struct flt_otel_conf_group *conf_group_tmp;
-
- list_for_each_entry(conf_group_tmp, &(conf->groups), list) {
- if ((conf_group_tmp != conf_group) && (strcmp(conf_group_tmp->id, conf_group->id) == 0)) {
- FLT_OTEL_ALERT("''%s' : duplicated " FLT_OTEL_PARSE_SECTION_GROUP_ID " '%s''", conf->id, conf_group->id);
-
- retval++;
-
- break;
- }
- }
- }
-
- /*
- * Checking that defined 'otel-scope' section names are unique.
- */
- list_for_each_entry(conf_scope, &(conf->scopes), list) {
- struct flt_otel_conf_scope *conf_scope_tmp;
-
- list_for_each_entry(conf_scope_tmp, &(conf->scopes), list) {
- if ((conf_scope_tmp != conf_scope) && (strcmp(conf_scope_tmp->id, conf_scope->id) == 0)) {
- FLT_OTEL_ALERT("''%s' : duplicated " FLT_OTEL_PARSE_SECTION_SCOPE_ID " '%s''", conf->id, conf_scope->id);
-
- retval++;
-
- break;
- }
- }
- }
-
- /*
- * Checking that defined 'otel-group' sections are not empty.
- */
- list_for_each_entry(conf_group, &(conf->groups), list)
- if (LIST_ISEMPTY(&(conf_group->ph_scopes)))
- FLT_OTEL_ALERT("''%s' : " FLT_OTEL_PARSE_SECTION_GROUP_ID " '%s' has no scopes'", conf->id, conf_group->id);
-
- /*
- * Checking that all defined 'otel-group' sections have correctly declared
- * 'otel-scope' sections (ie whether the declared 'otel-scope' sections have
- * corresponding definitions).
- */
- list_for_each_entry(conf_group, &(conf->groups), list)
- list_for_each_entry(ph_scope, &(conf_group->ph_scopes), list) {
- bool flag_found = 0;
-
- list_for_each_entry(conf_scope, &(conf->scopes), list)
- if (strcmp(ph_scope->id, conf_scope->id) == 0) {
- ph_scope->ptr = conf_scope;
- conf_scope->flag_used = 1;
- flag_found = 1;
-
- break;
- }
-
- if (!flag_found) {
- FLT_OTEL_ALERT("'" FLT_OTEL_PARSE_SECTION_GROUP_ID " '%s' : references undefined " FLT_OTEL_PARSE_SECTION_SCOPE_ID " '%s''", conf_group->id, ph_scope->id);
-
- retval++;
- }
- }
-
- if (conf->instr != NULL) {
- /*
- * Checking that all declared 'groups' keywords have correctly
- * defined 'otel-group' sections.
- */
- list_for_each_entry(ph_group, &(conf->instr->ph_groups), list) {
- bool flag_found = 0;
-
- list_for_each_entry(conf_group, &(conf->groups), list)
- if (strcmp(ph_group->id, conf_group->id) == 0) {
- ph_group->ptr = conf_group;
- conf_group->flag_used = 1;
- flag_found = 1;
-
- break;
- }
-
- if (!flag_found) {
- FLT_OTEL_ALERT("'" FLT_OTEL_PARSE_SECTION_INSTR_ID " '%s' : references undefined " FLT_OTEL_PARSE_SECTION_GROUP_ID " '%s''", conf->instr->id, ph_group->id);
-
- retval++;
- }
- }
-
- /*
- * Checking that all declared 'scopes' keywords have correctly
- * defined 'otel-scope' sections.
- */
- list_for_each_entry(ph_scope, &(conf->instr->ph_scopes), list) {
- bool flag_found = 0;
-
- list_for_each_entry(conf_scope, &(conf->scopes), list)
- if (strcmp(ph_scope->id, conf_scope->id) == 0) {
- ph_scope->ptr = conf_scope;
- conf_scope->flag_used = 1;
- flag_found = 1;
-
- break;
- }
-
- if (!flag_found) {
- FLT_OTEL_ALERT("'" FLT_OTEL_PARSE_SECTION_INSTR_ID " '%s' : references undefined " FLT_OTEL_PARSE_SECTION_SCOPE_ID " '%s''", conf->instr->id, ph_scope->id);
-
- retval++;
- }
- }
- }
-
- OTELC_DBG(DEBUG, "--- filter '%s' configuration ----------", conf->id);
- OTELC_DBG(DEBUG, "- defined spans ----------");
-
- /*
- * Walk every configured scope: for used ones, log the defined spans,
- * count root spans, and set the required analyzer bits; for unused
- * ones, record a warning so the operator is notified.
- */
- list_for_each_entry(conf_scope, &(conf->scopes), list) {
- if (conf_scope->flag_used) {
- struct flt_otel_conf_span *conf_span;
-
- /*
- * In principle, only one span should be labeled
- * as a root span.
- */
- list_for_each_entry(conf_span, &(conf_scope->spans), list) {
- FLT_OTEL_DBG_CONF_SPAN(" ", conf_span);
-
- span_root_cnt += conf_span->flag_root ? 1 : 0;
- }
-
-#ifdef DEBUG_OTEL
- conf->cnt.event[conf_scope->event].flag_used = 1;
-#endif
-
- /* Set the flags of the analyzers used. */
- conf->instr->analyzers |= flt_otel_event_data[conf_scope->event].an_bit;
-
- /* Track the minimum idle timeout. */
- if (conf_scope->event == FLT_OTEL_EVENT__IDLE_TIMEOUT)
- if ((conf->instr->idle_timeout == 0) || (conf_scope->idle_timeout < conf->instr->idle_timeout))
- conf->instr->idle_timeout = conf_scope->idle_timeout;
- } else {
- FLT_OTEL_ALERT("''%s' : unused " FLT_OTEL_PARSE_SECTION_SCOPE_ID " '%s''", conf->id, conf_scope->id);
-
- scope_unused_cnt++;
- }
- }
-
- /*
- * Unused scopes or a number of root spans other than one do not
- * necessarily have to be errors, but it is good to print it when
- * starting HAProxy.
- */
- if (scope_unused_cnt > 0)
- FLT_OTEL_ALERT("''%s' : %d scope(s) not in use'", conf->id, scope_unused_cnt);
-
- if (LIST_ISEMPTY(&(conf->scopes)))
- /* Do nothing. */;
- else if (span_root_cnt == 0)
- FLT_OTEL_ALERT("''%s' : no span is marked as the root span'", conf->id);
- else if (span_root_cnt > 1)
- FLT_OTEL_ALERT("''%s' : multiple spans are marked as the root span'", conf->id);
-
- OTELC_DBG(DEBUG, "- defined instruments ----------");
-
- /*
- * Validate update-form instruments: for each one, resolve its reference
- * to the matching create-form instrument definition.
- *
- * Validate create-form instruments: check that names are unique across
- * all scopes.
- */
- list_for_each_entry(conf_scope, &(conf->scopes), list) {
- struct flt_otel_conf_instrument *conf_instr, *instr;
- struct flt_otel_conf_scope *scope;
-
- list_for_each_entry(conf_instr, &(conf_scope->instruments), list) {
- if (conf_instr->type == OTELC_METRIC_INSTRUMENT_UPDATE) {
- FLT_OTEL_DBG_CONF_INSTRUMENT(" update ", conf_instr);
-
- /*
- * Search all scopes for a create-form instrument
- * whose name matches this update-form instrument.
- */
- list_for_each_entry(scope, &(conf->scopes), list) {
- list_for_each_entry(instr, &(scope->instruments), list) {
- if ((instr->type != OTELC_METRIC_INSTRUMENT_UPDATE) && (strcmp(instr->id, conf_instr->id) == 0))
- conf_instr->ref = instr;
-
- if (conf_instr->ref != NULL)
- break;
- }
-
- if (conf_instr->ref != NULL)
- break;
- }
-
- if (conf_instr->ref == NULL) {
- FLT_OTEL_ALERT("''%s' : update-form instrument has no matching create-form definition'", conf_instr->id);
-
- retval++;
- }
- } else {
- bool flag_past = false, flag_dup = false;
-
- FLT_OTEL_DBG_CONF_INSTRUMENT(" create ", conf_instr);
-
- if (LIST_ISEMPTY(&(conf_instr->samples))) {
- FLT_OTEL_ALERT("''%s' : create-form instrument '%s' has no value expression'", conf->id, conf_instr->id);
-
- retval++;
- }
-
- if ((conf_instr->aggr_type == OTELC_METRIC_AGGREGATION_UNSET) && (conf_instr->type == OTELC_METRIC_INSTRUMENT_HISTOGRAM_UINT64))
- conf_instr->aggr_type = OTELC_METRIC_AGGREGATION_HISTOGRAM;
-
- /*
- * Checking that create-form instrument names
- * are unique across all scopes. Only compare
- * forward to avoid reporting the same pair
- * twice.
- */
- list_for_each_entry(scope, &(conf->scopes), list) {
- list_for_each_entry(instr, &(scope->instruments), list)
- if (instr == conf_instr) {
- flag_past = true;
-
- continue;
- }
- else if (!flag_past || (instr->type == OTELC_METRIC_INSTRUMENT_UPDATE)) {
- continue;
- }
- else if (strcmp(instr->id, conf_instr->id) == 0) {
- FLT_OTEL_ALERT("''%s' : duplicated create-form instrument '%s''", conf->id, conf_instr->id);
-
- retval++;
-
- flag_dup = true;
- break;
- }
-
- if (flag_dup)
- break;
- }
- }
- }
- }
-
- OTELC_DBG(DEBUG, "- defined log records ----------");
-
- /*
- * Validate log-record span references: for each log-record that
- * names a span, verify that a span with that name exists in one
- * of the configured scopes.
- */
- list_for_each_entry(conf_scope, &(conf->scopes), list) {
- struct flt_otel_conf_log_record *conf_log;
-
- list_for_each_entry(conf_log, &(conf_scope->log_records), list) {
- FLT_OTEL_DBG_CONF_LOG_RECORD(" ", conf_log);
-
- if (conf_log->span != NULL) {
- struct flt_otel_conf_scope *find_scope;
- struct flt_otel_conf_span *find_span;
- bool flag_found = false;
-
- list_for_each_entry(find_scope, &(conf->scopes), list) {
- list_for_each_entry(find_span, &(find_scope->spans), list)
- if (strcmp(find_span->id, conf_log->span) == 0) {
- flag_found = true;
-
- break;
- }
-
- if (flag_found)
- break;
- }
-
- if (!flag_found) {
- FLT_OTEL_ALERT("'" FLT_OTEL_PARSE_SECTION_SCOPE_ID " '%s' : log-record references undefined span '%s''", conf_scope->id, conf_log->span);
-
- retval++;
- }
- }
- }
- }
-
- FLT_OTEL_DBG_LIST(conf, group, "", "defined", _group,
- FLT_OTEL_DBG_CONF_GROUP(" ", _group);
- FLT_OTEL_DBG_LIST(_group, ph_scope, " ", "used", _scope, FLT_OTEL_DBG_CONF_PH(" ", _scope)));
- FLT_OTEL_DBG_LIST(conf, scope, "", "defined", _scope, FLT_OTEL_DBG_CONF_SCOPE(" ", _scope));
-
- if (conf->instr != NULL) {
- OTELC_DBG(DEBUG, " --- instrumentation '%s' configuration ----------", conf->instr->id);
- FLT_OTEL_DBG_LIST(conf->instr, ph_group, " ", "used", _group, FLT_OTEL_DBG_CONF_PH(" ", _group));
- FLT_OTEL_DBG_LIST(conf->instr, ph_scope, " ", "used", _scope, FLT_OTEL_DBG_CONF_PH(" ", _scope));
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_ops_init_per_thread - per-thread init callback (flt_ops.init_per_thread)
- *
- * SYNOPSIS
- * static int flt_otel_ops_init_per_thread(struct proxy *p, struct flt_conf *fconf)
- *
- * ARGUMENTS
- * p - the proxy to which the filter is attached
- * fconf - the filter configuration
- *
- * DESCRIPTION
- * Per-thread filter initialization called after thread creation. Starts
- * the OTel tracer and meter threads via their start operations and enables
- * HTX stream filtering. Subsequent calls on the same filter are no-ops.
- *
- * RETURN VALUE
- * Returns a negative value if an error occurs, any other value otherwise.
- */
-static int flt_otel_ops_init_per_thread(struct proxy *p, struct flt_conf *fconf)
-{
- struct flt_otel_conf *conf = FLT_OTEL_DEREF(fconf, conf, NULL);
- int retval = FLT_OTEL_RET_ERROR;
-
- OTELC_FUNC("%p, %p", p, fconf);
-
- if (conf == NULL)
- OTELC_RETURN_INT(retval);
-
-#if defined(USE_THREAD) && defined(DEBUG_OTEL)
- flt_otel_tid[tid].id = pthread_self();
- flt_otel_tid[tid].registered = true;
-#endif
-
- /*
- * Start the OpenTelemetry library tracer thread. Enable HTX streams
- * filtering.
- */
- if (!(fconf->flags & FLT_CFG_FL_HTX)) {
- retval = OTELC_OPS(conf->instr->tracer, start);
- if (retval == OTELC_RET_ERROR)
- FLT_OTEL_ALERT("%s", conf->instr->tracer->err);
-
- if (retval != OTELC_RET_ERROR) {
- retval = OTELC_OPS(conf->instr->meter, start);
- if (retval == OTELC_RET_ERROR)
- FLT_OTEL_ALERT("%s", conf->instr->meter->err);
- }
-
- if (retval != OTELC_RET_ERROR) {
- retval = OTELC_OPS(conf->instr->logger, start);
- if (retval == OTELC_RET_ERROR)
- FLT_OTEL_ALERT("%s", conf->instr->logger->err);
- }
-
- if (retval != FLT_OTEL_RET_ERROR)
- fconf->flags |= FLT_CFG_FL_HTX;
- } else {
- retval = FLT_OTEL_RET_OK;
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-#ifdef DEBUG_OTEL
-
-/***
- * NAME
- * flt_otel_ops_deinit_per_thread - per-thread deinit callback (flt_ops.deinit_per_thread)
- *
- * SYNOPSIS
- * static void flt_otel_ops_deinit_per_thread(struct proxy *p, struct flt_conf *fconf)
- *
- * ARGUMENTS
- * p - the proxy to which the filter is attached
- * fconf - the filter configuration
- *
- * DESCRIPTION
- * It cleans up what the init_per_thread callback has done. It is called
- * in the context of a thread, before exiting it.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-static void flt_otel_ops_deinit_per_thread(struct proxy *p, struct flt_conf *fconf)
-{
- OTELC_FUNC("%p, %p", p, fconf);
-
- OTELC_RETURN();
-}
-
-#endif /* DEBUG_OTEL */
-
-
-/***
- * NAME
- * flt_otel_ops_attach - filter attach callback (flt_ops.attach)
- *
- * SYNOPSIS
- * static int flt_otel_ops_attach(struct stream *s, struct filter *f)
- *
- * ARGUMENTS
- * s - the stream to which the filter is being attached
- * f - the filter instance
- *
- * DESCRIPTION
- * It is called after a filter instance creation, when it is attached to a
- * stream. This happens when the stream is started for filters defined on
- * the stream's frontend and when the backend is set for filters declared
- * on the stream's backend. It is possible to ignore the filter, if needed,
- * by returning 0. This could be useful to have conditional filtering.
- *
- * RETURN VALUE
- * Returns a negative value if an error occurs, 0 to ignore the filter,
- * any other value otherwise.
- */
-static int flt_otel_ops_attach(struct stream *s, struct filter *f)
-{
- const struct flt_otel_conf *conf = FLT_OTEL_CONF(f);
- char *err = NULL;
-
- OTELC_FUNC("%p, %p", s, f);
-
- /* Skip attachment when the filter is globally disabled. */
- if (_HA_ATOMIC_LOAD(&(conf->instr->flag_disabled))) {
- OTELC_DBG(NOTICE, "filter '%s', type: %s (disabled)", conf->id, flt_otel_type(f));
-
-#ifdef FLT_OTEL_USE_COUNTERS
- _HA_ATOMIC_ADD(FLT_OTEL_CONF(f)->cnt.attached + 2, 1);
-#endif
-
- OTELC_RETURN_INT(FLT_OTEL_RET_IGNORE);
- }
- else if (_HA_ATOMIC_LOAD(&(conf->instr->rate_limit)) < FLT_OTEL_FLOAT_U32(100.0)) {
- uint32_t rnd = ha_random32();
- uint32_t rate = _HA_ATOMIC_LOAD(&(conf->instr->rate_limit));
-
- if (rate <= rnd) {
- OTELC_DBG(NOTICE, "filter '%s', type: %s (ignored: %u <= %u)", conf->id, flt_otel_type(f), rate, rnd);
-
-#ifdef FLT_OTEL_USE_COUNTERS
- _HA_ATOMIC_ADD(FLT_OTEL_CONF(f)->cnt.attached + 1, 1);
-#endif
-
- OTELC_RETURN_INT(FLT_OTEL_RET_IGNORE);
- }
- }
-
- OTELC_DBG(NOTICE, "filter '%s', type: %s (run)", conf->id, flt_otel_type(f));
-
- /* Create the per-stream runtime context. */
- f->ctx = flt_otel_runtime_context_init(s, f, &err);
- FLT_OTEL_ERR_FREE(err);
- if (f->ctx == NULL) {
- FLT_OTEL_LOG(LOG_EMERG, "failed to create context");
-
-#ifdef FLT_OTEL_USE_COUNTERS
- _HA_ATOMIC_ADD(FLT_OTEL_CONF(f)->cnt.attached + 3, 1);
-#endif
-
- OTELC_RETURN_INT(FLT_OTEL_RET_IGNORE);
- }
-
- /*
- * AN_REQ_WAIT_HTTP and AN_RES_WAIT_HTTP analyzers can only be used
- * in the .channel_post_analyze callback function.
- */
- f->pre_analyzers |= conf->instr->analyzers & ((AN_REQ_ALL & ~AN_REQ_WAIT_HTTP & ~AN_REQ_HTTP_TARPIT) | (AN_RES_ALL & ~AN_RES_WAIT_HTTP));
- f->post_analyzers |= conf->instr->analyzers & (AN_REQ_WAIT_HTTP | AN_RES_WAIT_HTTP);
-
-#ifdef FLT_OTEL_USE_COUNTERS
- _HA_ATOMIC_ADD(FLT_OTEL_CONF(f)->cnt.attached + 0, 1);
-#endif
- FLT_OTEL_LOG(LOG_INFO, "%08x %08x", f->pre_analyzers, f->post_analyzers);
-
-#ifdef USE_OTEL_VARS
- flt_otel_vars_dump(s);
-#endif
- flt_otel_http_headers_dump(&(s->req));
-
- OTELC_RETURN_INT(FLT_OTEL_RET_OK);
-}
-
-
-/***
- * NAME
- * flt_otel_ops_stream_start - stream start callback (flt_ops.stream_start)
- *
- * SYNOPSIS
- * static int flt_otel_ops_stream_start(struct stream *s, struct filter *f)
- *
- * ARGUMENTS
- * s - the stream that is being started
- * f - the filter instance
- *
- * DESCRIPTION
- * It is called when a stream is started. This callback can fail by returning
- * a negative value. It will be considered as a critical error by HAProxy
- * which disabled the listener for a short time. After the stream-start
- * event, it initializes the idle timer in the runtime context from the
- * precomputed minimum idle_timeout in the instrumentation configuration.
- *
- * RETURN VALUE
- * Returns a negative value if an error occurs, any other value otherwise.
- */
-static int flt_otel_ops_stream_start(struct stream *s, struct filter *f)
-{
- const struct flt_otel_conf *conf = FLT_OTEL_CONF(f);
- struct flt_otel_runtime_context *rt_ctx;
- char *err = NULL;
- int retval = FLT_OTEL_RET_OK;
-
- OTELC_FUNC("%p, %p", s, f);
-
- if (flt_otel_is_disabled(f FLT_OTEL_DBG_ARGS(, FLT_OTEL_EVENT__STREAM_START)))
- OTELC_RETURN_INT(retval);
-
- /* The result of the function is ignored. */
- (void)flt_otel_event_run(s, f, NULL, FLT_OTEL_EVENT__STREAM_START, &err);
-
- /*
- * Initialize the idle timer from the precomputed minimum idle_timeout
- * in the instrumentation configuration.
- */
- if (conf->instr->idle_timeout != 0) {
- rt_ctx = FLT_OTEL_RT_CTX(f->ctx);
-
- rt_ctx->idle_timeout = conf->instr->idle_timeout;
- rt_ctx->idle_exp = tick_add(now_ms, rt_ctx->idle_timeout);
-
- s->req.analyse_exp = tick_first(s->req.analyse_exp, rt_ctx->idle_exp);
- }
-
- OTELC_RETURN_INT(flt_otel_return_int(f, &err, retval));
-}
-
-
-/***
- * NAME
- * flt_otel_ops_stream_set_backend - stream set-backend callback (flt_ops.stream_set_backend)
- *
- * SYNOPSIS
- * static int flt_otel_ops_stream_set_backend(struct stream *s, struct filter *f, struct proxy *be)
- *
- * ARGUMENTS
- * s - the stream being processed
- * f - the filter instance
- * be - the backend proxy being assigned
- *
- * DESCRIPTION
- * It is called when a backend is set for a stream. This callback will be
- * called for all filters attached to a stream (frontend and backend). Note
- * this callback is not called if the frontend and the backend are the same.
- * It fires the on-backend-set event.
- *
- * RETURN VALUE
- * Returns a negative value if an error occurs, any other value otherwise.
- */
-static int flt_otel_ops_stream_set_backend(struct stream *s, struct filter *f, struct proxy *be)
-{
- char *err = NULL;
- int retval = FLT_OTEL_RET_OK;
-
- OTELC_FUNC("%p, %p, %p", s, f, be);
-
- if (flt_otel_is_disabled(f FLT_OTEL_DBG_ARGS(, FLT_OTEL_EVENT__BACKEND_SET)))
- OTELC_RETURN_INT(retval);
-
- OTELC_DBG(DEBUG, "backend: %s", be->id);
-
- (void)flt_otel_event_run(s, f, &(s->req), FLT_OTEL_EVENT__BACKEND_SET, &err);
-
- OTELC_RETURN_INT(flt_otel_return_int(f, &err, retval));
-}
-
-
-/***
- * NAME
- * flt_otel_ops_stream_stop - stream stop callback (flt_ops.stream_stop)
- *
- * SYNOPSIS
- * static void flt_otel_ops_stream_stop(struct stream *s, struct filter *f)
- *
- * ARGUMENTS
- * s - the stream being stopped
- * f - the filter instance
- *
- * DESCRIPTION
- * It is called when a stream is stopped. This callback always succeed.
- * Anyway, it is too late to return an error.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-static void flt_otel_ops_stream_stop(struct stream *s, struct filter *f)
-{
- char *err = NULL;
-
- OTELC_FUNC("%p, %p", s, f);
-
- if (flt_otel_is_disabled(f FLT_OTEL_DBG_ARGS(, FLT_OTEL_EVENT__STREAM_STOP)))
- OTELC_RETURN();
-
- /* The result of the function is ignored. */
- (void)flt_otel_event_run(s, f, NULL, FLT_OTEL_EVENT__STREAM_STOP, &err);
-
- flt_otel_return_void(f, &err);
-
- OTELC_RETURN();
-}
-
-
-/***
- * NAME
- * flt_otel_ops_detach - filter detach callback (flt_ops.detach)
- *
- * SYNOPSIS
- * static void flt_otel_ops_detach(struct stream *s, struct filter *f)
- *
- * ARGUMENTS
- * s - the stream from which the filter is being detached
- * f - the filter instance
- *
- * DESCRIPTION
- * It is called when a filter instance is detached from a stream, before its
- * destruction. This happens when the stream is stopped for filters defined
- * on the stream's frontend and when the analyze ends for filters defined on
- * the stream's backend.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-static void flt_otel_ops_detach(struct stream *s, struct filter *f)
-{
- OTELC_FUNC("%p, %p", s, f);
-
- OTELC_DBG(NOTICE, "filter '%s', type: %s", FLT_OTEL_CONF(f)->id, flt_otel_type(f));
-
- flt_otel_runtime_context_free(f);
-
- OTELC_RETURN();
-}
-
-
-/***
- * NAME
- * flt_otel_ops_check_timeouts - timeout callback (flt_ops.check_timeouts)
- *
- * SYNOPSIS
- * static void flt_otel_ops_check_timeouts(struct stream *s, struct filter *f)
- *
- * ARGUMENTS
- * s - the stream whose timer has expired
- * f - the filter instance
- *
- * DESCRIPTION
- * Timeout callback for the filter. When the idle-timeout timer has expired,
- * it fires the on-idle-timeout event via flt_otel_event_run() and reschedules
- * the timer for the next interval. It also sets the STRM_EVT_MSG pending
- * event flag on the <s> stream so that the stream processing loop
- * re-evaluates the message state after the timeout.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-static void flt_otel_ops_check_timeouts(struct stream *s, struct filter *f)
-{
- struct flt_otel_runtime_context *rt_ctx;
- char *err = NULL;
-
- OTELC_FUNC("%p, %p", s, f);
-
- if (flt_otel_is_disabled(f FLT_OTEL_DBG_ARGS(, -1)))
- OTELC_RETURN();
-
- rt_ctx = FLT_OTEL_RT_CTX(f->ctx);
-
- /*
- * This callback is invoked for every timer event on the stream,
- * not only for our idle timer. The filter API provides no way to
- * distinguish which timer expired, so the tick check below is the only
- * mechanism to determine whether our idle timer is the one that fired.
- */
- if (tick_isset(rt_ctx->idle_exp) && tick_is_expired(rt_ctx->idle_exp, now_ms)) {
- /* Fire the on-idle-timeout event. */
- (void)flt_otel_event_run(s, f, &(s->req), FLT_OTEL_EVENT__IDLE_TIMEOUT, &err);
-
- /* Reschedule the next idle timeout. */
- rt_ctx->idle_exp = tick_add(now_ms, rt_ctx->idle_timeout);
-
- /*
- * Reset analyse_exp if it has expired before merging in the new
- * idle tick. Without this, tick_first() would keep returning
- * the stale expired value, causing the stream task to wake in
- * a tight loop.
- */
- if (tick_is_expired(s->req.analyse_exp, now_ms))
- s->req.analyse_exp = TICK_ETERNITY;
-
- s->req.analyse_exp = tick_first(s->req.analyse_exp, rt_ctx->idle_exp);
-
- /* Force the request and response analysers to be re-evaluated. */
- s->pending_events |= STRM_EVT_MSG;
- }
-
- flt_otel_return_void(f, &err);
-
- OTELC_RETURN();
-}
-
-
-/***
- * NAME
- * flt_otel_ops_channel_start_analyze - channel start-analyze callback
- *
- * SYNOPSIS
- * static int flt_otel_ops_channel_start_analyze(struct stream *s, struct filter *f, struct channel *chn)
- *
- * ARGUMENTS
- * s - the stream being analyzed
- * f - the filter instance
- * chn - the channel on which the analyzing starts
- *
- * DESCRIPTION
- * Channel start-analyze callback. It registers the configured analyzers
- * on the <chn> channel and runs the client or server session-start event
- * depending on the channel direction.
- *
- * RETURN VALUE
- * Returns a negative value if an error occurs, 0 if it needs to wait,
- * any other value otherwise.
- */
-static int flt_otel_ops_channel_start_analyze(struct stream *s, struct filter *f, struct channel *chn)
-{
- char *err = NULL;
- int retval;
-
- OTELC_FUNC("%p, %p, %p", s, f, chn);
-
- if (flt_otel_is_disabled(f FLT_OTEL_DBG_ARGS(, (chn->flags & CF_ISRESP) ? FLT_OTEL_EVENT_RES_SERVER_SESS_START : FLT_OTEL_EVENT_REQ_CLIENT_SESS_START)))
- OTELC_RETURN_INT(FLT_OTEL_RET_OK);
-
- OTELC_DBG(DEBUG, "channel: %s, mode: %s (%s)", flt_otel_chn_label(chn), flt_otel_pr_mode(s), flt_otel_stream_pos(s));
-
- if (chn->flags & CF_ISRESP) {
- /* The response channel. */
- chn->analysers |= f->pre_analyzers & AN_RES_ALL;
-
- /* The event 'on-server-session-start'. */
- retval = flt_otel_event_run(s, f, chn, FLT_OTEL_EVENT_RES_SERVER_SESS_START, &err);
-
- /*
- * WAIT is currently never returned by flt_otel_event_run(),
- * this is kept for defensive purposes only.
- */
- if (retval == FLT_OTEL_RET_WAIT) {
- channel_dont_read(chn);
- channel_dont_close(chn);
- }
- } else {
- /* The request channel. */
- chn->analysers |= f->pre_analyzers & AN_REQ_ALL;
-
- /* The event 'on-client-session-start'. */
- retval = flt_otel_event_run(s, f, chn, FLT_OTEL_EVENT_REQ_CLIENT_SESS_START, &err);
- }
-
- /*
- * Data filter registration is intentionally disabled. The http_payload
- * and tcp_payload callbacks are debug-only stubs (registered via
- * OTELC_DBG_IFDEF) and do not process data.
- *
- * register_data_filter(s, chn, f);
- */
-
- /*
- * Propagate the idle-timeout expiry to the channel so the stream task
- * keeps waking at the configured interval.
- */
- if (tick_isset(FLT_OTEL_RT_CTX(f->ctx)->idle_exp))
- chn->analyse_exp = tick_first(chn->analyse_exp, FLT_OTEL_RT_CTX(f->ctx)->idle_exp);
-
- OTELC_RETURN_INT(flt_otel_return_int(f, &err, retval));
-}
-
-
-/***
- * NAME
- * flt_otel_get_event - look up an event index by analyzer bit
- *
- * SYNOPSIS
- * static int flt_otel_get_event(uint an_bit)
- *
- * ARGUMENTS
- * an_bit - analyzer bit to search for
- *
- * DESCRIPTION
- * Searches the flt_otel_event_data table for the entry whose an_bit field
- * matches <an_bit>.
- *
- * RETURN VALUE
- * Returns the table index on success, FLT_OTEL_RET_ERROR if no match is
- * found.
- */
-static int flt_otel_get_event(uint an_bit)
-{
- int i;
-
- for (i = 0; i < OTELC_TABLESIZE(flt_otel_event_data); i++)
- if (flt_otel_event_data[i].an_bit == an_bit)
- return i;
-
- return FLT_OTEL_RET_ERROR;
-}
-
-
-/***
- * NAME
- * flt_otel_ops_channel_pre_analyze - channel pre-analyze callback
- *
- * SYNOPSIS
- * static int flt_otel_ops_channel_pre_analyze(struct stream *s, struct filter *f, struct channel *chn, uint an_bit)
- *
- * ARGUMENTS
- * s - the stream being analyzed
- * f - the filter instance
- * chn - the channel on which the analyzing is done
- * an_bit - the analyzer identifier bit
- *
- * DESCRIPTION
- * Channel pre-analyze callback. It maps the <an_bit> analyzer bit to an
- * event index and runs the corresponding event via flt_otel_event_run().
- *
- * RETURN VALUE
- * Returns a negative value if an error occurs, 0 if it needs to wait,
- * any other value otherwise.
- */
-static int flt_otel_ops_channel_pre_analyze(struct stream *s, struct filter *f, struct channel *chn, uint an_bit)
-{
- char *err = NULL;
- int event, retval;
-
- OTELC_FUNC("%p, %p, %p, 0x%08x", s, f, chn, an_bit);
-
- event = flt_otel_get_event(an_bit);
- if (event == FLT_OTEL_RET_ERROR)
- OTELC_RETURN_INT(FLT_OTEL_RET_OK);
- else if (flt_otel_is_disabled(f FLT_OTEL_DBG_ARGS(, event)))
- OTELC_RETURN_INT(FLT_OTEL_RET_OK);
-
- OTELC_DBG(DEBUG, "channel: %s, mode: %s (%s), analyzer: %s", flt_otel_chn_label(chn), flt_otel_pr_mode(s), flt_otel_stream_pos(s), flt_otel_analyzer(an_bit));
-
- retval = flt_otel_event_run(s, f, chn, event, &err);
-
- /*
- * WAIT is currently never returned by flt_otel_event_run(), this is
- * kept for defensive purposes only.
- */
- if ((retval == FLT_OTEL_RET_WAIT) && (chn->flags & CF_ISRESP)) {
- channel_dont_read(chn);
- channel_dont_close(chn);
- }
-
- OTELC_RETURN_INT(flt_otel_return_int(f, &err, retval));
-}
-
-
-/***
- * NAME
- * flt_otel_ops_channel_post_analyze - channel post-analyze callback
- *
- * SYNOPSIS
- * static int flt_otel_ops_channel_post_analyze(struct stream *s, struct filter *f, struct channel *chn, uint an_bit)
- *
- * ARGUMENTS
- * s - the stream being analyzed
- * f - the filter instance
- * chn - the channel on which the analyzing is done
- * an_bit - the analyzer identifier bit
- *
- * DESCRIPTION
- * This function, for its part, is not resumable. It is called when a
- * filterable analyzer finishes its processing. So it is called once for
- * the same analyzer.
- *
- * RETURN VALUE
- * Returns a negative value if an error occurs, 0 if it needs to wait,
- * any other value otherwise.
- */
-static int flt_otel_ops_channel_post_analyze(struct stream *s, struct filter *f, struct channel *chn, uint an_bit)
-{
- char *err = NULL;
- int event, retval;
-
- OTELC_FUNC("%p, %p, %p, 0x%08x", s, f, chn, an_bit);
-
- event = flt_otel_get_event(an_bit);
- if (event == FLT_OTEL_RET_ERROR)
- OTELC_RETURN_INT(FLT_OTEL_RET_OK);
- else if (flt_otel_is_disabled(f FLT_OTEL_DBG_ARGS(, event)))
- OTELC_RETURN_INT(FLT_OTEL_RET_OK);
-
- OTELC_DBG(DEBUG, "channel: %s, mode: %s (%s), analyzer: %s", flt_otel_chn_label(chn), flt_otel_pr_mode(s), flt_otel_stream_pos(s), flt_otel_analyzer(an_bit));
-
- retval = flt_otel_event_run(s, f, chn, event, &err);
-
- OTELC_RETURN_INT(flt_otel_return_int(f, &err, retval));
-}
-
-
-/***
- * NAME
- * flt_otel_ops_channel_end_analyze - channel end-analyze callback
- *
- * SYNOPSIS
- * static int flt_otel_ops_channel_end_analyze(struct stream *s, struct filter *f, struct channel *chn)
- *
- * ARGUMENTS
- * s - the stream being analyzed
- * f - the filter instance
- * chn - the channel on which the analyzing ends
- *
- * DESCRIPTION
- * Channel end-analyze callback. It runs the client or server session-end
- * event depending on the <chn> channel direction. For the request channel,
- * it also fires the server-unavailable event if response analyzers were
- * configured but never executed.
- *
- * RETURN VALUE
- * Returns a negative value if an error occurs, 0 if it needs to wait,
- * any other value otherwise.
- */
-static int flt_otel_ops_channel_end_analyze(struct stream *s, struct filter *f, struct channel *chn)
-{
- char *err = NULL;
- int rc, retval;
-
- OTELC_FUNC("%p, %p, %p", s, f, chn);
-
- if (flt_otel_is_disabled(f FLT_OTEL_DBG_ARGS(, (chn->flags & CF_ISRESP) ? FLT_OTEL_EVENT_RES_SERVER_SESS_END : FLT_OTEL_EVENT_REQ_CLIENT_SESS_END)))
- OTELC_RETURN_INT(FLT_OTEL_RET_OK);
-
- OTELC_DBG(DEBUG, "channel: %s, mode: %s (%s)", flt_otel_chn_label(chn), flt_otel_pr_mode(s), flt_otel_stream_pos(s));
-
- if (chn->flags & CF_ISRESP) {
- /* The response channel, event 'on-server-session-end'. */
- retval = flt_otel_event_run(s, f, chn, FLT_OTEL_EVENT_RES_SERVER_SESS_END, &err);
- } else {
- /* The request channel, event 'on-client-session-end'. */
- retval = flt_otel_event_run(s, f, chn, FLT_OTEL_EVENT_REQ_CLIENT_SESS_END, &err);
-
- /*
- * In case an event using server response is defined and not
- * executed, event 'on-server-unavailable' is called here.
- */
- if ((FLT_OTEL_CONF(f)->instr->analyzers & AN_RES_ALL) && !(FLT_OTEL_RT_CTX(f->ctx)->analyzers & AN_RES_ALL)) {
- rc = flt_otel_event_run(s, f, chn, FLT_OTEL_EVENT_REQ_SERVER_UNAVAILABLE, &err);
- if ((retval == FLT_OTEL_RET_OK) && (rc != FLT_OTEL_RET_OK))
- retval = rc;
- }
- }
-
- OTELC_RETURN_INT(flt_otel_return_int(f, &err, retval));
-}
-
-
-/***
- * NAME
- * flt_otel_ops_http_headers - HTTP headers callback (flt_ops.http_headers)
- *
- * SYNOPSIS
- * static int flt_otel_ops_http_headers(struct stream *s, struct filter *f, struct http_msg *msg)
- *
- * ARGUMENTS
- * s - the stream being processed
- * f - the filter instance
- * msg - the HTTP message whose headers are ready
- *
- * DESCRIPTION
- * HTTP headers callback. It fires the on-http-headers-request or
- * on-http-headers-response event depending on the channel direction.
- *
- * RETURN VALUE
- * Returns a negative value if an error occurs, 0 if it needs to wait,
- * any other value otherwise.
- */
-static int flt_otel_ops_http_headers(struct stream *s, struct filter *f, struct http_msg *msg)
-{
- int event = (msg->chn->flags & CF_ISRESP) ? FLT_OTEL_EVENT_RES_HTTP_HEADERS : FLT_OTEL_EVENT_REQ_HTTP_HEADERS;
- char *err = NULL;
- int retval = FLT_OTEL_RET_OK;
-
- OTELC_FUNC("%p, %p, %p", s, f, msg);
-
- if (flt_otel_is_disabled(f FLT_OTEL_DBG_ARGS(, event)))
- OTELC_RETURN_INT(retval);
-
- OTELC_DBG(DEBUG, "channel: %s, mode: %s (%s)", flt_otel_chn_label(msg->chn), flt_otel_pr_mode(s), flt_otel_stream_pos(s));
-
- (void)flt_otel_event_run(s, f, msg->chn, event, &err);
-
- OTELC_RETURN_INT(flt_otel_return_int(f, &err, retval));
-}
-
-
-#ifdef DEBUG_OTEL
-
-/***
- * NAME
- * flt_otel_ops_http_payload - HTTP payload callback (flt_ops.http_payload)
- *
- * SYNOPSIS
- * static int flt_otel_ops_http_payload(struct stream *s, struct filter *f, struct http_msg *msg, uint offset, uint len)
- *
- * ARGUMENTS
- * s - the stream being processed
- * f - the filter instance
- * msg - the HTTP message containing the payload
- * offset - the offset in the HTX message where data starts
- * len - the maximum number of bytes to forward
- *
- * DESCRIPTION
- * Debug-only HTTP payload callback. It logs the channel direction, proxy
- * mode, offset and data length. No actual data processing is performed.
- *
- * RETURN VALUE
- * Returns the number of bytes to forward, or a negative value on error.
- */
-static int flt_otel_ops_http_payload(struct stream *s, struct filter *f, struct http_msg *msg, uint offset, uint len)
-{
- char *err = NULL;
- int retval = len;
-
- OTELC_FUNC("%p, %p, %p, %u, %u", s, f, msg, offset, len);
-
- if (flt_otel_is_disabled(f FLT_OTEL_DBG_ARGS(, -1)))
- OTELC_RETURN_INT(len);
-
- OTELC_DBG(DEBUG, "channel: %s, mode: %s (%s), offset: %u, len: %u, forward: %d", flt_otel_chn_label(msg->chn), flt_otel_pr_mode(s), flt_otel_stream_pos(s), offset, len, retval);
-
- /* Debug stub -- retval is always len, wakeup is never reached. */
- if (retval != len)
- task_wakeup(s->task, TASK_WOKEN_MSG);
-
- OTELC_RETURN_INT(flt_otel_return_int(f, &err, retval));
-}
-
-#endif /* DEBUG_OTEL */
-
-
-/***
- * NAME
- * flt_otel_ops_http_end - HTTP end callback (flt_ops.http_end)
- *
- * SYNOPSIS
- * static int flt_otel_ops_http_end(struct stream *s, struct filter *f, struct http_msg *msg)
- *
- * ARGUMENTS
- * s - the stream being processed
- * f - the filter instance
- * msg - the HTTP message that has ended
- *
- * DESCRIPTION
- * HTTP end callback. It fires the on-http-end-request or
- * on-http-end-response event depending on the channel direction.
- *
- * RETURN VALUE
- * Returns a negative value if an error occurs, 0 if it needs to wait,
- * any other value otherwise.
- */
-static int flt_otel_ops_http_end(struct stream *s, struct filter *f, struct http_msg *msg)
-{
- int event = (msg->chn->flags & CF_ISRESP) ? FLT_OTEL_EVENT_RES_HTTP_END : FLT_OTEL_EVENT_REQ_HTTP_END;
- char *err = NULL;
- int retval = FLT_OTEL_RET_OK;
-
- OTELC_FUNC("%p, %p, %p", s, f, msg);
-
- if (flt_otel_is_disabled(f FLT_OTEL_DBG_ARGS(, event)))
- OTELC_RETURN_INT(retval);
-
- OTELC_DBG(DEBUG, "channel: %s, mode: %s (%s)", flt_otel_chn_label(msg->chn), flt_otel_pr_mode(s), flt_otel_stream_pos(s));
-
- (void)flt_otel_event_run(s, f, msg->chn, event, &err);
-
- OTELC_RETURN_INT(flt_otel_return_int(f, &err, retval));
-}
-
-
-/***
- * NAME
- * flt_otel_ops_http_reply - HTTP reply callback (flt_ops.http_reply)
- *
- * SYNOPSIS
- * static void flt_otel_ops_http_reply(struct stream *s, struct filter *f, short status, const struct buffer *msg)
- *
- * ARGUMENTS
- * s - the stream being processed
- * f - the filter instance
- * status - the HTTP status code of the reply
- * msg - the reply message buffer, or NULL
- *
- * DESCRIPTION
- * HTTP reply callback. It fires the on-http-reply event when HAProxy
- * generates an internal reply (e.g. error page or deny response).
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-static void flt_otel_ops_http_reply(struct stream *s, struct filter *f, short status, const struct buffer *msg)
-{
- char *err = NULL;
-
- OTELC_FUNC("%p, %p, %hd, %p", s, f, status, msg);
-
- if (flt_otel_is_disabled(f FLT_OTEL_DBG_ARGS(, FLT_OTEL_EVENT_RES_HTTP_REPLY)))
- OTELC_RETURN();
-
- OTELC_DBG(DEBUG, "channel: -, mode: %s (%s), status: %hd", flt_otel_pr_mode(s), flt_otel_stream_pos(s), status);
-
- (void)flt_otel_event_run(s, f, &(s->res), FLT_OTEL_EVENT_RES_HTTP_REPLY, &err);
-
- flt_otel_return_void(f, &err);
-
- OTELC_RETURN();
-}
-
-
-#ifdef DEBUG_OTEL
-
-/***
- * NAME
- * flt_otel_ops_http_reset - HTTP reset callback (flt_ops.http_reset)
- *
- * SYNOPSIS
- * static void flt_otel_ops_http_reset(struct stream *s, struct filter *f, struct http_msg *msg)
- *
- * ARGUMENTS
- * s - the stream being processed
- * f - the filter instance
- * msg - the HTTP message being reset
- *
- * DESCRIPTION
- * Debug-only HTTP reset callback. It logs the channel direction and proxy
- * mode when an HTTP message is reset (e.g. due to a redirect or retry).
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-static void flt_otel_ops_http_reset(struct stream *s, struct filter *f, struct http_msg *msg)
-{
- char *err = NULL;
-
- OTELC_FUNC("%p, %p, %p", s, f, msg);
-
- if (flt_otel_is_disabled(f FLT_OTEL_DBG_ARGS(, -1)))
- OTELC_RETURN();
-
- OTELC_DBG(DEBUG, "channel: %s, mode: %s (%s)", flt_otel_chn_label(msg->chn), flt_otel_pr_mode(s), flt_otel_stream_pos(s));
-
- flt_otel_return_void(f, &err);
-
- OTELC_RETURN();
-}
-
-
-/***
- * NAME
- * flt_otel_ops_tcp_payload - TCP payload callback (flt_ops.tcp_payload)
- *
- * SYNOPSIS
- * static int flt_otel_ops_tcp_payload(struct stream *s, struct filter *f, struct channel *chn, uint offset, uint len)
- *
- * ARGUMENTS
- * s - the stream being processed
- * f - the filter instance
- * chn - the channel containing the payload data
- * offset - the offset in the buffer where data starts
- * len - the maximum number of bytes to forward
- *
- * DESCRIPTION
- * Debug-only TCP payload callback. It logs the channel direction, proxy
- * mode, offset and data length. No actual data processing is performed.
- *
- * RETURN VALUE
- * Returns the number of bytes to forward, or a negative value on error.
- */
-static int flt_otel_ops_tcp_payload(struct stream *s, struct filter *f, struct channel *chn, uint offset, uint len)
-{
- char *err = NULL;
- int retval = len;
-
- OTELC_FUNC("%p, %p, %p, %u, %u", s, f, chn, offset, len);
-
- if (flt_otel_is_disabled(f FLT_OTEL_DBG_ARGS(, -1)))
- OTELC_RETURN_INT(len);
-
- OTELC_DBG(DEBUG, "channel: %s, mode: %s (%s), offset: %u, len: %u, forward: %d", flt_otel_chn_label(chn), flt_otel_pr_mode(s), flt_otel_stream_pos(s), offset, len, retval);
-
- /* Debug stub -- no data processing implemented yet. */
- if (s->flags & SF_HTX) {
- } else {
- }
-
- /* Debug stub -- retval is always len, wakeup is never reached. */
- if (retval != len)
- task_wakeup(s->task, TASK_WOKEN_MSG);
-
- OTELC_RETURN_INT(flt_otel_return_int(f, &err, retval));
-}
-
-#endif /* DEBUG_OTEL */
-
-
-struct flt_ops flt_otel_ops = {
- /* Callbacks to manage the filter lifecycle. */
- .init = flt_otel_ops_init,
- .deinit = flt_otel_ops_deinit,
- .check = flt_otel_ops_check,
- .init_per_thread = flt_otel_ops_init_per_thread,
- .deinit_per_thread = OTELC_DBG_IFDEF(flt_otel_ops_deinit_per_thread, NULL),
-
- /* Stream callbacks. */
- .attach = flt_otel_ops_attach,
- .stream_start = flt_otel_ops_stream_start,
- .stream_set_backend = flt_otel_ops_stream_set_backend,
- .stream_stop = flt_otel_ops_stream_stop,
- .detach = flt_otel_ops_detach,
- .check_timeouts = flt_otel_ops_check_timeouts,
-
- /* Channel callbacks. */
- .channel_start_analyze = flt_otel_ops_channel_start_analyze,
- .channel_pre_analyze = flt_otel_ops_channel_pre_analyze,
- .channel_post_analyze = flt_otel_ops_channel_post_analyze,
- .channel_end_analyze = flt_otel_ops_channel_end_analyze,
-
- /* HTTP callbacks. */
- .http_headers = flt_otel_ops_http_headers,
- .http_payload = OTELC_DBG_IFDEF(flt_otel_ops_http_payload, NULL),
- .http_end = flt_otel_ops_http_end,
- .http_reset = OTELC_DBG_IFDEF(flt_otel_ops_http_reset, NULL),
- .http_reply = flt_otel_ops_http_reply,
-
- /* TCP callbacks. */
- .tcp_payload = OTELC_DBG_IFDEF(flt_otel_ops_tcp_payload, NULL)
-};
-
-
-/* Advertise OTel support in haproxy -vv output. */
-REGISTER_BUILD_OPTS("Built with OpenTelemetry support (C++ version " OTELCPP_VERSION ", C Wrapper version " OTELC_VERSION ").");
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "../include/include.h"
-
-
-/* Group data table built from the X-macro list. */
-#define FLT_OTEL_GROUP_DEF(a,b,c) { a, b, c },
-const struct flt_otel_group_data flt_otel_group_data[] = { FLT_OTEL_GROUP_DEFINES };
-#undef FLT_OTEL_GROUP_DEF
-
-
-/***
- * NAME
- * flt_otel_group_action - group action execution callback
- *
- * SYNOPSIS
- * static enum act_return flt_otel_group_action(struct act_rule *rule, struct proxy *px, struct session *sess, struct stream *s, int opts)
- *
- * ARGUMENTS
- * rule - action rule containing group configuration references
- * px - proxy instance
- * sess - current session
- * s - current stream
- * opts - action options (ACT_OPT_* flags)
- *
- * DESCRIPTION
- * Executes the action_ptr callback for the FLT_OTEL_ACTION_GROUP action.
- * Retrieves the filter configuration, group definition, and runtime context
- * from the rule's argument pointers. If the filter is disabled or not
- * attached to the stream, processing is skipped. Otherwise, iterates over
- * all scopes defined in the group and runs each via flt_otel_scope_run().
- * Scope execution errors are logged but do not prevent the remaining scopes
- * from executing.
- *
- * RETURN VALUE
- * Returns ACT_RET_CONT.
- */
-static enum act_return flt_otel_group_action(struct act_rule *rule, struct proxy *px, struct session *sess, struct stream *s, int opts)
-{
- const struct filter *filter;
- const struct flt_conf *fconf;
- const struct flt_otel_conf *conf;
- const struct flt_otel_conf_group *conf_group;
- const struct flt_otel_runtime_context *rt_ctx = NULL;
- const struct flt_otel_conf_ph *ph_scope;
- char *err = NULL;
- int i, rc;
-
- OTELC_FUNC("%p, %p, %p, %p, %d", rule, px, sess, s, opts);
-
- OTELC_DBG(DEBUG, "from: %d, arg.act %p:{ %p %p %p %p }", rule->from, &(rule->arg.act), rule->arg.act.p[0], rule->arg.act.p[1], rule->arg.act.p[2], rule->arg.act.p[3]);
-
- fconf = rule->arg.act.p[FLT_OTEL_ARG_FLT_CONF];
- conf = rule->arg.act.p[FLT_OTEL_ARG_CONF];
- conf_group = ((const struct flt_otel_conf_ph *)(rule->arg.act.p[FLT_OTEL_ARG_GROUP]))->ptr;
-
- if ((fconf == NULL) || (conf == NULL) || (conf_group == NULL)) {
- FLT_OTEL_LOG(LOG_ERR, FLT_OTEL_ACTION_GROUP ": internal error, invalid group action");
-
- OTELC_RETURN_EX(ACT_RET_CONT, enum act_return, "%d");
- }
-
- if (_HA_ATOMIC_LOAD(&(conf->instr->flag_disabled))) {
- OTELC_DBG(INFO, "filter '%s' disabled, group action '%s' ignored", conf->id, conf_group->id);
-
- OTELC_RETURN_EX(ACT_RET_CONT, enum act_return, "%d");
- }
-
- /* Find the OpenTelemetry filter instance from the current stream. */
- list_for_each_entry(filter, &(s->strm_flt.filters), list)
- if (filter->config == fconf) {
- rt_ctx = filter->ctx;
-
- break;
- }
-
- if (rt_ctx == NULL) {
- OTELC_DBG(INFO, "cannot find filter, probably not attached to the stream");
-
- OTELC_RETURN_EX(ACT_RET_CONT, enum act_return, "%d");
- }
- else if (flt_otel_is_disabled(filter FLT_OTEL_DBG_ARGS(, -1))) {
- OTELC_RETURN_EX(ACT_RET_CONT, enum act_return, "%d");
- }
- else {
- OTELC_DBG(DEBUG, "run group '%s'", conf_group->id);
- FLT_OTEL_DBG_CONF_GROUP("run group ", conf_group);
- }
-
- /*
- * Check the value of rule->from; in case it is incorrect,
- * report an error.
- */
- for (i = 0; i < OTELC_TABLESIZE(flt_otel_group_data); i++)
- if (flt_otel_group_data[i].act_from == rule->from)
- break;
-
- if (i >= OTELC_TABLESIZE(flt_otel_group_data)) {
- FLT_OTEL_LOG(LOG_ERR, FLT_OTEL_ACTION_GROUP ": internal error, invalid rule->from=%d", rule->from);
-
- OTELC_RETURN_EX(ACT_RET_CONT, enum act_return, "%d");
- }
-
- /* Execute each scope defined in this group. */
- list_for_each_entry(ph_scope, &(conf_group->ph_scopes), list) {
- rc = flt_otel_scope_run(s, rt_ctx->filter, (flt_otel_group_data[i].smp_opt_dir == SMP_OPT_DIR_REQ) ? &(s->req) : &(s->res), ph_scope->ptr, NULL, NULL, flt_otel_group_data[i].smp_opt_dir, &err);
- if ((rc == FLT_OTEL_RET_ERROR) && (opts & ACT_OPT_FINAL)) {
- FLT_OTEL_LOG(LOG_ERR, FLT_OTEL_ACTION_GROUP ": scope '%s' failed in group '%s'", ph_scope->id, conf_group->id);
- OTELC_SFREE_CLEAR(err);
- }
- }
-
- OTELC_RETURN_EX(ACT_RET_CONT, enum act_return, "%d");
-}
-
-
-/***
- * NAME
- * flt_otel_group_check - group action post-parse check callback
- *
- * SYNOPSIS
- * static int flt_otel_group_check(struct act_rule *rule, struct proxy *px, char **err)
- *
- * ARGUMENTS
- * rule - action rule to validate
- * px - proxy instance
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Validates the check_ptr callback for the FLT_OTEL_ACTION_GROUP action.
- * Resolves the filter ID and group ID string references stored during parsing
- * into direct pointers to the filter configuration and group configuration
- * structures. Searches the proxy's filter list for a matching OTel filter,
- * then locates the named group within that filter's configuration. On
- * success, replaces the string ID pointers in <rule>->arg.act.p with the
- * resolved configuration pointers.
- *
- * RETURN VALUE
- * Returns 1 on success, or 0 on failure with <err> filled.
- */
-static int flt_otel_group_check(struct act_rule *rule, struct proxy *px, char **err)
-{
- struct flt_conf *fconf_tmp, *fconf = NULL;
- struct flt_otel_conf *conf;
- struct flt_otel_conf_ph *ph_group;
- const char *filter_id;
- const char *group_id;
- bool flag_found = 0;
- int i;
-
- OTELC_FUNC("%p, %p, %p:%p", rule, px, OTELC_DPTR_ARGS(err));
-
- filter_id = rule->arg.act.p[FLT_OTEL_ARG_FILTER_ID];
- group_id = rule->arg.act.p[FLT_OTEL_ARG_GROUP_ID];
-
- OTELC_DBG(NOTICE, "checking filter_id='%s', group_id='%s'", filter_id, group_id);
-
- /*
- * Check the value of rule->from; in case it is incorrect, report an
- * error.
- */
- for (i = 0; i < OTELC_TABLESIZE(flt_otel_group_data); i++)
- if (flt_otel_group_data[i].act_from == rule->from)
- break;
-
- if (i >= OTELC_TABLESIZE(flt_otel_group_data)) {
- FLT_OTEL_ERR("internal error, unexpected rule->from=%d, please report this bug!", rule->from);
-
- OTELC_RETURN_INT(0);
- }
-
- /*
- * Try to find the OpenTelemetry filter by checking all filters for the
- * proxy <px>.
- */
- list_for_each_entry(fconf_tmp, &(px->filter_configs), list) {
- conf = fconf_tmp->conf;
-
- if (fconf_tmp->id != otel_flt_id) {
- /* This is not an OpenTelemetry filter. */
- continue;
- }
- else if (strcmp(conf->id, filter_id) == 0) {
- /* This is the good filter ID. */
- fconf = fconf_tmp;
-
- break;
- }
- }
-
- if (fconf == NULL) {
- FLT_OTEL_ERR("unable to find the OpenTelemetry filter '%s' used by the " FLT_OTEL_ACTION_GROUP " '%s'", filter_id, group_id);
-
- OTELC_RETURN_INT(0);
- }
-
- /*
- * Attempt to find if the group is defined in the OpenTelemetry filter
- * configuration.
- */
- list_for_each_entry(ph_group, &(conf->instr->ph_groups), list)
- if (strcmp(ph_group->id, group_id) == 0) {
- flag_found = 1;
-
- break;
- }
-
- if (!flag_found) {
- FLT_OTEL_ERR("unable to find group '%s' in the OpenTelemetry filter '%s' configuration", group_id, filter_id);
-
- OTELC_RETURN_INT(0);
- }
-
- OTELC_SFREE_CLEAR(rule->arg.act.p[FLT_OTEL_ARG_FILTER_ID]);
- OTELC_SFREE_CLEAR(rule->arg.act.p[FLT_OTEL_ARG_GROUP_ID]);
-
- /* Replace string IDs with resolved configuration pointers. */
- rule->arg.act.p[FLT_OTEL_ARG_FLT_CONF] = fconf;
- rule->arg.act.p[FLT_OTEL_ARG_CONF] = conf;
- rule->arg.act.p[FLT_OTEL_ARG_GROUP] = ph_group;
-
- OTELC_RETURN_INT(1);
-}
-
-
-/***
- * NAME
- * flt_otel_group_release - group action release callback
- *
- * SYNOPSIS
- * static void flt_otel_group_release(struct act_rule *rule)
- *
- * ARGUMENTS
- * rule - action rule being released
- *
- * DESCRIPTION
- * Provides the release_ptr callback for the FLT_OTEL_ACTION_GROUP action.
- * This is a no-op because the group action's argument pointers reference
- * shared configuration structures that are freed separately during filter
- * deinitialization.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-static void flt_otel_group_release(struct act_rule *rule)
-{
- OTELC_FUNC("%p", rule);
-
- OTELC_RETURN();
-}
-
-
-/***
- * NAME
- * flt_otel_group_parse - group action keyword parser
- *
- * SYNOPSIS
- * static enum act_parse_ret flt_otel_group_parse(const char **args, int *cur_arg, struct proxy *px, struct act_rule *rule, char **err)
- *
- * ARGUMENTS
- * args - configuration line arguments array
- * cur_arg - pointer to the current argument index
- * px - proxy instance
- * rule - action rule to populate
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Parses the FLT_OTEL_ACTION_GROUP action keyword from HAProxy configuration
- * rules. Expects two arguments: a filter ID and a group ID, optionally
- * followed by "if" or "unless" conditions. The filter ID and group ID are
- * duplicated and stored in the <rule>'s argument pointers for later
- * resolution by flt_otel_group_check(). The <rule>'s callbacks are set to
- * flt_otel_group_action(), flt_otel_group_check(), and
- * flt_otel_group_release(). This parser is registered for tcp-request,
- * tcp-response, http-request, http-response, and http-after-response action
- * contexts.
- *
- * RETURN VALUE
- * Returns ACT_RET_PRS_OK on success, or ACT_RET_PRS_ERR on failure.
- */
-static enum act_parse_ret flt_otel_group_parse(const char **args, int *cur_arg, struct proxy *px, struct act_rule *rule, char **err)
-{
- OTELC_FUNC("%p, %p, %p, %p, %p:%p", args, cur_arg, px, rule, OTELC_DPTR_ARGS(err));
-
- FLT_OTEL_ARGS_DUMP();
-
- if (!FLT_OTEL_ARG_ISVALID(*cur_arg) || !FLT_OTEL_ARG_ISVALID(*cur_arg + 1) ||
- (FLT_OTEL_ARG_ISVALID(*cur_arg + 2) &&
- !FLT_OTEL_PARSE_KEYWORD(*cur_arg + 2, FLT_OTEL_CONDITION_IF) &&
- !FLT_OTEL_PARSE_KEYWORD(*cur_arg + 2, FLT_OTEL_CONDITION_UNLESS))) {
- FLT_OTEL_ERR("expects: <filter-id> <group-id> [{ if | unless } ...]");
-
- OTELC_RETURN_EX(ACT_RET_PRS_ERR, enum act_parse_ret, "%d");
- }
-
- /* Copy the OpenTelemetry filter id. */
- rule->arg.act.p[FLT_OTEL_ARG_FILTER_ID] = OTELC_STRDUP(args[*cur_arg]);
- if (rule->arg.act.p[FLT_OTEL_ARG_FILTER_ID] == NULL) {
- FLT_OTEL_ERR("%s : out of memory", args[*cur_arg]);
-
- OTELC_RETURN_EX(ACT_RET_PRS_ERR, enum act_parse_ret, "%d");
- }
-
- /* Copy the OpenTelemetry group id. */
- rule->arg.act.p[FLT_OTEL_ARG_GROUP_ID] = OTELC_STRDUP(args[*cur_arg + 1]);
- if (rule->arg.act.p[FLT_OTEL_ARG_GROUP_ID] == NULL) {
- FLT_OTEL_ERR("%s : out of memory", args[*cur_arg + 1]);
-
- OTELC_SFREE_CLEAR(rule->arg.act.p[FLT_OTEL_ARG_FILTER_ID]);
-
- OTELC_RETURN_EX(ACT_RET_PRS_ERR, enum act_parse_ret, "%d");
- }
-
- /* Wire up the rule callbacks. */
- rule->action = ACT_CUSTOM;
- rule->action_ptr = flt_otel_group_action;
- rule->check_ptr = flt_otel_group_check;
- rule->release_ptr = flt_otel_group_release;
-
- *cur_arg += 2;
-
- OTELC_RETURN_EX(ACT_RET_PRS_OK, enum act_parse_ret, "%d");
-}
-
-
-/* TCP request content action keywords for the OTel group action. */
-static struct action_kw_list tcp_req_action_kws = { ILH, {
- { FLT_OTEL_ACTION_GROUP, flt_otel_group_parse },
- { /* END */ },
- }
-};
-
-INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_action_kws);
-
-/* TCP response content action keywords for the OTel group action. */
-static struct action_kw_list tcp_res_action_kws = { ILH, {
- { FLT_OTEL_ACTION_GROUP, flt_otel_group_parse },
- { /* END */ },
- }
-};
-
-INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_action_kws);
-
-/* HTTP request action keywords for the OTel group action. */
-static struct action_kw_list http_req_action_kws = { ILH, {
- { FLT_OTEL_ACTION_GROUP, flt_otel_group_parse },
- { /* END */ },
- }
-};
-
-INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_action_kws);
-
-/* HTTP response action keywords for the OTel group action. */
-static struct action_kw_list http_res_action_kws = { ILH, {
- { FLT_OTEL_ACTION_GROUP, flt_otel_group_parse },
- { /* END */ },
- }
-};
-
-INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_action_kws);
-
-/* HTTP after-response action keywords for the OTel group action. */
-static struct action_kw_list http_after_res_actions_kws = { ILH, {
- { FLT_OTEL_ACTION_GROUP, flt_otel_group_parse },
- { /* END */ },
- }
-};
-
-INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_actions_kws);
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "../include/include.h"
-
-
-#ifdef DEBUG_OTEL
-
-/***
- * NAME
- * flt_otel_http_headers_dump - debug HTTP headers dump
- *
- * SYNOPSIS
- * void flt_otel_http_headers_dump(const struct channel *chn)
- *
- * ARGUMENTS
- * chn - channel to dump HTTP headers from
- *
- * DESCRIPTION
- * Dumps all HTTP headers from the channel's HTX buffer. Iterates over HTX
- * blocks, logging each header name-value pair at NOTICE level. Processing
- * stops at the end-of-headers marker.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_http_headers_dump(const struct channel *chn)
-{
- const struct htx *htx;
- int32_t pos;
-
- OTELC_FUNC("%p", chn);
-
- if (chn == NULL)
- OTELC_RETURN();
-
- htx = htxbuf(&(chn->buf));
-
- if (htx_is_empty(htx))
- OTELC_RETURN();
-
- /* Walk HTX blocks and log each header until end-of-headers. */
- for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
- struct htx_blk *blk = htx_get_blk(htx, pos);
- enum htx_blk_type type = htx_get_blk_type(blk);
-
- if (type == HTX_BLK_HDR) {
- struct ist n = htx_get_blk_name(htx, blk);
- struct ist v = htx_get_blk_value(htx, blk);
-
- OTELC_DBG(NOTICE, "'%.*s: %.*s'", (int)n.len, n.ptr, (int)v.len, v.ptr);
- }
- else if (type == HTX_BLK_EOH)
- break;
- }
-
- OTELC_RETURN();
-}
-
-#endif /* DEBUG_OTEL */
-
-
-/***
- * NAME
- * flt_otel_http_headers_get - HTTP header extraction to text map
- *
- * SYNOPSIS
- * struct otelc_text_map *flt_otel_http_headers_get(struct channel *chn, const char *prefix, size_t len, char **err)
- *
- * ARGUMENTS
- * chn - channel containing HTTP headers
- * prefix - header name prefix to match (or NULL for all)
- * len - length of the prefix string
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Extracts HTTP headers matching a <prefix> from the channel's HTX buffer
- * into a newly allocated text map. When <prefix> is NULL or its length is
- * zero, all headers are extracted. If the prefix starts with
- * FLT_OTEL_PARSE_CTX_IGNORE_NAME, prefix matching is bypassed. The prefix
- * (including the separator dash) is stripped from header names before storing
- * in the text map. Empty header values are replaced with an empty string to
- * avoid misinterpretation by otelc_text_map_add(). This function is used by
- * the "extract" keyword to read span context from incoming request headers.
- *
- * RETURN VALUE
- * Returns a pointer to the populated text map, or NULL on failure or when
- * no matching headers are found.
- */
-struct otelc_text_map *flt_otel_http_headers_get(struct channel *chn, const char *prefix, size_t len, char **err)
-{
- const struct htx *htx;
- size_t prefix_len = (!OTELC_STR_IS_VALID(prefix) || (len == 0)) ? 0 : (len + 1);
- int32_t pos;
- struct otelc_text_map *retptr = NULL;
-
- OTELC_FUNC("%p, \"%s\", %zu, %p:%p", chn, OTELC_STR_ARG(prefix), len, OTELC_DPTR_ARGS(err));
-
- if (chn == NULL)
- OTELC_RETURN_PTR(retptr);
-
- /*
- * The keyword 'inject' allows you to define the name of the OpenTelemetry
- * context without using a prefix. In that case all HTTP headers are
- * transferred because it is not possible to separate them from the
- * OpenTelemetry context (this separation is usually done via a prefix).
- *
- * When using the 'extract' keyword, the context name must be specified.
- * To allow all HTTP headers to be extracted, the first character of
- * that name must be set to FLT_OTEL_PARSE_CTX_IGNORE_NAME.
- */
- if (OTELC_STR_IS_VALID(prefix) && (*prefix == FLT_OTEL_PARSE_CTX_IGNORE_NAME))
- prefix_len = 0;
-
- htx = htxbuf(&(chn->buf));
-
- for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
- struct htx_blk *blk = htx_get_blk(htx, pos);
- enum htx_blk_type type = htx_get_blk_type(blk);
-
- if (type == HTX_BLK_HDR) {
- struct ist v, n = htx_get_blk_name(htx, blk);
-
- if ((prefix_len == 0) || ((n.len >= prefix_len) && (strncasecmp(n.ptr, prefix, len) == 0))) {
- if (retptr == NULL) {
- retptr = OTELC_TEXT_MAP_NEW(NULL, 8);
- if (retptr == NULL) {
- FLT_OTEL_ERR("failed to create HTTP header data");
-
- break;
- }
- }
-
- v = htx_get_blk_value(htx, blk);
-
- /*
- * In case the data of the HTTP header is not
- * specified, v.ptr will have some non-null
- * value and v.len will be equal to 0. The
- * otelc_text_map_add() function will not
- * interpret this well. In this case v.ptr
- * is set to an empty string.
- */
- if (v.len == 0)
- v = ist("");
-
- /*
- * Here, an HTTP header (which is actually part
- * of the span context) is added to the text_map.
- *
- * Before adding, the prefix is removed from the
- * HTTP header name.
- */
- if (OTELC_TEXT_MAP_ADD(retptr, n.ptr + prefix_len, n.len - prefix_len, v.ptr, v.len, OTELC_TEXT_MAP_AUTO) == -1) {
- FLT_OTEL_ERR("failed to add HTTP header data");
-
- otelc_text_map_destroy(&retptr);
-
- break;
- }
- }
- }
- else if (type == HTX_BLK_EOH)
- break;
- }
-
- OTELC_TEXT_MAP_DUMP(retptr, "extracted HTTP headers");
-
- if ((retptr != NULL) && (retptr->count == 0)) {
- OTELC_DBG(NOTICE, "WARNING: no HTTP headers found");
-
- otelc_text_map_destroy(&retptr);
- }
-
- OTELC_RETURN_PTR(retptr);
-}
-
-
-/***
- * NAME
- * flt_otel_http_header_set - HTTP header set or remove
- *
- * SYNOPSIS
- * int flt_otel_http_header_set(struct channel *chn, const char *prefix, const char *name, const char *value, char **err)
- *
- * ARGUMENTS
- * chn - channel containing HTTP headers
- * prefix - header name prefix (or NULL)
- * name - header name suffix (or NULL)
- * value - header value to set (or NULL to remove only)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Sets or removes an HTTP header in the channel's HTX buffer. The full
- * header name is constructed by combining <prefix> and <name> with a dash
- * separator; if only one is provided, it is used directly. All existing
- * occurrences of the header are removed first. If <name> is NULL, all
- * headers starting with <prefix> are removed. If <value> is non-NULL, the
- * header is then added with the new value. A NULL <value> causes only the
- * removal, with no subsequent addition.
- *
- * RETURN VALUE
- * Returns 0 on success, or FLT_OTEL_RET_ERROR on failure.
- */
-int flt_otel_http_header_set(struct channel *chn, const char *prefix, const char *name, const char *value, char **err)
-{
- struct http_hdr_ctx ctx = { .blk = NULL };
- struct ist ist_name;
- struct buffer *buffer = NULL;
- struct htx *htx;
- int retval = FLT_OTEL_RET_ERROR;
-
- OTELC_FUNC("%p, \"%s\", \"%s\", \"%s\", %p:%p", chn, OTELC_STR_ARG(prefix), OTELC_STR_ARG(name), OTELC_STR_ARG(value), OTELC_DPTR_ARGS(err));
-
- if ((chn == NULL) || (!OTELC_STR_IS_VALID(prefix) && !OTELC_STR_IS_VALID(name)))
- OTELC_RETURN_INT(retval);
-
- htx = htxbuf(&(chn->buf));
-
- /*
- * Very rare (about 1% of cases), htx is empty.
- * In order to avoid segmentation fault, we exit this function.
- */
- if (htx_is_empty(htx)) {
- FLT_OTEL_ERR("HTX is empty");
-
- OTELC_RETURN_INT(retval);
- }
-
- if (!OTELC_STR_IS_VALID(prefix)) {
- ist_name = ist2((char *)name, strlen(name));
- }
- else if (!OTELC_STR_IS_VALID(name)) {
- ist_name = ist2((char *)prefix, strlen(prefix));
- }
- else {
- buffer = flt_otel_trash_alloc(0, err);
- if (buffer == NULL)
- OTELC_RETURN_INT(retval);
-
- (void)chunk_printf(buffer, "%s-%s", prefix, name);
-
- ist_name = ist2(buffer->area, buffer->data);
- }
-
- /* Remove all occurrences of the header. */
- while (http_find_header(htx, ist(""), &ctx, 1) == 1) {
- struct ist n = htx_get_blk_name(htx, ctx.blk);
-#ifdef DEBUG_OTEL
- struct ist v = htx_get_blk_value(htx, ctx.blk);
-#endif
-
- /*
- * If the <name> parameter is not set, then remove all headers
- * that start with the contents of the <prefix> parameter.
- */
- if (!OTELC_STR_IS_VALID(name))
- n.len = ist_name.len;
-
- if (isteqi(n, ist_name))
- if (http_remove_header(htx, &ctx) == 1)
- OTELC_DBG(DEBUG, "HTTP header '%.*s: %.*s' removed", (int)n.len, n.ptr, (int)v.len, v.ptr);
- }
-
- /*
- * If the value pointer has a value of NULL, the HTTP header is not set
- * after deletion.
- */
- if (value == NULL) {
- retval = 0;
- }
- else if (http_add_header(htx, ist_name, ist(value), 1) == 1) {
- retval = 0;
-
- OTELC_DBG(DEBUG, "HTTP header '%s: %s' added", ist_name.ptr, value);
- }
- else {
- FLT_OTEL_ERR("failed to set HTTP header '%s: %s'", ist_name.ptr, value);
- }
-
- flt_otel_trash_free(&buffer);
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_http_headers_remove - HTTP headers removal by prefix
- *
- * SYNOPSIS
- * int flt_otel_http_headers_remove(struct channel *chn, const char *prefix, char **err)
- *
- * ARGUMENTS
- * chn - channel containing HTTP headers
- * prefix - header name prefix to match for removal
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Removes all HTTP headers matching the given <prefix> from the channel's HTX
- * buffer. This is a convenience wrapper around flt_otel_http_header_set()
- * with NULL <name> and <value> arguments.
- *
- * RETURN VALUE
- * Returns 0 on success, or FLT_OTEL_RET_ERROR on failure.
- */
-int flt_otel_http_headers_remove(struct channel *chn, const char *prefix, char **err)
-{
- int retval;
-
- OTELC_FUNC("%p, \"%s\", %p:%p", chn, OTELC_STR_ARG(prefix), OTELC_DPTR_ARGS(err));
-
- retval = flt_otel_http_header_set(chn, prefix, NULL, NULL, err);
-
- OTELC_RETURN_INT(retval);
-}
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "../include/include.h"
-
-
-/***
- * NAME
- * flt_otel_text_map_writer_set_cb - text map injection writer callback
- *
- * SYNOPSIS
- * static int flt_otel_text_map_writer_set_cb(struct otelc_text_map_writer *writer, const char *key, const char *value)
- *
- * ARGUMENTS
- * writer - text map writer instance
- * key - context key name
- * value - context key value
- *
- * DESCRIPTION
- * Writer callback for text map injection. Called by the OTel C wrapper
- * library during span context injection to store each key-value pair in the
- * <writer>'s text map.
- *
- * RETURN VALUE
- * Returns the result of OTELC_TEXT_MAP_ADD().
- */
-static int flt_otel_text_map_writer_set_cb(struct otelc_text_map_writer *writer, const char *key, const char *value)
-{
- OTELC_FUNC("%p, \"%s\", \"%s\"", writer, OTELC_STR_ARG(key), OTELC_STR_ARG(value));
-
- OTELC_RETURN_INT(OTELC_TEXT_MAP_ADD(&(writer->text_map), key, 0, value, 0, OTELC_TEXT_MAP_AUTO));
-}
-
-
-/***
- * NAME
- * flt_otel_http_headers_writer_set_cb - HTTP headers injection writer callback
- *
- * SYNOPSIS
- * static int flt_otel_http_headers_writer_set_cb(struct otelc_http_headers_writer *writer, const char *key, const char *value)
- *
- * ARGUMENTS
- * writer - HTTP headers writer instance
- * key - context key name
- * value - context key value
- *
- * DESCRIPTION
- * Writer callback for HTTP headers injection. Called by the OTel C wrapper
- * library during span context injection to store each key-value pair in the
- * <writer>'s text map.
- *
- * RETURN VALUE
- * Returns the result of OTELC_TEXT_MAP_ADD().
- */
-static int flt_otel_http_headers_writer_set_cb(struct otelc_http_headers_writer *writer, const char *key, const char *value)
-{
- OTELC_FUNC("%p, \"%s\", \"%s\"", writer, OTELC_STR_ARG(key), OTELC_STR_ARG(value));
-
- OTELC_RETURN_INT(OTELC_TEXT_MAP_ADD(&(writer->text_map), key, 0, value, 0, OTELC_TEXT_MAP_AUTO));
-}
-
-
-/***
- * NAME
- * flt_otel_inject_text_map - text map context injection
- *
- * SYNOPSIS
- * int flt_otel_inject_text_map(const struct otelc_span *span, struct otelc_text_map_writer *carrier)
- *
- * ARGUMENTS
- * span - span instance to inject context from
- * carrier - text map writer carrier
- *
- * DESCRIPTION
- * Injects the span context into a text map carrier. Initializes the
- * <carrier> structure, sets the writer callback to
- * flt_otel_text_map_writer_set_cb(), and delegates to the <span>'s
- * inject_text_map() method.
- *
- * RETURN VALUE
- * Returns the result of the <span>'s inject_text_map() method,
- * or FLT_OTEL_RET_ERROR if arguments are NULL.
- */
-int flt_otel_inject_text_map(const struct otelc_span *span, struct otelc_text_map_writer *carrier)
-{
- OTELC_FUNC("%p, %p", span, carrier);
-
- if ((span == NULL) || (carrier == NULL))
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
-
- (void)memset(carrier, 0, sizeof(*carrier));
- carrier->set = flt_otel_text_map_writer_set_cb;
-
- OTELC_RETURN_INT(OTELC_OPS(span, inject_text_map, carrier));
-}
-
-
-/***
- * NAME
- * flt_otel_inject_http_headers - HTTP headers context injection
- *
- * SYNOPSIS
- * int flt_otel_inject_http_headers(const struct otelc_span *span, struct otelc_http_headers_writer *carrier)
- *
- * ARGUMENTS
- * span - span instance to inject context from
- * carrier - HTTP headers writer carrier
- *
- * DESCRIPTION
- * Injects the span context into an HTTP headers carrier. Initializes the
- * <carrier> structure, sets the writer callback to
- * flt_otel_http_headers_writer_set_cb(), and delegates to the <span>'s
- * inject_http_headers() method.
- *
- * RETURN VALUE
- * Returns the result of the <span>'s inject_http_headers() method,
- * or FLT_OTEL_RET_ERROR if arguments are NULL.
- */
-int flt_otel_inject_http_headers(const struct otelc_span *span, struct otelc_http_headers_writer *carrier)
-{
- OTELC_FUNC("%p, %p", span, carrier);
-
- if ((span == NULL) || (carrier == NULL))
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
-
- (void)memset(carrier, 0, sizeof(*carrier));
- carrier->set = flt_otel_http_headers_writer_set_cb;
-
- OTELC_RETURN_INT(OTELC_OPS(span, inject_http_headers, carrier));
-}
-
-
-/***
- * NAME
- * flt_otel_text_map_reader_foreach_key_cb - text map extraction reader callback
- *
- * SYNOPSIS
- * static int flt_otel_text_map_reader_foreach_key_cb(const struct otelc_text_map_reader *reader, int (*handler)(void *arg, const char *key, const char *value), void *arg)
- *
- * ARGUMENTS
- * reader - text map reader instance
- * handler - callback function invoked for each key-value pair
- * arg - opaque argument passed to the handler
- *
- * DESCRIPTION
- * Reader callback for text map extraction. Iterates over all key-value
- * pairs in the <reader>'s text map and invokes <handler> for each. Iteration
- * stops if the <handler> returns -1.
- *
- * RETURN VALUE
- * Returns the last <handler> return value, or 0 if the text map is empty.
- */
-static int flt_otel_text_map_reader_foreach_key_cb(const struct otelc_text_map_reader *reader, int (*handler)(void *arg, const char *key, const char *value), void *arg)
-{
- size_t i;
- int retval = 0;
-
- OTELC_FUNC("%p, %p, %p", reader, handler, arg);
-
- for (i = 0; (retval != -1) && (i < reader->text_map.count); i++) {
- OTELC_DBG(OTELC, "\"%s\" -> \"%s\"", OTELC_STR_ARG(reader->text_map.key[i]), OTELC_STR_ARG(reader->text_map.value[i]));
-
- retval = handler(arg, reader->text_map.key[i], reader->text_map.value[i]);
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_http_headers_reader_foreach_key_cb - HTTP headers extraction reader callback
- *
- * SYNOPSIS
- * static int flt_otel_http_headers_reader_foreach_key_cb(const struct otelc_http_headers_reader *reader, int (*handler)(void *arg, const char *key, const char *value), void *arg)
- *
- * ARGUMENTS
- * reader - HTTP headers reader instance
- * handler - callback function invoked for each key-value pair
- * arg - opaque argument passed to the handler
- *
- * DESCRIPTION
- * Reader callback for HTTP headers extraction. Iterates over all key-value
- * pairs in the <reader>'s text map and invokes <handler> for each. Iteration
- * stops if the <handler> returns -1.
- *
- * RETURN VALUE
- * Returns the last <handler> return value, or 0 if the text map is empty.
- */
-static int flt_otel_http_headers_reader_foreach_key_cb(const struct otelc_http_headers_reader *reader, int (*handler)(void *arg, const char *key, const char *value), void *arg)
-{
- size_t i;
- int retval = 0;
-
- OTELC_FUNC("%p, %p, %p", reader, handler, arg);
-
- for (i = 0; (retval != -1) && (i < reader->text_map.count); i++) {
- OTELC_DBG(OTELC, "\"%s\" -> \"%s\"", OTELC_STR_ARG(reader->text_map.key[i]), OTELC_STR_ARG(reader->text_map.value[i]));
-
- retval = handler(arg, reader->text_map.key[i], reader->text_map.value[i]);
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_extract_text_map - text map context extraction
- *
- * SYNOPSIS
- * struct otelc_span_context *flt_otel_extract_text_map(struct otelc_tracer *tracer, struct otelc_text_map_reader *carrier, const struct otelc_text_map *text_map)
- *
- * ARGUMENTS
- * tracer - OTel tracer instance
- * carrier - text map reader carrier
- * text_map - text map containing the context data (or NULL)
- *
- * DESCRIPTION
- * Extracts a span context from a text map carrier via the <tracer>.
- * Initializes the <carrier> structure, sets the foreach_key callback to
- * flt_otel_text_map_reader_foreach_key_cb(), and copies the <text_map> data
- * into the <carrier>. Delegates to the <tracer>'s extract_text_map() method.
- *
- * RETURN VALUE
- * Returns a pointer to the extracted span context, or NULL on failure.
- */
-struct otelc_span_context *flt_otel_extract_text_map(struct otelc_tracer *tracer, struct otelc_text_map_reader *carrier, const struct otelc_text_map *text_map)
-{
- OTELC_FUNC("%p, %p, %p", tracer, carrier, text_map);
-
- if ((tracer == NULL) || (carrier == NULL))
- OTELC_RETURN_PTR(NULL);
-
- (void)memset(carrier, 0, sizeof(*carrier));
- carrier->foreach_key = flt_otel_text_map_reader_foreach_key_cb;
-
- if (text_map != NULL)
- (void)memcpy(&(carrier->text_map), text_map, sizeof(carrier->text_map));
-
- OTELC_RETURN_PTR(OTELC_OPS(tracer, extract_text_map, carrier));
-}
-
-
-/***
- * NAME
- * flt_otel_extract_http_headers - HTTP headers context extraction
- *
- * SYNOPSIS
- * struct otelc_span_context *flt_otel_extract_http_headers(struct otelc_tracer *tracer, struct otelc_http_headers_reader *carrier, const struct otelc_text_map *text_map)
- *
- * ARGUMENTS
- * tracer - OTel tracer instance
- * carrier - HTTP headers reader carrier
- * text_map - text map containing the context data (or NULL)
- *
- * DESCRIPTION
- * Extracts a span context from an HTTP headers carrier via the <tracer>.
- * Initializes the <carrier> structure, sets the foreach_key callback to
- * flt_otel_http_headers_reader_foreach_key_cb(), and copies the <text_map>
- * data into the <carrier>. Delegates to the <tracer>'s
- * extract_http_headers() method.
- *
- * RETURN VALUE
- * Returns a pointer to the extracted span context, or NULL on failure.
- */
-struct otelc_span_context *flt_otel_extract_http_headers(struct otelc_tracer *tracer, struct otelc_http_headers_reader *carrier, const struct otelc_text_map *text_map)
-{
- OTELC_FUNC("%p, %p, %p", tracer, carrier, text_map);
-
- if ((tracer == NULL) || (carrier == NULL))
- OTELC_RETURN_PTR(NULL);
-
- (void)memset(carrier, 0, sizeof(*carrier));
- carrier->foreach_key = flt_otel_http_headers_reader_foreach_key_cb;
-
- if (text_map != NULL)
- (void)memcpy(&(carrier->text_map), text_map, sizeof(carrier->text_map));
-
- OTELC_RETURN_PTR(OTELC_OPS(tracer, extract_http_headers, carrier));
-}
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "../include/include.h"
-
-
-#ifdef OTELC_DBG_MEM
-static struct otelc_dbg_mem_data dbg_mem_data[1000000];
-static struct otelc_dbg_mem dbg_mem;
-#endif
-
-static struct flt_otel_conf *flt_otel_current_config = NULL;
-static struct flt_otel_conf_instr *flt_otel_current_instr = NULL;
-static struct flt_otel_conf_group *flt_otel_current_group = NULL;
-static struct flt_otel_conf_scope *flt_otel_current_scope = NULL;
-static struct flt_otel_conf_span *flt_otel_current_span = NULL;
-
-
-/***
- * NAME
- * flt_otel_parse_strdup - string duplication with error handling
- *
- * SYNOPSIS
- * static int flt_otel_parse_strdup(char **dst, size_t *dst_len, const char *src, char **err, const char *err_msg)
- *
- * ARGUMENTS
- * dst - pointer to the destination string pointer
- * dst_len - optional pointer to store the duplicated string length
- * src - source string to duplicate
- * err - indirect pointer to error message string
- * err_msg - context label used in error messages
- *
- * DESCRIPTION
- * Duplicates the string <src> into <*dst> with error handling. Optionally
- * stores the string length in <dst_len>. On failure, an error message is
- * formatted using <err_msg> as context.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_parse_strdup(char **dst, size_t *dst_len, const char *src, char **err, const char *err_msg)
-{
- int retval = ERR_NONE;
-
- OTELC_FUNC("%p:%p, %p, %p, %p:%p, \"%s\"", OTELC_DPTR_ARGS(dst), dst_len, src, OTELC_DPTR_ARGS(err), OTELC_STR_ARG(err_msg));
-
- /* dst_len is not set if the string has not been copied. */
- *dst = OTELC_STRDUP(src);
- if (*dst == NULL)
- FLT_OTEL_PARSE_ERR(err, "'%s' : out of memory", err_msg);
- else if (dst_len != NULL)
- *dst_len = strlen(*dst);
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_parse_keyword - keyword argument parser
- *
- * SYNOPSIS
- * static int flt_otel_parse_keyword(char **ptr, char **args, int cur_arg, int pos, char **err, const char *err_msg)
- *
- * ARGUMENTS
- * ptr - pointer to the destination string pointer
- * args - configuration line arguments array
- * cur_arg - current argument index for error reporting
- * pos - position of the keyword in <args>
- * err - indirect pointer to error message string
- * err_msg - context label used in error messages
- *
- * DESCRIPTION
- * Parses a single keyword argument from the configuration line. Checks
- * that the keyword has not already been set and that a value is present
- * at position <pos> + 1 in <args>. The value is duplicated via
- * flt_otel_parse_strdup() into <*ptr>.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_parse_keyword(char **ptr, char **args, int cur_arg, int pos, char **err, const char *err_msg)
-{
- int retval = ERR_NONE;
-
- OTELC_FUNC("%p:%p, %p, %d, %d, %p:%p, \"%s\"", OTELC_DPTR_ARGS(ptr), args, cur_arg, pos, OTELC_DPTR_ARGS(err), OTELC_STR_ARG(err_msg));
-
- /* Reject duplicate keyword assignments. */
- if (*ptr != NULL) {
- if (cur_arg == pos)
- FLT_OTEL_PARSE_ERR(err, FLT_OTEL_FMT_TYPE "%s already set", err_msg);
- else
- FLT_OTEL_PARSE_ERR(err, "'%s' : %s already set", args[cur_arg], err_msg);
- }
- else if (!FLT_OTEL_ARG_ISVALID(pos + 1)) {
- if (cur_arg == pos)
- FLT_OTEL_PARSE_ERR(err, FLT_OTEL_FMT_TYPE "no %s set", err_msg);
- else
- FLT_OTEL_PARSE_ERR(err, "'%s' : no %s set", args[cur_arg], err_msg);
- }
- else {
- retval = flt_otel_parse_strdup(ptr, NULL, args[pos + 1], err, args[cur_arg]);
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_parse_invalid_char - name character validation
- *
- * SYNOPSIS
- * static const char *flt_otel_parse_invalid_char(const char *name, int type)
- *
- * ARGUMENTS
- * name - string to validate
- * type - validation type selector
- *
- * DESCRIPTION
- * Validates characters in a <name> string according to the specified <type>.
- * Uses HAProxy's invalid_char() for identifiers, invalid_domainchar() for
- * domains, invalid_prefix_char() for context prefixes, and a custom
- * alphanumeric check for variables.
- *
- * RETURN VALUE
- * Returns a pointer to the first invalid character in <name>,
- * or NULL if all characters are valid.
- */
-static const char *flt_otel_parse_invalid_char(const char *name, int type)
-{
- const char *retptr = NULL;
-
- OTELC_FUNC("\"%s\", %d", OTELC_STR_ARG(name), type);
-
- if (!OTELC_STR_IS_VALID(name))
- OTELC_RETURN_EX(retptr, const char *, "%p");
-
- /* Dispatch to the appropriate character validation function. */
- if (type == FLT_OTEL_PARSE_INVALID_CHAR) {
- retptr = invalid_char(name);
- }
- else if (type == FLT_OTEL_PARSE_INVALID_DOM) {
- retptr = invalid_domainchar(name);
- }
- else if (type == FLT_OTEL_PARSE_INVALID_CTX) {
- retptr = invalid_prefix_char(name);
- }
- else if (type == FLT_OTEL_PARSE_INVALID_VAR) {
- retptr = name;
-
- /*
- * Allowed characters are letters, numbers and '_', the first
- * character in the string must not be a number.
- */
- if (!isdigit(*retptr))
- for (++retptr; (*retptr == '_') || isalnum(*retptr); retptr++);
-
- if (*retptr == '\0')
- retptr = NULL;
- }
-
- OTELC_RETURN_EX(retptr, const char *, "%p");
-}
-
-
-/***
- * NAME
- * flt_otel_parse_cfg_check - configuration keyword validation
- *
- * SYNOPSIS
- * static int flt_otel_parse_cfg_check(const char *file, int line, char **args, const void *id, const struct flt_otel_parse_data *parse_data, size_t parse_data_size, const struct flt_otel_parse_data **pdata, char **err)
- *
- * ARGUMENTS
- * file - configuration file path
- * line - configuration file line number
- * args - configuration line arguments array
- * id - parent section identifier
- * parse_data - keyword definition table
- * parse_data_size - number of entries in <parse_data>
- * pdata - output pointer to the matched keyword entry
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Common validation for configuration keywords. Looks up <args[0]> in the
- * <parse_data> table, checks argument count bounds, validates the first
- * argument's characters according to the keyword's check_name type, and
- * verifies that the parent section ID is set when required.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_parse_cfg_check(const char *file, int line, char **args, const void *id, const struct flt_otel_parse_data *parse_data, size_t parse_data_size, const struct flt_otel_parse_data **pdata, char **err)
-{
- int i, argc, retval = ERR_NONE;
-
- OTELC_FUNC("\"%s\", %d, %p, %p, %p, %zu, %p:%p, %p:%p", OTELC_STR_ARG(file), line, args, id, parse_data, parse_data_size, OTELC_DPTR_ARGS(pdata), OTELC_DPTR_ARGS(err));
-
- FLT_OTEL_ARGS_DUMP();
-
- *pdata = NULL;
-
- /* First check here if args[0] is the correct keyword. */
- for (i = 0; (*pdata == NULL) && (i < parse_data_size); i++)
- if (FLT_OTEL_PARSE_KEYWORD(0, parse_data[i].name))
- *pdata = parse_data + i;
-
- if (*pdata == NULL)
- FLT_OTEL_PARSE_ERR(err, "'%s' : unknown keyword", args[0]);
- else
- argc = flt_otel_args_count((const char **)args);
-
- if ((retval & ERR_CODE) || (id == NULL))
- /* Do nothing. */;
- else if ((id != flt_otel_current_instr) && (flt_otel_current_config->instr == NULL))
- FLT_OTEL_PARSE_ERR(err, "instrumentation not defined");
-
- /*
- * Checking that fewer arguments are specified in the configuration
- * line than is required.
- */
- if (!(retval & ERR_CODE))
- if (argc < (*pdata)->args_min)
- FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[0], (*pdata)->name, (*pdata)->usage);
-
- /*
- * Checking that more arguments are specified in the configuration
- * line than the maximum allowed.
- */
- if (!(retval & ERR_CODE) && ((*pdata)->args_max > 0))
- if (argc > (*pdata)->args_max)
- FLT_OTEL_PARSE_ERR(err, "'%s' : too many arguments (use '%s%s')", args[0], (*pdata)->name, (*pdata)->usage);
-
- /* Checking that the first argument has only allowed characters. */
- if (!(retval & ERR_CODE) && ((*pdata)->check_name != FLT_OTEL_PARSE_INVALID_NONE)) {
- const char *ic;
-
- ic = flt_otel_parse_invalid_char(args[1], (*pdata)->check_name);
- if (ic != NULL)
- FLT_OTEL_PARSE_ERR(err, "%s '%s' : invalid character '%c'", args[0], args[1], *ic);
- }
-
- /* Checking that the data group name is defined. */
- if (!(retval & ERR_CODE) && (*pdata)->flag_check_id && (id == NULL))
- FLT_OTEL_PARSE_ERR(err, "'%s' : %s ID not set (use '%s%s')", args[0], parse_data[1].name, parse_data[1].name, parse_data[1].usage);
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_parse_cfg_sample_expr - sample expression parser
- *
- * SYNOPSIS
- * static int flt_otel_parse_cfg_sample_expr(const char *file, int line, char **args, int *idx, struct list *head, char **err)
- *
- * ARGUMENTS
- * file - configuration file path
- * line - configuration file line number
- * args - configuration line arguments array
- * idx - pointer to the current position in <args>
- * head - list head for parsed sample expressions
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Parses a single HAProxy sample expression at position <*idx> in <args>.
- * Creates a conf_sample_expr structure and calls sample_parse_expr() to
- * compile the expression.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_parse_cfg_sample_expr(const char *file, int line, char **args, int *idx, struct list *head, char **err)
-{
- struct flt_otel_conf_sample_expr *expr;
- int retval = ERR_NONE;
-
- OTELC_FUNC("\"%s\", %d, %p, %p, %p, %p:%p", OTELC_STR_ARG(file), line, args, idx, head, OTELC_DPTR_ARGS(err));
-
- expr = flt_otel_conf_sample_expr_init(args[*idx], line, head, err);
- if (expr != NULL) {
- expr->expr = sample_parse_expr(args, idx, file, line, err, &(flt_otel_current_config->proxy->conf.args), NULL);
- if (expr->expr != NULL)
- OTELC_DBG(DEBUG, "sample expression '%s' added", expr->fmt_expr);
- else
- retval |= ERR_ABORT | ERR_ALERT;
- } else {
- retval |= ERR_ABORT | ERR_ALERT;
- }
-
- if (retval & ERR_CODE)
- flt_otel_conf_sample_expr_free(&expr);
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_parse_cfg_sample - sample definition parser
- *
- * SYNOPSIS
- * static int flt_otel_parse_cfg_sample(const char *file, int line, char **args, int idx, int n, const struct otelc_value *extra, struct list *head, char **err)
- *
- * ARGUMENTS
- * file - configuration file path
- * line - configuration file line number
- * args - configuration line arguments array
- * idx - args[] position where the sample value starts
- * n - maximum number of sample expressions to parse (0 means unlimited)
- * extra - optional extra data (event name or status code)
- * head - list head for parsed sample definitions
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Parses a complete sample definition starting at index <idx> in the
- * <args> array. A new conf_sample structure is allocated and initialized
- * via flt_otel_conf_sample_init_ex() with the optional <extra> data (an
- * event name or a status code), then the sample expressions are parsed.
- *
- * When <args>[<idx>] contains the "%[" sequence, the argument is parsed
- * as a log-format string via parse_logformat_string(): the lf_used flag
- * is set and the result is stored in the lf_expr member while the exprs
- * list remains empty. Otherwise the arguments are treated as bare sample
- * expressions: the proxy configuration context is set and the function
- * calls flt_otel_parse_cfg_sample_expr() in a loop to populate exprs.
- *
- * When <n> is 0 all remaining valid arguments are consumed; otherwise at
- * most <n> expressions are parsed. On error the allocated conf_sample
- * structure is freed before returning.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success, or a combination of ERR_* flags
- * if an error is encountered.
- */
-static int flt_otel_parse_cfg_sample(const char *file, int line, char **args, int idx, int n, const struct otelc_value *extra, struct list *head, char **err)
-{
- struct flt_otel_conf_sample *sample;
- int retval = ERR_NONE;
- int count = 0;
-
- OTELC_FUNC("\"%s\", %d, %p, %d, %d, %p, %p, %p:%p", OTELC_STR_ARG(file), line, args, idx, n, extra, head, OTELC_DPTR_ARGS(err));
-
- sample = flt_otel_conf_sample_init_ex((const char **)args, idx, n, extra, line, head, err);
- if (sample == NULL)
- FLT_OTEL_PARSE_ERR(err, "'%s' : out of memory", args[0]);
-
- if (retval & ERR_CODE) {
- /* Do nothing. */
- }
- else if (strstr(args[idx], "%[") != NULL) {
- /*
- * Log-format path: parse the single argument as a log-format
- * string into the sample structure.
- */
- sample->lf_used = 1;
-
- if (parse_logformat_string(args[idx], flt_otel_current_config->proxy, &(sample->lf_expr), LOG_OPT_HTTP, SMP_VAL_FE_LOG_END, err) == 0)
- retval |= ERR_ABORT | ERR_ALERT;
- else
- OTELC_DBG(DEBUG, "sample '%s' -> log-format '%s' added", sample->key, sample->fmt_string);
- }
- else {
- /*
- * Bare sample expression path.
- */
- flt_otel_current_config->proxy->conf.args.ctx = ARGC_OTEL;
- flt_otel_current_config->proxy->conf.args.file = file;
- flt_otel_current_config->proxy->conf.args.line = line;
-
- while (!(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(idx) && ((n == 0) || (count < n))) {
- retval = flt_otel_parse_cfg_sample_expr(file, line, args, &idx, &(sample->exprs), err);
- if (!(retval & ERR_CODE))
- count++;
- }
-
- flt_otel_current_config->proxy->conf.args.file = NULL;
- flt_otel_current_config->proxy->conf.args.line = 0;
-
- OTELC_DBG(DEBUG, "sample '%s' -> '%s' added, (%d %d)", sample->key, sample->fmt_string, sample->num_exprs, count);
- }
-
- if (retval & ERR_CODE)
- flt_otel_conf_sample_free(&sample);
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_parse_cfg_str - string list parser
- *
- * SYNOPSIS
- * static int flt_otel_parse_cfg_str(const char *file, int line, char **args, struct list *head, char **err)
- *
- * ARGUMENTS
- * file - configuration file path
- * line - configuration file line number
- * args - configuration line arguments array
- * head - list head for parsed string entries
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Parses one or more string arguments into a conf_str list. All arguments
- * starting from index 1 are added to <head>. Used for the "finish" keyword.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_parse_cfg_str(const char *file, int line, char **args, struct list *head, char **err)
-{
- int i, retval = ERR_NONE;
-
- OTELC_FUNC("\"%s\", %d, %p, %p, %p:%p", OTELC_STR_ARG(file), line, args, head, OTELC_DPTR_ARGS(err));
-
- for (i = 1; !(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(i); i++)
- if (flt_otel_conf_str_init(args[i], line, head, err) == NULL)
- retval |= ERR_ABORT | ERR_ALERT;
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_parse_cfg_file - file path argument parser
- *
- * SYNOPSIS
- * static int flt_otel_parse_cfg_file(char **ptr, const char *file, int line, char **args, char **err, const char *err_msg)
- *
- * ARGUMENTS
- * ptr - pointer to the destination file path string pointer
- * file - configuration file path
- * line - configuration file line number
- * args - configuration line arguments array
- * err - indirect pointer to error message string
- * err_msg - context label used in error messages
- *
- * DESCRIPTION
- * Parses and validates a file path argument. Checks that the argument is
- * present, that no extra arguments follow, and that the file exists and is
- * readable.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_parse_cfg_file(char **ptr, const char *file, int line, char **args, char **err, const char *err_msg)
-{
- int retval = ERR_NONE;
-
- OTELC_FUNC("%p:%p, \"%s\", %d, %p, %p:%p, \"%s\"", OTELC_DPTR_ARGS(ptr), OTELC_STR_ARG(file), line, args, OTELC_DPTR_ARGS(err), err_msg);
-
- if (!FLT_OTEL_ARG_ISVALID(1))
- FLT_OTEL_PARSE_ERR(err, "'%s' : no %s specified", flt_otel_current_instr->id, err_msg);
- else if (alertif_too_many_args(1, file, line, args, &retval))
- retval |= ERR_ABORT | ERR_ALERT;
- else if (access(args[1], R_OK) == -1)
- FLT_OTEL_PARSE_ERR(err, "'%s' : %s", args[1], strerror(errno));
- else
- retval = flt_otel_parse_keyword(ptr, args, 0, 0, err, err_msg);
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_parse_check_scope - configuration scope filter
- *
- * SYNOPSIS
- * static bool flt_otel_parse_check_scope(void)
- *
- * ARGUMENTS
- * This function takes no arguments.
- *
- * DESCRIPTION
- * Checks whether the current configuration parsing is within the correct
- * HAProxy cfg_scope filter. When cfg_scope is set and does not match the
- * current filter ID, the configuration line is skipped.
- *
- * RETURN VALUE
- * Returns TRUE in case the configuration is not in the currently
- * defined scope, FALSE otherwise.
- */
-static bool flt_otel_parse_check_scope(void)
-{
- bool retval = 0;
-
- if ((cfg_scope != NULL) && (flt_otel_current_config->id != NULL) && (strcmp(flt_otel_current_config->id, cfg_scope) != 0)) {
- OTELC_DBG(INFO, "cfg_scope: '%s', id: '%s'", cfg_scope, flt_otel_current_config->id);
-
- retval = 1;
- }
-
- return retval;
-}
-
-
-/***
- * NAME
- * flt_otel_parse_cfg_instr - otel-instrumentation section parser
- *
- * SYNOPSIS
- * static int flt_otel_parse_cfg_instr(const char *file, int line, char **args, int kw_mod)
- *
- * ARGUMENTS
- * file - configuration file path
- * line - configuration file line number
- * args - configuration line arguments array
- * kw_mod - keyword modifier flags (e.g. KWM_NO)
- *
- * DESCRIPTION
- * Section parser for the otel-instrumentation configuration block. Handles
- * keywords: instrumentation ID, log, config, groups, scopes, acl, rate-limit,
- * option (disabled/hard-errors/dontlog-normal), and debug-level.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_parse_cfg_instr(const char *file, int line, char **args, int kw_mod)
-{
-#define FLT_OTEL_PARSE_INSTR_DEF(a,b,c,d,e,f,g) { FLT_OTEL_PARSE_INSTR_##a, b, FLT_OTEL_PARSE_INVALID_##c, d, e, f, g },
- static const struct flt_otel_parse_data parse_data[] = { FLT_OTEL_PARSE_INSTR_DEFINES };
-#undef FLT_OTEL_PARSE_INSTR_DEF
- const struct flt_otel_parse_data *pdata = NULL;
- char *err = NULL, *err_log = NULL;
- int i, retval = ERR_NONE;
-
- OTELC_FUNC("\"%s\", %d, %p, 0x%08x", OTELC_STR_ARG(file), line, args, kw_mod);
-
- if (flt_otel_parse_check_scope())
- OTELC_RETURN_INT(retval);
-
- /* Validate and identify the instrumentation keyword. */
- retval = flt_otel_parse_cfg_check(file, line, args, flt_otel_current_instr, parse_data, OTELC_TABLESIZE(parse_data), &pdata, &err);
- if (retval & ERR_CODE) {
- FLT_OTEL_PARSE_IFERR_ALERT();
-
- OTELC_RETURN_INT(retval);
- }
-
- /* Handle keyword-specific instrumentation configuration. */
- if (pdata->keyword == FLT_OTEL_PARSE_INSTR_ID) {
- if (flt_otel_current_config->instr != NULL) {
- FLT_OTEL_PARSE_ERR(&err, "'%s' : instrumentation can be defined only once", args[1]);
- } else {
- flt_otel_current_instr = flt_otel_conf_instr_init(args[1], line, NULL, &err);
- if (flt_otel_current_instr == NULL)
- retval |= ERR_ABORT | ERR_ALERT;
- }
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_INSTR_LOG) {
- if (parse_logger(args, &(flt_otel_current_instr->proxy_log.loggers), kw_mod == KWM_NO, file, line, &err_log) == 0) {
- FLT_OTEL_PARSE_ERR(&err, "'%s %s ...' : %s", args[0], args[1], err_log);
- OTELC_SFREE_CLEAR(err_log);
- } else {
- flt_otel_current_instr->logging |= FLT_OTEL_LOGGING_ON;
- }
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_INSTR_CONFIG) {
- retval = flt_otel_parse_cfg_file(&(flt_otel_current_instr->config), file, line, args, &err, "configuration file");
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_INSTR_GROUPS) {
- for (i = 1; !(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(i); i++)
- if (flt_otel_conf_ph_init(args[i], line, &(flt_otel_current_instr->ph_groups), &err) == NULL)
- retval |= ERR_ABORT | ERR_ALERT;
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_INSTR_SCOPES) {
- for (i = 1; !(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(i); i++)
- if (flt_otel_conf_ph_init(args[i], line, &(flt_otel_current_instr->ph_scopes), &err) == NULL)
- retval |= ERR_ABORT | ERR_ALERT;
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_INSTR_ACL) {
- if (FLT_OTEL_PARSE_KEYWORD(1, "or"))
- FLT_OTEL_PARSE_ERR(&err, "'%s %s ...' : invalid ACL name", args[0], args[1]);
- else if (parse_acl((const char **)args + 1, &(flt_otel_current_instr->acls), &err, &(flt_otel_current_config->proxy->conf.args), file, line) == NULL)
- retval |= ERR_ABORT | ERR_ALERT;
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_INSTR_RATE_LIMIT) {
- double value;
-
- if (flt_otel_strtod(args[1], &value, 0.0, 100.0, &err))
- flt_otel_current_instr->rate_limit = FLT_OTEL_FLOAT_U32(value);
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_INSTR_OPTION) {
- if (FLT_OTEL_PARSE_KEYWORD(1, FLT_OTEL_PARSE_OPTION_DISABLED)) {
- flt_otel_current_instr->flag_disabled = (kw_mod == KWM_NO) ? 0 : 1;
- }
- else if (FLT_OTEL_PARSE_KEYWORD(1, FLT_OTEL_PARSE_OPTION_HARDERR)) {
- flt_otel_current_instr->flag_harderr = (kw_mod == KWM_NO) ? 0 : 1;
- }
- else if (FLT_OTEL_PARSE_KEYWORD(1, FLT_OTEL_PARSE_OPTION_NOLOGNORM)) {
- if (kw_mod == KWM_NO)
- flt_otel_current_instr->logging &= ~FLT_OTEL_LOGGING_NOLOGNORM;
- else
- flt_otel_current_instr->logging |= FLT_OTEL_LOGGING_NOLOGNORM;
- }
- else
- FLT_OTEL_PARSE_ERR(&err, "'%s' : unknown option '%s'", args[0], args[1]);
- }
-#ifdef DEBUG_OTEL
- else if (pdata->keyword == FLT_OTEL_PARSE_INSTR_DEBUG_LEVEL) {
- int64_t value;
-
- if (flt_otel_strtoll(args[1], &value, 0, OTELC_DBG_LEVEL_MASK, &err))
- otelc_dbg_level = value;
- }
-#else
- else {
- FLT_OTEL_PARSE_WARNING("'%s' : keyword ignored", file, line, args[0]);
- }
-#endif
-
- FLT_OTEL_PARSE_IFERR_ALERT();
-
- if ((retval & ERR_CODE) && (flt_otel_current_instr != NULL))
- flt_otel_conf_instr_free(&flt_otel_current_instr);
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_post_parse_cfg_instr - otel-instrumentation post-parse check
- *
- * SYNOPSIS
- * static int flt_otel_post_parse_cfg_instr(void)
- *
- * ARGUMENTS
- * This function takes no arguments.
- *
- * DESCRIPTION
- * Post-parse callback for the otel-instrumentation section. Links the parsed
- * instrumentation structure to the filter configuration and verifies that a
- * configuration file path is specified.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_post_parse_cfg_instr(void)
-{
- int retval = ERR_NONE;
-
- OTELC_FUNC("");
-
- if (flt_otel_current_instr == NULL)
- OTELC_RETURN_INT(retval);
-
- flt_otel_current_config->instr = flt_otel_current_instr;
-
- if (flt_otel_current_instr->id == NULL)
- OTELC_RETURN_INT(retval);
-
- if (flt_otel_current_instr->config == NULL)
- FLT_OTEL_POST_PARSE_ALERT("instrumentation '%s' has no configuration file specified", flt_otel_current_instr->cfg_line, flt_otel_current_instr->id);
-
- flt_otel_current_instr = NULL;
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_parse_cfg_group - otel-group section parser
- *
- * SYNOPSIS
- * static int flt_otel_parse_cfg_group(const char *file, int line, char **args, int kw_mod)
- *
- * ARGUMENTS
- * file - configuration file path
- * line - configuration file line number
- * args - configuration line arguments array
- * kw_mod - keyword modifier flags (e.g. KWM_NO)
- *
- * DESCRIPTION
- * Section parser for the otel-group configuration block. Handles keywords:
- * group ID and scopes.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_parse_cfg_group(const char *file, int line, char **args, int kw_mod)
-{
-#define FLT_OTEL_PARSE_GROUP_DEF(a,b,c,d,e,f,g) { FLT_OTEL_PARSE_GROUP_##a, b, FLT_OTEL_PARSE_INVALID_##c, d, e, f, g },
- static const struct flt_otel_parse_data parse_data[] = { FLT_OTEL_PARSE_GROUP_DEFINES };
-#undef FLT_OTEL_PARSE_GROUP_DEF
- const struct flt_otel_parse_data *pdata = NULL;
- char *err = NULL;
- int i, retval = ERR_NONE;
-
- OTELC_FUNC("\"%s\", %d, %p, 0x%08x", OTELC_STR_ARG(file), line, args, kw_mod);
-
- if (flt_otel_parse_check_scope())
- OTELC_RETURN_INT(retval);
-
- /* Validate and identify the group keyword. */
- retval = flt_otel_parse_cfg_check(file, line, args, flt_otel_current_group, parse_data, OTELC_TABLESIZE(parse_data), &pdata, &err);
- if (retval & ERR_CODE) {
- FLT_OTEL_PARSE_IFERR_ALERT();
-
- OTELC_RETURN_INT(retval);
- }
-
- /* Handle keyword-specific group configuration. */
- if (pdata->keyword == FLT_OTEL_PARSE_GROUP_ID) {
- flt_otel_current_group = flt_otel_conf_group_init(args[1], line, &(flt_otel_current_config->groups), &err);
- if (flt_otel_current_group == NULL)
- retval |= ERR_ABORT | ERR_ALERT;
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_GROUP_SCOPES) {
- for (i = 1; !(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(i); i++)
- if (flt_otel_conf_ph_init(args[i], line, &(flt_otel_current_group->ph_scopes), &err) == NULL)
- retval |= ERR_ABORT | ERR_ALERT;
- }
-
- FLT_OTEL_PARSE_IFERR_ALERT();
-
- if ((retval & ERR_CODE) && (flt_otel_current_group != NULL))
- flt_otel_conf_group_free(&flt_otel_current_group);
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_post_parse_cfg_group - otel-group post-parse check
- *
- * SYNOPSIS
- * static int flt_otel_post_parse_cfg_group(void)
- *
- * ARGUMENTS
- * This function takes no arguments.
- *
- * DESCRIPTION
- * Post-parse callback for the otel-group section. Verifies that at least one
- * scope is defined in the group.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_post_parse_cfg_group(void)
-{
- int retval = ERR_NONE;
-
- OTELC_FUNC("");
-
- if (flt_otel_current_group == NULL)
- OTELC_RETURN_INT(retval);
-
- /* Check that the group has at least one scope defined. */
- if (LIST_ISEMPTY(&(flt_otel_current_group->ph_scopes)))
- FLT_OTEL_POST_PARSE_ALERT("group '%s' has no defined scope(s)", flt_otel_current_group->cfg_line, flt_otel_current_group->id);
-
- flt_otel_current_group = NULL;
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_parse_cfg_scope_ctx - context storage type parser
- *
- * SYNOPSIS
- * static int flt_otel_parse_cfg_scope_ctx(char **args, int cur_arg, char **err)
- *
- * ARGUMENTS
- * args - configuration line arguments array
- * cur_arg - index of the storage type argument in <args>
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Parses the context storage type argument for inject/extract keywords.
- * Accepts "use-headers" or (when USE_OTEL_VARS is defined) "use-vars".
- * Both types may be used simultaneously on the same span.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_parse_cfg_scope_ctx(char **args, int cur_arg, char **err)
-{
- uint8_t flags = 0;
- int retval = ERR_NONE;
-
- OTELC_FUNC("%p, %d, %p:%p", args, cur_arg, OTELC_DPTR_ARGS(err));
-
- if (FLT_OTEL_PARSE_KEYWORD(cur_arg, FLT_OTEL_PARSE_CTX_USE_HEADERS))
- flags = FLT_OTEL_CTX_USE_HEADERS;
-#ifdef USE_OTEL_VARS
- else if (FLT_OTEL_PARSE_KEYWORD(cur_arg, FLT_OTEL_PARSE_CTX_USE_VARS))
- flags = FLT_OTEL_CTX_USE_VARS;
-#endif
- else
- FLT_OTEL_PARSE_ERR(err, "'%s' : invalid context storage type", args[0]);
-
- if (flags == 0)
- /* Do nothing. */;
- else if (flt_otel_current_span->ctx_flags & flags)
- FLT_OTEL_PARSE_ERR(err, "'%s' : %s already used", args[0], args[cur_arg]);
- else
- flt_otel_current_span->ctx_flags |= flags;
-
- OTELC_DBG(NOTICE, "ctx_flags: 0x%02hhx (0x%02hhx)", flt_otel_current_span->ctx_flags, flags);
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_parse_acl - ACL condition builder
- *
- * SYNOPSIS
- * static struct acl_cond *flt_otel_parse_acl(const char *file, int line, struct proxy *px, const char **args, char **err, struct list *head, ...)
- *
- * ARGUMENTS
- * file - configuration file path
- * line - configuration file line number
- * px - proxy instance for ACL resolution
- * args - condition arguments (if/unless followed by ACL names)
- * err - indirect pointer to error message string
- * head - first ACL list head to search
- *
- * DESCRIPTION
- * Builds an ACL condition by trying multiple ACL lists in order. The
- * variadic arguments provide a sequence of ACL list heads to search; the
- * first successful build_acl_cond() result is returned.
- *
- * RETURN VALUE
- * Returns a pointer to the built ACL condition, or NULL if no condition could
- * be built from any of the provided lists.
- */
-static struct acl_cond *flt_otel_parse_acl(const char *file, int line, struct proxy *px, const char **args, char **err, struct list *head, ...)
-{
- va_list ap;
- int n = 0;
- struct acl_cond *retptr = NULL;
-
- OTELC_FUNC("\"%s\", %d, %p, %p, %p:%p, %p, ...", OTELC_STR_ARG(file), line, px, args, OTELC_DPTR_ARGS(err), head);
-
- /* Try each ACL list in order until a condition is built. */
- for (va_start(ap, head); (retptr == NULL) && (head != NULL); head = va_arg(ap, typeof(head)), n++) {
- retptr = build_acl_cond(file, line, head, px, args, (n == 0) ? err : NULL);
- if (retptr != NULL)
- OTELC_DBG(NOTICE, "ACL build done, using list %p %d", head, n);
- }
- va_end(ap);
-
- if ((retptr != NULL) && (err != NULL))
- ha_free(err);
-
- OTELC_RETURN_PTR(retptr);
-}
-
-
-/***
- * NAME
- * flt_otel_parse_bounds - histogram boundary string parser
- *
- * SYNOPSIS
- * static int flt_otel_parse_bounds(const char *str, double **bounds, size_t *bounds_num, char **err, const char *err_msg)
- *
- * ARGUMENTS
- * str - space-separated numeric boundary string
- * bounds - pointer to the destination boundary array
- * bounds_num - pointer to store the number of boundaries
- * err - indirect pointer to error message string
- * err_msg - context label used in error messages
- *
- * DESCRIPTION
- * Parses a space-separated string of numbers into a dynamically allocated
- * array of doubles suitable for the meter add_view API. The string is
- * duplicated internally and tokenized with strtok(). Each token is
- * converted with flt_otel_strtod(). The values are sorted internally.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_parse_bounds(const char *str, double **bounds, size_t *bounds_num, char **err, const char *err_msg)
-{
- char *buffer, *token, *lasts;
- size_t bounds_len = 0, bounds_size = 8;
- double value, *ptr;
- int retval = ERR_NONE;
-
- OTELC_FUNC("\"%s\", %p, %p, %p:%p, \"%s\"", OTELC_STR_ARG(str), bounds, bounds_num, OTELC_DPTR_ARGS(err), OTELC_STR_ARG(err_msg));
-
- buffer = OTELC_STRDUP(str);
- *bounds = OTELC_CALLOC(bounds_size, sizeof(**bounds));
- if ((buffer == NULL) || (*bounds == NULL)) {
- OTELC_SFREE(buffer);
- OTELC_SFREE(*bounds);
-
- FLT_OTEL_PARSE_ERR(err, "'%s' : out of memory", err_msg);
-
- OTELC_RETURN_INT(retval);
- }
-
- /* Tokenize and parse space-separated boundary values. */
- for (token = strtok_r(buffer, " \t", &lasts); token != NULL; token = strtok_r(NULL, " \t", &lasts)) {
- if (!flt_otel_strtod(token, &value, 0.0, DBL_MAX, err)) {
- retval |= ERR_ABORT | ERR_ALERT;
-
- break;
- }
- else if (bounds_len >= bounds_size) {
- ptr = OTELC_REALLOC(*bounds, (bounds_size + 8) * sizeof(*ptr));
- if (ptr == NULL) {
- FLT_OTEL_PARSE_ERR(err, "'%s' : out of memory", err_msg);
-
- OTELC_SFREE_CLEAR(*bounds);
-
- break;
- }
-
- *bounds = ptr;
- bounds_size += 8;
- }
-
- (*bounds)[bounds_len++] = value;
- }
-
- /* Sort the bounds and reject duplicates. */
- if ((*bounds != NULL) && (bounds_len > 1)) {
- size_t i;
-
- qsort(*bounds, bounds_len, sizeof(**bounds), flt_otel_qsort_compar_double);
-
- for (i = 1; i < bounds_len; i++)
- if (flt_otel_qsort_compar_double(*bounds + i - 1, *bounds + i) == 0) {
- FLT_OTEL_PARSE_ERR(err, "'%s' : duplicate boundary value '%.2f'", err_msg, (*bounds)[i]);
-
- OTELC_SFREE_CLEAR(*bounds);
-
- break;
- }
- }
-
- OTELC_SFREE(buffer);
-
- if (*bounds == NULL) {
- *bounds_num = 0;
- }
- else if (bounds_len == 0) {
- FLT_OTEL_PARSE_ERR(err, "'%s' : empty bounds", err_msg);
-
- OTELC_SFREE_CLEAR(*bounds);
- *bounds_num = 0;
- }
- else {
- *bounds_num = bounds_len;
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_parse_cfg_instrument - instrument keyword parser
- *
- * SYNOPSIS
- * static int flt_otel_parse_cfg_instrument(const char *file, int line, char **args, const struct flt_otel_parse_data *pdata, char **err)
- *
- * ARGUMENTS
- * file - configuration file path
- * line - configuration file line number
- * args - configuration line arguments array
- * pdata - keyword metadata (name, usage, argument limits)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Parses the "instrument" keyword inside an otel-scope section. Two forms
- * are supported: the "update" form that references an existing instrument by
- * name and adds attributes to it, and the "create" form that defines a new
- * metric instrument with a type, name, optional aggregation type (preceded by
- * the 'aggr' keyword), optional description, optional unit, a single sample
- * expression for the value, and optional histogram bucket boundaries
- * (preceded by the 'bounds' keyword). The 'bounds' keyword is only valid for
- * histogram instrument types.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_parse_cfg_instrument(const char *file, int line, char **args, const struct flt_otel_parse_data *pdata, char **err)
-{
-#define FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEF(a,b) { OTELC_METRIC_INSTRUMENT_##a, b },
- static const struct {
- otelc_metric_instrument_t type;
- const char *keyword;
- } instr_type[] = { FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEFINES };
-#undef FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEF
- struct flt_otel_conf_instrument *instr;
- int i, retval = ERR_NONE;
-
- OTELC_FUNC("\"%s\", %d, %p, %p, %p:%p", OTELC_STR_ARG(file), line, args, pdata, OTELC_DPTR_ARGS(err));
-
- /* Look up the instrument type from args[1]. */
- for (i = 0; i < OTELC_TABLESIZE(instr_type); i++)
- if (FLT_OTEL_PARSE_KEYWORD(1, instr_type[i].keyword)) {
- OTELC_DBG(DEBUG, "instrument type: %d '%s'", instr_type[i].type, instr_type[i].keyword);
-
- break;
- }
-
- if (i >= OTELC_TABLESIZE(instr_type)) {
- FLT_OTEL_PARSE_ERR(err, "'%s' : invalid instrument type", args[1]);
-
- OTELC_RETURN_INT(retval);
- }
-
- /*
- * Only one create and one update instrument per name are allowed.
- * Pass NULL as head for update instruments to bypass the generic
- * duplicate check (which would reject the shared name), check for
- * update duplicates separately, and append to the list manually.
- */
- if (instr_type[i].type == OTELC_METRIC_INSTRUMENT_UPDATE) {
- list_for_each_entry(instr, &(flt_otel_current_scope->instruments), list)
- if ((instr->type == OTELC_METRIC_INSTRUMENT_UPDATE) && FLT_OTEL_PARSE_KEYWORD(2, instr->id)) {
- FLT_OTEL_ERR("'%s' : already defined", args[2]);
-
- OTELC_RETURN_INT(retval);
- }
-
- instr = flt_otel_conf_instrument_init(args[2], line, NULL, err);
- if (instr != NULL)
- LIST_APPEND(&(flt_otel_current_scope->instruments), &(instr->list));
- } else {
- instr = flt_otel_conf_instrument_init(args[2], line, &(flt_otel_current_scope->instruments), err);
- }
-
- if (instr == NULL) {
- retval |= ERR_ABORT | ERR_ALERT;
- }
- else if (instr_type[i].type == OTELC_METRIC_INSTRUMENT_UPDATE) {
- bool flag_add_attr = false;
-
- instr->type = instr_type[i].type;
-
- /* Update instruments only accept additional attributes. */
- for (i = 3; !(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(i); i++) {
- if (flag_add_attr) {
- if (!FLT_OTEL_ARG_ISVALID(i) || !FLT_OTEL_ARG_ISVALID(i + 1))
- FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
- else {
- retval = flt_otel_parse_cfg_sample(file, line, args, i + 1, 1, NULL, &(instr->attributes), err);
- if (!(retval & ERR_CODE))
- i++;
- }
- }
- else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_INSTRUMENT_ATTR)) {
- flag_add_attr = true;
- }
- else {
- FLT_OTEL_PARSE_ERR(err, "'%s' : unknown keyword (use '%s%s')", args[i], pdata->name, pdata->usage);
- }
- }
-
- if (flag_add_attr && LIST_ISEMPTY(&(instr->attributes)))
- FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
- }
- else {
- instr->type = instr_type[i].type;
-
- /*
- * Create instruments accept aggr, description, unit, value,
- * and bounds.
- */
- for (i = 3; !(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(i); i++) {
- if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_INSTRUMENT_AGGR)) {
- if (!FLT_OTEL_ARG_ISVALID(i + 1))
- FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
- else if (instr->aggr_type != OTELC_METRIC_AGGREGATION_UNSET)
- FLT_OTEL_PARSE_ERR(err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
- else {
- otelc_metric_aggregation_type_t type = otelc_meter_aggr_parse(args[++i]);
-
- if (type == OTELC_RET_ERROR)
- FLT_OTEL_PARSE_ERR(err, "'%s' : invalid aggregation type", args[i]);
- else
- instr->aggr_type = type;
- }
- }
- else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_INSTRUMENT_DESC)) {
- if (!FLT_OTEL_ARG_ISVALID(i + 1))
- FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
- else if (instr->description == NULL)
- retval = flt_otel_parse_strdup(&(instr->description), NULL, args[++i], err, args[0]);
- else
- FLT_OTEL_PARSE_ERR(err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
- }
- else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_INSTRUMENT_UNIT)) {
- if (!FLT_OTEL_ARG_ISVALID(i + 1))
- FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
- else if (instr->unit == NULL)
- retval = flt_otel_parse_strdup(&(instr->unit), NULL, args[++i], err, args[0]);
- else
- FLT_OTEL_PARSE_ERR(err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
- }
- else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_INSTRUMENT_VALUE)) {
- if (!FLT_OTEL_ARG_ISVALID(i + 1))
- FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
- else if (!LIST_ISEMPTY(&(instr->samples)))
- FLT_OTEL_PARSE_ERR(err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
- else {
- retval = flt_otel_parse_cfg_sample(file, line, args, ++i, 1, NULL, &(instr->samples), err);
-
- if (!(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(i + 1) && !FLT_OTEL_PARSE_KEYWORD(i + 1, FLT_OTEL_PARSE_INSTRUMENT_AGGR) && !FLT_OTEL_PARSE_KEYWORD(i + 1, FLT_OTEL_PARSE_INSTRUMENT_DESC) && !FLT_OTEL_PARSE_KEYWORD(i + 1, FLT_OTEL_PARSE_INSTRUMENT_UNIT) && !FLT_OTEL_PARSE_KEYWORD(i + 1, FLT_OTEL_PARSE_INSTRUMENT_VALUE) && !FLT_OTEL_PARSE_KEYWORD(i + 1, FLT_OTEL_PARSE_INSTRUMENT_BOUNDS))
- FLT_OTEL_PARSE_ERR(err, "'%s' : only one sample expression allowed per instrument", args[0]);
- }
- }
- else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_INSTRUMENT_BOUNDS)) {
- if (!FLT_OTEL_ARG_ISVALID(i + 1))
- FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
- else if (instr->type != OTELC_METRIC_INSTRUMENT_HISTOGRAM_UINT64)
- FLT_OTEL_PARSE_ERR(err, "'%s' : bounds only valid for hist_int instruments", args[i]);
- else if (instr->bounds != NULL)
- FLT_OTEL_PARSE_ERR(err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
- else
- retval = flt_otel_parse_bounds(args[++i], &(instr->bounds), &(instr->bounds_num), err, args[0]);
- }
- else {
- FLT_OTEL_PARSE_ERR(err, "'%s' : invalid argument (use '%s%s')", args[i], pdata->name, pdata->usage);
- }
- }
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_parse_cfg_log_record - log-record keyword parser
- *
- * SYNOPSIS
- * static int flt_otel_parse_cfg_log_record(const char *file, int line, char **args, const struct flt_otel_parse_data *pdata, char **err)
- *
- * ARGUMENTS
- * file - configuration file path
- * line - configuration file line number
- * args - configuration line arguments array
- * pdata - keyword metadata (name, usage, argument limits)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Parses the "log-record" keyword inside an otel-scope section. The first
- * argument is a required severity level string. Optional keywords "id",
- * "event", "span", and "attr" follow in any order. The remaining arguments
- * at the end are parsed as fetch expressions or a log-format string.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_parse_cfg_log_record(const char *file, int line, char **args, const struct flt_otel_parse_data *pdata, char **err)
-{
- struct flt_otel_conf_log_record *log;
- otelc_log_severity_t severity;
- int i, retval = ERR_NONE;
-
- OTELC_FUNC("\"%s\", %d, %p, %p, %p:%p", OTELC_STR_ARG(file), line, args, pdata, OTELC_DPTR_ARGS(err));
-
- /* Look up the severity level from args[1]. */
- severity = otelc_logger_severity_parse(args[1]);
- if (severity == OTELC_LOG_SEVERITY_INVALID) {
- FLT_OTEL_PARSE_ERR(err, "'%s' : invalid log severity", args[1]);
-
- OTELC_RETURN_INT(retval);
- }
-
- log = flt_otel_conf_log_record_init(FLT_OTEL_CONF_HDR_SPECIAL "log-record", line, &(flt_otel_current_scope->log_records), err);
- if (log == NULL) {
- retval |= ERR_ABORT | ERR_ALERT;
-
- OTELC_RETURN_INT(retval);
- }
-
- log->severity = severity;
-
- /* Parse optional keywords starting from args[2]. */
- for (i = 2; !(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(i); i++) {
- if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_LOG_RECORD_ID)) {
- if (!FLT_OTEL_ARG_ISVALID(i + 1))
- FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
- else if (log->event_id != 0)
- FLT_OTEL_PARSE_ERR(err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
- else if (!flt_otel_strtoll(args[++i], &(log->event_id), 0, LLONG_MAX, err))
- retval |= ERR_ABORT | ERR_ALERT;
- }
- else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_LOG_RECORD_EVENT)) {
- if (!FLT_OTEL_ARG_ISVALID(i + 1))
- FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
- else if (log->event_name != NULL)
- FLT_OTEL_PARSE_ERR(err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
- else
- retval = flt_otel_parse_strdup(&(log->event_name), NULL, args[++i], err, args[0]);
- }
- else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_LOG_RECORD_SPAN)) {
- if (!FLT_OTEL_ARG_ISVALID(i + 1))
- FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
- else if (log->span != NULL)
- FLT_OTEL_PARSE_ERR(err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
- else
- retval = flt_otel_parse_strdup(&(log->span), NULL, args[++i], err, args[0]);
- }
- else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_LOG_RECORD_ATTR)) {
- if (!FLT_OTEL_ARG_ISVALID(i + 1) || !FLT_OTEL_ARG_ISVALID(i + 2))
- FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
- else {
- retval = flt_otel_parse_cfg_sample(file, line, args, i + 2, 1, NULL, &(log->attributes), err);
- if (!(retval & ERR_CODE))
- i += 2;
- }
- }
- else {
- /*
- * Not a recognized keyword -- the remaining arguments
- * are sample fetch expressions or a log-format string.
- */
- retval = flt_otel_parse_cfg_sample(file, line, args, i, 0, NULL, &(log->samples), err);
-
- break;
- }
- }
-
- if (!(retval & ERR_CODE) && LIST_ISEMPTY(&(log->samples)))
- FLT_OTEL_PARSE_ERR(err, "'%s' : missing body expression (use '%s%s')", args[0], pdata->name, pdata->usage);
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_parse_cfg_scope - otel-scope section parser
- *
- * SYNOPSIS
- * static int flt_otel_parse_cfg_scope(const char *file, int line, char **args, int kw_mod)
- *
- * ARGUMENTS
- * file - configuration file path
- * line - configuration file line number
- * args - configuration line arguments array
- * kw_mod - keyword modifier flags (e.g. KWM_NO)
- *
- * DESCRIPTION
- * Section parser for the otel-scope configuration block. Handles keywords:
- * scope ID, span (with optional root/parent/link modifiers), link, attribute,
- * event, baggage, status, inject, extract, finish, instrument, log-record,
- * acl, and otel-event (with optional if/unless conditions).
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_parse_cfg_scope(const char *file, int line, char **args, int kw_mod)
-{
-#define FLT_OTEL_PARSE_SCOPE_DEF(a,b,c,d,e,f,g) { FLT_OTEL_PARSE_SCOPE_##a, b, FLT_OTEL_PARSE_INVALID_##c, d, e, f, g },
- static const struct flt_otel_parse_data parse_data[] = { FLT_OTEL_PARSE_SCOPE_DEFINES };
-#undef FLT_OTEL_PARSE_SCOPE_DEF
- const struct flt_otel_parse_data *pdata = NULL;
- char *err = NULL;
- int i, retval = ERR_NONE;
-
- OTELC_FUNC("\"%s\", %d, %p, 0x%08x", OTELC_STR_ARG(file), line, args, kw_mod);
-
- if (flt_otel_parse_check_scope())
- OTELC_RETURN_INT(retval);
-
- /* Validate and identify the scope keyword. */
- retval = flt_otel_parse_cfg_check(file, line, args, flt_otel_current_span, parse_data, OTELC_TABLESIZE(parse_data), &pdata, &err);
- if (retval & ERR_CODE) {
- FLT_OTEL_PARSE_IFERR_ALERT();
-
- OTELC_RETURN_INT(retval);
- }
-
- /* Handle keyword-specific scope configuration. */
- if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_ID) {
- /* Initialization of a new scope. */
- flt_otel_current_scope = flt_otel_conf_scope_init(args[1], line, &(flt_otel_current_config->scopes), &err);
- if (flt_otel_current_scope == NULL)
- retval |= ERR_ABORT | ERR_ALERT;
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_SPAN) {
- /*
- * Checking if this is the beginning of the definition of
- * a new span.
- */
- if (flt_otel_current_span != NULL) {
- OTELC_DBG(DEBUG, "span '%s' (done)", flt_otel_current_span->id);
-
- flt_otel_current_span = NULL;
- }
-
- /* Initialization of a new span. */
- flt_otel_current_span = flt_otel_conf_span_init(args[1], line, &(flt_otel_current_scope->spans), &err);
-
- /*
- * In case the span has a defined reference (parent), the
- * correctness of the arguments is checked here.
- */
- if (flt_otel_current_span == NULL) {
- retval |= ERR_ABORT | ERR_ALERT;
- }
- else if (FLT_OTEL_ARG_ISVALID(2)) {
- for (i = 2; (i < pdata->args_max) && FLT_OTEL_ARG_ISVALID(i); i++) {
- if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_SPAN_ROOT)) {
- if (flt_otel_current_span->flag_root)
- FLT_OTEL_PARSE_ERR(&err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
- else
- flt_otel_current_span->flag_root = 1;
- }
- else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_SPAN_PARENT)) {
- if (FLT_OTEL_ARG_ISVALID(i + 1))
- retval |= flt_otel_parse_strdup(&(flt_otel_current_span->ref_id), &(flt_otel_current_span->ref_id_len), args[++i], &err, args[1]);
- else
- FLT_OTEL_PARSE_ERR(&err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
- }
- else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_SPAN_LINK)) {
- if (FLT_OTEL_ARG_ISVALID(i + 1)) {
- if (flt_otel_conf_link_init(args[++i], line, &(flt_otel_current_span->links), &err) == NULL)
- retval |= ERR_ABORT | ERR_ALERT;
- } else {
- FLT_OTEL_PARSE_ERR(&err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
- }
- }
- else {
- FLT_OTEL_PARSE_ERR(&err, "'%s' : invalid argument (use '%s%s')", args[i], pdata->name, pdata->usage);
- }
- }
- }
- else {
- /*
- * This is not a faulty configuration, only such a case
- * will be logged.
- */
- OTELC_DBG(DEBUG, "new span '%s' without reference", flt_otel_current_span->id);
- }
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_LINK) {
- for (i = 1; !(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(i); i++)
- if (flt_otel_conf_link_init(args[i], line, &(flt_otel_current_span->links), &err) == NULL)
- retval |= ERR_ABORT | ERR_ALERT;
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_ATTRIBUTE) {
- retval = flt_otel_parse_cfg_sample(file, line, args, 2, 0, NULL, &(flt_otel_current_span->attributes), &err);
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_EVENT) {
- struct otelc_value extra = { .u_type = OTELC_VALUE_STRING, .u.value_string = args[1] };
-
- retval = flt_otel_parse_cfg_sample(file, line, args, 3, 0, &extra, &(flt_otel_current_span->events), &err);
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_BAGGAGE) {
- retval = flt_otel_parse_cfg_sample(file, line, args, 2, 0, NULL, &(flt_otel_current_span->baggages), &err);
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_STATUS) {
-#define FLT_OTEL_PARSE_SCOPE_STATUS_DEF(a,b) { OTELC_SPAN_STATUS_##a, b },
- static const struct {
- int code;
- const char *keyword;
- } status[] = { FLT_OTEL_PARSE_SCOPE_STATUS_DEFINES };
-#undef FLT_OTEL_PARSE_SCOPE_STATUS_DEF
-
- for (i = 0; i < OTELC_TABLESIZE(status); i++)
- if (FLT_OTEL_PARSE_KEYWORD(1, status[i].keyword)) {
- OTELC_DBG(DEBUG, "span status: %d '%s'", status[i].code, status[i].keyword);
-
- break;
- }
-
- /*
- * Regardless of the use of the list, only one status per event
- * is allowed.
- */
- if (i >= OTELC_TABLESIZE(status)) {
- FLT_OTEL_PARSE_ERR(&err, "'%s' : invalid span status", args[1]);
- }
- else if (LIST_ISEMPTY(&(flt_otel_current_span->statuses))) {
- struct otelc_value extra = { .u_type = OTELC_VALUE_INT32, .u.value_int32 = status[i].code };
-
- retval = flt_otel_parse_cfg_sample(file, line, args, 2, 0, &extra, &(flt_otel_current_span->statuses), &err);
- }
- else {
- FLT_OTEL_PARSE_ERR(&err, "only one status per event is allowed");
- }
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_INJECT) {
- /*
- * Automatic context name generation can be specified here if
- * the contents of the FLT_OTEL_PARSE_CTX_AUTONAME macro are
- * used as the name. In that case, if the context is after a
- * particular event, it gets its name; otherwise it gets the
- * name of the current span.
- */
- if (flt_otel_current_span->ctx_id != NULL)
- FLT_OTEL_PARSE_ERR(&err, "'%s' : only one context per span is allowed", args[1]);
- else if (!FLT_OTEL_PARSE_KEYWORD(1, FLT_OTEL_PARSE_CTX_AUTONAME))
- retval = flt_otel_parse_strdup(&(flt_otel_current_span->ctx_id), &(flt_otel_current_span->ctx_id_len), args[1], &err, args[0]);
- else if (flt_otel_current_scope->event != FLT_OTEL_EVENT__NONE)
- retval = flt_otel_parse_strdup(&(flt_otel_current_span->ctx_id), &(flt_otel_current_span->ctx_id_len), flt_otel_event_data[flt_otel_current_scope->event].name, &err, args[0]);
- else {
- const char *ch;
-
- ch = invalid_prefix_char(flt_otel_current_span->id);
- if (ch == NULL)
- retval = flt_otel_parse_strdup(&(flt_otel_current_span->ctx_id), &(flt_otel_current_span->ctx_id_len), flt_otel_current_span->id, &err, args[0]);
- else
- FLT_OTEL_PARSE_ERR(&err, "'%s' : character '%c' is not permitted in the context name", flt_otel_current_span->id, *ch);
- }
-
- if (flt_otel_current_span->ctx_id != NULL) {
- /*
- * Here is checked the context storage type; which, if
- * not explicitly specified, is set to HTTP headers.
- *
- * It is possible to use both types of context storage
- * at the same time.
- */
- if (FLT_OTEL_ARG_ISVALID(2)) {
- retval |= flt_otel_parse_cfg_scope_ctx(args, 2, &err);
- if (!(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(3))
- retval |= flt_otel_parse_cfg_scope_ctx(args, 3, &err);
- } else {
- flt_otel_current_span->ctx_flags = FLT_OTEL_CTX_USE_HEADERS;
- }
- }
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_EXTRACT) {
- struct flt_otel_conf_context *conf_ctx;
-
- /*
- * Here is checked the context storage type; which, if
- * not explicitly specified, is set to HTTP headers.
- */
- conf_ctx = flt_otel_conf_context_init(args[1], line, &(flt_otel_current_scope->contexts), &err);
- if (conf_ctx == NULL)
- retval |= ERR_ABORT | ERR_ALERT;
- else if (!FLT_OTEL_ARG_ISVALID(2))
- conf_ctx->flags = FLT_OTEL_CTX_USE_HEADERS;
- else if (FLT_OTEL_PARSE_KEYWORD(2, FLT_OTEL_PARSE_CTX_USE_HEADERS))
- conf_ctx->flags = FLT_OTEL_CTX_USE_HEADERS;
-#ifdef USE_OTEL_VARS
- else if (FLT_OTEL_PARSE_KEYWORD(2, FLT_OTEL_PARSE_CTX_USE_VARS))
- conf_ctx->flags = FLT_OTEL_CTX_USE_VARS;
-#endif
- else
- FLT_OTEL_PARSE_ERR(&err, "'%s' : invalid context storage type", args[2]);
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_FINISH) {
- retval = flt_otel_parse_cfg_str(file, line, args, &(flt_otel_current_scope->spans_to_finish), &err);
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_INSTRUMENT) {
- retval = flt_otel_parse_cfg_instrument(file, line, args, pdata, &err);
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_LOG_RECORD) {
- retval = flt_otel_parse_cfg_log_record(file, line, args, pdata, &err);
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_ACL) {
- if (FLT_OTEL_PARSE_KEYWORD(1, "or"))
- FLT_OTEL_PARSE_ERR(&err, "'%s %s ...' : invalid ACL name", args[0], args[1]);
- else if (parse_acl((const char **)args + 1, &(flt_otel_current_scope->acls), &err, &(flt_otel_current_config->proxy->conf.args), file, line) == NULL)
- retval |= ERR_ABORT | ERR_ALERT;
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_IDLE_TIMEOUT) {
- const char *res;
- uint timeout;
-
- res = parse_time_err(args[1], &timeout, TIME_UNIT_MS);
- if (res == PARSE_TIME_OVER)
- FLT_OTEL_PARSE_ERR(&err, "'%s' : timer overflow in argument '%s'", args[0], args[1]);
- else if (res == PARSE_TIME_UNDER)
- FLT_OTEL_PARSE_ERR(&err, "'%s' : timer underflow in argument '%s'", args[0], args[1]);
- else if (res != NULL)
- FLT_OTEL_PARSE_ERR(&err, "'%s' : unexpected character '%c' in '%s'", args[0], *res, args[1]);
- else if (timeout == 0)
- FLT_OTEL_PARSE_ERR(&err, "'%s' : value must be greater than zero", args[0]);
- else
- flt_otel_current_scope->idle_timeout = timeout;
- }
- else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_ON_EVENT) {
- /* Scope can only have one event defined. */
- if (flt_otel_current_scope->event != FLT_OTEL_EVENT__NONE) {
- FLT_OTEL_PARSE_ERR(&err, "'%s' : event already set", flt_otel_current_scope->id);
- } else {
- /* Check the event name. */
- for (i = 0; i < OTELC_TABLESIZE(flt_otel_event_data); i++)
- if (FLT_OTEL_PARSE_KEYWORD(1, flt_otel_event_data[i].name)) {
- flt_otel_current_scope->event = i;
-
- break;
- }
-
- /*
- * The event can have some condition defined and this
- * is checked here.
- */
- if (flt_otel_current_scope->event == FLT_OTEL_EVENT__NONE) {
- FLT_OTEL_PARSE_ERR(&err, "'%s' : unknown event", args[1]);
- }
- else if (!FLT_OTEL_ARG_ISVALID(2)) {
- /* Do nothing. */
- }
- else if (FLT_OTEL_PARSE_KEYWORD(2, FLT_OTEL_CONDITION_IF) || FLT_OTEL_PARSE_KEYWORD(2, FLT_OTEL_CONDITION_UNLESS)) {
- /*
- * We will first try to build ACL condition using
- * local settings and then if that fails, using
- * global settings (from instrumentation block).
- * If it also fails, then try to use ACL defined
- * in the HAProxy configuration.
- */
- if (flt_otel_current_config->instr == NULL) {
- FLT_OTEL_PARSE_ERR(&err, "'%s' : instrumentation not defined", args[1]);
- } else {
- flt_otel_current_scope->cond = flt_otel_parse_acl(file, line, flt_otel_current_config->proxy, (const char **)args + 2, &err, &(flt_otel_current_scope->acls), &(flt_otel_current_config->instr->acls), &(flt_otel_current_config->proxy->acl), NULL);
- if (flt_otel_current_scope->cond == NULL)
- retval |= ERR_ABORT | ERR_ALERT;
- }
- }
- else {
- FLT_OTEL_PARSE_ERR(&err, "'%s' : expects either 'if' or 'unless' followed by a condition but found '%s'", args[1], args[2]);
- }
-
- if (!(retval & ERR_CODE))
- OTELC_DBG(DEBUG, "event '%s'", args[1]);
- }
- }
-
- FLT_OTEL_PARSE_IFERR_ALERT();
-
- if ((retval & ERR_CODE) && (flt_otel_current_scope != NULL)) {
- flt_otel_conf_scope_free(&flt_otel_current_scope);
-
- flt_otel_current_span = NULL;
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_post_parse_cfg_scope - otel-scope post-parse check
- *
- * SYNOPSIS
- * static int flt_otel_post_parse_cfg_scope(void)
- *
- * ARGUMENTS
- * This function takes no arguments.
- *
- * DESCRIPTION
- * Post-parse callback for the otel-scope section. Verifies that HTTP header
- * injection is only used on events that support it.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_post_parse_cfg_scope(void)
-{
- struct flt_otel_conf_span *conf_span;
- int retval = ERR_NONE;
-
- OTELC_FUNC("");
-
- if (flt_otel_current_scope == NULL)
- OTELC_RETURN_INT(retval);
-
- /* If span context inject is used, check that this is possible. */
- list_for_each_entry(conf_span, &(flt_otel_current_scope->spans), list)
- if ((conf_span->ctx_id != NULL) && (conf_span->ctx_flags & FLT_OTEL_CTX_USE_HEADERS))
- if (!flt_otel_event_data[flt_otel_current_scope->event].flag_http_inject)
- FLT_OTEL_POST_PARSE_ALERT("inject '%s' : cannot use on this event", conf_span->cfg_line, conf_span->ctx_id);
-
- /* Validate idle-timeout / on-idle-timeout consistency. */
- if (flt_otel_current_scope->idle_timeout == 0) {
- if (flt_otel_current_scope->event == FLT_OTEL_EVENT__IDLE_TIMEOUT)
- FLT_OTEL_POST_PARSE_ALERT("'%s' : 'idle-timeout' is required for event 'on-idle-timeout'", flt_otel_current_scope->cfg_line, flt_otel_current_scope->id);
- }
- else if (flt_otel_current_scope->event != FLT_OTEL_EVENT__IDLE_TIMEOUT) {
- FLT_OTEL_POST_PARSE_ALERT("'%s' : 'idle-timeout' can only be used with event 'on-idle-timeout'", flt_otel_current_scope->cfg_line, flt_otel_current_scope->id);
- }
-
- if (retval & ERR_CODE)
- flt_otel_conf_scope_free(&flt_otel_current_scope);
-
- flt_otel_current_scope = NULL;
- flt_otel_current_span = NULL;
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_parse_cfg - OTel configuration file parser
- *
- * SYNOPSIS
- * static int flt_otel_parse_cfg(struct flt_otel_conf *conf, const char *flt_name, char **err)
- *
- * ARGUMENTS
- * conf - pointer to the filter configuration structure
- * flt_name - filter name for error reporting
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Parses the OTel filter configuration file. Backs up the current HAProxy
- * section parsers, registers temporary otel-instrumentation, otel-group, and
- * otel-scope section parsers, loads and parses the file, then restores the
- * original sections.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_parse_cfg(struct flt_otel_conf *conf, const char *flt_name, char **err)
-{
- struct list backup_sections;
- struct cfgfile cfg_file;
- int retval = ERR_ABORT | ERR_ALERT;
-
- OTELC_FUNC("%p, \"%s\", %p:%p", conf, OTELC_STR_ARG(flt_name), OTELC_DPTR_ARGS(err));
-
- flt_otel_current_config = conf;
-
- /* Backup sections. */
- LIST_INIT(&backup_sections);
- cfg_backup_sections(&backup_sections);
-
- /* Register new OTEL sections and parse the OTEL filter configuration file. */
- if (!cfg_register_section(FLT_OTEL_PARSE_SECTION_INSTR_ID, flt_otel_parse_cfg_instr, flt_otel_post_parse_cfg_instr))
- /* Do nothing. */;
- else if (!cfg_register_section(FLT_OTEL_PARSE_SECTION_GROUP_ID, flt_otel_parse_cfg_group, flt_otel_post_parse_cfg_group))
- /* Do nothing. */;
- else if (!cfg_register_section(FLT_OTEL_PARSE_SECTION_SCOPE_ID, flt_otel_parse_cfg_scope, flt_otel_post_parse_cfg_scope))
- /* Do nothing. */;
- else if (access(conf->cfg_file, R_OK) == -1)
- FLT_OTEL_PARSE_ERR(err, "'%s' : %s", conf->cfg_file, strerror(errno));
- else {
- struct list saved_args = LIST_HEAD_INIT(saved_args);
-
- /*
- * Sample fetch arguments queued during parsing are normally
- * resolved by smp_resolve_args() in the proxy
- * post-configuration phase. That call uses the proxy's own
- * capabilities, so backend-only fetches like be_conn would
- * fail when the filter is attached to a frontend.
- *
- * The OTel filter spans both request and response channels,
- * so its sample fetches must be resolved with full FE+BE
- * capabilities. To achieve this the proxy's arg list is saved
- * and replaced with a fresh one before parsing. The OTel
- * config parser adds only ARGC_OTEL entries to the new list.
- * After parsing, those entries are moved to conf->smp_args and
- * resolved later in flt_otel_check(), which runs after all
- * configuration sections have been parsed so that backends and
- * servers are available.
- */
- LIST_SPLICE(&saved_args, &(conf->proxy->conf.args.list));
- LIST_INIT(&(conf->proxy->conf.args.list));
-
- (void)memset(&cfg_file, 0, sizeof(cfg_file));
- cfg_file.filename = conf->cfg_file;
- cfg_file.size = load_cfg_in_mem(cfg_file.filename, &(cfg_file.content));
- if (cfg_file.size >= 0)
- retval = parse_cfg(&cfg_file);
- ha_free(&(cfg_file.content));
-
- /* Stash OTEL args for deferred resolution. */
- LIST_SPLICE(&(conf->smp_args), &(conf->proxy->conf.args.list));
- LIST_INIT(&(conf->proxy->conf.args.list));
-
- /* Restore the original arg list unchanged. */
- LIST_SPLICE(&(conf->proxy->conf.args.list), &saved_args);
- }
-
- /* Unregister OTEL sections and restore previous sections. */
- cfg_unregister_sections();
- cfg_restore_sections(&backup_sections);
-
- flt_otel_current_config = NULL;
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_parse - main filter parser entry point
- *
- * SYNOPSIS
- * static int flt_otel_parse(char **args, int *cur_arg, struct proxy *px, struct flt_conf *fconf, char **err, void *private)
- *
- * ARGUMENTS
- * args - configuration line arguments array
- * cur_arg - pointer to the current argument index
- * px - proxy instance owning the filter
- * fconf - filter configuration structure to populate
- * err - indirect pointer to error message string
- * private - unused private data pointer
- *
- * DESCRIPTION
- * Main filter parser entry point, registered for the "otel" filter keyword.
- * Parses the filter ID and configuration file path from the HAProxy
- * configuration line. If no filter ID is specified, the default ID is used.
- *
- * RETURN VALUE
- * Returns ERR_NONE (== 0) in case of success,
- * or a combination of ERR_* flags if an error is encountered.
- */
-static int flt_otel_parse(char **args, int *cur_arg, struct proxy *px, struct flt_conf *fconf, char **err, void *private)
-{
- struct flt_otel_conf *conf = NULL;
- int pos, retval = ERR_NONE;
-
- OTELC_FUNC("%p, %p, %p, %p, %p:%p, %p", args, cur_arg, px, fconf, OTELC_DPTR_ARGS(err), private);
-
- OTELC_DBG_IFDEF(otelc_dbg_level = FLT_OTEL_DEBUG_LEVEL, );
-
-#ifdef OTELC_DBG_MEM
- /* Initialize the debug memory tracker before the first allocation. */
- FLT_OTEL_RUN_ONCE(
- if (otelc_dbg_mem_init(&dbg_mem, dbg_mem_data, OTELC_TABLESIZE(dbg_mem_data)) == -1) {
- FLT_OTEL_PARSE_ERR(err, "cannot initialize memory debugger");
-
- OTELC_RETURN_INT(retval);
- }
- );
-#endif
-
- FLT_OTEL_ARGS_DUMP();
-
- conf = flt_otel_conf_init(px);
- if (conf == NULL) {
- FLT_OTEL_PARSE_ERR(err, "'%s' : out of memory", args[*cur_arg]);
-
- OTELC_RETURN_INT(retval);
- }
-
- /* Process filter option key-value pairs. */
- for (pos = *cur_arg + 1; !(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(pos); pos++) {
- OTELC_DBG(DEBUG, "args[%d:2]: { '%s' '%s' }", pos, args[pos], args[pos + 1]);
-
- if (FLT_OTEL_PARSE_KEYWORD(pos, FLT_OTEL_OPT_FILTER_ID)) {
- retval = flt_otel_parse_keyword(&(conf->id), args, *cur_arg, pos, err, "name");
- pos++;
- }
- else if (FLT_OTEL_PARSE_KEYWORD(pos, FLT_OTEL_OPT_CONFIG)) {
- retval = flt_otel_parse_keyword(&(conf->cfg_file), args, *cur_arg, pos, err, "configuration file");
- if (!(retval & ERR_CODE))
- retval = flt_otel_parse_cfg(conf, args[*cur_arg], err);
- pos++;
- }
- else {
- FLT_OTEL_PARSE_ERR(err, "'%s' : unknown keyword '%s'", args[*cur_arg], args[pos]);
- }
- }
-
- /* If the OpenTelemetry filter ID is not set, use default name. */
- if (!(retval & ERR_CODE) && (conf->id == NULL)) {
- ha_warning("parsing : " FLT_OTEL_FMT_TYPE FLT_OTEL_FMT_NAME "'no filter id set, using default id '%s'\n", FLT_OTEL_OPT_FILTER_ID_DEFAULT);
-
- retval = flt_otel_parse_strdup(&(conf->id), NULL, FLT_OTEL_OPT_FILTER_ID_DEFAULT, err, args[*cur_arg]);
- }
-
- if (!(retval & ERR_CODE) && (conf->cfg_file == NULL))
- FLT_OTEL_PARSE_ERR(err, "'%s' : no configuration file specified", args[*cur_arg]);
-
- if (retval & ERR_CODE) {
- flt_otel_conf_free(&conf);
- } else {
- fconf->id = otel_flt_id;
- fconf->ops = &flt_otel_ops;
- fconf->conf = conf;
-
- *cur_arg = pos;
-
- OTELC_DBG(DEBUG, "filter set: id '%s', config '%s'", conf->id, conf->cfg_file);
- FLT_OTEL_DBG_CONF("- conf ", (typeof(conf))fconf->conf);
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/* Declare the filter parser for FLT_OTEL_OPT_NAME keyword. */
-static struct flt_kw_list flt_kws = { FLT_OTEL_SCOPE, { }, {
- { FLT_OTEL_OPT_NAME, flt_otel_parse, NULL },
- { NULL, NULL, NULL },
- }
-};
-
-INITCALL1(STG_REGISTER, flt_register_keywords, &flt_kws);
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "../include/include.h"
-
-
-struct pool_head *pool_head_otel_scope_span __read_mostly = NULL;
-struct pool_head *pool_head_otel_scope_context __read_mostly = NULL;
-struct pool_head *pool_head_otel_runtime_context __read_mostly = NULL;
-struct pool_head *pool_head_otel_span_context __read_mostly = NULL;
-
-#ifdef USE_POOL_OTEL_SCOPE_SPAN
-REGISTER_POOL(&pool_head_otel_scope_span, "otel_scope_span", sizeof(struct flt_otel_scope_span));
-#endif
-#ifdef USE_POOL_OTEL_SCOPE_CONTEXT
-REGISTER_POOL(&pool_head_otel_scope_context, "otel_scope_context", sizeof(struct flt_otel_scope_context));
-#endif
-#ifdef USE_POOL_OTEL_RUNTIME_CONTEXT
-REGISTER_POOL(&pool_head_otel_runtime_context, "otel_runtime_context", sizeof(struct flt_otel_runtime_context));
-#endif
-#ifdef USE_POOL_OTEL_SPAN_CONTEXT
-REGISTER_POOL(&pool_head_otel_span_context, "otel_span_context", MAX(sizeof(struct otelc_span), sizeof(struct otelc_span_context)));
-#endif
-
-
-/***
- * NAME
- * flt_otel_pool_alloc - pool-aware memory allocation
- *
- * SYNOPSIS
- * void *flt_otel_pool_alloc(struct pool_head *pool, size_t size, bool flag_clear, char **err)
- *
- * ARGUMENTS
- * pool - HAProxy memory pool to allocate from (or NULL for heap)
- * size - number of bytes to allocate
- * flag_clear - whether to zero-fill the allocated memory
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Allocates <size> bytes of memory from the HAProxy memory <pool>. If <pool>
- * is NULL, the allocation falls back to the heap via OTELC_MALLOC(). When
- * <flag_clear> is set, the allocated memory is zero-filled. On allocation
- * failure, an error message is stored via <err>.
- *
- * RETURN VALUE
- * Returns a pointer to the allocated memory, or NULL on failure.
- */
-void *flt_otel_pool_alloc(struct pool_head *pool, size_t size, bool flag_clear, char **err)
-{
- void *retptr;
-
- OTELC_FUNC("%p, %zu, %hhu, %p:%p", pool, size, flag_clear, OTELC_DPTR_ARGS(err));
-
- if (pool != NULL) {
- retptr = pool_alloc(pool);
- if (retptr != NULL)
- OTELC_DBG(NOTICE, "POOL_ALLOC: %s:%d(%p %zu)", __func__, __LINE__, retptr, FLT_OTEL_DEREF(pool, size, size));
- } else {
- retptr = OTELC_MALLOC(size);
- }
-
- if (retptr == NULL)
- FLT_OTEL_ERR("out of memory");
- else if (flag_clear)
- (void)memset(retptr, 0, size);
-
- OTELC_RETURN_PTR(retptr);
-}
-
-
-/***
- * NAME
- * flt_otel_pool_strndup - pool-aware string duplication
- *
- * SYNOPSIS
- * void *flt_otel_pool_strndup(struct pool_head *pool, const char *s, size_t size, char **err)
- *
- * ARGUMENTS
- * pool - HAProxy memory pool to allocate from (or NULL for heap)
- * s - source string to duplicate
- * size - maximum number of characters to copy
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Duplicates up to <size> characters from the string <s> using the HAProxy
- * memory <pool>. If <pool> is NULL, the duplication falls back to
- * OTELC_STRNDUP(). When using a pool, the copy is truncated to <pool>->size-1
- * bytes and null-terminated.
- *
- * RETURN VALUE
- * Returns a pointer to the duplicated string, or NULL on failure.
- */
-void *flt_otel_pool_strndup(struct pool_head *pool, const char *s, size_t size, char **err)
-{
- void *retptr;
-
- OTELC_FUNC("%p, \"%.*s\", %zu, %p:%p", pool, (int)size, s, size, OTELC_DPTR_ARGS(err));
-
- if (pool != NULL) {
- retptr = pool_alloc(pool);
- if (retptr != NULL) {
- (void)memcpy(retptr, s, MIN(pool->size - 1, size));
-
- ((uint8_t *)retptr)[MIN(pool->size - 1, size)] = '\0';
- }
- } else {
- retptr = OTELC_STRNDUP(s, size);
- }
-
- if (retptr != NULL)
- OTELC_DBG(NOTICE, "POOL_STRNDUP: %s:%d(%p %zu)", __func__, __LINE__, retptr, FLT_OTEL_DEREF(pool, size, size));
- else
- FLT_OTEL_ERR("out of memory");
-
- OTELC_RETURN_PTR(retptr);
-}
-
-
-/***
- * NAME
- * flt_otel_pool_free - pool-aware memory deallocation
- *
- * SYNOPSIS
- * void flt_otel_pool_free(struct pool_head *pool, void **ptr)
- *
- * ARGUMENTS
- * pool - HAProxy memory pool to return memory to (or NULL for heap)
- * ptr - indirect pointer to the memory to free
- *
- * DESCRIPTION
- * Returns memory referenced by <*ptr> to the HAProxy memory <pool>. If
- * <pool> is NULL, the memory is freed via OTELC_SFREE(). The pointer <*ptr>
- * is set to NULL after freeing. If <ptr> is NULL or <*ptr> is already NULL,
- * the function returns immediately.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_pool_free(struct pool_head *pool, void **ptr)
-{
- OTELC_FUNC("%p, %p:%p", pool, OTELC_DPTR_ARGS(ptr));
-
- if ((ptr == NULL) || (*ptr == NULL))
- OTELC_RETURN();
-
- OTELC_DBG(NOTICE, "POOL_FREE: %s:%d(%p %u)", __func__, __LINE__, *ptr, FLT_OTEL_DEREF(pool, size, 0));
-
- if (pool != NULL)
- pool_free(pool, *ptr);
- else
- OTELC_SFREE(*ptr);
-
- *ptr = NULL;
-
- OTELC_RETURN();
-}
-
-
-/***
- * NAME
- * flt_otel_pool_init - OTel filter memory pool initialization
- *
- * SYNOPSIS
- * int flt_otel_pool_init(void)
- *
- * ARGUMENTS
- * This function takes no arguments.
- *
- * DESCRIPTION
- * Initializes all memory pools used by the OTel filter. Each pool is
- * created only when the corresponding USE_POOL_OTEL_* macro is defined.
- *
- * RETURN VALUE
- * Returns FLT_OTEL_RET_OK on success, FLT_OTEL_RET_ERROR on failure.
- */
-int flt_otel_pool_init(void)
-{
- int retval = FLT_OTEL_RET_OK;
-
- OTELC_FUNC("");
-
-#ifdef USE_POOL_OTEL_SCOPE_SPAN
- FLT_OTEL_POOL_INIT(pool_head_otel_scope_span, "otel_scope_span", sizeof(struct flt_otel_scope_span), retval);
-#endif
-#ifdef USE_POOL_OTEL_SCOPE_CONTEXT
- FLT_OTEL_POOL_INIT(pool_head_otel_scope_context, "otel_scope_context", sizeof(struct flt_otel_scope_context), retval);
-#endif
-#ifdef USE_POOL_OTEL_RUNTIME_CONTEXT
- FLT_OTEL_POOL_INIT(pool_head_otel_runtime_context, "otel_runtime_context", sizeof(struct flt_otel_runtime_context), retval);
-#endif
-#ifdef USE_POOL_OTEL_SPAN_CONTEXT
- FLT_OTEL_POOL_INIT(pool_head_otel_span_context, "otel_span_context", OTELC_MAX(sizeof(struct otelc_span), sizeof(struct otelc_span_context)), retval);
-#endif
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_pool_destroy - OTel filter memory pool destruction
- *
- * SYNOPSIS
- * void flt_otel_pool_destroy(void)
- *
- * ARGUMENTS
- * This function takes no arguments.
- *
- * DESCRIPTION
- * Destroys all memory pools used by the OTel filter. Each pool is
- * destroyed only when the corresponding USE_POOL_OTEL_* macro is defined.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_pool_destroy(void)
-{
- OTELC_FUNC("");
-
-#ifdef USE_POOL_OTEL_SCOPE_SPAN
- FLT_OTEL_POOL_DESTROY(pool_head_otel_scope_span);
-#endif
-#ifdef USE_POOL_OTEL_SCOPE_CONTEXT
- FLT_OTEL_POOL_DESTROY(pool_head_otel_scope_context);
-#endif
-#ifdef USE_POOL_OTEL_RUNTIME_CONTEXT
- FLT_OTEL_POOL_DESTROY(pool_head_otel_runtime_context);
-#endif
-#ifdef USE_POOL_OTEL_SPAN_CONTEXT
- FLT_OTEL_POOL_DESTROY(pool_head_otel_span_context);
-#endif
-
- OTELC_RETURN();
-}
-
-
-#ifdef DEBUG_OTEL
-
-/***
- * NAME
- * flt_otel_pool_info - debug pool sizes logging
- *
- * SYNOPSIS
- * void flt_otel_pool_info(void)
- *
- * ARGUMENTS
- * This function takes no arguments.
- *
- * DESCRIPTION
- * Logs the sizes of all registered HAProxy memory pools used by the OTel
- * filter (buffer, trash, scope_span, scope_context, runtime_context).
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_pool_info(void)
-{
- OTELC_DBG(NOTICE, "--- pool info ----------");
-
- /*
- * In case we have some error in the configuration file,
- * it is possible that this pool was not initialized.
- */
-#ifdef USE_POOL_BUFFER
- OTELC_DBG(NOTICE, " buffer: %p %u", pool_head_buffer, FLT_OTEL_DEREF(pool_head_buffer, size, 0));
-#endif
-#ifdef USE_TRASH_CHUNK
- OTELC_DBG(NOTICE, " trash: %p %u", pool_head_trash, FLT_OTEL_DEREF(pool_head_trash, size, 0));
-#endif
-
-#ifdef USE_POOL_OTEL_SCOPE_SPAN
- OTELC_DBG(NOTICE, " otel_scope_span: %p %u", pool_head_otel_scope_span, FLT_OTEL_DEREF(pool_head_otel_scope_span, size, 0));
-#endif
-#ifdef USE_POOL_OTEL_SCOPE_CONTEXT
- OTELC_DBG(NOTICE, " otel_scope_context: %p %u", pool_head_otel_scope_context, FLT_OTEL_DEREF(pool_head_otel_scope_context, size, 0));
-#endif
-#ifdef USE_POOL_OTEL_RUNTIME_CONTEXT
- OTELC_DBG(NOTICE, " otel_runtime_context: %p %u", pool_head_otel_runtime_context, FLT_OTEL_DEREF(pool_head_otel_runtime_context, size, 0));
-#endif
-#ifdef USE_POOL_OTEL_SPAN_CONTEXT
- OTELC_DBG(NOTICE, " otel_span_context: %p %u", pool_head_otel_span_context, FLT_OTEL_DEREF(pool_head_otel_span_context, size, 0));
-#endif
-}
-
-#endif /* DEBUG_OTEL */
-
-
-/***
- * NAME
- * flt_otel_trash_alloc - trash buffer allocation
- *
- * SYNOPSIS
- * struct buffer *flt_otel_trash_alloc(bool flag_clear, char **err)
- *
- * ARGUMENTS
- * flag_clear - whether to zero-fill the buffer area
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Allocates a temporary buffer chunk for use as scratch space. When
- * USE_TRASH_CHUNK is defined, the buffer is obtained via alloc_trash_chunk();
- * otherwise, a buffer structure and its data area are allocated from the heap
- * using global.tune.bufsize as the buffer size. When <flag_clear> is set,
- * the buffer's data area is zero-filled.
- *
- * RETURN VALUE
- * Returns a pointer to the allocated buffer, or NULL on failure.
- */
-struct buffer *flt_otel_trash_alloc(bool flag_clear, char **err)
-{
- struct buffer *retptr;
-
- OTELC_FUNC("%hhu, %p:%p", flag_clear, OTELC_DPTR_ARGS(err));
-
-#ifdef USE_TRASH_CHUNK
- retptr = alloc_trash_chunk();
- if (retptr != NULL)
- OTELC_DBG(NOTICE, "TRASH_ALLOC: %s:%d(%p %zu)", __func__, __LINE__, retptr, retptr->size);
-#else
- retptr = OTELC_MALLOC(sizeof(*retptr));
- if (retptr != NULL) {
- chunk_init(retptr, OTELC_MALLOC(global.tune.bufsize), global.tune.bufsize);
- if (retptr->area == NULL)
- OTELC_SFREE_CLEAR(retptr);
- else
- *(retptr->area) = '\0';
- }
-#endif
-
- if (retptr == NULL)
- FLT_OTEL_ERR("out of memory");
- else if (flag_clear)
- (void)memset(retptr->area, 0, retptr->size);
-
- OTELC_RETURN_PTR(retptr);
-}
-
-
-/***
- * NAME
- * flt_otel_trash_free - trash buffer deallocation
- *
- * SYNOPSIS
- * void flt_otel_trash_free(struct buffer **ptr)
- *
- * ARGUMENTS
- * ptr - indirect pointer to the buffer to free
- *
- * DESCRIPTION
- * Frees a trash buffer chunk previously allocated by flt_otel_trash_alloc().
- * When USE_TRASH_CHUNK is defined, the buffer is freed via
- * free_trash_chunk(); otherwise, both the data area and the buffer structure
- * are freed individually. The pointer <*ptr> is set to NULL after freeing.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_trash_free(struct buffer **ptr)
-{
- OTELC_FUNC("%p:%p", OTELC_DPTR_ARGS(ptr));
-
- if ((ptr == NULL) || (*ptr == NULL))
- OTELC_RETURN();
-
- OTELC_DBG(NOTICE, "TRASH_FREE: %s:%d(%p %zu)", __func__, __LINE__, *ptr, (*ptr)->size);
-
-#ifdef USE_TRASH_CHUNK
- free_trash_chunk(*ptr);
-#else
- OTELC_SFREE((*ptr)->area);
- OTELC_SFREE(*ptr);
-#endif
-
- *ptr = NULL;
-
- OTELC_RETURN();
-}
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "../include/include.h"
-
-
-/***
- * NAME
- * flt_otel_runtime_context_init - per-stream runtime context allocation
- *
- * SYNOPSIS
- * struct flt_otel_runtime_context *flt_otel_runtime_context_init(struct stream *s, struct filter *f, char **err)
- *
- * ARGUMENTS
- * s - the stream to which the context belongs
- * f - the filter instance
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Allocates and initializes a per-stream runtime context from pool memory.
- * It copies the hard-error, disabled and logging flags from the filter
- * configuration, generates a UUID and stores it in the sess.otel.uuid
- * HAProxy variable.
- *
- * RETURN VALUE
- * Returns a pointer to the new runtime context, or NULL on failure.
- */
-struct flt_otel_runtime_context *flt_otel_runtime_context_init(struct stream *s, struct filter *f, char **err)
-{
- const struct flt_otel_conf *conf = FLT_OTEL_CONF(f);
- struct buffer uuid;
- struct flt_otel_runtime_context *retptr = NULL;
-
- OTELC_FUNC("%p, %p, %p:%p", s, f, OTELC_DPTR_ARGS(err));
-
- retptr = flt_otel_pool_alloc(pool_head_otel_runtime_context, sizeof(*retptr), 1, err);
- if (retptr == NULL)
- OTELC_RETURN_PTR(retptr);
-
- /* Initialize runtime context fields and generate a session UUID. */
- retptr->stream = s;
- retptr->filter = f;
- retptr->flag_harderr = _HA_ATOMIC_LOAD(&(conf->instr->flag_harderr));
- retptr->flag_disabled = _HA_ATOMIC_LOAD(&(conf->instr->flag_disabled));
- retptr->logging = _HA_ATOMIC_LOAD(&(conf->instr->logging));
- retptr->idle_timeout = 0;
- retptr->idle_exp = TICK_ETERNITY;
- LIST_INIT(&(retptr->spans));
- LIST_INIT(&(retptr->contexts));
-
- uuid = b_make(retptr->uuid, sizeof(retptr->uuid), 0, 0);
- ha_generate_uuid_v4(&uuid);
-
-#ifdef USE_OTEL_VARS
- /*
- * The HAProxy variable 'sess.otel.uuid' is registered here,
- * after which its value is set to runtime context UUID.
- */
- if (flt_otel_var_register(FLT_OTEL_VAR_UUID, err) != -1)
- (void)flt_otel_var_set(s, FLT_OTEL_VAR_UUID, retptr->uuid, SMP_OPT_DIR_REQ, err);
-#endif
-
- FLT_OTEL_DBG_RUNTIME_CONTEXT("session context: ", retptr);
-
- OTELC_RETURN_PTR(retptr);
-}
-
-
-/***
- * NAME
- * flt_otel_runtime_context_free - per-stream runtime context cleanup
- *
- * SYNOPSIS
- * void flt_otel_runtime_context_free(struct filter *f)
- *
- * ARGUMENTS
- * f - the filter instance whose runtime context is to be freed
- *
- * DESCRIPTION
- * Frees the per-stream runtime context attached to <f>. It ends all active
- * spans with the current monotonic timestamp, destroys all extracted
- * contexts, and returns the pool memory.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_runtime_context_free(struct filter *f)
-{
- struct flt_otel_runtime_context *rt_ctx = f->ctx;
-
- OTELC_FUNC("%p", f);
-
- if (rt_ctx == NULL)
- OTELC_RETURN();
-
- FLT_OTEL_DBG_RUNTIME_CONTEXT("session context: ", rt_ctx);
-
- /* End all active spans with a common timestamp. */
- if (!LIST_ISEMPTY(&(rt_ctx->spans))) {
- struct timespec ts_steady;
- struct flt_otel_scope_span *span, *span_back;
-
- /* All spans should be completed at the same time. */
- (void)clock_gettime(CLOCK_MONOTONIC, &ts_steady);
-
- list_for_each_entry_safe(span, span_back, &(rt_ctx->spans), list) {
- OTELC_OPSR(span->span, end_with_options, &ts_steady, OTELC_SPAN_STATUS_IGNORE, NULL);
- flt_otel_scope_span_free(&span);
- }
- }
-
- /* Destroy all extracted span contexts. */
- if (!LIST_ISEMPTY(&(rt_ctx->contexts))) {
- struct flt_otel_scope_context *ctx, *ctx_back;
-
- list_for_each_entry_safe(ctx, ctx_back, &(rt_ctx->contexts), list)
- flt_otel_scope_context_free(&ctx);
- }
-
- flt_otel_pool_free(pool_head_otel_runtime_context, &(f->ctx));
-
- OTELC_RETURN();
-}
-
-
-/***
- * NAME
- * flt_otel_scope_span_init - scope span lookup or creation
- *
- * SYNOPSIS
- * struct flt_otel_scope_span *flt_otel_scope_span_init(struct flt_otel_runtime_context *rt_ctx, const char *id, size_t id_len, const char *ref_id, size_t ref_id_len, uint dir, char **err)
- *
- * ARGUMENTS
- * rt_ctx - the runtime context owning the span list
- * id - the span operation name
- * id_len - length of the <id> string
- * ref_id - the parent span or context name, or NULL
- * ref_id_len - length of the <ref_id> string
- * dir - the sample fetch direction (SMP_OPT_DIR_REQ/RES)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Finds an existing scope span by <id> in the runtime context or creates a
- * new one. If <ref_id> is set, it resolves the parent reference by searching
- * the span list first, then the extracted context list.
- *
- * RETURN VALUE
- * Returns the existing or new scope span, or NULL on failure.
- */
-struct flt_otel_scope_span *flt_otel_scope_span_init(struct flt_otel_runtime_context *rt_ctx, const char *id, size_t id_len, const char *ref_id, size_t ref_id_len, uint dir, char **err)
-{
- struct otelc_span *ref_span = NULL;
- struct otelc_span_context *ref_ctx = NULL;
- struct flt_otel_scope_span *span, *retptr = NULL;
- struct flt_otel_scope_context *ctx;
-
- OTELC_FUNC("%p, \"%s\", %zu, \"%s\", %zu, %u, %p:%p", rt_ctx, OTELC_STR_ARG(id), id_len, OTELC_STR_ARG(ref_id), ref_id_len, dir, OTELC_DPTR_ARGS(err));
-
- if ((rt_ctx == NULL) || (id == NULL))
- OTELC_RETURN_PTR(retptr);
-
- /* Return the existing span if one matches this ID. */
- list_for_each_entry(span, &(rt_ctx->spans), list)
- if (FLT_OTEL_CONF_STR_CMP(span->id, id)) {
- OTELC_DBG(NOTICE, "found span %p", span);
-
- OTELC_RETURN_PTR(span);
- }
-
- /* Resolve the parent reference from spans or contexts. */
- if (ref_id != NULL) {
- list_for_each_entry(span, &(rt_ctx->spans), list)
- if (FLT_OTEL_CONF_STR_CMP(span->id, ref_id)) {
- ref_span = span->span;
-
- break;
- }
-
- if (ref_span != NULL) {
- OTELC_DBG(NOTICE, "found referenced span %p", span);
- } else {
- list_for_each_entry(ctx, &(rt_ctx->contexts), list)
- if (FLT_OTEL_CONF_STR_CMP(ctx->id, ref_id)) {
- ref_ctx = ctx->context;
-
- break;
- }
-
- if (ref_ctx != NULL) {
- OTELC_DBG(NOTICE, "found referenced context %p", ctx);
- } else {
- FLT_OTEL_ERR("cannot find referenced span/context '%s'", ref_id);
-
- OTELC_RETURN_PTR(retptr);
- }
- }
- }
-
- retptr = flt_otel_pool_alloc(pool_head_otel_scope_span, sizeof(*retptr), 1, err);
- if (retptr == NULL)
- OTELC_RETURN_PTR(retptr);
-
- /* Populate the new scope span and insert it into the list. */
- retptr->id = id;
- retptr->id_len = id_len;
- retptr->smp_opt_dir = dir;
- retptr->ref_span = ref_span;
- retptr->ref_ctx = ref_ctx;
- LIST_INSERT(&(rt_ctx->spans), &(retptr->list));
-
- FLT_OTEL_DBG_SCOPE_SPAN("new span ", retptr);
-
- OTELC_RETURN_PTR(retptr);
-}
-
-
-/***
- * NAME
- * flt_otel_scope_span_free - scope span cleanup
- *
- * SYNOPSIS
- * void flt_otel_scope_span_free(struct flt_otel_scope_span **ptr)
- *
- * ARGUMENTS
- * ptr - pointer to the scope span pointer to free
- *
- * DESCRIPTION
- * Frees a scope span entry pointed to by <ptr> and removes it from its list.
- * If the OTel span is still active (non-NULL), the function refuses to free
- * it and returns immediately.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_scope_span_free(struct flt_otel_scope_span **ptr)
-{
- OTELC_FUNC("%p:%p", OTELC_DPTR_ARGS(ptr));
-
- if ((ptr == NULL) || (*ptr == NULL))
- OTELC_RETURN();
-
- FLT_OTEL_DBG_SCOPE_SPAN("", *ptr);
-
- /* If the span is still active, do nothing. */
- if ((*ptr)->span != NULL) {
- OTELC_DBG(NOTICE, "cannot finish active span");
-
- OTELC_RETURN();
- }
-
- FLT_OTEL_LIST_DEL(&((*ptr)->list));
- flt_otel_pool_free(pool_head_otel_scope_span, (void **)ptr);
-
- OTELC_RETURN();
-}
-
-
-/***
- * NAME
- * flt_otel_scope_context_init - scope context extraction
- *
- * SYNOPSIS
- * struct flt_otel_scope_context *flt_otel_scope_context_init(struct flt_otel_runtime_context *rt_ctx, struct otelc_tracer *tracer, const char *id, size_t id_len, const struct otelc_text_map *text_map, uint dir, char **err)
- *
- * ARGUMENTS
- * rt_ctx - the runtime context owning the context list
- * tracer - the OTel tracer used for context extraction
- * id - the context name
- * id_len - length of the <id> string
- * text_map - the carrier text map to extract from
- * dir - the sample fetch direction (SMP_OPT_DIR_REQ/RES)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Finds an existing scope context by <id> in the runtime context or creates
- * a new one by extracting the span context from the <text_map> carrier via
- * the <tracer>.
- *
- * RETURN VALUE
- * Returns the existing or new scope context, or NULL on failure.
- */
-struct flt_otel_scope_context *flt_otel_scope_context_init(struct flt_otel_runtime_context *rt_ctx, struct otelc_tracer *tracer, const char *id, size_t id_len, const struct otelc_text_map *text_map, uint dir, char **err)
-{
- struct otelc_http_headers_reader reader;
- struct otelc_span_context *span_ctx;
- struct flt_otel_scope_context *retptr = NULL;
-
- OTELC_FUNC("%p, %p, \"%s\", %zu, %p, %u, %p:%p", rt_ctx, tracer, OTELC_STR_ARG(id), id_len, text_map, dir, OTELC_DPTR_ARGS(err));
-
- if ((rt_ctx == NULL) || (tracer == NULL) || (id == NULL) || (text_map == NULL))
- OTELC_RETURN_PTR(retptr);
-
- /* Return the existing context if one matches this ID. */
- list_for_each_entry(retptr, &(rt_ctx->contexts), list)
- if (FLT_OTEL_CONF_STR_CMP(retptr->id, id)) {
- OTELC_DBG(NOTICE, "found context %p", retptr);
-
- OTELC_RETURN_PTR(retptr);
- }
-
- retptr = flt_otel_pool_alloc(pool_head_otel_scope_context, sizeof(*retptr), 1, err);
- if (retptr == NULL)
- OTELC_RETURN_PTR(retptr);
-
- span_ctx = flt_otel_extract_http_headers(tracer, &reader, text_map);
- if (span_ctx == NULL) {
- flt_otel_scope_context_free(&retptr);
-
- OTELC_RETURN_PTR(retptr);
- }
-
- /* Populate the new scope context and insert it into the list. */
- retptr->id = id;
- retptr->id_len = id_len;
- retptr->smp_opt_dir = dir;
- retptr->context = span_ctx;
- LIST_INSERT(&(rt_ctx->contexts), &(retptr->list));
-
- FLT_OTEL_DBG_SCOPE_CONTEXT("new context ", retptr);
-
- OTELC_RETURN_PTR(retptr);
-}
-
-
-/***
- * NAME
- * flt_otel_scope_context_free - scope context cleanup
- *
- * SYNOPSIS
- * void flt_otel_scope_context_free(struct flt_otel_scope_context **ptr)
- *
- * ARGUMENTS
- * ptr - pointer to the scope context pointer to free
- *
- * DESCRIPTION
- * Frees a scope context entry pointed to by <ptr>. It destroys the
- * underlying OTel span context, removes the entry from its list, and
- * returns the pool memory.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_scope_context_free(struct flt_otel_scope_context **ptr)
-{
- OTELC_FUNC("%p:%p", OTELC_DPTR_ARGS(ptr));
-
- if ((ptr == NULL) || (*ptr == NULL))
- OTELC_RETURN();
-
- FLT_OTEL_DBG_SCOPE_CONTEXT("", *ptr);
-
- if ((*ptr)->context != NULL)
- OTELC_OPSR((*ptr)->context, destroy);
-
- FLT_OTEL_LIST_DEL(&((*ptr)->list));
- flt_otel_pool_free(pool_head_otel_scope_context, (void **)ptr);
-
- OTELC_RETURN();
-}
-
-
-#ifdef DEBUG_OTEL
-
-/***
- * NAME
- * flt_otel_scope_data_dump - debug scope data dump
- *
- * SYNOPSIS
- * void flt_otel_scope_data_dump(const struct flt_otel_scope_data *data)
- *
- * ARGUMENTS
- * data - the scope data structure to dump
- *
- * DESCRIPTION
- * Dumps the contents of a scope <data> structure for debugging: baggage
- * key-value pairs, attributes, events with their attributes, span links,
- * and the status code/description.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_scope_data_dump(const struct flt_otel_scope_data *data)
-{
- size_t i;
-
- if (data == NULL)
- return;
-
- if (data->baggage.attr == NULL) {
- OTELC_DBG(WORKER, "baggage %p:{ }", &(data->baggage));
- } else {
- OTELC_DBG(WORKER, "baggage %p:{", &(data->baggage));
- for (i = 0; i < data->baggage.cnt; i++)
- OTELC_DBG_KV(WORKER, " ", data->baggage.attr + i);
- OTELC_DBG(WORKER, "}");
- }
-
- if (data->attributes.attr == NULL) {
- OTELC_DBG(WORKER, "attributes %p:{ }", &(data->attributes));
- } else {
- OTELC_DBG(WORKER, "attributes %p:{", &(data->attributes));
- for (i = 0; i < data->attributes.cnt; i++)
- OTELC_DBG_KV(WORKER, " ", data->attributes.attr + i);
- OTELC_DBG(WORKER, "}");
- }
-
- if (LIST_ISEMPTY(&(data->events))) {
- OTELC_DBG(WORKER, "events %p:{ }", &(data->events));
- } else {
- struct flt_otel_scope_data_event *event;
-
- OTELC_DBG(WORKER, "events %p:{", &(data->events));
- list_for_each_entry_rev(event, &(data->events), list) {
- OTELC_DBG(WORKER, " '%s' %zu/%zu", event->name, event->cnt, event->size);
- if (event->attr != NULL)
- for (i = 0; i < event->cnt; i++)
- OTELC_DBG_KV(WORKER, " ", event->attr + i);
- }
- OTELC_DBG(WORKER, "}");
- }
-
- if (LIST_ISEMPTY(&(data->links))) {
- OTELC_DBG(WORKER, "links %p:{ }", &(data->links));
- } else {
- struct flt_otel_scope_data_link *link;
-
- OTELC_DBG(WORKER, "links %p:{", &(data->links));
- list_for_each_entry(link, &(data->links), list)
- OTELC_DBG(WORKER, " %p %p", link->span, link->context);
- OTELC_DBG(WORKER, "}");
- }
-
- if ((data->status.code == 0) && (data->status.description == NULL))
- OTELC_DBG(WORKER, "status %p:{ }", &(data->status));
- else
- FLT_OTEL_DBG_SCOPE_DATA_STATUS("status ", &(data->status));
-}
-
-#endif /* DEBUG_OTEL */
-
-
-/***
- * NAME
- * flt_otel_scope_data_init - scope data zero-initialization
- *
- * SYNOPSIS
- * void flt_otel_scope_data_init(struct flt_otel_scope_data *ptr)
- *
- * ARGUMENTS
- * ptr - the scope data structure to initialize
- *
- * DESCRIPTION
- * Zero-initializes the scope data structure pointed to by <ptr> and sets up
- * the event and link list heads.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_scope_data_init(struct flt_otel_scope_data *ptr)
-{
- OTELC_FUNC("%p", ptr);
-
- if (ptr == NULL)
- OTELC_RETURN();
-
- (void)memset(ptr, 0, sizeof(*ptr));
- LIST_INIT(&(ptr->events));
- LIST_INIT(&(ptr->links));
-
- OTELC_RETURN();
-}
-
-
-/***
- * NAME
- * flt_otel_scope_data_free - scope data cleanup
- *
- * SYNOPSIS
- * void flt_otel_scope_data_free(struct flt_otel_scope_data *ptr)
- *
- * ARGUMENTS
- * ptr - the scope data structure to free
- *
- * DESCRIPTION
- * Frees all contents of the scope data structure pointed to by <ptr>: baggage
- * and attribute key-value arrays, event entries with their attributes, link
- * entries, and the status description string.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_scope_data_free(struct flt_otel_scope_data *ptr)
-{
- struct flt_otel_scope_data_event *event, *event_back;
- struct flt_otel_scope_data_link *link, *link_back;
-
- OTELC_FUNC("%p", ptr);
-
- if (ptr == NULL)
- OTELC_RETURN();
-
- FLT_OTEL_DBG_SCOPE_DATA("", ptr);
-
- /* Destroy all dynamic scope data contents. */
- otelc_kv_destroy(&(ptr->baggage.attr), ptr->baggage.cnt);
- otelc_kv_destroy(&(ptr->attributes.attr), ptr->attributes.cnt);
- list_for_each_entry_safe(event, event_back, &(ptr->events), list) {
- otelc_kv_destroy(&(event->attr), event->cnt);
- OTELC_SFREE(event->name);
- OTELC_SFREE(event);
- }
- /* Free all resolved link entries. */
- list_for_each_entry_safe(link, link_back, &(ptr->links), list)
- OTELC_SFREE(link);
- OTELC_SFREE(ptr->status.description);
-
- (void)memset(ptr, 0, sizeof(*ptr));
-
- OTELC_RETURN();
-}
-
-
-/***
- * NAME
- * flt_otel_scope_finish_mark - mark spans and contexts for finishing
- *
- * SYNOPSIS
- * int flt_otel_scope_finish_mark(const struct flt_otel_runtime_context *rt_ctx, const char *id, size_t id_len)
- *
- * ARGUMENTS
- * rt_ctx - the runtime context containing spans and contexts
- * id - the target name, or a wildcard ("*", "*req*", "*res*")
- * id_len - length of the <id> string
- *
- * DESCRIPTION
- * Marks spans and contexts for finishing. The <id> argument supports
- * wildcards: "*" marks all spans and contexts, "*req*" marks the request
- * channel only, "*res*" marks the response channel only. Otherwise, a named
- * span or context is looked up by exact match.
- *
- * RETURN VALUE
- * Returns the number of spans and contexts that were marked.
- */
-int flt_otel_scope_finish_mark(const struct flt_otel_runtime_context *rt_ctx, const char *id, size_t id_len)
-{
- struct flt_otel_scope_span *span;
- struct flt_otel_scope_context *ctx;
- int span_cnt = 0, ctx_cnt = 0, retval;
-
- OTELC_FUNC("%p, \"%s\", %zu", rt_ctx, OTELC_STR_ARG(id), id_len);
-
- /* Handle wildcard finish marks: all, request-only, response-only. */
- if (FLT_OTEL_STR_CMP(FLT_OTEL_SCOPE_SPAN_FINISH_ALL, id)) {
- list_for_each_entry(span, &(rt_ctx->spans), list) {
- span->flag_finish = 1;
- span_cnt++;
- }
-
- list_for_each_entry(ctx, &(rt_ctx->contexts), list) {
- ctx->flag_finish = 1;
- ctx_cnt++;
- }
-
- OTELC_DBG(NOTICE, "marked %d span(s), %d context(s)", span_cnt, ctx_cnt);
- }
- else if (FLT_OTEL_STR_CMP(FLT_OTEL_SCOPE_SPAN_FINISH_REQ, id)) {
- list_for_each_entry(span, &(rt_ctx->spans), list)
- if (span->smp_opt_dir == SMP_OPT_DIR_REQ) {
- span->flag_finish = 1;
- span_cnt++;
- }
-
- list_for_each_entry(ctx, &(rt_ctx->contexts), list)
- if (ctx->smp_opt_dir == SMP_OPT_DIR_REQ) {
- ctx->flag_finish = 1;
- ctx_cnt++;
- }
-
- OTELC_DBG(NOTICE, "marked REQuest channel %d span(s), %d context(s)", span_cnt, ctx_cnt);
- }
- else if (FLT_OTEL_STR_CMP(FLT_OTEL_SCOPE_SPAN_FINISH_RES, id)) {
- list_for_each_entry(span, &(rt_ctx->spans), list)
- if (span->smp_opt_dir == SMP_OPT_DIR_RES) {
- span->flag_finish = 1;
- span_cnt++;
- }
-
- list_for_each_entry(ctx, &(rt_ctx->contexts), list)
- if (ctx->smp_opt_dir == SMP_OPT_DIR_RES) {
- ctx->flag_finish = 1;
- ctx_cnt++;
- }
-
- OTELC_DBG(NOTICE, "marked RESponse channel %d span(s), %d context(s)", span_cnt, ctx_cnt);
- }
- else {
- list_for_each_entry(span, &(rt_ctx->spans), list)
- if (FLT_OTEL_CONF_STR_CMP(span->id, id)) {
- span->flag_finish = 1;
- span_cnt++;
-
- break;
- }
-
- list_for_each_entry(ctx, &(rt_ctx->contexts), list)
- if (FLT_OTEL_CONF_STR_CMP(ctx->id, id)) {
- ctx->flag_finish = 1;
- ctx_cnt++;
-
- break;
- }
-
- if (span_cnt > 0)
- OTELC_DBG(NOTICE, "marked span '%s'", id);
- if (ctx_cnt > 0)
- OTELC_DBG(NOTICE, "marked context '%s'", id);
- if ((span_cnt + ctx_cnt) == 0)
- OTELC_DBG(NOTICE, "cannot find span/context '%s'", id);
- }
-
- retval = span_cnt + ctx_cnt;
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_scope_finish_marked - finish marked spans and contexts
- *
- * SYNOPSIS
- * void flt_otel_scope_finish_marked(const struct flt_otel_runtime_context *rt_ctx, const struct timespec *ts_finish)
- *
- * ARGUMENTS
- * rt_ctx - the runtime context containing spans and contexts
- * ts_finish - the monotonic timestamp to use as the span end time
- *
- * DESCRIPTION
- * Ends all spans and destroys all contexts that have been marked for
- * finishing by flt_otel_scope_finish_mark(). Each span is ended with the
- * <ts_finish> timestamp; each context's OTel span context is destroyed.
- * The finish flags are cleared after processing.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_scope_finish_marked(const struct flt_otel_runtime_context *rt_ctx, const struct timespec *ts_finish)
-{
- struct flt_otel_scope_span *span;
- struct flt_otel_scope_context *ctx;
-
- OTELC_FUNC("%p, %p", rt_ctx, ts_finish);
-
- /* End all spans that have been marked for finishing. */
- list_for_each_entry(span, &(rt_ctx->spans), list)
- if (span->flag_finish) {
- FLT_OTEL_DBG_SCOPE_SPAN("finishing span ", span);
-
- OTELC_OPSR(span->span, end_with_options, ts_finish, OTELC_SPAN_STATUS_IGNORE, NULL);
-
- span->flag_finish = 0;
- }
-
- /* Destroy all contexts that have been marked for finishing. */
- list_for_each_entry(ctx, &(rt_ctx->contexts), list)
- if (ctx->flag_finish) {
- FLT_OTEL_DBG_SCOPE_CONTEXT("finishing context ", ctx);
-
- if (ctx->context != NULL)
- OTELC_OPSR(ctx->context, destroy);
-
- ctx->flag_finish = 0;
- }
-
- OTELC_RETURN();
-}
-
-
-/***
- * NAME
- * flt_otel_scope_free_unused - remove unused spans and contexts
- *
- * SYNOPSIS
- * void flt_otel_scope_free_unused(struct flt_otel_runtime_context *rt_ctx, struct channel *chn)
- *
- * ARGUMENTS
- * rt_ctx - the runtime context to clean up
- * chn - the channel for HTTP header cleanup
- *
- * DESCRIPTION
- * Removes scope spans with a NULL OTel span and scope contexts with a NULL
- * OTel context from the runtime context. For each removed context, the
- * associated HTTP headers and HAProxy variables are also cleaned up via
- * <chn>.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_scope_free_unused(struct flt_otel_runtime_context *rt_ctx, struct channel *chn)
-{
- OTELC_FUNC("%p, %p", rt_ctx, chn);
-
- if (rt_ctx == NULL)
- OTELC_RETURN();
-
- /* Remove spans that were never successfully created. */
- if (!LIST_ISEMPTY(&(rt_ctx->spans))) {
- struct flt_otel_scope_span *span, *span_back;
-
- list_for_each_entry_safe(span, span_back, &(rt_ctx->spans), list)
- if (span->span == NULL)
- flt_otel_scope_span_free(&span);
- }
-
- /* Remove contexts that failed extraction and clean up their traces. */
- if (!LIST_ISEMPTY(&(rt_ctx->contexts))) {
- struct flt_otel_scope_context *ctx, *ctx_back;
-
- list_for_each_entry_safe(ctx, ctx_back, &(rt_ctx->contexts), list)
- if (ctx->context == NULL) {
- /*
- * All headers and variables associated with
- * the context in question should be deleted.
- */
- (void)flt_otel_http_headers_remove(chn, ctx->id, NULL);
-#ifdef USE_OTEL_VARS
- (void)flt_otel_vars_unset(rt_ctx->stream, FLT_OTEL_VARS_SCOPE, ctx->id, ctx->smp_opt_dir, NULL);
-#endif
-
- flt_otel_scope_context_free(&ctx);
- }
- }
-
- FLT_OTEL_DBG_RUNTIME_CONTEXT("session context: ", rt_ctx);
-
- OTELC_RETURN();
-}
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "../include/include.h"
-
-
-#ifdef DEBUG_OTEL
-
-/***
- * NAME
- * flt_otel_args_dump - debug configuration arguments dump
- *
- * SYNOPSIS
- * void flt_otel_args_dump(const char **args)
- *
- * ARGUMENTS
- * args - configuration line arguments array
- *
- * DESCRIPTION
- * Dumps all configuration arguments to stderr. Counts the number of valid
- * arguments via flt_otel_args_count() and prints each one surrounded by
- * single quotes.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_args_dump(const char **args)
-{
- int i, argc;
-
- argc = flt_otel_args_count(args);
-
- (void)fprintf(stderr, OTELC_DBG_FMT("args[%d]: { '%s' "), argc, args[0]);
-
- for (i = 1; i < argc; i++)
- (void)fprintf(stderr, "'%s' ", args[i]);
-
- (void)fprintf(stderr, "}\n");
-}
-
-
-/***
- * NAME
- * flt_otel_filters_dump - debug OTel filter instances dump
- *
- * SYNOPSIS
- * void flt_otel_filters_dump(void)
- *
- * ARGUMENTS
- * This function takes no arguments.
- *
- * DESCRIPTION
- * Dumps all OTel filter instances across all proxies. Iterates the global
- * proxy list, logging each proxy name and its associated OTel filter IDs.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_filters_dump(void)
-{
- struct flt_conf *fconf;
- struct proxy *px;
-
- OTELC_FUNC("");
-
- for (px = proxies_list; px != NULL; px = px->next) {
- OTELC_DBG(NOTICE, "proxy '%s'", px->id);
-
- list_for_each_entry(fconf, &(px->filter_configs), list)
- if (fconf->id == otel_flt_id) {
- struct flt_otel_conf *conf = fconf->conf;
-
- OTELC_DBG(NOTICE, " OTEL filter '%s'", conf->id);
- }
- }
-
- OTELC_RETURN();
-}
-
-
-/***
- * NAME
- * flt_otel_chn_label - channel direction label
- *
- * SYNOPSIS
- * const char *flt_otel_chn_label(const struct channel *chn)
- *
- * ARGUMENTS
- * chn - channel to identify
- *
- * DESCRIPTION
- * Returns a human-readable label indicating the channel direction based on
- * the CF_ISRESP flag.
- *
- * RETURN VALUE
- * Returns "RESponse" for response channels, or "REQuest" for request
- * channels.
- */
-const char *flt_otel_chn_label(const struct channel *chn)
-{
- return (chn == NULL) ? "-" : ((chn->flags & CF_ISRESP) ? "RESponse" : "REQuest");
-}
-
-
-/***
- * NAME
- * flt_otel_pr_mode - proxy mode label
- *
- * SYNOPSIS
- * const char *flt_otel_pr_mode(const struct stream *s)
- *
- * ARGUMENTS
- * s - stream to check
- *
- * DESCRIPTION
- * Returns a human-readable label indicating the proxy mode. Uses the
- * backend proxy if a backend is assigned, otherwise the frontend proxy.
- *
- * RETURN VALUE
- * Returns "HTTP" for HTTP mode proxies, or "TCP" for TCP mode proxies.
- */
-const char *flt_otel_pr_mode(const struct stream *s)
-{
- struct proxy *px = (s->flags & SF_BE_ASSIGNED) ? s->be : strm_fe(s);
-
- return (px->mode == PR_MODE_HTTP) ? "HTTP" : "TCP";
-}
-
-
-/***
- * NAME
- * flt_otel_stream_pos - stream position label
- *
- * SYNOPSIS
- * const char *flt_otel_stream_pos(const struct stream *s)
- *
- * ARGUMENTS
- * s - stream to check
- *
- * DESCRIPTION
- * Returns a human-readable label indicating the stream position based on the
- * SF_BE_ASSIGNED flag.
- *
- * RETURN VALUE
- * Returns "backend" if a backend is assigned, or "frontend" otherwise.
- */
-const char *flt_otel_stream_pos(const struct stream *s)
-{
- return (s->flags & SF_BE_ASSIGNED) ? "backend" : "frontend";
-}
-
-
-/***
- * NAME
- * flt_otel_type - filter type label
- *
- * SYNOPSIS
- * const char *flt_otel_type(const struct filter *f)
- *
- * ARGUMENTS
- * f - filter instance to check
- *
- * DESCRIPTION
- * Returns a human-readable label indicating the filter type based on the
- * FLT_FL_IS_BACKEND_FILTER flag.
- *
- * RETURN VALUE
- * Returns "backend" for backend filters, or "frontend" for frontend filters.
- */
-const char *flt_otel_type(const struct filter *f)
-{
- return (f->flags & FLT_FL_IS_BACKEND_FILTER) ? "backend" : "frontend";
-}
-
-
-/***
- * NAME
- * flt_otel_analyzer - analyzer bit name lookup
- *
- * SYNOPSIS
- * const char *flt_otel_analyzer(uint an_bit)
- *
- * ARGUMENTS
- * an_bit - the analyzer identifier bit
- *
- * DESCRIPTION
- * Looks up the human-readable analyzer name for the given <an_bit> value from
- * the flt_otel_event_data table. If the bit is not found, a formatted error
- * string is returned from a thread-local buffer.
- *
- * RETURN VALUE
- * Returns the analyzer name string, or a formatted error message if the bit
- * is invalid.
- */
-const char *flt_otel_analyzer(uint an_bit)
-{
- static THREAD_LOCAL char retbuf[32];
- const char *retptr = NULL;
- int i;
-
- for (i = 0; i < OTELC_TABLESIZE(flt_otel_event_data); i++)
- if (flt_otel_event_data[i].an_bit == an_bit) {
- retptr = flt_otel_event_data[i].an_name;
-
- break;
- }
-
- if (retptr == NULL)
- (void)snprintf(retbuf, sizeof(retbuf), "invalid an_bit: 0x%08x", an_bit);
-
- return (retptr == NULL) ? retbuf : retptr;
-}
-
-
-/***
- * NAME
- * flt_otel_list_dump - debug list summary
- *
- * SYNOPSIS
- * const char *flt_otel_list_dump(const struct list *head)
- *
- * ARGUMENTS
- * head - list head to summarize
- *
- * DESCRIPTION
- * Returns a concise summary string describing the state of a linked list.
- * For NULL or empty lists, returns a descriptive label. For single-element
- * lists, returns the element pointer. For multi-element lists, returns the
- * first and last pointers along with the element count. Uses a rotating
- * thread-local buffer for the return value.
- *
- * RETURN VALUE
- * Returns a pointer to a thread-local string describing the list.
- */
-const char *flt_otel_list_dump(const struct list *head)
-{
- FLT_OTEL_BUFFER_THR(retbuf, 4, 64, retptr);
-
- if ((head == NULL) || LIST_ISEMPTY(head)) {
- (void)strncpy(retptr, (head == NULL) ? "{ null list }" : "{ empty list }", sizeof(retbuf[0]));
- }
- else if (head->p == head->n) {
- (void)snprintf(retptr, sizeof(retbuf[0]), "{ %p * 1 }", head->p);
- }
- else {
- const struct list *ptr;
- size_t count = 0;
-
- for (ptr = head->n; ptr != head; ptr = ptr->n, count++);
-
- (void)snprintf(retptr, sizeof(retbuf[0]), "{ %p %p %zu }", head->p, head->n, count);
- }
-
- return (retptr);
-}
-
-#endif /* DEBUG_OTEL */
-
-
-/***
- * NAME
- * flt_otel_args_count - argument count
- *
- * SYNOPSIS
- * int flt_otel_args_count(const char **args)
- *
- * ARGUMENTS
- * args - configuration line arguments array
- *
- * DESCRIPTION
- * Counts the number of valid (non-NULL) arguments in <args>. Scans up to
- * MAX_LINE_ARGS entries, handling gaps from blank arguments by returning the
- * index of the last valid argument incremented by one.
- *
- * RETURN VALUE
- * Returns the number of valid arguments.
- */
-int flt_otel_args_count(const char **args)
-{
- int i, retval = 0;
-
- if (args == NULL)
- return retval;
-
- /*
- * It is possible that some arguments within the configuration line
- * are not specified; that is, they are set to a blank string.
- *
- * For example:
- * keyword '' arg_2
- *
- * In that case the content of the args field will be like this:
- * args[0]: 'keyword'
- * args[1]: NULL pointer
- * args[2]: 'arg_2'
- * args[3 .. MAX_LINE_ARGS): NULL pointers
- *
- * The total number of arguments is the index of the last argument
- * (increased by 1) that is not a NULL pointer.
- */
- for (i = 0; i < MAX_LINE_ARGS; i++)
- if (FLT_OTEL_ARG_ISVALID(i))
- retval = i + 1;
-
- return retval;
-}
-
-
-/***
- * NAME
- * flt_otel_args_concat - argument concatenation
- *
- * SYNOPSIS
- * int flt_otel_args_concat(const char **args, int idx, int n, char **str)
- *
- * ARGUMENTS
- * args - configuration line arguments array
- * idx - starting index for concatenation
- * n - maximum number of arguments to concatenate (0 means all)
- * str - indirect pointer to the result string
- *
- * DESCRIPTION
- * Concatenates arguments starting from index <idx> into a single
- * space-separated string. The result is built via memprintf() into <*str>.
- * NULL arguments within the range are treated as empty strings.
- *
- * RETURN VALUE
- * Returns the number of concatenated arguments, or FLT_OTEL_RET_ERROR on
- * failure.
- */
-int flt_otel_args_concat(const char **args, int idx, int n, char **str)
-{
- int i, argc;
-
- if ((args == NULL) || (str == NULL))
- return FLT_OTEL_RET_ERROR;
- else if ((idx < 0) || (n < 0))
- return FLT_OTEL_RET_ERROR;
-
- argc = (n == 0) ? flt_otel_args_count(args) : OTELC_MIN(flt_otel_args_count(args), idx + n);
-
- for (i = idx; i < argc; i++)
- (void)memprintf(str, "%s%s%s", (*str == NULL) ? "" : *str, (i == idx) ? "" : " ", (args[i] == NULL) ? "" : args[i]);
-
- OTELC_DBG(DEBUG, "args[%d, %d]: '%s'", idx, argc, (*str == NULL) ? "" : *str);
-
- return (*str == NULL) ? FLT_OTEL_RET_ERROR : (i - idx);
-}
-
-
-/*
- * Comparator for qsort: ascending order of doubles. Values within
- * FLT_OTEL_DBL_EPSILON of each other are treated as equal.
- */
-int flt_otel_qsort_compar_double(const void *p1, const void *p2)
-{
- double a = *(const double *)p1;
- double b = *(const double *)p2;
-
- return (fabs(a - b) < FLT_OTEL_DBL_EPSILON) ? 0 : ((a < b) ? -1 : 1);
-}
-
-
-/***
- * NAME
- * flt_otel_strtod - string to double conversion with range check
- *
- * SYNOPSIS
- * bool flt_otel_strtod(const char *nptr, double *value, double limit_min, double limit_max, char **err)
- *
- * ARGUMENTS
- * nptr - string to parse
- * value - pointer to store the parsed double result
- * limit_min - minimum allowed value
- * limit_max - maximum allowed value
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Parses the string <nptr> as a double-precision floating-point number.
- * Validates that the entire string is consumed and that the result falls
- * within the range [<limit_min>, <limit_max>]. On parse error or range
- * violation, an error message is stored via <err>.
- *
- * RETURN VALUE
- * Returns true on success, false on failure.
- */
-bool flt_otel_strtod(const char *nptr, double *value, double limit_min, double limit_max, char **err)
-{
- char *endptr = NULL;
- bool retval = false;
-
- if (value == NULL)
- return retval;
-
- errno = 0;
-
- *value = strtod(nptr, &endptr);
- if ((errno != 0) || OTELC_STR_IS_VALID(endptr))
- FLT_OTEL_ERR("'%s' : invalid value", nptr);
- else if (!OTELC_IN_RANGE(*value, limit_min, limit_max))
- FLT_OTEL_ERR("'%s' : value out of range [%.2f, %.2f]", nptr, limit_min, limit_max);
- else
- retval = true;
-
- return retval;
-}
-
-
-/***
- * NAME
- * flt_otel_strtoll - string to int64_t conversion with range check
- *
- * SYNOPSIS
- * bool flt_otel_strtoll(const char *nptr, int64_t *value, int64_t limit_min, int64_t limit_max, char **err)
- *
- * ARGUMENTS
- * nptr - string to parse
- * value - pointer to store the parsed int64_t result
- * limit_min - minimum allowed value
- * limit_max - maximum allowed value
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Parses the string <nptr> as a 64-bit integer using base auto-detection.
- * Validates that the entire string is consumed and that the result falls
- * within the range [<limit_min>, <limit_max>]. On parse error or range
- * violation, an error message is stored via <err>.
- *
- * RETURN VALUE
- * Returns true on success, false on failure.
- */
-bool flt_otel_strtoll(const char *nptr, int64_t *value, int64_t limit_min, int64_t limit_max, char **err)
-{
- char *endptr = NULL;
- bool retval = false;
-
- if (value == NULL)
- return retval;
-
- errno = 0;
-
- *value = strtoll(nptr, &endptr, 0);
- if ((errno != 0) || OTELC_STR_IS_VALID(endptr))
- FLT_OTEL_ERR("'%s' : invalid value", nptr);
- else if (!OTELC_IN_RANGE(*value, limit_min, limit_max))
- FLT_OTEL_ERR("'%s' : value out of range [%" PRId64 ", %" PRId64 "]", nptr, limit_min, limit_max);
- else
- retval = true;
-
- return retval;
-}
-
-
-/***
- * NAME
- * flt_otel_sample_to_str - sample data to string conversion
- *
- * SYNOPSIS
- * int flt_otel_sample_to_str(const struct sample_data *data, char *value, size_t size, char **err)
- *
- * ARGUMENTS
- * data - sample data to convert
- * value - output buffer for the string representation
- * size - output buffer size
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Converts sample data to its string representation. Handles bool, sint,
- * IPv4, IPv6, str, and HTTP method types. Boolean values are written as
- * "0" or "1". Integer values use snprintf(). IP addresses are converted
- * via inet_ntop(). String values are copied directly. HTTP methods are
- * resolved to their standard string names; the HTTP_METH_OTHER type uses
- * the method's raw string data. Binary and unknown types produce an error.
- *
- * RETURN VALUE
- * Returns the number of characters written to <value>,
- * or FLT_OTEL_RET_ERROR on failure.
- */
-int flt_otel_sample_to_str(const struct sample_data *data, char *value, size_t size, char **err)
-{
- int retval = FLT_OTEL_RET_ERROR;
-
- OTELC_FUNC("%p, %p, %zu, %p:%p", data, value, size, OTELC_DPTR_ARGS(err));
-
- if ((data == NULL) || (value == NULL) || (size == 0))
- OTELC_RETURN_INT(retval);
-
- *value = '\0';
-
- /* Convert the sample value to a string based on its type. */
- if (data->type == SMP_T_ANY) {
- FLT_OTEL_ERR("invalid sample data type %d", data->type);
- }
- else if (data->type == SMP_T_BOOL) {
- value[0] = data->u.sint ? '1' : '0';
- value[1] = '\0';
-
- retval = 1;
- }
- else if (data->type == SMP_T_SINT) {
- retval = snprintf(value, size, "%lld", data->u.sint);
- }
- else if (data->type == SMP_T_ADDR) {
- /* This type is never used to qualify a sample. */
- }
- else if (data->type == SMP_T_IPV4) {
- if (INET_ADDRSTRLEN > size)
- FLT_OTEL_ERR("sample data size too large");
- else if (inet_ntop(AF_INET, &(data->u.ipv4), value, INET_ADDRSTRLEN) == NULL)
- FLT_OTEL_ERR("invalid IPv4 address");
- else
- retval = strlen(value);
- }
- else if (data->type == SMP_T_IPV6) {
- if (INET6_ADDRSTRLEN > size)
- FLT_OTEL_ERR("sample data size too large");
- else if (inet_ntop(AF_INET6, &(data->u.ipv6), value, INET6_ADDRSTRLEN) == NULL)
- FLT_OTEL_ERR("invalid IPv6 address");
- else
- retval = strlen(value);
- }
- else if (data->type == SMP_T_STR) {
- if (data->u.str.data >= size) {
- FLT_OTEL_ERR("sample data size too large");
- }
- else if (data->u.str.data > 0) {
- retval = data->u.str.data;
- (void)memcpy(value, data->u.str.area, retval);
- value[retval] = '\0';
- }
- else {
- /*
- * There is no content to add but we will still return
- * the correct status.
- */
- retval = 0;
- }
- }
- else if (data->type == SMP_T_BIN) {
- FLT_OTEL_ERR("invalid sample data type %d", data->type);
- }
- else if (data->type != SMP_T_METH) {
- FLT_OTEL_ERR("invalid sample data type %d", data->type);
- }
- else if (OTELC_IN_RANGE(data->u.meth.meth, HTTP_METH_OPTIONS, HTTP_METH_CONNECT)) {
-#define FLT_OTEL_HTTP_METH_DEF(a) { #a, FLT_OTEL_STR_SIZE(#a) },
- static const struct {
- const char *str;
- size_t len;
- } http_meth_str[] = { FLT_OTEL_HTTP_METH_DEFINES };
-#undef FLT_OTEL_HTTP_METH_DEF
-
- retval = http_meth_str[data->u.meth.meth].len;
- (void)memcpy(value, http_meth_str[data->u.meth.meth].str, retval + 1);
- }
- else if (data->u.meth.meth == HTTP_METH_OTHER) {
- if (data->u.meth.str.data >= size) {
- FLT_OTEL_ERR("sample data size too large");
- } else {
- retval = data->u.meth.str.data;
- (void)memcpy(value, data->u.meth.str.area, retval);
- value[retval] = '\0';
- }
- }
- else {
- FLT_OTEL_ERR("invalid HTTP method");
- }
-
- if (retval != FLT_OTEL_RET_ERROR)
- OTELC_DBG(DEBUG, "sample value (%d): '%.*s' %d", data->type, retval, value, retval);
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_sample_to_value - sample data to OTel value conversion
- *
- * SYNOPSIS
- * int flt_otel_sample_to_value(const char *key, const struct sample_data *data, struct otelc_value *value, char **err)
- *
- * ARGUMENTS
- * key - sample key name (for debug output)
- * data - sample data to convert
- * value - output OTel value structure
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Converts sample data to an otelc_value structure. Boolean samples are
- * stored as OTELC_VALUE_BOOL, integer samples as OTELC_VALUE_INT64. All
- * other types are converted to a string via flt_otel_sample_to_str() and
- * stored as OTELC_VALUE_DATA with heap-allocated storage.
- *
- * RETURN VALUE
- * Returns the size of the converted value, or FLT_OTEL_RET_ERROR on failure.
- */
-int flt_otel_sample_to_value(const char *key, const struct sample_data *data, struct otelc_value *value, char **err)
-{
- int retval = FLT_OTEL_RET_ERROR;
-
- OTELC_FUNC("\"%s\", %p, %p, %p:%p", OTELC_STR_ARG(key), data, value, OTELC_DPTR_ARGS(err));
-
- if ((data == NULL) || (value == NULL))
- OTELC_RETURN_INT(retval);
-
- /* Convert the sample value to an otelc_value based on its type. */
- if (data->type == SMP_T_BOOL) {
- value->u_type = OTELC_VALUE_BOOL;
- value->u.value_bool = data->u.sint ? 1 : 0;
-
- retval = sizeof(value->u.value_bool);
- }
- else if (data->type == SMP_T_SINT) {
- value->u_type = OTELC_VALUE_INT64;
- value->u.value_int64 = data->u.sint;
-
- retval = sizeof(value->u.value_int64);
- }
- else {
- value->u_type = OTELC_VALUE_DATA;
- value->u.value_data = OTELC_MALLOC(global.tune.bufsize);
-
- if (value->u.value_data == NULL)
- FLT_OTEL_ERR("out of memory");
- else
- retval = flt_otel_sample_to_str(data, value->u.value_data, global.tune.bufsize, err);
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_sample_add_event - span event attribute addition
- *
- * SYNOPSIS
- * static int flt_otel_sample_add_event(struct list *events, struct flt_otel_conf_sample *sample, const struct otelc_value *value)
- *
- * ARGUMENTS
- * events - list of span events (flt_otel_scope_data_event)
- * sample - configured sample with event name and key
- * value - OTel value to add as an attribute
- *
- * DESCRIPTION
- * Adds a sample value as a span event attribute. Searches the existing
- * events list for an event with a matching name; if not found, creates a new
- * event entry with an initial attribute array of FLT_OTEL_ATTR_INIT_SIZE
- * elements. If the attribute array is full, it is grown by
- * FLT_OTEL_ATTR_INC_SIZE elements. The key-value pair is appended to the
- * event's attribute array.
- *
- * RETURN VALUE
- * Returns the attribute count for the event, or FLT_OTEL_RET_ERROR on
- * failure.
- */
-static int flt_otel_sample_add_event(struct list *events, struct flt_otel_conf_sample *sample, const struct otelc_value *value)
-{
- struct flt_otel_scope_data_event *ptr, *event = NULL;
- struct otelc_kv *attr = NULL;
- bool flag_list_insert = 0;
-
- OTELC_FUNC("%p, %p, %p", events, sample, value);
-
- if ((events == NULL) || (sample == NULL) || (value == NULL))
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
-
- /*
- * First try to find an event with the same name in the list of events,
- * if it succeeds, new data is added to the event found.
- */
- if (!LIST_ISEMPTY(events))
- list_for_each_entry(ptr, events, list)
- if (strcmp(ptr->name, OTELC_VALUE_STR(&(sample->extra))) == 0) {
- event = ptr;
-
- break;
- }
-
- /*
- * If an event with the required name is not found, a new event is added
- * to the list. Initially, the number of attributes for the new event
- * is set to FLT_OTEL_ATTR_INIT_SIZE.
- */
- if (event == NULL) {
- event = OTELC_CALLOC(1, sizeof(*event));
- if (event == NULL)
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
-
- event->name = OTELC_STRDUP(OTELC_VALUE_STR(&(sample->extra)));
- event->attr = OTELC_CALLOC(FLT_OTEL_ATTR_INIT_SIZE, sizeof(*(event->attr)));
- event->cnt = 0;
- event->size = FLT_OTEL_ATTR_INIT_SIZE;
- if ((event->name == NULL) || (event->attr == NULL)) {
- OTELC_SFREE(event->name);
- OTELC_SFREE(event->attr);
- OTELC_SFREE(event);
-
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
- }
-
- flag_list_insert = 1;
-
- OTELC_DBG(DEBUG, "scope event data initialized");
- }
-
- /*
- * In case event attributes are added to an already existing event in
- * the list, it is checked whether the number of attributes should be
- * increased. If necessary, it will be increased by the amount
- * FLT_OTEL_ATTR_INC_SIZE.
- */
- if (event->cnt == event->size) {
- typeof(event->attr) ptr = OTELC_REALLOC(event->attr, sizeof(*ptr) * (event->size + FLT_OTEL_ATTR_INC_SIZE));
- if (ptr == NULL)
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
-
- event->attr = ptr;
- event->size += FLT_OTEL_ATTR_INC_SIZE;
-
- OTELC_DBG(DEBUG, "scope event data reallocated");
- }
-
- attr = event->attr + event->cnt++;
- attr->key = sample->key;
- attr->key_is_dynamic = false;
- (void)memcpy(&(attr->value), value, sizeof(attr->value));
-
- if (flag_list_insert) {
- if (LIST_ISEMPTY(events))
- LIST_INIT(events);
-
- LIST_INSERT(events, &(event->list));
- }
-
- OTELC_RETURN_INT(event->cnt);
-}
-
-
-/***
- * NAME
- * flt_otel_sample_set_status - span status setter
- *
- * SYNOPSIS
- * static int flt_otel_sample_set_status(struct flt_otel_scope_data_status *status, struct flt_otel_conf_sample *sample, const struct otelc_value *value, char **err)
- *
- * ARGUMENTS
- * status - span status structure to populate
- * sample - configured sample with status code in extra data
- * value - OTel value for the status description
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Sets the span status code and description from sample data. The status
- * code is taken from the sample's extra field (an int32 value) and the
- * description from <value>, which must be a string type. Multiple status
- * settings for the same span are rejected with an error.
- *
- * RETURN VALUE
- * Returns 1 on success, or FLT_OTEL_RET_ERROR on failure.
- */
-static int flt_otel_sample_set_status(struct flt_otel_scope_data_status *status, struct flt_otel_conf_sample *sample, const struct otelc_value *value, char **err)
-{
- OTELC_FUNC("%p, %p, %p, %p:%p", status, sample, value, OTELC_DPTR_ARGS(err));
-
- if ((status == NULL) || (sample == NULL) || (value == NULL))
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
-
- /*
- * This scenario should never occur, but the check is still enforced -
- * multiple status settings are not allowed within the filter
- * configuration for each span event.
- */
- if (status->description != NULL) {
- FLT_OTEL_ERR("'%s' : span status already set", sample->key);
-
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
- }
- else if ((value->u_type != OTELC_VALUE_STRING) && (value->u_type != OTELC_VALUE_DATA)) {
- FLT_OTEL_ERR("'%s' : status description must be a string value", sample->key);
-
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
- }
-
- status->code = sample->extra.u.value_int32;
- status->description = OTELC_STRDUP(OTELC_VALUE_STR(value));
- if (status->description == NULL) {
- FLT_OTEL_ERR("out of memory");
-
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
- }
-
- OTELC_RETURN_INT(1);
-}
-
-
-/***
- * NAME
- * flt_otel_sample_add_kv - key-value attribute addition
- *
- * SYNOPSIS
- * int flt_otel_sample_add_kv(struct flt_otel_scope_data_kv *kv, const char *key, const struct otelc_value *value)
- *
- * ARGUMENTS
- * kv - key-value storage (attributes or baggage)
- * key - attribute or baggage key name
- * value - OTel value to add
- *
- * DESCRIPTION
- * Adds a sample value as a key-value attribute or baggage entry. If the
- * key-value array is not yet allocated, it is created with
- * FLT_OTEL_ATTR_INIT_SIZE elements via otelc_kv_new(). When the array is
- * full, it is grown by FLT_OTEL_ATTR_INC_SIZE elements. The key-value pair
- * is appended to the array.
- *
- * RETURN VALUE
- * Returns the current element count, or FLT_OTEL_RET_ERROR on failure.
- */
-int flt_otel_sample_add_kv(struct flt_otel_scope_data_kv *kv, const char *key, const struct otelc_value *value)
-{
- struct otelc_kv *attr = NULL;
-
- OTELC_FUNC("%p, \"%s\", %p", kv, OTELC_STR_ARG(key), value);
-
- if ((kv == NULL) || (key == NULL) || (value == NULL))
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
-
- if (kv->attr == NULL) {
- kv->attr = otelc_kv_new(FLT_OTEL_ATTR_INIT_SIZE);
- if (kv->attr == NULL)
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
-
- kv->cnt = 0;
- kv->size = FLT_OTEL_ATTR_INIT_SIZE;
-
- OTELC_DBG(DEBUG, "scope kv data initialized");
- }
-
- if (kv->cnt == kv->size) {
- typeof(kv->attr) ptr = OTELC_REALLOC(kv->attr, sizeof(*ptr) * (kv->size + FLT_OTEL_ATTR_INC_SIZE));
- if (ptr == NULL)
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
-
- kv->attr = ptr;
- kv->size += FLT_OTEL_ATTR_INC_SIZE;
-
- OTELC_DBG(DEBUG, "scope kv data reallocated");
- }
-
- attr = kv->attr + kv->cnt++;
- attr->key = (typeof(attr->key))key;
- attr->key_is_dynamic = false;
- (void)memcpy(&(attr->value), value, sizeof(attr->value));
-
- OTELC_RETURN_INT(kv->cnt);
-}
-
-
-/***
- * NAME
- * flt_otel_sample_eval - sample expression evaluation
- *
- * SYNOPSIS
- * int flt_otel_sample_eval(struct stream *s, uint dir, struct flt_otel_conf_sample *sample, bool flag_native, struct otelc_value *value, char **err)
- *
- * ARGUMENTS
- * s - current stream
- * dir - the sample fetch direction (SMP_OPT_DIR_REQ/RES)
- * sample - configured sample definition
- * flag_native - preserve native types for single-expression samples
- * value - evaluated result (caller-owned)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Evaluates all sample expressions for a configured sample definition and
- * stores the result in <value>. Two evaluation paths are supported:
- * log-format expressions (sample->lf_used) are evaluated via build_logline();
- * bare sample expressions are evaluated via sample_process().
- *
- * When <flag_native> is true and the sample has exactly one expression, the
- * native HAProxy sample type is preserved via flt_otel_sample_to_value()
- * (e.g. bool, int64). Otherwise, all expression results are concatenated
- * into a string (OTELC_VALUE_DATA).
- *
- * On success, ownership of any dynamically allocated data within <value>
- * (value->u.value_data for OTELC_VALUE_DATA) is transferred to the caller.
- * On error, no cleanup is required.
- *
- * RETURN VALUE
- * Returns FLT_OTEL_RET_OK on success, FLT_OTEL_RET_ERROR on failure.
- */
-int flt_otel_sample_eval(struct stream *s, uint dir, struct flt_otel_conf_sample *sample, bool flag_native, struct otelc_value *value, char **err)
-{
- const struct flt_otel_conf_sample_expr *expr;
- struct sample smp;
- struct buffer buffer;
- int idx = 0, rc, retval = FLT_OTEL_RET_OK;
-
- OTELC_FUNC("%p, %u, %p, %hhu, %p, %p:%p", s, dir, sample, flag_native, value, OTELC_DPTR_ARGS(err));
-
- FLT_OTEL_DBG_CONF_SAMPLE("sample ", sample);
-
- (void)memset(value, 0, sizeof(*value));
- (void)memset(&buffer, 0, sizeof(buffer));
-
- /* Evaluate the sample: log-format path or expression list path. */
- if (sample->lf_used) {
- /*
- * Log-format path: evaluate the log-format expression into a
- * dynamically allocated buffer.
- */
- chunk_init(&buffer, OTELC_CALLOC(1, global.tune.bufsize), global.tune.bufsize);
- if (buffer.area == NULL) {
- FLT_OTEL_ERR("out of memory");
-
- retval = FLT_OTEL_RET_ERROR;
- } else {
- buffer.data = build_logline(s, buffer.area, buffer.size, &(sample->lf_expr));
-
- value->u_type = OTELC_VALUE_DATA;
- value->u.value_data = buffer.area;
- }
- } else {
- list_for_each_entry(expr, &(sample->exprs), list) {
- FLT_OTEL_DBG_CONF_SAMPLE_EXPR("sample expression ", expr);
-
- (void)memset(&smp, 0, sizeof(smp));
-
- if (sample_process(s->be, s->sess, s, dir | SMP_OPT_FINAL, expr->expr, &smp) != NULL) {
- OTELC_DBG(DEBUG, "data type %d: '%s'", smp.data.type, expr->fmt_expr);
- } else {
- OTELC_DBG(NOTICE, "WARNING: failed to fetch '%s' value", expr->fmt_expr);
-
- /*
- * In case the fetch failed, we will set the result
- * (sample) to an empty static string.
- */
- (void)memset(&(smp.data), 0, sizeof(smp.data));
- smp.data.type = SMP_T_STR;
- smp.data.u.str.area = "";
- }
-
- /*
- * If we have only one expression to process, then the data
- * type that is the result of the expression is converted to
- * an equivalent data type (if possible) that is written to
- * the tracer.
- *
- * If conversion is not possible, or if we have multiple
- * expressions to process, then the result is converted to
- * a string and as such sent to the tracer.
- */
- if ((sample->num_exprs == 1) && flag_native) {
- if (flt_otel_sample_to_value(sample->key, &(smp.data), value, err) == FLT_OTEL_RET_ERROR)
- retval = FLT_OTEL_RET_ERROR;
- } else {
- if (buffer.area == NULL) {
- chunk_init(&buffer, OTELC_CALLOC(1, global.tune.bufsize), global.tune.bufsize);
- if (buffer.area == NULL) {
- FLT_OTEL_ERR("out of memory");
-
- retval = FLT_OTEL_RET_ERROR;
-
- break;
- }
- }
-
- rc = flt_otel_sample_to_str(&(smp.data), buffer.area + buffer.data, buffer.size - buffer.data, err);
- if (rc == FLT_OTEL_RET_ERROR) {
- retval = FLT_OTEL_RET_ERROR;
- } else {
- buffer.data += rc;
-
- if (sample->num_exprs == ++idx) {
- value->u_type = OTELC_VALUE_DATA;
- value->u.value_data = buffer.area;
- }
- }
- }
- }
- }
-
- /* On error, free any dynamically allocated value data. */
- if (retval == FLT_OTEL_RET_ERROR) {
- if (buffer.area != NULL)
- OTELC_SFREE(buffer.area);
- else if (value->u_type == OTELC_VALUE_DATA)
- OTELC_SFREE(value->u.value_data);
-
- (void)memset(value, 0, sizeof(*value));
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_sample_add - top-level sample evaluator and dispatcher
- *
- * SYNOPSIS
- * int flt_otel_sample_add(struct stream *s, uint dir, struct flt_otel_conf_sample *sample, struct flt_otel_scope_data *data, int type, char **err)
- *
- * ARGUMENTS
- * s - current stream
- * dir - the sample fetch direction (SMP_OPT_DIR_REQ/RES)
- * sample - configured sample definition
- * data - scope data to populate
- * type - sample type (FLT_OTEL_EVENT_SAMPLE_*)
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Evaluates all sample expressions for a configured sample definition via
- * flt_otel_sample_eval(), then dispatches the result to the appropriate
- * handler: flt_otel_sample_add_kv() for attributes and baggage,
- * flt_otel_sample_add_event() for events, or flt_otel_sample_set_status()
- * for status.
- *
- * RETURN VALUE
- * Returns a negative value if an error occurs, 0 if it needs to wait,
- * any other value otherwise.
- */
-int flt_otel_sample_add(struct stream *s, uint dir, struct flt_otel_conf_sample *sample, struct flt_otel_scope_data *data, int type, char **err)
-{
- struct otelc_value value;
- bool flag_native;
- int retval;
-
- OTELC_FUNC("%p, %u, %p, %p, %d, %p:%p", s, dir, sample, data, type, OTELC_DPTR_ARGS(err));
-
- flag_native = ((type == FLT_OTEL_EVENT_SAMPLE_ATTRIBUTE) || (type == FLT_OTEL_EVENT_SAMPLE_EVENT));
- retval = flt_otel_sample_eval(s, dir, sample, flag_native, &value, err);
-
- /* Dispatch the evaluated value to the appropriate collection. */
- if (retval == FLT_OTEL_RET_ERROR) {
- /* Do nothing. */
- }
- else if (type == FLT_OTEL_EVENT_SAMPLE_ATTRIBUTE) {
- retval = flt_otel_sample_add_kv(&(data->attributes), sample->key, &value);
- if (retval == FLT_OTEL_RET_ERROR)
- FLT_OTEL_ERR("out of memory");
- }
- else if (type == FLT_OTEL_EVENT_SAMPLE_EVENT) {
- retval = flt_otel_sample_add_event(&(data->events), sample, &value);
- if (retval == FLT_OTEL_RET_ERROR)
- FLT_OTEL_ERR("out of memory");
- }
- else if (type == FLT_OTEL_EVENT_SAMPLE_BAGGAGE) {
- retval = flt_otel_sample_add_kv(&(data->baggage), sample->key, &value);
- if (retval == FLT_OTEL_RET_ERROR)
- FLT_OTEL_ERR("out of memory");
- }
- else if (type == FLT_OTEL_EVENT_SAMPLE_STATUS) {
- retval = flt_otel_sample_set_status(&(data->status), sample, &value, err);
- }
- else {
- FLT_OTEL_ERR("invalid event sample type: %d", type);
-
- retval = FLT_OTEL_RET_ERROR;
- }
-
- /*
- * Free dynamically allocated value data that was not transferred to
- * a key-value array. For ATTRIBUTE, EVENT, and BAGGAGE, the value
- * pointer is shallow-copied into the kv array on success and will be
- * freed by otelc_kv_destroy(). For STATUS, the handler creates its
- * own copy, so the original must be freed. On any error, no handler
- * consumed the value.
- */
- if ((retval != FLT_OTEL_RET_ERROR) && (type != FLT_OTEL_EVENT_SAMPLE_STATUS))
- /* Do nothing. */;
- else if (value.u_type == OTELC_VALUE_DATA)
- OTELC_SFREE(value.u.value_data);
-
- flt_otel_scope_data_dump(data);
-
- OTELC_RETURN_INT(retval);
-}
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "../include/include.h"
-
-
-#ifdef DEBUG_OTEL
-
-/***
- * NAME
- * flt_otel_vars_scope_dump - debug variable scope dump
- *
- * SYNOPSIS
- * static void flt_otel_vars_scope_dump(struct vars *vars, const char *scope)
- *
- * ARGUMENTS
- * vars - HAProxy variable store to dump
- * scope - scope label for log output
- *
- * DESCRIPTION
- * Dumps the contents of all variables defined for a particular <scope>.
- * Acquires a read lock on the variable store, iterates over all name root
- * trees, and logs each variable's name hash and string value.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-static void flt_otel_vars_scope_dump(struct vars *vars, const char *scope)
-{
- int i;
-
- if (vars == NULL)
- return;
-
- /* Lock the variable store for safe iteration. */
- vars_rdlock(vars);
- for (i = 0; i < VAR_NAME_ROOTS; i++) {
- struct ceb_node *node = cebu64_imm_first(&(vars->name_root[i]));
-
- for ( ; node != NULL; node = cebu64_imm_next(&(vars->name_root[i]), node)) {
- struct var *var = container_of(node, struct var, name_node);
-
- OTELC_DBG(NOTICE, "'%s.%016" PRIx64 "' -> '%.*s'", scope, var->name_hash, (int)b_data(&(var->data.u.str)), b_orig(&(var->data.u.str)));
- }
- }
- vars_rdunlock(vars);
-}
-
-
-/***
- * NAME
- * flt_otel_vars_dump - debug all variables dump
- *
- * SYNOPSIS
- * void flt_otel_vars_dump(struct stream *s)
- *
- * ARGUMENTS
- * s - stream whose variables to dump
- *
- * DESCRIPTION
- * Dumps all variables across all scopes (PROC, SESS, TXN, REQ/RES) by calling
- * flt_otel_vars_scope_dump() for each scope's variable store.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-void flt_otel_vars_dump(struct stream *s)
-{
- OTELC_FUNC("%p", s);
-
- /*
- * It would be nice if we could use the get_vars() function from HAProxy
- * source here to get the value of the 'vars' pointer, but it is defined
- * as 'static inline', so unfortunately none of this is possible.
- */
- flt_otel_vars_scope_dump(&(proc_vars), "PROC");
- flt_otel_vars_scope_dump(&(s->sess->vars), "SESS");
- flt_otel_vars_scope_dump(&(s->vars_txn), "TXN");
- flt_otel_vars_scope_dump(&(s->vars_reqres), "REQ/RES");
-
- OTELC_RETURN();
-}
-
-#endif /* DEBUG_OTEL */
-
-
-/***
- * NAME
- * flt_otel_normalize_name - variable name normalization
- *
- * SYNOPSIS
- * static int flt_otel_normalize_name(char *var_name, size_t size, int *len, const char *name, bool flag_cpy, char **err)
- *
- * ARGUMENTS
- * var_name - output buffer for the normalized name
- * size - output buffer size
- * len - pointer to the current position in the output buffer
- * name - source name to normalize
- * flag_cpy - whether to copy name without normalization
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Normalizes a variable name component into the output buffer. Adds a
- * dot separator between components when needed. When <flag_cpy> is set,
- * the name is copied verbatim; otherwise, dashes are replaced with
- * FLT_OTEL_VAR_CHAR_DASH, spaces with FLT_OTEL_VAR_CHAR_SPACE, and uppercase
- * letters are converted to lowercase.
- *
- * RETURN VALUE
- * Returns the number of characters written, or FLT_OTEL_RET_ERROR on failure.
- */
-static int flt_otel_normalize_name(char *var_name, size_t size, int *len, const char *name, bool flag_cpy, char **err)
-{
- int retval = 0;
-
- OTELC_FUNC("%p, %zu, %p, \"%s\", %hhu, %p:%p", var_name, size, len, OTELC_STR_ARG(name), flag_cpy, OTELC_DPTR_ARGS(err));
-
- if (!OTELC_STR_IS_VALID(name))
- OTELC_RETURN_INT(retval);
-
- /*
- * In case the name of the variable consists of several elements,
- * the character '.' is added between them.
- */
- if ((*len == 0) || (var_name[*len - 1] == '.'))
- /* Do nothing. */;
- else if (*len < (size - 1))
- var_name[(*len)++] = '.';
- else {
- FLT_OTEL_ERR("failed to normalize variable name, buffer too small");
-
- retval = FLT_OTEL_RET_ERROR;
- }
-
- if (retval == FLT_OTEL_RET_ERROR) {
- /* Do nothing. */
- }
- else if (flag_cpy) {
- /* Copy variable name without modification. */
- retval = strlen(name);
- if ((*len + retval + 1) > size) {
- FLT_OTEL_ERR("failed to normalize variable name, buffer too small");
-
- retval = FLT_OTEL_RET_ERROR;
- } else {
- (void)memcpy(var_name + *len, name, retval + 1);
-
- *len += retval;
- }
- } else {
- /*
- * HAProxy does not allow the use of variable names containing
- * '-' or ' '. This of course applies to HTTP header names as
- * well. Also, here the capital letters are converted to
- * lowercase.
- */
- while (retval != FLT_OTEL_RET_ERROR)
- if (*len >= (size - 1)) {
- FLT_OTEL_ERR("failed to normalize variable name, buffer too small");
-
- retval = FLT_OTEL_RET_ERROR;
- } else {
- uint8_t ch = name[retval];
-
- if (ch == '\0')
- break;
- else if (ch == '-')
- ch = FLT_OTEL_VAR_CHAR_DASH;
- else if (ch == ' ')
- ch = FLT_OTEL_VAR_CHAR_SPACE;
- else if (isupper(ch))
- ch = ist_lc[ch];
-
- var_name[(*len)++] = ch;
- retval++;
- }
-
- var_name[*len] = '\0';
- }
-
- OTELC_DBG(DEBUG, "var_name: \"%s\" %d/%d", OTELC_STR_ARG(var_name), retval, *len);
-
- if (retval == FLT_OTEL_RET_ERROR)
- *len = retval;
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_denormalize_name - reverse variable name normalization
- *
- * SYNOPSIS
- * static int flt_otel_denormalize_name(const char *var_name, char *name, size_t size, char **err)
- *
- * ARGUMENTS
- * var_name - normalized variable name
- * name - output buffer for the denormalized name
- * size - output buffer size
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Reverses the normalization applied by flt_otel_normalize_name(). Restores
- * dashes from FLT_OTEL_VAR_CHAR_DASH and spaces from FLT_OTEL_VAR_CHAR_SPACE.
- *
- * RETURN VALUE
- * Returns the length of the denormalized name, or FLT_OTEL_RET_ERROR if the
- * output buffer is too small.
- */
-static int flt_otel_denormalize_name(const char *var_name, char *name, size_t size, char **err)
-{
- int len;
-
- /* Reverse character substitutions applied during normalization. */
- for (len = 0; var_name[len] != '\0'; len++) {
- if (len >= (size - 1)) {
- FLT_OTEL_ERR("failed to reverse variable name, buffer too small");
-
- return FLT_OTEL_RET_ERROR;
- }
-
- if (var_name[len] == FLT_OTEL_VAR_CHAR_DASH)
- name[len] = '-';
- else if (var_name[len] == FLT_OTEL_VAR_CHAR_SPACE)
- name[len] = ' ';
- else
- name[len] = var_name[len];
- }
- name[len] = '\0';
-
- return len;
-}
-
-
-/***
- * NAME
- * flt_otel_var_name - full variable name construction
- *
- * SYNOPSIS
- * static int flt_otel_var_name(const char *scope, const char *prefix, const char *name, bool flag_cpy, char *var_name, size_t size, char **err)
- *
- * ARGUMENTS
- * scope - variable scope component
- * prefix - variable prefix component
- * name - variable name component
- * flag_cpy - whether to copy name without normalization
- * var_name - output buffer for the constructed name
- * size - output buffer size
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Constructs a full variable name from <scope>, <prefix>, and <name>
- * components, separated by dots. Each component is normalized via
- * flt_otel_normalize_name(). NULL components are skipped.
- *
- * RETURN VALUE
- * Returns the total name length, or FLT_OTEL_RET_ERROR on failure.
- */
-static int flt_otel_var_name(const char *scope, const char *prefix, const char *name, bool flag_cpy, char *var_name, size_t size, char **err)
-{
- int retval = 0;
-
- OTELC_FUNC("\"%s\", \"%s\", \"%s\", %hhu, %p, %zu, %p:%p", OTELC_STR_ARG(scope), OTELC_STR_ARG(prefix), OTELC_STR_ARG(name), flag_cpy, var_name, size, OTELC_DPTR_ARGS(err));
-
- if (flt_otel_normalize_name(var_name, size, &retval, scope, 0, err) >= 0)
- if (flt_otel_normalize_name(var_name, size, &retval, prefix, 0, err) >= 0)
- (void)flt_otel_normalize_name(var_name, size, &retval, name, flag_cpy, err);
-
- if (retval == FLT_OTEL_RET_ERROR)
- FLT_OTEL_ERR("failed to construct variable name '%s.%s.%s'", scope, prefix, name);
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_smp_init - sample structure initialization
- *
- * SYNOPSIS
- * static inline void flt_otel_smp_init(struct stream *s, struct sample *smp, uint opt, int type, const char *data)
- *
- * ARGUMENTS
- * s - current stream
- * smp - sample structure to initialize
- * opt - sample option flags
- * type - sample data type
- * data - string data to store (or NULL)
- *
- * DESCRIPTION
- * Initializes the <smp> structure and sets stream ownership via
- * smp_set_owner(). If the <data> argument is non-NULL, the sample_data
- * member is also initialized with the given <type> and string content.
- *
- * RETURN VALUE
- * This function does not return a value.
- */
-static inline void flt_otel_smp_init(struct stream *s, struct sample *smp, uint opt, int type, const char *data)
-{
- (void)memset(smp, 0, sizeof(*smp));
- (void)smp_set_owner(smp, s->be, s->sess, s, opt | SMP_OPT_FINAL);
-
- if (data != NULL) {
- smp->data.type = type;
-
- chunk_initstr(&(smp->data.u.str), data);
- }
-}
-
-
-#ifndef USE_OTEL_VARS_NAME
-
-/***
- * NAME
- * flt_otel_smp_add - context variable name registration
- *
- * SYNOPSIS
- * static int flt_otel_smp_add(struct sample_data *data, const char *name, size_t len, char **err)
- *
- * ARGUMENTS
- * data - binary sample data buffer
- * name - context variable name to append
- * len - length of the variable name
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Appends a context variable name to the binary sample data buffer used for
- * tracking registered context variables. If the buffer is not yet allocated,
- * it is initialized with global.tune.bufsize bytes. The name is stored as a
- * length-prefixed entry (FLT_OTEL_VAR_CTX_SIZE byte followed by the name
- * data). Validates that the name length fits in the size field and that the
- * buffer has sufficient room.
- *
- * RETURN VALUE
- * Returns the buffer offset before appending, or FLT_OTEL_RET_ERROR on
- * failure.
- */
-static int flt_otel_smp_add(struct sample_data *data, const char *name, size_t len, char **err)
-{
- bool flag_alloc = 0;
- int retval = FLT_OTEL_RET_ERROR;
-
- OTELC_FUNC("%p, \"%.*s\", %zu, %p:%p", data, (int)len, name, len, OTELC_DPTR_ARGS(err));
-
- FLT_OTEL_DBG_BUF(INFO, &(data->u.str));
-
- /* Lazily allocate the sample buffer on first use. */
- if (b_orig(&(data->u.str)) == NULL) {
- data->type = SMP_T_BIN;
- chunk_init(&(data->u.str), OTELC_MALLOC(global.tune.bufsize), global.tune.bufsize);
-
- flag_alloc = (b_orig(&(data->u.str)) != NULL);
- }
-
- /* Verify the buffer allocation succeeded. */
- if (b_orig(&(data->u.str)) == NULL) {
- FLT_OTEL_ERR("failed to add ctx '%.*s', not enough memory", (int)len, name);
- }
- else if (len > ((UINT64_C(1) << ((sizeof(FLT_OTEL_VAR_CTX_SIZE) << 3) - 1)) - 1)) {
- FLT_OTEL_ERR("failed to add ctx '%.*s', name too long", (int)len, name);
- }
- else if ((len + sizeof(FLT_OTEL_VAR_CTX_SIZE)) > b_room(&(data->u.str))) {
- FLT_OTEL_ERR("failed to add ctx '%.*s', too many names", (int)len, name);
- }
- else {
- retval = b_data(&(data->u.str));
-
- b_putchr(&(data->u.str), len);
- (void)__b_putblk(&(data->u.str), name, len);
-
- FLT_OTEL_DBG_BUF(INFO, &(data->u.str));
- }
-
- if ((retval == FLT_OTEL_RET_ERROR) && flag_alloc)
- OTELC_SFREE(b_orig(&(data->u.str)));
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_ctx_loop - context variable name iterator
- *
- * SYNOPSIS
- * static int flt_otel_ctx_loop(struct sample *smp, const char *scope, const char *prefix, char **err, flt_otel_ctx_loop_cb func, void *ptr)
- *
- * ARGUMENTS
- * smp - sample used to retrieve the context tracking variable
- * scope - variable scope
- * prefix - variable prefix
- * err - indirect pointer to error message string
- * func - callback function invoked for each context variable
- * ptr - opaque data passed to the callback
- *
- * DESCRIPTION
- * Iterates over all context variable names stored in the binary tracking
- * buffer. Retrieves the tracking variable by constructing its name from
- * <scope> and <prefix>. Each stored entry (length-prefixed name) is
- * extracted and passed to the <func> callback. Iteration stops if the
- * callback returns a positive value (match found) or FLT_OTEL_RET_ERROR.
- *
- * RETURN VALUE
- * Returns the match position (positive), 0 if no match,
- * or FLT_OTEL_RET_ERROR on failure.
- */
-static int flt_otel_ctx_loop(struct sample *smp, const char *scope, const char *prefix, char **err, flt_otel_ctx_loop_cb func, void *ptr)
-{
- FLT_OTEL_VAR_CTX_SIZE var_ctx_size;
- char var_name[BUFSIZ], var_ctx[BUFSIZ];
- int i, var_name_len, var_ctx_len, rc, n = 1, retval = 0;
-
- OTELC_FUNC("%p, \"%s\", \"%s\", %p:%p, %p, %p", smp, OTELC_STR_ARG(scope), OTELC_STR_ARG(prefix), OTELC_DPTR_ARGS(err), func, ptr);
-
- /*
- * The variable in which we will save the name of the OpenTelemetry
- * context variable.
- */
- var_name_len = flt_otel_var_name(scope, prefix, NULL, 0, var_name, sizeof(var_name), err);
- if (var_name_len == FLT_OTEL_RET_ERROR)
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
-
- /*
- * Here we will try to find all the previously recorded variables from
- * the currently set OpenTelemetry context. If we find the required
- * variable and it is marked as deleted, we will mark it as active.
- * If we do not find it, then it is added to the end of the previously
- * saved names.
- */
- if (vars_get_by_name(var_name, var_name_len, smp, NULL) == 0) {
- OTELC_DBG(NOTICE, "ctx '%s' no variable found", var_name);
- }
- else if (smp->data.type != SMP_T_BIN) {
- FLT_OTEL_ERR("ctx '%s' invalid data type %d", var_name, smp->data.type);
-
- retval = FLT_OTEL_RET_ERROR;
- }
- else {
- FLT_OTEL_DBG_BUF(INFO, &(smp->data.u.str));
-
- for (i = 0; i < b_data(&(smp->data.u.str)); i += sizeof(var_ctx_size) + var_ctx_len, n++) {
- var_ctx_size = *((typeof(var_ctx_size) *)(b_orig(&(smp->data.u.str)) + i));
- var_ctx_len = abs(var_ctx_size);
-
- if ((i + sizeof(var_ctx_size) + var_ctx_len) > b_data(&(smp->data.u.str))) {
- FLT_OTEL_ERR("ctx '%s' invalid data size", var_name);
-
- retval = FLT_OTEL_RET_ERROR;
-
- break;
- }
-
- (void)memcpy(var_ctx, b_orig(&(smp->data.u.str)) + i + sizeof(var_ctx_size), var_ctx_len);
- var_ctx[var_ctx_len] = '\0';
-
- rc = func(smp, i, scope, prefix, var_ctx, var_ctx_size, err, ptr);
- if (rc == FLT_OTEL_RET_ERROR) {
- retval = FLT_OTEL_RET_ERROR;
-
- break;
- }
- else if (rc > 0) {
- retval = n;
-
- break;
- }
- }
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_ctx_set_cb - context variable existence check callback
- *
- * SYNOPSIS
- * static int flt_otel_ctx_set_cb(struct sample *smp, size_t idx, const char *scope, const char *prefix, const char *name, FLT_OTEL_VAR_CTX_SIZE name_len, char **err, void *ptr)
- *
- * ARGUMENTS
- * smp - current sample (unused)
- * idx - buffer offset (unused)
- * scope - variable scope (unused)
- * prefix - variable prefix (unused)
- * name - context variable name to check
- * name_len - length of the name
- * err - unused
- * ptr - pointer to flt_otel_ctx structure with the search target
- *
- * DESCRIPTION
- * Callback for flt_otel_ctx_loop() that checks whether a context variable
- * <name> matches the search target stored in the flt_otel_ctx structure.
- *
- * RETURN VALUE
- * Returns 1 if the <name> matches, or 0 otherwise.
- */
-static int flt_otel_ctx_set_cb(struct sample *smp, size_t idx, const char *scope, const char *prefix, const char *name, FLT_OTEL_VAR_CTX_SIZE name_len, char **err, void *ptr)
-{
- struct flt_otel_ctx *ctx = ptr;
- int retval = 0;
-
- OTELC_FUNC("%p, %zu, \"%s\", \"%s\", \"%s\", %hhd, %p:%p, %p", smp, idx, OTELC_STR_ARG(scope), OTELC_STR_ARG(prefix), OTELC_STR_ARG(name), name_len, OTELC_DPTR_ARGS(err), ptr);
-
- if ((name_len == ctx->value_len) && (strncmp(name, ctx->value, name_len) == 0)) {
- OTELC_DBG(NOTICE, "ctx '%s' found", name);
-
- retval = 1;
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_ctx_set - context variable tracking registration
- *
- * SYNOPSIS
- * static int flt_otel_ctx_set(struct stream *s, const char *scope, const char *prefix, const char *name, uint opt, char **err)
- *
- * ARGUMENTS
- * s - current stream
- * scope - variable scope
- * prefix - variable prefix
- * name - context variable name to register
- * opt - sample option flags
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Registers a context variable name in the binary tracking buffer if it is
- * not already present. Constructs the tracking variable name from <scope>
- * and <prefix>, then uses flt_otel_ctx_loop() with flt_otel_ctx_set_cb() to
- * check for duplicates. If not found, the normalized name is appended to the
- * tracking buffer via flt_otel_smp_add() and the updated buffer is stored
- * back into the HAProxy variable.
- *
- * RETURN VALUE
- * Returns the buffer data size on success, a positive value if already
- * registered, or FLT_OTEL_RET_ERROR on failure.
- */
-static int flt_otel_ctx_set(struct stream *s, const char *scope, const char *prefix, const char *name, uint opt, char **err)
-{
- struct flt_otel_ctx ctx;
- struct sample smp_ctx;
- char var_name[BUFSIZ];
- bool flag_alloc = 0;
- int rc, var_name_len, retval = FLT_OTEL_RET_ERROR;
-
- OTELC_FUNC("%p, \"%s\", \"%s\", \"%s\", %u, %p:%p", s, OTELC_STR_ARG(scope), OTELC_STR_ARG(prefix), OTELC_STR_ARG(name), opt, OTELC_DPTR_ARGS(err));
-
- /*
- * The variable in which we will save the name of the OpenTelemetry
- * context variable.
- */
- var_name_len = flt_otel_var_name(scope, prefix, NULL, 0, var_name, sizeof(var_name), err);
- if (var_name_len == FLT_OTEL_RET_ERROR)
- OTELC_RETURN_INT(retval);
-
- /* Normalized name of the OpenTelemetry context variable. */
- ctx.value_len = flt_otel_var_name(name, NULL, NULL, 0, ctx.value, sizeof(ctx.value), err);
- if (ctx.value_len == FLT_OTEL_RET_ERROR)
- OTELC_RETURN_INT(retval);
-
- flt_otel_smp_init(s, &smp_ctx, opt, 0, NULL);
-
- /* Loop through existing context variables and apply set operations. */
- retval = flt_otel_ctx_loop(&smp_ctx, scope, prefix, err, flt_otel_ctx_set_cb, &ctx);
- if (retval == 0) {
- rc = flt_otel_smp_add(&(smp_ctx.data), ctx.value, ctx.value_len, err);
- if (rc == FLT_OTEL_RET_ERROR)
- retval = FLT_OTEL_RET_ERROR;
-
- flag_alloc = (rc == 0);
- }
-
- /* Persist the context data as a HAProxy variable. */
- if (retval == FLT_OTEL_RET_ERROR) {
- /* Do nothing. */
- }
- else if (retval > 0) {
- OTELC_DBG(NOTICE, "ctx '%s' data found", ctx.value);
- }
- else if (vars_set_by_name_ifexist(var_name, var_name_len, &smp_ctx) == 0) {
- FLT_OTEL_ERR("failed to set ctx '%s'", var_name);
-
- retval = FLT_OTEL_RET_ERROR;
- }
- else {
- OTELC_DBG(NOTICE, "ctx '%s' -> '%.*s' set", var_name, (int)b_data(&(smp_ctx.data.u.str)), b_orig(&(smp_ctx.data.u.str)));
-
- retval = b_data(&(smp_ctx.data.u.str));
- }
-
- if (flag_alloc)
- OTELC_SFREE(b_orig(&(smp_ctx.data.u.str)));
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_vars_unset_cb - context variable unset callback
- *
- * SYNOPSIS
- * static int flt_otel_vars_unset_cb(struct sample *smp, size_t idx, const char *scope, const char *prefix, const char *name, FLT_OTEL_VAR_CTX_SIZE name_len, char **err, void *ptr)
- *
- * ARGUMENTS
- * smp - current sample with stream context
- * idx - buffer offset (unused)
- * scope - variable scope
- * prefix - variable prefix
- * name - context variable name to unset
- * name_len - length of the name (unused)
- * err - indirect pointer to error message string
- * ptr - unused
- *
- * DESCRIPTION
- * Callback for flt_otel_ctx_loop() that unsets a single context variable.
- * Constructs the full variable name from <scope>, <prefix>, and <name>, then
- * calls vars_unset_by_name_ifexist() to remove it.
- *
- * RETURN VALUE
- * Returns 0 on success, or FLT_OTEL_RET_ERROR on failure.
- */
-static int flt_otel_vars_unset_cb(struct sample *smp, size_t idx, const char *scope, const char *prefix, const char *name, FLT_OTEL_VAR_CTX_SIZE name_len, char **err, void *ptr)
-{
- struct sample smp_ctx;
- char var_ctx[BUFSIZ];
- int var_ctx_len, retval = FLT_OTEL_RET_ERROR;
-
- OTELC_FUNC("%p, %zu, \"%s\", \"%s\", \"%s\", %hhd, %p:%p, %p", smp, idx, OTELC_STR_ARG(scope), OTELC_STR_ARG(prefix), OTELC_STR_ARG(name), name_len, OTELC_DPTR_ARGS(err), ptr);
-
- var_ctx_len = flt_otel_var_name(scope, prefix, name, 1, var_ctx, sizeof(var_ctx), err);
- if (var_ctx_len == FLT_OTEL_RET_ERROR) {
- FLT_OTEL_ERR("ctx '%s' invalid", name);
-
- OTELC_RETURN_INT(retval);
- }
-
- flt_otel_smp_init(smp->strm, &smp_ctx, smp->opt, 0, NULL);
-
- if (vars_unset_by_name_ifexist(var_ctx, var_ctx_len, &smp_ctx) == 0) {
- FLT_OTEL_ERR("ctx '%s' no variable found", var_ctx);
- } else {
- OTELC_DBG(NOTICE, "ctx '%s' unset", var_ctx);
-
- retval = 0;
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_vars_unset - context variables bulk unset
- *
- * SYNOPSIS
- * int flt_otel_vars_unset(struct stream *s, const char *scope, const char *prefix, uint opt, char **err)
- *
- * ARGUMENTS
- * s - current stream
- * scope - variable scope
- * prefix - variable prefix
- * opt - sample option flags
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Unsets all context variables for a given <prefix> by iterating the tracking
- * buffer via flt_otel_ctx_loop() with flt_otel_vars_unset_cb(). After all
- * individual context variables are removed, the tracking variable itself
- * (which stores the list of names) is also unset.
- *
- * RETURN VALUE
- * Returns 1 on success, 0 if no tracking variable exists,
- * or FLT_OTEL_RET_ERROR on failure.
- */
-int flt_otel_vars_unset(struct stream *s, const char *scope, const char *prefix, uint opt, char **err)
-{
- struct sample smp_ctx;
- char var_name[BUFSIZ];
- int var_name_len, retval;
-
- OTELC_FUNC("%p, \"%s\", \"%s\", %u, %p:%p", s, OTELC_STR_ARG(scope), OTELC_STR_ARG(prefix), opt, OTELC_DPTR_ARGS(err));
-
- flt_otel_smp_init(s, &smp_ctx, opt, 0, NULL);
-
- retval = flt_otel_ctx_loop(&smp_ctx, scope, prefix, err, flt_otel_vars_unset_cb, NULL);
- if (retval != FLT_OTEL_RET_ERROR) {
- /*
- * After all ctx variables have been unset, the variable used
- * to store their names should also be unset.
- */
- var_name_len = flt_otel_var_name(scope, prefix, NULL, 0, var_name, sizeof(var_name), err);
- if (var_name_len == FLT_OTEL_RET_ERROR)
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
-
- flt_otel_smp_init(s, &smp_ctx, opt, 0, NULL);
-
- if (vars_unset_by_name_ifexist(var_name, var_name_len, &smp_ctx) == 0) {
- OTELC_DBG(NOTICE, "variable '%s' not found", var_name);
- } else {
- OTELC_DBG(NOTICE, "variable '%s' unset", var_name);
-
- retval = 1;
- }
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_vars_get_cb - context variable value reader callback
- *
- * SYNOPSIS
- * static int flt_otel_vars_get_cb(struct sample *smp, size_t idx, const char *scope, const char *prefix, const char *name, FLT_OTEL_VAR_CTX_SIZE name_len, char **err, void *ptr)
- *
- * ARGUMENTS
- * smp - current sample with stream context
- * idx - buffer offset (unused)
- * scope - variable scope
- * prefix - variable prefix
- * name - normalized context variable name
- * name_len - length of the name (unused)
- * err - indirect pointer to error message string
- * ptr - pointer to the output text map pointer
- *
- * DESCRIPTION
- * Callback for flt_otel_ctx_loop() that reads a single context variable value
- * and adds it to a text map. Constructs the full variable name, reads its
- * value via vars_get_by_name(), reverses the <name> normalization (restoring
- * dashes and spaces), and stores the key-value pair in the text map. The
- * text map is lazily allocated on first use.
- *
- * RETURN VALUE
- * Returns 0 on success, or FLT_OTEL_RET_ERROR on failure.
- */
-static int flt_otel_vars_get_cb(struct sample *smp, size_t idx, const char *scope, const char *prefix, const char *name, FLT_OTEL_VAR_CTX_SIZE name_len, char **err, void *ptr)
-{
- struct otelc_text_map **map = ptr;
- struct sample smp_ctx;
- char var_ctx[BUFSIZ], otel_var_name[BUFSIZ];
- int var_ctx_len, retval = FLT_OTEL_RET_ERROR;
-
- OTELC_FUNC("%p, %zu, \"%s\", \"%s\", \"%s\", %hhd, %p:%p, %p", smp, idx, OTELC_STR_ARG(scope), OTELC_STR_ARG(prefix), OTELC_STR_ARG(name), name_len, OTELC_DPTR_ARGS(err), ptr);
-
- /* Build the HAProxy variable name for this context key. */
- var_ctx_len = flt_otel_var_name(scope, prefix, name, 1, var_ctx, sizeof(var_ctx), err);
- if (var_ctx_len == FLT_OTEL_RET_ERROR) {
- FLT_OTEL_ERR("ctx '%s' invalid", name);
-
- OTELC_RETURN_INT(retval);
- }
-
- flt_otel_smp_init(smp->strm, &smp_ctx, smp->opt, 0, NULL);
-
- /* Retrieve the context variable and build a text map entry. */
- if (vars_get_by_name(var_ctx, var_ctx_len, &smp_ctx, NULL) != 0) {
- OTELC_DBG(NOTICE, "'%s' -> '%.*s'", var_ctx, (int)b_data(&(smp_ctx.data.u.str)), b_orig(&(smp_ctx.data.u.str)));
-
- if (*map == NULL) {
- *map = OTELC_TEXT_MAP_NEW(NULL, 8);
- if (*map == NULL) {
- FLT_OTEL_ERR("failed to create map data");
-
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
- }
- }
-
- /*
- * Eh, because the use of some characters is not allowed in the
- * variable name, the conversion of the replaced characters to
- * the original is performed here.
- */
- retval = flt_otel_denormalize_name(name, otel_var_name, OTELC_TABLESIZE_1(otel_var_name), err);
- if (retval >= 0)
- retval = OTELC_TEXT_MAP_ADD(*map, otel_var_name, retval, b_orig(&(smp_ctx.data.u.str)), b_data(&(smp_ctx.data.u.str)), OTELC_TEXT_MAP_AUTO);
- if (retval == FLT_OTEL_RET_ERROR) {
- FLT_OTEL_ERR("failed to add map data");
-
- otelc_text_map_destroy(map);
- } else {
- retval = 0;
- }
- } else {
- OTELC_DBG(NOTICE, "ctx '%s' no variable found", var_ctx);
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_vars_get - context variables to text map extraction
- *
- * SYNOPSIS
- * struct otelc_text_map *flt_otel_vars_get(struct stream *s, const char *scope, const char *prefix, uint opt, char **err)
- *
- * ARGUMENTS
- * s - current stream
- * scope - variable scope
- * prefix - variable prefix
- * opt - sample option flags
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Reads all context variables for a given <prefix> into a text map. Iterates
- * the tracking buffer via flt_otel_ctx_loop() with flt_otel_vars_get_cb().
- * If the resulting text map is empty, it is destroyed and NULL is returned.
- * This function is used by the "extract" keyword with variable storage.
- *
- * RETURN VALUE
- * Returns a pointer to the populated text map, or NULL if no variables are
- * found.
- */
-struct otelc_text_map *flt_otel_vars_get(struct stream *s, const char *scope, const char *prefix, uint opt, char **err)
-{
- struct sample smp_ctx;
- struct otelc_text_map *retptr = NULL;
-
- OTELC_FUNC("%p, \"%s\", \"%s\", %u, %p:%p", s, OTELC_STR_ARG(scope), OTELC_STR_ARG(prefix), opt, OTELC_DPTR_ARGS(err));
-
- flt_otel_smp_init(s, &smp_ctx, opt, 0, NULL);
-
- (void)flt_otel_ctx_loop(&smp_ctx, scope, prefix, err, flt_otel_vars_get_cb, &retptr);
-
- OTELC_TEXT_MAP_DUMP(retptr, "extracted variables");
-
- if ((retptr != NULL) && (retptr->count == 0)) {
- OTELC_DBG(NOTICE, "WARNING: no variables found");
-
- otelc_text_map_destroy(&retptr);
- }
-
- OTELC_RETURN_PTR(retptr);
-}
-
-#else
-
-/***
- * NAME
- * flt_otel_vars_get_scope - resolve scope string to variable store
- *
- * SYNOPSIS
- * static struct vars *flt_otel_vars_get_scope(struct stream *s, const char *scope)
- *
- * ARGUMENTS
- * s - current stream
- * scope - variable scope string ("proc", "sess", "txn", "req", "res")
- *
- * DESCRIPTION
- * Resolves a scope name string to the corresponding HAProxy variable
- * store for the given <stream>.
- *
- * RETURN VALUE
- * Returns a pointer to the variable store, or NULL if the <scope>
- * is unknown.
- */
-static struct vars *flt_otel_vars_get_scope(struct stream *s, const char *scope)
-{
- if (strcmp(scope, "txn") == 0)
- return &(s->vars_txn);
- else if (strcmp(scope, "req") == 0)
- return &(s->vars_reqres);
- else if (strcmp(scope, "res") == 0)
- return &(s->vars_reqres);
- else if (strcmp(scope, "sess") == 0)
- return &(s->sess->vars);
- else if (strcmp(scope, "proc") == 0)
- return &proc_vars;
-
- return NULL;
-}
-
-
-/***
- * NAME
- * flt_otel_vars_unset - context variables bulk unset via prefix scan
- *
- * SYNOPSIS
- * int flt_otel_vars_unset(struct stream *s, const char *scope, const char *prefix, uint opt, char **err)
- *
- * ARGUMENTS
- * s - current stream
- * scope - variable scope
- * prefix - variable prefix
- * opt - sample option flags
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Unsets all context variables whose name starts with the normalized
- * <prefix> followed by a dot. Walks the CEB tree of the variable
- * store for the given <scope> and removes each matching variable.
- *
- * RETURN VALUE
- * Returns the number of variables removed, 0 if none found,
- * or FLT_OTEL_RET_ERROR on failure.
- */
-int flt_otel_vars_unset(struct stream *s, const char *scope, const char *prefix, uint opt, char **err)
-{
- struct vars *vars;
- char norm_prefix[BUFSIZ];
- unsigned int size = 0;
- int prefix_len, retval = 0, i;
-
- OTELC_FUNC("%p, \"%s\", \"%s\", %u, %p:%p", s, OTELC_STR_ARG(scope), OTELC_STR_ARG(prefix), opt, OTELC_DPTR_ARGS(err));
-
- prefix_len = flt_otel_var_name(prefix, NULL, NULL, 0, norm_prefix, sizeof(norm_prefix), err);
- if (prefix_len == FLT_OTEL_RET_ERROR)
- OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
-
- vars = flt_otel_vars_get_scope(s, scope);
- if (vars == NULL)
- OTELC_RETURN_INT(0);
-
- /* Lock and iterate all variables, clearing those matching the prefix. */
- vars_wrlock(vars);
- for (i = 0; i < VAR_NAME_ROOTS; i++) {
- struct ceb_node *node = cebu64_imm_first(&(vars->name_root[i]));
-
- while (node != NULL) {
- struct var *var = container_of(node, struct var, name_node);
- struct ceb_node *next = cebu64_imm_next(&(vars->name_root[i]), node);
-
- if ((var->name != NULL) &&
- (strncmp(var->name, norm_prefix, prefix_len) == 0) &&
- (var->name[prefix_len] == '.')) {
- OTELC_DBG(NOTICE, "prefix unset '%s'", var->name);
-
- size += var_clear(vars, var, 1);
- retval++;
- }
-
- node = next;
- }
- }
- vars_wrunlock(vars);
-
- if (size > 0)
- var_accounting_diff(vars, s->sess, s, -(int)size);
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_vars_get - context variables to text map via prefix scan
- *
- * SYNOPSIS
- * struct otelc_text_map *flt_otel_vars_get(struct stream *s, const char *scope, const char *prefix, uint opt, char **err)
- *
- * ARGUMENTS
- * s - current stream
- * scope - variable scope
- * prefix - variable prefix
- * opt - sample option flags
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Reads all context variables whose name starts with the normalized
- * <prefix> followed by a dot. Walks the CEB tree of the variable
- * store for the given <scope>, denormalizes each matching variable
- * name, and adds the key-value pair to the returned text map.
- *
- * RETURN VALUE
- * Returns a pointer to the populated text map, or NULL if no
- * variables are found.
- */
-struct otelc_text_map *flt_otel_vars_get(struct stream *s, const char *scope, const char *prefix, uint opt, char **err)
-{
- struct vars *vars;
- struct otelc_text_map *retptr = NULL;
- char norm_prefix[BUFSIZ], otel_name[BUFSIZ];
- int prefix_len, i;
-
- OTELC_FUNC("%p, \"%s\", \"%s\", %u, %p:%p", s, OTELC_STR_ARG(scope), OTELC_STR_ARG(prefix), opt, OTELC_DPTR_ARGS(err));
-
- prefix_len = flt_otel_var_name(prefix, NULL, NULL, 0, norm_prefix, sizeof(norm_prefix), err);
- if (prefix_len == FLT_OTEL_RET_ERROR)
- OTELC_RETURN_PTR(NULL);
-
- vars = flt_otel_vars_get_scope(s, scope);
- if (vars == NULL)
- OTELC_RETURN_PTR(NULL);
-
- /* Read-lock and collect all variables matching the prefix into a text map. */
- vars_rdlock(vars);
- for (i = 0; i < VAR_NAME_ROOTS; i++) {
- struct ceb_node *node = cebu64_imm_first(&(vars->name_root[i]));
-
- for ( ; node != NULL; node = cebu64_imm_next(&(vars->name_root[i]), node)) {
- struct var *var = container_of(node, struct var, name_node);
- const char *key;
- int otel_name_len;
-
- if ((var->name == NULL) ||
- (strncmp(var->name, norm_prefix, prefix_len) != 0) ||
- (var->name[prefix_len] != '.'))
- continue;
-
- /* Skip the "prefix." part to get the key name. */
- key = var->name + prefix_len + 1;
-
- otel_name_len = flt_otel_denormalize_name(key, otel_name, sizeof(otel_name), err);
- if (otel_name_len == FLT_OTEL_RET_ERROR) {
- FLT_OTEL_ERR("failed to reverse variable name, buffer too small");
-
- break;
- }
-
- if ((var->data.type != SMP_T_STR) && (var->data.type != SMP_T_BIN)) {
- OTELC_DBG(NOTICE, "skipping '%s', unsupported type %d", var->name, var->data.type);
-
- continue;
- }
-
- OTELC_DBG(NOTICE, "'%s' -> '%.*s'", var->name, (int)b_data(&(var->data.u.str)), b_orig(&(var->data.u.str)));
-
- if (retptr == NULL) {
- retptr = OTELC_TEXT_MAP_NEW(NULL, 8);
- if (retptr == NULL) {
- FLT_OTEL_ERR("failed to create map data");
-
- break;
- }
- }
-
- if (OTELC_TEXT_MAP_ADD(retptr, otel_name, otel_name_len, b_orig(&(var->data.u.str)), b_data(&(var->data.u.str)), OTELC_TEXT_MAP_AUTO) == -1) {
- FLT_OTEL_ERR("failed to add map data");
-
- otelc_text_map_destroy(&retptr);
-
- break;
- }
- }
- }
- vars_rdunlock(vars);
-
- OTELC_TEXT_MAP_DUMP(retptr, "extracted variables");
-
- if ((retptr != NULL) && (retptr->count == 0)) {
- OTELC_DBG(NOTICE, "WARNING: no variables found");
-
- otelc_text_map_destroy(&retptr);
- }
-
- OTELC_RETURN_PTR(retptr);
-}
-
-#endif /* USE_OTEL_VARS_NAME */
-
-
-/***
- * NAME
- * flt_otel_var_register - HAProxy variable registration
- *
- * SYNOPSIS
- * int flt_otel_var_register(const char *scope, const char *prefix, const char *name, char **err)
- *
- * ARGUMENTS
- * scope - variable scope
- * prefix - variable prefix
- * name - variable name
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Registers a HAProxy variable by constructing its full name from <scope>,
- * <prefix>, and <name>, then calling vars_check_arg() to make it available
- * at runtime.
- *
- * RETURN VALUE
- * Returns the variable name length on success, or FLT_OTEL_RET_ERROR on
- * failure.
- */
-int flt_otel_var_register(const char *scope, const char *prefix, const char *name, char **err)
-{
- struct arg arg;
- char var_name[BUFSIZ];
- int retval = FLT_OTEL_RET_ERROR, var_name_len;
-
- OTELC_FUNC("\"%s\", \"%s\", \"%s\", %p:%p", OTELC_STR_ARG(scope), OTELC_STR_ARG(prefix), OTELC_STR_ARG(name), OTELC_DPTR_ARGS(err));
-
- var_name_len = flt_otel_var_name(scope, prefix, name, 0, var_name, sizeof(var_name), err);
- if (var_name_len == FLT_OTEL_RET_ERROR)
- OTELC_RETURN_INT(retval);
-
- /* Set <size> to 0 to not release var_name memory in vars_check_arg(). */
- (void)memset(&arg, 0, sizeof(arg));
- arg.type = ARGT_STR;
- arg.data.str.area = var_name;
- arg.data.str.data = var_name_len;
-
- if (vars_check_arg(&arg, err) == 0) {
- FLT_OTEL_ERR_APPEND("failed to register variable '%s': %s", var_name, *err);
- } else {
- OTELC_DBG(NOTICE, "variable '%s' registered", var_name);
-
- retval = var_name_len;
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-
-/***
- * NAME
- * flt_otel_var_set - HAProxy variable value setter
- *
- * SYNOPSIS
- * int flt_otel_var_set(struct stream *s, const char *scope, const char *prefix, const char *name, const char *value, uint opt, char **err)
- *
- * ARGUMENTS
- * s - current stream
- * scope - variable scope
- * prefix - variable prefix
- * name - variable name
- * value - string value to set
- * opt - sample option flags
- * err - indirect pointer to error message string
- *
- * DESCRIPTION
- * Sets a HAProxy variable to the given string <value>. The full variable
- * name is constructed from <scope>, <prefix>, and <name>. If the variable's
- * scope matches FLT_OTEL_VARS_SCOPE, the name is also registered in the
- * context tracking buffer via flt_otel_ctx_set().
- *
- * RETURN VALUE
- * Returns the variable name length on success, the context tracking result
- * for context-scope variables, or FLT_OTEL_RET_ERROR on failure.
- */
-int flt_otel_var_set(struct stream *s, const char *scope, const char *prefix, const char *name, const char *value, uint opt, char **err)
-{
- struct sample smp;
- char var_name[BUFSIZ];
- int retval = FLT_OTEL_RET_ERROR, var_name_len;
-
- OTELC_FUNC("%p, \"%s\", \"%s\", \"%s\", \"%s\", %u, %p:%p", s, OTELC_STR_ARG(scope), OTELC_STR_ARG(prefix), OTELC_STR_ARG(name), OTELC_STR_ARG(value), opt, OTELC_DPTR_ARGS(err));
-
- var_name_len = flt_otel_var_name(scope, prefix, name, 0, var_name, sizeof(var_name), err);
- if (var_name_len == FLT_OTEL_RET_ERROR)
- OTELC_RETURN_INT(retval);
-
- flt_otel_smp_init(s, &smp, opt, SMP_T_STR, value);
-
- /* Set the variable if it already exists. */
- if (vars_set_by_name_ifexist(var_name, var_name_len, &smp) == 0) {
- FLT_OTEL_ERR("failed to set variable '%s'", var_name);
- } else {
- OTELC_DBG(NOTICE, "variable '%s' set", var_name);
-
- retval = var_name_len;
-
-#ifndef USE_OTEL_VARS_NAME
- if (strcmp(scope, FLT_OTEL_VARS_SCOPE) == 0)
- retval = flt_otel_ctx_set(s, scope, prefix, name, opt, err);
-#endif
- }
-
- OTELC_RETURN_INT(retval);
-}
-
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * End:
- *
- * vi: noexpandtab shiftwidth=8 tabstop=8
- */
+++ /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.
+++ /dev/null
---- rate-limit 100.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 182.58us 129.11us 16.19ms 98.11%
- Req/Sec 5.63k 240.83 6.29k 69.74%
- Latency Distribution
- 50% 169.00us
- 75% 183.00us
- 90% 209.00us
- 99% 367.00us
- 13438310 requests in 5.00m, 3.24GB read
-Requests/sec: 44779.51
-Transfer/sec: 11.06MB
-----------------------------------------------------------------------
-
---- rate-limit 75.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 180.10us 122.51us 14.57ms 98.00%
- Req/Sec 5.70k 253.08 6.28k 70.41%
- Latency Distribution
- 50% 169.00us
- 75% 184.00us
- 90% 206.00us
- 99% 362.00us
- 13613023 requests in 5.00m, 3.28GB read
-Requests/sec: 45361.63
-Transfer/sec: 11.20MB
-----------------------------------------------------------------------
-
---- rate-limit 50.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 176.59us 125.34us 15.58ms 98.02%
- Req/Sec 5.81k 230.84 6.42k 72.14%
- Latency Distribution
- 50% 166.00us
- 75% 182.00us
- 90% 202.00us
- 99% 361.00us
- 13888448 requests in 5.00m, 3.35GB read
-Requests/sec: 46279.45
-Transfer/sec: 11.43MB
-----------------------------------------------------------------------
-
---- rate-limit 25.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 173.57us 118.35us 13.12ms 97.91%
- Req/Sec 5.91k 257.69 6.46k 66.83%
- Latency Distribution
- 50% 162.00us
- 75% 178.00us
- 90% 199.00us
- 99% 362.00us
- 14122906 requests in 5.00m, 3.41GB read
-Requests/sec: 47060.71
-Transfer/sec: 11.62MB
-----------------------------------------------------------------------
-
---- rate-limit 10.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 170.85us 112.24us 10.72ms 97.84%
- Req/Sec 6.00k 269.81 8.10k 69.46%
- Latency Distribution
- 50% 159.00us
- 75% 172.00us
- 90% 194.00us
- 99% 361.00us
- 14342642 requests in 5.00m, 3.46GB read
-Requests/sec: 47792.96
-Transfer/sec: 11.80MB
-----------------------------------------------------------------------
-
---- rate-limit 2.5 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 169.11us 127.52us 16.90ms 98.06%
- Req/Sec 6.08k 261.57 6.55k 67.30%
- Latency Distribution
- 50% 158.00us
- 75% 168.00us
- 90% 186.00us
- 99% 367.00us
- 14527714 requests in 5.00m, 3.50GB read
-Requests/sec: 48409.62
-Transfer/sec: 11.96MB
-----------------------------------------------------------------------
-
---- rate-limit 0.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 168.09us 108.96us 9.07ms 97.81%
- Req/Sec 6.10k 284.22 6.55k 70.65%
- Latency Distribution
- 50% 157.00us
- 75% 167.00us
- 90% 184.00us
- 99% 362.00us
- 14580762 requests in 5.00m, 3.52GB read
-Requests/sec: 48586.42
-Transfer/sec: 12.00MB
-----------------------------------------------------------------------
-
---- rate-limit disabled --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 168.57us 118.05us 13.51ms 97.94%
- Req/Sec 6.09k 251.47 6.99k 67.33%
- Latency Distribution
- 50% 158.00us
- 75% 167.00us
- 90% 184.00us
- 99% 363.00us
- 14557824 requests in 5.00m, 3.51GB read
-Requests/sec: 48509.96
-Transfer/sec: 11.98MB
-----------------------------------------------------------------------
-
---- rate-limit off --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 168.64us 120.15us 14.49ms 98.01%
- Req/Sec 6.09k 267.94 6.57k 66.19%
- Latency Distribution
- 50% 158.00us
- 75% 167.00us
- 90% 184.00us
- 99% 361.00us
- 14551312 requests in 5.00m, 3.51GB read
-Requests/sec: 48488.23
-Transfer/sec: 11.98MB
-----------------------------------------------------------------------
-
+++ /dev/null
---- rate-limit 100.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 270.35us 136.92us 16.02ms 97.98%
- Req/Sec 3.77k 217.57 5.33k 67.74%
- Latency Distribution
- 50% 264.00us
- 75% 287.00us
- 90% 309.00us
- 99% 494.00us
- 9012538 requests in 5.00m, 2.17GB read
-Requests/sec: 30031.85
-Transfer/sec: 7.42MB
-----------------------------------------------------------------------
-
---- rate-limit 75.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 242.56us 131.47us 9.82ms 94.77%
- Req/Sec 4.21k 218.42 5.61k 68.12%
- Latency Distribution
- 50% 246.00us
- 75% 279.00us
- 90% 308.00us
- 99% 464.00us
- 10050409 requests in 5.00m, 2.42GB read
-Requests/sec: 33490.26
-Transfer/sec: 8.27MB
-----------------------------------------------------------------------
-
---- rate-limit 50.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 215.92us 130.82us 9.93ms 96.81%
- Req/Sec 4.73k 243.84 7.13k 67.13%
- Latency Distribution
- 50% 208.00us
- 75% 264.00us
- 90% 300.00us
- 99% 439.00us
- 11307386 requests in 5.00m, 2.73GB read
-Requests/sec: 37678.82
-Transfer/sec: 9.31MB
-----------------------------------------------------------------------
-
---- rate-limit 25.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 192.36us 132.47us 13.75ms 96.79%
- Req/Sec 5.33k 260.46 6.17k 66.30%
- Latency Distribution
- 50% 166.00us
- 75% 227.00us
- 90% 280.00us
- 99% 407.00us
- 12734770 requests in 5.00m, 3.07GB read
-Requests/sec: 42448.91
-Transfer/sec: 10.48MB
-----------------------------------------------------------------------
-
---- rate-limit 10.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 180.08us 127.35us 13.34ms 97.06%
- Req/Sec 5.71k 272.98 6.40k 67.94%
- Latency Distribution
- 50% 161.00us
- 75% 183.00us
- 90% 250.00us
- 99% 386.00us
- 13641901 requests in 5.00m, 3.29GB read
-Requests/sec: 45457.92
-Transfer/sec: 11.23MB
-----------------------------------------------------------------------
-
---- rate-limit 2.5 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 171.64us 107.08us 5.69ms 96.57%
- Req/Sec 5.97k 289.99 6.55k 68.53%
- Latency Distribution
- 50% 159.00us
- 75% 171.00us
- 90% 195.00us
- 99% 372.00us
- 14268464 requests in 5.00m, 3.44GB read
-Requests/sec: 47545.77
-Transfer/sec: 11.74MB
-----------------------------------------------------------------------
-
---- rate-limit 0.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 168.86us 104.53us 5.75ms 97.73%
- Req/Sec 6.07k 282.19 6.59k 67.47%
- Latency Distribution
- 50% 158.00us
- 75% 168.00us
- 90% 186.00us
- 99% 361.00us
- 14498699 requests in 5.00m, 3.50GB read
-Requests/sec: 48312.96
-Transfer/sec: 11.93MB
-----------------------------------------------------------------------
-
---- rate-limit disabled --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 168.36us 129.52us 17.68ms 98.13%
- Req/Sec 6.11k 263.70 6.83k 70.42%
- Latency Distribution
- 50% 157.00us
- 75% 167.00us
- 90% 183.00us
- 99% 363.00us
- 14590953 requests in 5.00m, 3.52GB read
-Requests/sec: 48620.36
-Transfer/sec: 12.01MB
-----------------------------------------------------------------------
-
---- rate-limit off --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 168.73us 120.51us 15.03ms 98.02%
- Req/Sec 6.09k 270.88 6.55k 68.99%
- Latency Distribution
- 50% 158.00us
- 75% 167.00us
- 90% 185.00us
- 99% 360.00us
- 14538507 requests in 5.00m, 3.51GB read
-Requests/sec: 48445.53
-Transfer/sec: 11.97MB
-----------------------------------------------------------------------
-
+++ /dev/null
---- rate-limit 100.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 238.75us 129.45us 15.59ms 98.45%
- Req/Sec 4.27k 75.22 5.28k 77.45%
- Latency Distribution
- 50% 228.00us
- 75% 243.00us
- 90% 262.00us
- 99% 410.00us
- 10206938 requests in 5.00m, 2.46GB read
-Requests/sec: 34011.80
-Transfer/sec: 8.40MB
-----------------------------------------------------------------------
-
---- rate-limit 75.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 230.08us 201.50us 32.12ms 99.25%
- Req/Sec 4.46k 83.43 5.36k 75.61%
- Latency Distribution
- 50% 222.00us
- 75% 241.00us
- 90% 261.00us
- 99% 401.00us
- 10641998 requests in 5.00m, 2.57GB read
-Requests/sec: 35461.59
-Transfer/sec: 8.76MB
-----------------------------------------------------------------------
-
---- rate-limit 50.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 222.33us 242.52us 35.13ms 99.43%
- Req/Sec 4.62k 99.86 5.78k 79.00%
- Latency Distribution
- 50% 211.00us
- 75% 237.00us
- 90% 259.00us
- 99% 400.00us
- 11046951 requests in 5.00m, 2.66GB read
-Requests/sec: 36810.91
-Transfer/sec: 9.09MB
-----------------------------------------------------------------------
-
---- rate-limit 25.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 210.95us 117.20us 10.57ms 97.85%
- Req/Sec 4.84k 101.85 6.04k 68.10%
- Latency Distribution
- 50% 198.00us
- 75% 222.00us
- 90% 252.00us
- 99% 394.00us
- 11551741 requests in 5.00m, 2.79GB read
-Requests/sec: 38493.03
-Transfer/sec: 9.51MB
-----------------------------------------------------------------------
-
---- rate-limit 10.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 206.15us 209.34us 31.73ms 99.27%
- Req/Sec 4.99k 112.62 5.36k 71.21%
- Latency Distribution
- 50% 193.00us
- 75% 210.00us
- 90% 237.00us
- 99% 387.00us
- 11924489 requests in 5.00m, 2.88GB read
-Requests/sec: 39735.09
-Transfer/sec: 9.81MB
-----------------------------------------------------------------------
-
---- rate-limit 2.5 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 202.35us 180.56us 27.28ms 99.10%
- Req/Sec 5.08k 145.06 8.27k 71.24%
- Latency Distribution
- 50% 191.00us
- 75% 205.00us
- 90% 223.00us
- 99% 374.00us
- 12131047 requests in 5.00m, 2.93GB read
-Requests/sec: 40423.43
-Transfer/sec: 9.98MB
-----------------------------------------------------------------------
-
---- rate-limit 0.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 200.88us 223.19us 32.70ms 99.44%
- Req/Sec 5.13k 151.03 6.55k 69.46%
- Latency Distribution
- 50% 190.00us
- 75% 203.00us
- 90% 218.00us
- 99% 367.00us
- 12256706 requests in 5.00m, 2.96GB read
-Requests/sec: 40842.16
-Transfer/sec: 10.09MB
-----------------------------------------------------------------------
-
---- rate-limit disabled --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 194.91us 222.55us 33.80ms 99.47%
- Req/Sec 5.29k 167.52 5.95k 68.32%
- Latency Distribution
- 50% 184.00us
- 75% 197.00us
- 90% 214.00us
- 99% 353.00us
- 12633928 requests in 5.00m, 3.05GB read
-Requests/sec: 42112.54
-Transfer/sec: 10.40MB
-----------------------------------------------------------------------
-
---- rate-limit off --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 194.37us 166.76us 28.75ms 99.09%
- Req/Sec 5.28k 160.02 5.86k 68.02%
- Latency Distribution
- 50% 184.00us
- 75% 197.00us
- 90% 214.00us
- 99% 355.00us
- 12622896 requests in 5.00m, 3.04GB read
-Requests/sec: 42062.31
-Transfer/sec: 10.39MB
-----------------------------------------------------------------------
-
+++ /dev/null
---- rate-limit 100.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 213.08us 136.99us 17.58ms 98.10%
- Req/Sec 4.80k 251.04 5.97k 68.01%
- Latency Distribution
- 50% 203.00us
- 75% 223.00us
- 90% 245.00us
- 99% 405.00us
- 11464278 requests in 5.00m, 2.77GB read
-Requests/sec: 38201.61
-Transfer/sec: 9.44MB
-----------------------------------------------------------------------
-
---- rate-limit 75.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 202.18us 121.42us 12.04ms 97.72%
- Req/Sec 5.05k 248.48 5.85k 65.69%
- Latency Distribution
- 50% 194.00us
- 75% 219.00us
- 90% 245.00us
- 99% 393.00us
- 12071015 requests in 5.00m, 2.91GB read
-Requests/sec: 40223.31
-Transfer/sec: 9.94MB
-----------------------------------------------------------------------
-
---- rate-limit 50.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 190.49us 117.32us 7.33ms 97.62%
- Req/Sec 5.37k 265.60 6.98k 65.98%
- Latency Distribution
- 50% 181.00us
- 75% 208.00us
- 90% 237.00us
- 99% 383.00us
- 12837427 requests in 5.00m, 3.10GB read
-Requests/sec: 42777.17
-Transfer/sec: 10.57MB
-----------------------------------------------------------------------
-
---- rate-limit 25.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 180.46us 123.40us 13.20ms 97.80%
- Req/Sec 5.69k 242.93 7.75k 68.66%
- Latency Distribution
- 50% 165.00us
- 75% 194.00us
- 90% 223.00us
- 99% 375.00us
- 13595213 requests in 5.00m, 3.28GB read
-Requests/sec: 45302.34
-Transfer/sec: 11.19MB
-----------------------------------------------------------------------
-
---- rate-limit 10.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 174.69us 129.17us 16.50ms 97.93%
- Req/Sec 5.89k 260.40 7.03k 69.57%
- Latency Distribution
- 50% 160.00us
- 75% 178.00us
- 90% 210.00us
- 99% 374.00us
- 14068388 requests in 5.00m, 3.39GB read
-Requests/sec: 46879.07
-Transfer/sec: 11.58MB
-----------------------------------------------------------------------
-
---- rate-limit 2.5 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 170.58us 116.89us 11.49ms 97.74%
- Req/Sec 6.03k 249.35 6.54k 67.44%
- Latency Distribution
- 50% 158.00us
- 75% 170.00us
- 90% 192.00us
- 99% 375.00us
- 14402604 requests in 5.00m, 3.47GB read
-Requests/sec: 47992.71
-Transfer/sec: 11.85MB
-----------------------------------------------------------------------
-
---- rate-limit 0.0 --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 167.96us 114.36us 10.99ms 97.81%
- Req/Sec 6.12k 266.16 6.57k 70.70%
- Latency Distribution
- 50% 157.00us
- 75% 166.00us
- 90% 183.00us
- 99% 370.00us
- 14622790 requests in 5.00m, 3.53GB read
-Requests/sec: 48726.40
-Transfer/sec: 12.04MB
-----------------------------------------------------------------------
-
---- rate-limit disabled --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 167.74us 114.11us 11.10ms 97.82%
- Req/Sec 6.13k 251.71 6.57k 69.59%
- Latency Distribution
- 50% 157.00us
- 75% 166.00us
- 90% 182.00us
- 99% 368.00us
- 14641307 requests in 5.00m, 3.53GB read
-Requests/sec: 48788.18
-Transfer/sec: 12.05MB
-----------------------------------------------------------------------
-
---- rate-limit off --------------------------------------------------
-Running 5m test @ http://localhost:10080/index.html
- 8 threads and 8 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 168.00us 112.83us 11.07ms 97.79%
- Req/Sec 6.12k 264.12 7.23k 68.95%
- Latency Distribution
- 50% 157.00us
- 75% 166.00us
- 90% 183.00us
- 99% 369.00us
- 14613970 requests in 5.00m, 3.53GB read
-Requests/sec: 48697.01
-Transfer/sec: 12.03MB
-----------------------------------------------------------------------
-
+++ /dev/null
- -----------------------------------------
- HAProxy OTEL filter speed test guide
- Version 1.0
- ( Last update: 2026-04-09 )
- -----------------------------------------
- Author : Miroslav Zagorac
- Contact : mzagorac at haproxy dot com
-
-
-SUMMARY
---------
-
- 1. Overview
- 2. Prerequisites
- 3. Running the test
- 4. Test parameters
- 5. Rate-limit levels
- 6. Test configurations
- 7. Results
- 7.1. Standalone (sa)
- 7.2. Comparison (cmp)
- 7.3. Context propagation (ctx)
- 7.4. Frontend / backend (fe-be)
- 8. Summary
-
-
-1. Overview
-------------
-
-The test-speed.sh script measures the performance impact of the OTEL filter on
-HAProxy at various rate-limit settings. For each test configuration, the script
-iterates through a series of rate-limit values -- from full tracing (100%) down
-to the filter being completely removed -- measuring throughput and latency at
-each level.
-
-The script uses template files (haproxy.cfg.in and otel.cfg.in) from each test
-directory to generate the actual configuration files. A sed substitution
-adjusts the rate-limit value (or disables/removes the filter) before each run.
-
-
-2. Prerequisites
------------------
-
-The following tools must be installed and available in PATH:
-
- - thttpd : a lightweight HTTP server used as the backend origin server.
- It serves a small static HTML file (index.html) on port 8000.
-
- - wrk : an HTTP benchmarking tool that generates the test load.
- See https://github.com/wg/wrk
-
-
-3. Running the test
---------------------
-
-The test is executed from the test directory. It can be run for all
-configurations at once or for a single configuration.
-
-To run all configurations:
-
- % ./test-speed.sh all
-
-This produces result files in the _logs directory:
-
- _logs/README-speed-fe-be
- _logs/README-speed-sa
- _logs/README-speed-cmp
- _logs/README-speed-ctx
-
-To run a single configuration:
-
- % ./test-speed.sh <cfg> [<dir>]
-
-Where <cfg> corresponds to a run-<cfg>.sh script and <dir> is the configuration
-directory (defaults to <cfg>). For example:
-
- % ./test-speed.sh sa
- % ./test-speed.sh fe-be fe
- % ./test-speed.sh cmp
- % ./test-speed.sh ctx
-
-
-4. Test parameters
--------------------
-
-The wrk benchmarking tool is invoked with the following parameters:
-
- -t8 8 threads
- -c8 8 concurrent connections
- -d300 5-minute test duration (300 seconds)
- --latency latency distribution reporting
-
-Each rate-limit level is tested sequentially. Between runs, HAProxy is stopped
-via SIGUSR1 and restarted with the next rate-limit configuration. A 10-second
-pause separates consecutive runs.
-
-The backend origin server (thttpd) serves a small static HTML page (index.html,
-approximately 50 bytes) on port 8000. HAProxy listens on port 10080 and proxies
-requests to the origin.
-
-
-5. Rate-limit levels
----------------------
-
-The script tests nine rate-limit levels in the following order:
-
- 100.0 - the filter processes every stream (worst case)
- 75.0 - the filter processes 75% of streams
- 50.0 - the filter processes 50% of streams
- 25.0 - the filter processes 25% of streams
- 10.0 - the filter processes 10% of streams
- 2.5 - the filter processes 2.5% of streams
- 0.0 - the filter is loaded and attached to every stream but never
- processes any telemetry (the rate-limit check always fails);
- this measures the per-stream attach and detach overhead
-
- disabled - the filter is loaded but disabled via 'option disabled'; it is not
- attached to streams at all; this measures the cost of loading and
- initializing the filter library without any per-stream work
-
- off - the 'filter opentelemetry' and 'otel-group' directives are
- commented out of haproxy.cfg; the filter is not loaded and has zero
- presence in the processing path; this is the absolute baseline
-
-In the result tables, the 'overhead' column is the throughput loss relative to
-the 'off' baseline, expressed as a percentage:
-
- overhead = (req/s_off - req/s_test) / req/s_off * 100
-
-
-6. Test configurations
------------------------
-
-Four OTEL filter configurations are tested. They differ in complexity and in
-the features they exercise:
-
- sa - Standalone. Uses all possible HAProxy filter events with spans,
- attributes, events, links, baggage, status, metrics and groups.
- This is the most comprehensive single-instance configuration and
- represents the worst-case scenario.
-
- cmp - Comparison. A simplified configuration made for comparison with
- other tracing implementations. It uses a reduced span hierarchy
- without context propagation, groups or metrics. This is closer to
- a typical production deployment.
-
- ctx - Context propagation. Similar to 'sa' in scope coverage, but spans
- are opened using extracted span contexts (inject/extract via 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.
-
- fe-be - Frontend / backend. Two cascaded HAProxy instances: the frontend
- (fe) creates the root trace and injects span context into HTTP
- headers; the backend (be) extracts the context and continues the
- trace. This configuration measures the combined overhead of two
- OTEL filter instances plus the inter-process context propagation
- cost.
-
- Note: the rate-limit is varied only on the frontend. The backend
- always runs with its default configuration (filter enabled,
- hard-errors on). The backend configuration is only modified for
- the 'disabled' and 'off' levels.
-
-
-7. Results
------------
-
-The tables below summarize the benchmarking results. The 'req/s' column shows
-the sustained request rate reported by wrk, 'avg latency' is the average
-response time, and 'overhead' is the throughput loss relative to the 'off'
-baseline.
-
-
-7.1. Standalone (sa)
----------------------
-
- ---------------------------------------------------------------
- rate-limit req/s avg latency overhead
- ---------------------------------------------------------------
- 100.0% 38,202 213.08 us 21.6%
- 75.0% 40,223 202.18 us 17.4%
- 50.0% 42,777 190.49 us 12.2%
- 25.0% 45,302 180.46 us 7.0%
- 10.0% 46,879 174.69 us 3.7%
- 2.5% 47,993 170.58 us 1.4%
- 0.0% 48,726 167.96 us ~0
- disabled 48,788 167.74 us ~0
- off 48,697 168.00 us baseline
- ---------------------------------------------------------------
-
-With all possible events active, the sa configuration at 100% rate-limit shows
-a 22% throughput reduction. At 10% rate-limit the overhead drops to 3.7%, and
-at 2.5% it is barely measurable at 1.4%. The 'disabled' and '0.0' levels show
-no measurable overhead above the baseline.
-
-
-7.2. Comparison (cmp)
-----------------------
-
- ---------------------------------------------------------------
- rate-limit req/s avg latency overhead
- ---------------------------------------------------------------
- 100.0% 44,780 182.58 us 7.6%
- 75.0% 45,362 180.10 us 6.4%
- 50.0% 46,279 176.59 us 4.6%
- 25.0% 47,061 173.57 us 2.9%
- 10.0% 47,793 170.85 us 1.4%
- 2.5% 48,410 169.11 us 0.2%
- 0.0% 48,586 168.09 us ~0
- disabled 48,510 168.57 us ~0
- off 48,488 168.64 us baseline
- ---------------------------------------------------------------
-
-The simplified cmp configuration shows significantly lower overhead than sa at
-every rate-limit level. At 100% rate-limit the overhead is 7.6%, at 10% it is
-1.4%, and below 2.5% it becomes indistinguishable from the baseline.
-
-
-7.3. Context propagation (ctx)
--------------------------------
-
- ---------------------------------------------------------------
- rate-limit req/s avg latency overhead
- ---------------------------------------------------------------
- 100.0% 30,032 270.35 us 38.0%
- 75.0% 33,490 242.56 us 30.9%
- 50.0% 37,679 215.92 us 22.2%
- 25.0% 42,449 192.36 us 12.4%
- 10.0% 45,458 180.08 us 6.2%
- 2.5% 47,546 171.64 us 1.9%
- 0.0% 48,313 168.86 us 0.3%
- disabled 48,620 168.36 us ~0
- off 48,446 168.73 us baseline
- ---------------------------------------------------------------
-
-The ctx configuration is the most expensive due to the inject/extract cycle on
-every scope execution. At 100% rate-limit the overhead reaches 38%. However,
-the cost scales linearly with the rate-limit: at 10% the overhead is 6.2%, and
-at 2.5% it drops to 1.9%. The filter attachment overhead (0.0% vs off) is
-negligible at 0.3%.
-
-
-7.4. Frontend / backend (fe-be)
---------------------------------
-
- ---------------------------------------------------------------
- rate-limit req/s avg latency overhead
- ---------------------------------------------------------------
- 100.0% 34,012 238.75 us 19.1%
- 75.0% 35,462 230.08 us 15.7%
- 50.0% 36,811 222.33 us 12.5%
- 25.0% 38,493 210.95 us 8.5%
- 10.0% 39,735 206.15 us 5.5%
- 2.5% 40,423 202.35 us 3.9%
- 0.0% 40,842 200.88 us 2.9%
- disabled 42,113 194.91 us ~0
- off 42,062 194.37 us baseline
- ---------------------------------------------------------------
-
-The fe-be configuration involves two HAProxy instances in series, so the
-absolute baseline (off) is already lower at 42,062 req/s due to the extra
-network hop. The rate-limit is varied only on the frontend; the backend
-always has the filter loaded with hard-errors enabled.
-
-This explains the 2.9% overhead at rate-limit 0.0: even though the frontend
-never traces, the backend filter still attaches to every stream, attempts to
-extract context from the HTTP headers, fails (because the frontend did not
-inject any context), and the hard-errors option stops further processing.
-This per-stream attach/extract/error cycle accounts for the residual cost.
-
-When both instances have the filter disabled (disabled level), the overhead
-is within measurement noise, consistent with the single-instance
-configurations.
-
-
-8. Summary
------------
-
-The table below shows the overhead for each configuration at selected rate-limit
-levels:
-
- ---------------------------------------------------
- rate-limit sa cmp ctx fe-be
- ---------------------------------------------------
- 100.0% 21.6% 7.6% 38.0% 19.1%
- 25.0% 7.0% 2.9% 12.4% 8.5%
- 10.0% 3.7% 1.4% 6.2% 5.5%
- 2.5% 1.4% 0.2% 1.9% 3.9%
- ---------------------------------------------------
-
-Key observations:
-
- - The overhead scales approximately linearly with the rate-limit value.
- Reducing the rate-limit from 100% to 10% eliminates the vast majority
- of the cost in all configurations.
-
- - The cmp configuration, which uses a reduced span hierarchy typical of
- production deployments, adds only 1.4% overhead at a 10% rate-limit.
-
- - The sa configuration, which exercises all possible events, stays at about
- 7% overhead at a 25% rate-limit and below 4% at 10%.
-
- - The ctx configuration is the most expensive due to the inject/extract
- context propagation on every scope. It is designed as a stress test for
- the propagation mechanism rather than a practical production template.
-
- - The fe-be configuration carries a higher fixed cost because two HAProxy
- instances are involved and the backend filter processes context extraction
- regardless of the frontend rate-limit setting.
-
- - Loading the filter but disabling it via 'option disabled' adds no measurable
- overhead in any configuration.
-
- - The filter attachment cost without any telemetry processing (rate-limit 0.0)
- is 0.3% or less for single-instance configurations (sa, cmp, ctx).
-
- - In typical production use with a rate-limit of 10% or less, the performance
- impact of the OTEL filter should be negligible for single-instance deployments.
+++ /dev/null
-global
- stats socket /tmp/haproxy-be.sock mode 666 level admin
-
-listen stats
- mode http
- bind *:8002
- stats uri /
- stats admin if TRUE
- stats refresh 10s
-
-frontend otel-test-be-frontend
- bind *:11080
- default_backend servers-backend
-
- # OTel filter
- filter opentelemetry id otel-test-be config be/otel.cfg
-
-backend servers-backend
- server server-1 127.0.0.1:8000
+++ /dev/null
-[otel-test-be]
- otel-instrumentation otel-test-instrumentation
- config be/otel.yml
-# log localhost:514 local7 debug
- option dontlog-normal
- option hard-errors
- no option disabled
-
- scopes frontend_http_request
- scopes backend_tcp_request
- scopes backend_http_request
- scopes client_session_end
-
- scopes server_session_start
- scopes tcp_response
- scopes http_response
- scopes server_session_end
-
- otel-scope frontend_http_request
- extract "otel-ctx" use-headers
- span "HAProxy session" parent "otel-ctx" root
- baggage "haproxy_id" var(sess.otel.uuid)
- span "Client session" parent "HAProxy session"
- span "Frontend HTTP request" parent "Client session"
- attribute "http.method" method
- attribute "http.url" url
- attribute "http.version" str("HTTP/") req.ver
- otel-event on-frontend-http-request
-
- otel-scope backend_tcp_request
- span "Backend TCP request" parent "Frontend HTTP request"
- finish "Frontend HTTP request"
- otel-event on-backend-tcp-request
-
- otel-scope backend_http_request
- span "Backend HTTP request" parent "Backend TCP request"
- finish "Backend TCP request"
- otel-event on-backend-http-request
-
- otel-scope client_session_end
- finish "Client session"
- otel-event on-client-session-end
-
- otel-scope server_session_start
- span "Server session" parent "HAProxy session"
- finish "Backend HTTP request"
- otel-event on-server-session-start
-
- otel-scope tcp_response
- span "TCP response" parent "Server session"
- otel-event on-tcp-response
-
- otel-scope http_response
- span "HTTP response" parent "TCP response"
- attribute "http.status_code" status
- finish "TCP response"
- otel-event on-http-response
-
- otel-scope server_session_end
- finish *
- otel-event on-server-session-end
+++ /dev/null
-exporters:
- exporter_traces_otlp_file:
- type: otlp_file
- thread_name: "OTLP/file trace"
- file_pattern: "__be_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: __be_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: "__be_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: __be_metrics
-
- exporter_metrics_memory:
- type: memory
- buffer_size: 256
-
- exporter_logs_otlp_file:
- type: otlp_file
- thread_name: "OTLP/file logs"
- file_pattern: "__be_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: __be_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-be"
- - service.name: "be"
- - service.namespace: "HAProxy traces test"
-
- provider_metrics:
- resources:
- - service.version: "1.0.0"
- - service.instance.id: "id-be"
- - service.name: "be"
- - service.namespace: "HAProxy metrics test"
-
- provider_logs:
- resources:
- - service.version: "1.0.0"
- - service.instance.id: "id-be"
- - service.name: "be"
- - 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
+++ /dev/null
-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-cmp-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-cmp config cmp/otel.cfg
-
-backend servers-backend
- server server-1 127.0.0.1:8000
+++ /dev/null
-[otel-test-cmp]
- otel-instrumentation otel-test-instrumentation
- config cmp/otel.yml
-# log localhost:514 local7 debug
- option dontlog-normal
- option hard-errors
- no option disabled
- rate-limit 100.0
-
- scopes client_session_start
- scopes frontend_tcp_request
- scopes frontend_http_request
- scopes backend_tcp_request
- scopes backend_http_request
- scopes server_unavailable
-
- scopes server_session_start
- scopes tcp_response
- scopes http_response http_response-error server_session_end client_session_end
-
- otel-scope client_session_start
- span "HAProxy session" root
- baggage "haproxy_id" var(sess.otel.uuid)
- span "Client session" parent "HAProxy session"
- otel-event on-client-session-start
-
- otel-scope frontend_tcp_request
- span "Frontend TCP request" parent "Client session"
- otel-event on-frontend-tcp-request
-
- otel-scope frontend_http_request
- span "Frontend HTTP request" parent "Frontend TCP request"
- attribute "http.method" method
- attribute "http.url" url
- attribute "http.version" str("HTTP/") req.ver
- finish "Frontend TCP request"
- otel-event on-frontend-http-request
-
- otel-scope backend_tcp_request
- span "Backend TCP request" parent "Frontend HTTP request"
- finish "Frontend HTTP request"
- otel-event on-backend-tcp-request
-
- otel-scope backend_http_request
- span "Backend HTTP request" parent "Backend TCP request"
- finish "Backend TCP request"
- otel-event on-backend-http-request
-
- otel-scope server_unavailable
- span "HAProxy session"
- status "error" str("503 Service Unavailable")
- finish *
- otel-event on-server-unavailable
-
- otel-scope server_session_start
- span "Server session" parent "HAProxy session"
- finish "Backend HTTP request"
- otel-event on-server-session-start
-
- otel-scope tcp_response
- span "TCP response" parent "Server session"
- otel-event on-tcp-response
-
- otel-scope http_response
- span "HTTP response" parent "TCP response"
- attribute "http.status_code" status
- finish "TCP response"
- otel-event on-http-response
-
- otel-scope http_response-error
- span "HTTP response"
- status "error" str("!acl-http-status-ok")
- otel-event on-http-response if !acl-http-status-ok
-
- otel-scope server_session_end
- finish "HTTP response" "Server session"
- otel-event on-http-response
-
- otel-scope client_session_end
- finish "*"
- otel-event on-http-response
+++ /dev/null
-exporters:
- exporter_traces_otlp_file:
- type: otlp_file
- thread_name: "OTLP/file trace"
- file_pattern: "__cmp_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: __cmp_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: "__cmp_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: __cmp_metrics
-
- exporter_metrics_memory:
- type: memory
- buffer_size: 256
-
- exporter_logs_otlp_file:
- type: otlp_file
- thread_name: "OTLP/file logs"
- file_pattern: "__cmp_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: __cmp_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-cmp"
- - service.name: "cmp"
- - service.namespace: "HAProxy traces test"
-
- provider_metrics:
- resources:
- - service.version: "1.0.0"
- - service.instance.id: "id-cmp"
- - service.name: "cmp"
- - service.namespace: "HAProxy metrics test"
-
- provider_logs:
- resources:
- - service.version: "1.0.0"
- - service.instance.id: "id-cmp"
- - service.name: "cmp"
- - 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
+++ /dev/null
-#!/bin/sh -u
-#
-# Copyright 2026 HAProxy Technologies, Miroslav Zagorac <mzagorac@haproxy.com>
-#
-SH_FILE="${1:-}"
- SH_EXT="${2:-}"
-
-
-if test ${#} -ne 2; then
- echo
- echo "usage: $(basename "${0}") input-file test-name"
- echo
- exit 64
-fi
-
-sed '
- s/^\( *\)\(filename:\)\( *\)_\(_[a-z]*\)/\1\2\3__'"${SH_EXT}"'\4/g
- s/^\( *\)\(file_pattern:\)\( *\)"_\(_[a-z]*_[^"]*\)"/\1\2\3"__'"${SH_EXT}"'\4"/g
- s/^\( *\)\(- service.instance.id:\)\( *\).*/\1\2\3"id-'"${SH_EXT}"'"/g
- s/^\( *\)\(- service.name:\)\( *\).*/\1\2\3"'"${SH_EXT}"'"/g
- s/^\( *\)\(- service.namespace:\)\( *\)\("otelc\)/\1\2\3"HAProxy/g
- s/^\( *\)\(scope_name:\)\( *\)"OTEL C wrapper /\1\2 "HAProxy OTEL /g
- s/^\( *\)\(exporters:\)\( *\)\(exporter_[a-z]*_\).*/\1\2\3\4otlp_http/g
-' "${SH_FILE}"
+++ /dev/null
-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-ctx-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-ctx config ctx/otel.cfg
-
- # run response scopes for successful responses
- http-response otel-group otel-test-ctx http_response_group if acl-http-status-ok
-
- # run after-response scopes for error responses
- http-after-response otel-group otel-test-ctx http_after_response_group if !acl-http-status-ok
-
-backend servers-backend
- server server-1 127.0.0.1:8000
+++ /dev/null
-[otel-test-ctx]
- otel-instrumentation otel-test-instrumentation
- debug-level 0x77f
- log localhost:514 local7 debug
- config ctx/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 client_session_start_1
- scopes client_session_start_2
- 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 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 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_1" "hdr.content" res.hdr("content-type") str("; length: ") res.hdr("content-length") str(" bytes")
-
- otel-scope http_response_2
- span "HTTP response"
- event "event_2" "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 client_session_start_1
- span "HAProxy session" root
- inject "otel_ctx_1" use-headers use-vars
- baggage "haproxy_id" var(sess.otel.uuid)
- otel-event on-client-session-start
-
- otel-scope client_session_start_2
- extract "otel_ctx_1" use-vars
- span "Client session" parent "otel_ctx_1"
- inject "otel_ctx_2" use-headers use-vars
- otel-event on-client-session-start
-
- otel-scope frontend_tcp_request
- extract "otel_ctx_2" use-vars
- span "Frontend TCP request" parent "otel_ctx_2"
- inject "otel_ctx_3" use-headers use-vars
- otel-event on-frontend-tcp-request
-
- otel-scope http_wait_request
- extract "otel_ctx_3" use-vars
- span "HTTP wait request" parent "otel_ctx_3"
- inject "otel_ctx_4" use-headers use-vars
- finish "Frontend TCP request" "otel_ctx_3"
- otel-event on-http-wait-request
-
- otel-scope http_body_request
- extract "otel_ctx_4" use-vars
- span "HTTP body request" parent "otel_ctx_4"
- inject "otel_ctx_5" use-headers use-vars
- finish "HTTP wait request" "otel_ctx_4"
- otel-event on-http-body-request
-
- otel-scope frontend_http_request
- extract "otel_ctx_5" use-vars
- span "Frontend HTTP request" parent "otel_ctx_5"
- attribute "http.method" method
- attribute "http.url" url
- attribute "http.version" str("HTTP/") req.ver
- inject "otel_ctx_6" use-headers use-vars
- finish "HTTP body request" "otel_ctx_5"
- otel-event on-frontend-http-request
-
- otel-scope switching_rules_request
- extract "otel_ctx_6" use-vars
- span "Switching rules request" parent "otel_ctx_6"
- inject "otel_ctx_7" use-headers use-vars
- finish "Frontend HTTP request" "otel_ctx_6"
- otel-event on-switching-rules-request
-
- otel-scope backend_tcp_request
- extract "otel_ctx_7" use-vars
- span "Backend TCP request" parent "otel_ctx_7"
- inject "otel_ctx_8" use-headers use-vars
- finish "Switching rules request" "otel_ctx_7"
- otel-event on-backend-tcp-request
-
- otel-scope backend_http_request
- extract "otel_ctx_8" use-vars
- span "Backend HTTP request" parent "otel_ctx_8"
- inject "otel_ctx_9" use-headers use-vars
- finish "Backend TCP request" "otel_ctx_8"
- otel-event on-backend-http-request
-
- otel-scope process_server_rules_request
- extract "otel_ctx_9" use-vars
- span "Process server rules request" parent "otel_ctx_9"
- inject "otel_ctx_10" use-headers use-vars
- finish "Backend HTTP request" "otel_ctx_9"
- otel-event on-process-server-rules-request
-
- otel-scope http_process_request
- extract "otel_ctx_10" use-vars
- span "HTTP process request" parent "otel_ctx_10"
- inject "otel_ctx_11" use-headers use-vars
- finish "Process server rules request" "otel_ctx_10"
- otel-event on-http-process-request
-
- otel-scope tcp_rdp_cookie_request
- extract "otel_ctx_11" use-vars
- span "TCP RDP cookie request" parent "otel_ctx_11"
- inject "otel_ctx_12" use-headers use-vars
- finish "HTTP process request" "otel_ctx_11"
- otel-event on-tcp-rdp-cookie-request
-
- otel-scope process_sticking_rules_request
- extract "otel_ctx_12" use-vars
- span "Process sticking rules request" parent "otel_ctx_12"
- inject "otel_ctx_13" use-headers use-vars
- finish "TCP RDP cookie request" "otel_ctx_12"
- otel-event on-process-sticking-rules-request
-
- otel-scope client_session_end
- finish "Client session" "otel_ctx_2"
- otel-event on-client-session-end
-
- otel-scope server_unavailable
- finish *
- otel-event on-server-unavailable
-
- otel-scope server_session_start
- span "Server session" parent "otel_ctx_1"
- inject "otel_ctx_14" use-vars
- extract "otel_ctx_13" use-vars
- finish "Process sticking rules request" "otel_ctx_13"
- otel-event on-server-session-start
-
- otel-scope tcp_response
- extract "otel_ctx_14" use-vars
- span "TCP response" parent "otel_ctx_14"
- inject "otel_ctx_15" use-vars
- otel-event on-tcp-response
-
- otel-scope http_wait_response
- extract "otel_ctx_15" use-vars
- span "HTTP wait response" parent "otel_ctx_15"
- inject "otel_ctx_16" use-headers use-vars
- finish "TCP response" "otel_ctx_15"
- otel-event on-http-wait-response
-
- otel-scope process_store_rules_response
- extract "otel_ctx_16" use-vars
- span "Process store rules response" parent "otel_ctx_16"
- inject "otel_ctx_17" use-headers use-vars
- finish "HTTP wait response" "otel_ctx_16"
- otel-event on-process-store-rules-response
-
- otel-scope http_response
- extract "otel_ctx_17" use-vars
- span "HTTP response" parent "otel_ctx_17"
- attribute "http.status_code" status
- finish "Process store rules response" "otel_ctx_17"
- otel-event on-http-response
-
- otel-scope http_response-error
- span "HTTP response"
- status "error" str("!acl-http-status-ok")
- otel-event on-http-response if !acl-http-status-ok
-
- otel-scope server_session_end
- finish *
- otel-event on-server-session-end
+++ /dev/null
-exporters:
- exporter_traces_otlp_file:
- type: otlp_file
- thread_name: "OTLP/file trace"
- file_pattern: "__ctx_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: __ctx_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: "__ctx_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: __ctx_metrics
-
- exporter_metrics_memory:
- type: memory
- buffer_size: 256
-
- exporter_logs_otlp_file:
- type: otlp_file
- thread_name: "OTLP/file logs"
- file_pattern: "__ctx_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: __ctx_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-ctx"
- - service.name: "ctx"
- - service.namespace: "HAProxy traces test"
-
- provider_metrics:
- resources:
- - service.version: "1.0.0"
- - service.instance.id: "id-ctx"
- - service.name: "ctx"
- - service.namespace: "HAProxy metrics test"
-
- provider_logs:
- resources:
- - service.version: "1.0.0"
- - service.instance.id: "id-ctx"
- - service.name: "ctx"
- - 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
+++ /dev/null
-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-empty
- bind *:10080
- default_backend servers-backend
-
- # OTel filter
- filter opentelemetry id otel-test-empty config empty/otel.cfg
-
-backend servers-backend
- server server-1 127.0.0.1:8000
+++ /dev/null
-otel-instrumentation otel-test-instrumentation
- config empty/otel.yml
+++ /dev/null
-exporters:
- exporter_traces_otlp_file:
- type: otlp_file
- thread_name: "OTLP/file trace"
- file_pattern: "__empty_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: __empty_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: "__empty_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: __empty_metrics
-
- exporter_metrics_memory:
- type: memory
- buffer_size: 256
-
- exporter_logs_otlp_file:
- type: otlp_file
- thread_name: "OTLP/file logs"
- file_pattern: "__empty_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: __empty_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-empty"
- - service.name: "empty"
- - service.namespace: "HAProxy traces test"
-
- provider_metrics:
- resources:
- - service.version: "1.0.0"
- - service.instance.id: "id-empty"
- - service.name: "empty"
- - service.namespace: "HAProxy metrics test"
-
- provider_logs:
- resources:
- - service.version: "1.0.0"
- - service.instance.id: "id-empty"
- - service.name: "empty"
- - 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
+++ /dev/null
-global
- stats socket /tmp/haproxy-fe.sock mode 666 level admin
-
-listen stats
- mode http
- bind *:8001
- stats uri /
- stats admin if TRUE
- stats refresh 10s
-
-frontend otel-test-fe-frontend
- bind *:10080
- default_backend servers-backend
-
- # OTel filter
- filter opentelemetry id otel-test-fe config fe/otel.cfg
-
-backend servers-backend
- server server-1 127.0.0.1:11080
+++ /dev/null
-[otel-test-fe]
- otel-instrumentation otel-test-instrumentation
- config fe/otel.yml
-# log localhost:514 local7 debug
- option dontlog-normal
- option hard-errors
- no option disabled
- rate-limit 100.0
-
- scopes client_session_start
- scopes frontend_tcp_request
- scopes frontend_http_request
- scopes backend_tcp_request
- scopes backend_http_request
- scopes client_session_end
-
- scopes server_session_start
- scopes tcp_response
- scopes http_response
- scopes server_session_end
-
- otel-scope client_session_start
- span "HAProxy session" root
- baggage "haproxy_id" var(sess.otel.uuid)
- span "Client session" parent "HAProxy session"
- otel-event on-client-session-start
-
- otel-scope frontend_tcp_request
- span "Frontend TCP request" parent "Client session"
- otel-event on-frontend-tcp-request
-
- otel-scope frontend_http_request
- span "Frontend HTTP request" parent "Frontend TCP request"
- attribute "http.method" method
- attribute "http.url" url
- attribute "http.version" str("HTTP/") req.ver
- finish "Frontend TCP request"
- otel-event on-frontend-http-request
-
- otel-scope backend_tcp_request
- span "Backend TCP request" parent "Frontend HTTP request"
- finish "Frontend HTTP request"
- otel-event on-backend-tcp-request
-
- otel-scope backend_http_request
- span "Backend HTTP request" parent "Backend TCP request"
- finish "Backend TCP request"
- span "HAProxy session"
- inject "otel-ctx" use-headers
- otel-event on-backend-http-request
-
- otel-scope client_session_end
- finish "Client session"
- otel-event on-client-session-end
-
- otel-scope server_session_start
- span "Server session" parent "HAProxy session"
- finish "Backend HTTP request"
- otel-event on-server-session-start
-
- otel-scope tcp_response
- span "TCP response" parent "Server session"
- otel-event on-tcp-response
-
- otel-scope http_response
- span "HTTP response" parent "TCP response"
- attribute "http.status_code" status
- finish "TCP response"
- otel-event on-http-response
-
- otel-scope server_session_end
- finish *
- otel-event on-server-session-end
+++ /dev/null
-exporters:
- exporter_traces_otlp_file:
- type: otlp_file
- thread_name: "OTLP/file trace"
- file_pattern: "__fe_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: __fe_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: "__fe_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: __fe_metrics
-
- exporter_metrics_memory:
- type: memory
- buffer_size: 256
-
- exporter_logs_otlp_file:
- type: otlp_file
- thread_name: "OTLP/file logs"
- file_pattern: "__fe_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: __fe_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-fe"
- - service.name: "fe"
- - service.namespace: "HAProxy traces test"
-
- provider_metrics:
- resources:
- - service.version: "1.0.0"
- - service.instance.id: "id-fe"
- - service.name: "fe"
- - service.namespace: "HAProxy metrics test"
-
- provider_logs:
- resources:
- - service.version: "1.0.0"
- - service.instance.id: "id-fe"
- - service.name: "fe"
- - 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
+++ /dev/null
-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
+++ /dev/null
-[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" src attr "attr_2_key" src_port 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" str("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" method 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" str("response")
- instrument update "haproxy.http.latency" attr "phase" str("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
+++ /dev/null
-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
+++ /dev/null
-global
-# nbthread 1
- maxconn 5000
- hard-stop-after 10s
-# log localhost:514 local7 debug
-# debug
-
-defaults
-# log global
-# option httplog
-# option dontlognull
-# option httpclose
- mode http
- retries 3
- maxconn 4000
- timeout connect 5000
- timeout client 50000
- timeout server 50000
+++ /dev/null
-<html><body><p>Did I err?</p></body></html>
+++ /dev/null
-run-test-config.sh
\ No newline at end of file
+++ /dev/null
-run-test-config.sh
\ No newline at end of file
+++ /dev/null
-run-test-config.sh
\ No newline at end of file
+++ /dev/null
-#!/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_TIME="$(date +%s)"
- SH_LOG_DIR="_logs"
- SH_LOG_FE="${SH_LOG_DIR}/_log-$(basename "${0}" fe-be.sh)fe-${SH_TIME}"
- SH_LOG_BE="${SH_LOG_DIR}/_log-$(basename "${0}" fe-be.sh)be-${SH_TIME}"
-
-
-__exit ()
-{
- test -z "${2}" && {
- echo
- echo "Script killed!"
-
- echo "Waiting for jobs to complete..."
- pkill --signal SIGUSR1 haproxy
- wait
- }
-
- test -n "${1}" && {
- echo
- echo "${1}"
- echo
- }
-
- exit ${2:-100}
-}
-
-
-trap __exit INT TERM
-
-test -x "${SH_ARG_HAPROXY}" || __exit "${SH_ARG_HAPROXY}: executable does not exist" 1
-mkdir -p "${SH_LOG_DIR}" || __exit "${SH_ARG_HAPROXY}: cannot create log directory" 2
-
-echo "\n------------------------------------------------------------------------"
-set -- -f haproxy-common.cfg -f be/haproxy.cfg -p "${SH_ARG_PIDFILE}"
-echo "--- executing: ${SH_ARG_HAPROXY} ${@}" >${SH_LOG_BE}
-"${SH_ARG_HAPROXY}" "${@}" >>"${SH_LOG_BE}" 2>&1 &
-
-set -- -f haproxy-common.cfg -f fe/haproxy.cfg -p "${SH_ARG_PIDFILE}"
-echo "--- executing: ${SH_ARG_HAPROXY} ${@}" >${SH_LOG_FE}
-"${SH_ARG_HAPROXY}" "${@}" >>"${SH_LOG_FE}" 2>&1 &
-echo "------------------------------------------------------------------------\n"
-
-echo "Press CTRL-C to quit..."
-wait
+++ /dev/null
-run-test-config.sh
\ No newline at end of file
+++ /dev/null
-run-test-config.sh
\ No newline at end of file
+++ /dev/null
-#!/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_NAME="$(basename "${0}" .sh)"
- SH_CONFDIR="${SH_NAME#run-}"
- SH_LOG_DIR="_logs"
- SH_LOG="${SH_LOG_DIR}/_log-${SH_NAME}-$(date +%s)"
-
-
-test -x "${SH_ARG_HAPROXY}" || exit 1
-mkdir -p "${SH_LOG_DIR}" || exit 2
-
-set -- -f haproxy-common.cfg -f "${SH_CONFDIR}/haproxy.cfg" -p "${SH_ARG_PIDFILE}"
-echo "executing: ${SH_ARG_HAPROXY} ${@}" >${SH_LOG}
-"${SH_ARG_HAPROXY}" "${@}" >>"${SH_LOG}" 2>&1
+++ /dev/null
-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-sa-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-sa config sa/otel.cfg
-
- # run response scopes for successful responses
- http-response otel-group otel-test-sa http_response_group if acl-http-status-ok
-
- # run after-response scopes for error responses
- http-after-response otel-group otel-test-sa http_after_response_group if !acl-http-status-ok
-
-backend servers-backend
- server server-1 127.0.0.1:8000
+++ /dev/null
-[otel-test-sa]
- otel-instrumentation otel-test-instrumentation
- debug-level 0x77f
- log localhost:514 local7 debug
- config sa/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 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 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 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 "Client session" attr "attr_1_key" src attr "attr_2_key" src_port 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 *
- 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 client_session_start
- span "Client session" parent "HAProxy session"
- otel-event on-client-session-start
-
- otel-scope frontend_tcp_request
- span "Frontend TCP request" parent "Client session"
- otel-event on-frontend-tcp-request
-
- otel-scope http_wait_request
- span "HTTP wait request" parent "Frontend TCP request"
- finish "Frontend TCP request"
- otel-event on-http-wait-request
-
- otel-scope http_body_request
- span "HTTP body request" parent "HTTP wait request"
- finish "HTTP wait request"
- 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"
- instrument update "haproxy.http.latency" attr "phase" str("request")
- 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 1001 event "http-request" span "Frontend HTTP request" attr "http.method" method 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"
- otel-event on-switching-rules-request
-
- otel-scope backend_tcp_request
- span "Backend TCP request" parent "Switching rules request"
- finish "Switching rules request"
- otel-event on-backend-tcp-request
-
- otel-scope backend_http_request
- span "Backend HTTP request" parent "Backend TCP request"
- finish "Backend TCP request"
- 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"
- 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"
- 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"
- 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"
- otel-event on-process-sticking-rules-request
-
- otel-scope client_session_end
- instrument update "haproxy.sessions.active"
- finish "*req*"
- otel-event on-client-session-end
-
- otel-scope server_unavailable
- finish "*req*" "*res*"
- otel-event on-server-unavailable
-
- otel-scope server_session_start
- span "Server session" parent "HAProxy session"
- link "HAProxy session" "Client session"
- finish "Process sticking rules request"
- otel-event on-server-session-start
-
- otel-scope tcp_response
- span "TCP response" parent "Server session"
- otel-event on-tcp-response
-
- otel-scope http_wait_response
- span "HTTP wait response" parent "TCP response"
- finish "TCP response"
- 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"
- otel-event on-process-store-rules-response
-
- otel-scope http_response
- instrument update "haproxy.http.requests" attr "phase" str("response")
- instrument update "haproxy.http.latency" attr "phase" str("response")
- instrument update "haproxy.fe.connections"
- span "HTTP response" parent "Process store rules response"
- attribute "http.status_code" status
- finish "Process store rules response"
- 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 server_session_end
- finish "*res*"
- otel-event on-server-session-end
+++ /dev/null
-exporters:
- exporter_traces_otlp_file:
- type: otlp_file
- thread_name: "OTLP/file trace"
- file_pattern: "__sa_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"
-# background_thread_wait_for: 10000
- 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: __sa_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: "__sa_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"
-# background_thread_wait_for: 15000
- 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: __sa_metrics
-
- exporter_metrics_memory:
- type: memory
- buffer_size: 256
-
- exporter_logs_otlp_file:
- type: otlp_file
- thread_name: "OTLP/file logs"
- file_pattern: "__sa_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"
-# background_thread_wait_for: 20000
- 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: __sa_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-sa"
- - service.name: "sa"
- - service.namespace: "HAProxy traces test"
-
- provider_metrics:
- resources:
- - service.version: "1.0.0"
- - service.instance.id: "id-sa"
- - service.name: "sa"
- - service.namespace: "HAProxy metrics test"
-
- provider_logs:
- resources:
- - service.version: "1.0.0"
- - service.instance.id: "id-sa"
- - service.name: "sa"
- - 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
+++ /dev/null
-#!/bin/sh -u
-#
- __= # echo
- SH_ARG_CFG=
- SH_ARG_DIR=
- SH_ARG_RATE="100.0 75.0 50.0 25.0 10.0 2.5 0.0 disabled off"
- SH_ARG_DURATION="300"
- SH_NAME="$(basename "${0}")"
- SH_LOG_DIR="_logs"
-SH_HAPROXY_PIDFILE="${SH_LOG_DIR}/haproxy.pid"
- SH_HTTPD_PIDFILE="${SH_LOG_DIR}/thttpd.pid"
- SH_USAGE_MSG="usage: ${SH_NAME} [-d duration] [-h] [-r rate-limits] cfg [dir]"
-
-
-sh_exit ()
-{
- sh_backup_clean "${SH_ARG_DIR}"
-
- test -z "${2:-}" && {
- echo
- echo "Script killed!"
- }
-
- test -n "${1:-}" && {
- echo
- echo "${1}"
- echo
- }
-
- ${__} sh_httpd_stop
- rmdir -p "${SH_LOG_DIR}" 2>/dev/null
- exit ${2:-64}
-}
-
-sh_backup_make()
-{
- _arg_dir="${1}"
- _var_file=
-
- for _var_file in haproxy.cfg otel.cfg otel.yml; do
- test -e "${_arg_dir}/${_var_file}.orig" || cp -af "${_arg_dir}/${_var_file}" "${_arg_dir}/${_var_file}.orig"
- done
-
- test "${_arg_dir}" = "fe" && sh_backup_make "be"
-}
-
-sh_backup_clean()
-{
- _arg_dir="${1}"
- _var_file=
-
- for _var_file in haproxy.cfg otel.cfg otel.yml; do
- test -e "${_arg_dir}/${_var_file}.orig" && mv "${_arg_dir}/${_var_file}.orig" "${_arg_dir}/${_var_file}"
- done
-
- test "${_arg_dir}" = "fe" && sh_backup_clean "be"
-}
-
-sh_httpd_run ()
-{
-
- test -e "${SH_HTTPD_PIDFILE}" && return
-
- thttpd -p 8000 -d . -nos -nov -l /dev/null -i "${SH_HTTPD_PIDFILE}"
-}
-
-sh_httpd_stop ()
-{
- test -e "${SH_HTTPD_PIDFILE}" || return
-
- kill -TERM "$(cat ${SH_HTTPD_PIDFILE})"
- rm "${SH_HTTPD_PIDFILE}"
-}
-
-sh_haproxy_run ()
-{
- _arg_cfg="${1}"
- _arg_dir="${2}"
- _arg_ratio="${3}"
- _var_sed_haproxy=
- _var_sed_otel=
- _var_sed_yml="s/\(exporters: *exporter_[a-z]*_\).*/\1dev_null/g"
-
- if test "${_arg_ratio}" = "disabled"; then
- _var_sed_otel="s/no \(option disabled\)/\1/"
- elif test "${_arg_ratio}" = "off"; then
- _var_sed_haproxy="s/^\(.* filter opentelemetry .*\)/#\1/g; s/^\(.* otel-group .*\)/#\1/g"
- else
- _var_sed_otel="s/\(rate-limit\) 100.0/\1 ${_arg_ratio}/"
- fi
-
- sed "${_var_sed_haproxy}" "${_arg_dir}/haproxy.cfg.orig" > "${_arg_dir}/haproxy.cfg"
- sed "${_var_sed_otel}" "${_arg_dir}/otel.cfg.orig" > "${_arg_dir}/otel.cfg"
- sed "${_var_sed_yml}" "${_arg_dir}/otel.yml.orig" > "${_arg_dir}/otel.yml"
-
- if test "${_arg_dir}" = "fe"; then
- sed "${_var_sed_yml}" "be/otel.yml.orig" > "be/otel.yml"
-
- if test "${_arg_ratio}" = "disabled" -o "${_arg_ratio}" = "off"; then
- sed "${_var_sed_haproxy}" "be/haproxy.cfg.orig" > "be/haproxy.cfg"
- sed "${_var_sed_otel}" "be/otel.cfg.orig" > "be/otel.cfg"
- fi
- fi
-
- ./run-${_arg_cfg}.sh "" "${SH_HAPROXY_PIDFILE}" &
- sleep 5
-}
-
-sh_haproxy_stop ()
-{
- # HAProxy does not create a pidfile if it is not running in daemon mode,
- # this is not used but is left regardless.
- #
- if test -e "${SH_HAPROXY_PIDFILE}"; then
- kill -TERM "$(cat ${SH_HAPROXY_PIDFILE})"
- rm "${SH_HAPROXY_PIDFILE}"
- fi
-
- pkill --signal SIGUSR1 haproxy
- wait
-}
-
-sh_wrk_run ()
-{
- _arg_ratio="${1}"
-
- echo "--- rate-limit ${_arg_ratio} --------------------------------------------------"
- wrk -c8 -d${SH_ARG_DURATION} -t8 --latency http://localhost:10080/index.html
- echo "----------------------------------------------------------------------"
- echo
-
- sleep 10
-}
-
-
-while getopts d:hr: _var_getopt; do
- case "${_var_getopt}" in
- d) SH_ARG_DURATION="${OPTARG}" ;;
- h) sh_exit "${SH_USAGE_MSG}" 0 ;;
- r) SH_ARG_RATE="${OPTARG}" ;;
- \?) sh_exit "${SH_USAGE_MSG}" 64
- esac
-done
-
-shift $(expr ${OPTIND} - 1)
-SH_ARG_CFG="${1:-}"
-SH_ARG_DIR="${2:-${SH_ARG_CFG}}"
-
-
-command -v thttpd >/dev/null 2>&1 || sh_exit "thttpd: command not found" 5
-command -v wrk >/dev/null 2>&1 || sh_exit "wrk: command not found" 6
-
-test -z "${SH_ARG_CFG}" -o -z "${SH_ARG_DIR}" && sh_exit "${SH_USAGE_MSG}" 64
-mkdir -p "${SH_LOG_DIR}" || sh_exit "${SH_LOG_DIR}: Cannot create log directory" 1
-
-if test "${SH_ARG_CFG}" = "all"; then
- _var_args="-r ${SH_ARG_RATE}"
- "${0}" "${_var_args}" sa sa
- "${0}" "${_var_args}" cmp cmp
- "${0}" "${_var_args}" ctx ctx
- "${0}" "${_var_args}" fe-be fe
- exit 0
-elif test "${SH_ARG_CFG}" = "fe-be"; then
- SH_ARG_DIR="fe"
-fi
-
-test -f "run-${SH_ARG_CFG}.sh" || sh_exit "run-${SH_ARG_CFG}.sh: No such test script" 2
-test -d "${SH_ARG_DIR}" || sh_exit "${SH_ARG_DIR}: No such directory" 3
-
-trap sh_exit INT TERM
-
-echo "Running speed test ${SH_ARG_CFG}, start at $(date +"%F %T")"
-exec 1>"${SH_LOG_DIR}/README-speed-${SH_ARG_CFG}"
-
-${__} sh_backup_make "${SH_ARG_DIR}"
-${__} sh_httpd_run
-for _var_rate in ${SH_ARG_RATE}; do
- ${__} sh_haproxy_run "${SH_ARG_CFG}" "${SH_ARG_DIR}" "${_var_rate}"
- ${__} sh_wrk_run "${_var_rate}"
- ${__} sh_haproxy_stop
-done
-sh_exit "" 0