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
------------------------------------------------------------------------------

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                      (log.proxy.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_stop                 (stops 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_exception        (exceptions list)
      |       +-- flt_otel_conf_sample       (message and attributes lists)
      |           +-- 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_sample               (set_vars list)
      |   +-- flt_otel_conf_sample_expr      (exprs list)
      +-- flt_otel_conf_set_var_ctx          (set_var_ctxs list)
      +-- flt_otel_conf_unset_var            (unset_vars list)
      |   +-- flt_otel_conf_str              (vars 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_ops_check), these names are resolved to pointers to the actual
flt_otel_conf_group or flt_otel_conf_scope structures via the ptr field.
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.
  sec_name   The configuration file section name (NULL = the filter id).
  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.
  ctx_name               Name of the signals context to select from the YAML.
  ctx                    The YAML configuration and selected signals context.
  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.
  flag_reqctx            No telemetry unless an upstream context is extracted.
  flag_noflush           Drop buffered telemetry at deinit instead of flushing.
  flag_data_req          Request channel needs a data filter for http_end.
  flag_data_res          Response channel needs a data filter for http_end.
  flag_started           Atomic claim so the OTel SDK is started once.
  kw_used                Once-only keywords already seen (FLT_OTEL_INSTR_KW_*).
  log                    Runtime-log emission state (struct flt_otel_log).
  n_harderr              Hard-error episodes (filter disabled), for the CLI.
  n_softerr              Soft-error occurrences (error swallowed), for the CLI.
  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).

The embedded flt_otel_log structure carries the runtime-log emission state:
the logging mode ('type', 0, 1 or 3), the log server list ('proxy', a HAProxy
proxy structure whose loggers list holds the configured targets), the sliding
emission-rate window ('rate'), the per-episode latch bits ('latch'), the count
of log lines suppressed since the last emission ('sup_pending') and the lifetime
total of suppressed lines ('sup_total'), which no reset clears.

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.
  flag_stop              Whether the scope stops tracing for the connection.
  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.
  stops                  The list of otel-stop directives (conf_stop).
  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.
  set_vars               The list of set-var directives (conf_sample).
  set_var_ctxs           The list of set-var-ctx directives.
  unset_vars             The list of unset-var directives (conf_unset_var).

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.
  flag_define                Whether the line carried creation arguments.
  kind                       The span kind (default SERVER).
  links                      The set of linked span or context 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.
  exceptions                 Recorded exceptions (flt_otel_conf_exception).

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_exception

  FLT_OTEL_CONF_HDR(id)  Required by macro; member <id> is not used directly.
  type                   The exception type string ('exception.type').
  message                Optional message value (single conf_sample list).
  attributes             Additional event attributes (conf_sample list).
  cond                   Optional if/unless condition controlling the record.

This structure backs the span 'exception' keyword: at runtime its type, the
evaluated message and the attributes are emitted via the record_exception()
operation as a span event named 'exception'.

4.7  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, FAILED
                         (-3) once creation was given up, or >= 0 for the actual
                         meter index.
  fail_num               Number of failed creation attempts.
  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                    The create line owning this name's instrument.
  scope                  The scope the instrument is in, the creating one once
                         it exists.
  cond                   Optional if/unless condition controlling recording.

Instruments come in two forms: create-form (defines a new metric with type,
description, unit, and optional histogram bounds) and update-form (records on
the instrument of its name through the ref pointer).  The chain supplying the
value is the one of the update's own scope when a create line stands there,
otherwise the one of the scope the instrument is in; at runtime the first chain
line whose condition holds supplies the recorded value.  Update-form attributes
are stored as flt_otel_conf_sample entries and evaluated at runtime.

4.8  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.
  time                   Optional timestamp expression (single conf_sample).
  attributes             Log record attributes (flt_otel_conf_sample list).
  samples                Sample expressions for the body.
  cond                   Optional if/unless condition controlling the record.

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.

The optional timestamp expression, set via the 'time' keyword, is stored as a
single-entry list of flt_otel_conf_sample with the unit selector (s, ms, us
or ns) packed into the sample's 'extra' data field as an OTELC_VALUE_INT32.
At runtime the sample is evaluated, normalised to int64, scaled by the chosen
unit, and packed into a struct timespec that becomes the event timestamp, while
the wall-clock value is passed as the observed timestamp.

4.9  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.10  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.
  time                    Optional per-event timestamp (single sample).
  cond                    Optional if/unless condition controlling this item.

The extra field carries type-specific data: the event name strings (of type
OTELC_VALUE_DATA) for span events, status code integers (OTELC_VALUE_INT32)
for span statuses.

When the sample value argument begins with 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.  An explicit '%[ ... ]'
wrapper around the entire argument marks the inner string as a log-format string
that may itself contain HAProxy aliases such as %ci, %t or %ft; the outer '%['
and trailing ']' are stripped before parse_logformat_string() is called.  At
runtime, if the lf_used flag is true, the log-format expression is evaluated
via build_logline() instead of the sample expression list.

4.11  flt_otel_conf_sample_expr

  FLT_OTEL_CONF_HDR(fmt_expr)  The original expression format string.
  expr                         The sample expression (struct sample_expr).

4.12  flt_otel_conf_set_var_ctx

  FLT_OTEL_CONF_HDR(name)  The HAProxy variable name.
  ref                      The referenced span or context name.
  field                    FLT_OTEL_VAR_FIELD_* identifier.
  field_key                The baggage or tracestate key, or NULL.
  cond                     Optional if/unless condition on the assignment.

This structure backs the set-var-ctx directive: it captures a field of a
referenced span or extracted context into a HAProxy variable at runtime.

4.13  flt_otel_conf_unset_var

  FLT_OTEL_CONF_HDR(id)  Required by macro; member <id> is not used directly.
  vars                   The variable names to remove (flt_otel_conf_str list).
  cond                   Optional if/unless condition controlling the removal.

This structure backs the unset-var directive: it groups one or more variable
names with an optional condition, so one condition covers the whole removal.

4.14  Simple Types

  flt_otel_conf_hdr   Generic header; unused, conf_str is its live twin.
  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 and the unset-var name lists.
  flt_otel_conf_link  Span or context link (HDR field named "ref") with an
                      optional attributes sample list for key:value data and an
                      optional if/unless condition.
  flt_otel_conf_stop  A single otel-stop directive with its optional if/unless
                      condition.
  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 the limit that the macro names, which
     is FLT_OTEL_ID_MAXLEN (128) for a name and FLT_OTEL_LEN_UNLIMITED for the
     text of a sample expression, which is not an identifier.
  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 and time.  Calls lf_expr_init for lf_expr.

  conf_link:
    LIST_INIT for attributes.

  conf_span:
    LIST_INIT for links, attributes, events, baggages, statuses, exceptions.
    Sets kind to OTELC_SPAN_KIND_SERVER.

  conf_exception:
    LIST_INIT for message, attributes.

  conf_scope:
    LIST_INIT for acls, stops, contexts, spans, spans_to_finish, instruments,
    log_records, set_vars, set_var_ctxs, unset_vars.

  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, attributes.

  conf_log_record:
    LIST_INIT for time, attributes, samples.

  conf_unset_var:
    LIST_INIT for vars.

  conf_instr:
    Sets rate_limit to FLT_OTEL_FLOAT_U32(100.0) (100%).  Calls init_new_proxy
    for log.proxy.  LIST_INIT for acls, ph_groups, ph_scopes.

Types with no custom body (hdr, str, ph, sample_expr, context, set_var_ctx,
stop) 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, the argument before
     the value unless the caller names one, as a 'log-record' body does.
  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, instrument samples, and set-var values.

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, scopes and smp_args 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.  Destroys the time list (per-event timestamp sample) and
    frees the optional ACL condition.

  conf_sample_expr:
    Releases the HAProxy sample expression via release_sample_expr.

  conf_link:
    Destroys the attributes list and frees the optional ACL condition.

  conf_span:
    Frees ref_id and ctx_id strings.
    Destroys the links, attributes, events, baggages, statuses, and exceptions
    lists.

  conf_exception:
    Frees the type string.  Destroys the message and attributes lists and frees
    the optional ACL condition.

  conf_instrument:
    Frees description, unit, and bounds.  Destroys the samples list and the
    attributes list (the update-form attributes, stored as conf_sample
    entries).  Frees the optional ACL condition.

  conf_log_record:
    Frees event_name and span strings.  Destroys the time, attributes and
    samples lists.  Frees the optional ACL condition.

  conf_set_var_ctx:
    Frees the ref and field_key strings and the optional ACL condition.

  conf_unset_var:
    Destroys the vars list and frees the optional ACL condition.

  conf_stop:
    Frees the optional ACL condition.

  conf_scope:
    Prunes and frees each ACL entry.  Frees the ACL condition via free_acl_cond.
    Destroys the stops and contexts lists, then the spans, spans_to_finish,
    instruments, log_records, set_vars, set_var_ctxs, and unset_vars lists.

  conf_group:
    Destroys the ph_scopes list.

  conf_instr:
    Frees the config and ctx_name strings.  Prunes and frees each ACL entry.
    Frees each logger entry from log.proxy.loggers.  Destroys the ph_groups and
    ph_scopes lists.

Types with no custom cleanup (hdr, str, 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, cfg_file and sec_name 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. Deletes and frees each sample fetch argument still on the smp_args list,
     which only an error path leaves unresolved.
  6. 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        ref        macro   attributes
  conf_ph          id         macro   (none)
  conf_sample_expr fmt_expr   macro   (none)
  conf_sample      key        macro   exprs, lf_expr, time, cond
  conf_sample (ex) key        manual  extra, fmt_string, exprs
  conf_context     id         macro   (none)
  conf_span        id         macro   sub-lists
  conf_exception   id         macro   message, attributes
  conf_instrument  id         macro   idx, type, samples
  conf_log_record  id         macro   time, attributes, samples
  conf_set_var_ctx name       macro   (none)
  conf_unset_var   id         macro   vars
  conf_stop        id         macro   (none)
  conf_scope       id         macro   sub-lists
  conf_group       id         macro   ph_scopes
  conf_instr       id         macro   rate_limit, log.proxy, acls, ph_groups, ph_scopes
