mirror of
https://github.com/haproxy/haproxy.git
synced 2026-04-13 04:46:15 -04:00
Compare commits
No commits in common. "master" and "v3.2-dev16" have entirely different histories.
master
...
v3.2-dev16
962 changed files with 23966 additions and 85719 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
FreeBSD_task:
|
FreeBSD_task:
|
||||||
freebsd_instance:
|
freebsd_instance:
|
||||||
matrix:
|
matrix:
|
||||||
image_family: freebsd-14-3
|
image_family: freebsd-14-2
|
||||||
only_if: $CIRRUS_BRANCH =~ 'master|next'
|
only_if: $CIRRUS_BRANCH =~ 'master|next'
|
||||||
install_script:
|
install_script:
|
||||||
- pkg update -f && pkg upgrade -y && pkg install -y openssl git gmake lua54 socat pcre2
|
- pkg update -f && pkg upgrade -y && pkg install -y openssl git gmake lua54 socat pcre2
|
||||||
|
|
|
||||||
48
.github/actions/setup-vtest/action.yml
vendored
48
.github/actions/setup-vtest/action.yml
vendored
|
|
@ -1,48 +0,0 @@
|
||||||
name: 'setup VTest'
|
|
||||||
description: 'ssss'
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: "composite"
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: Setup coredumps
|
|
||||||
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
sudo sysctl -w fs.suid_dumpable=1
|
|
||||||
sudo sysctl kernel.core_pattern=/tmp/core.%h.%e.%t
|
|
||||||
|
|
||||||
- name: Setup ulimit for core dumps
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
# This is required for macOS which does not actually allow to increase
|
|
||||||
# the '-n' soft limit to the hard limit, thus failing to run.
|
|
||||||
ulimit -n 65536
|
|
||||||
ulimit -c unlimited
|
|
||||||
|
|
||||||
- name: Get VTest latest commit SHA
|
|
||||||
id: vtest-sha
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "sha=$(git ls-remote https://code.vinyl-cache.org/vtest/VTest2 HEAD | cut -f1)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Cache VTest
|
|
||||||
id: cache-vtest
|
|
||||||
uses: actions/cache@v5
|
|
||||||
with:
|
|
||||||
path: ${{ github.workspace }}/vtest
|
|
||||||
key: vtest-${{ runner.os }}-${{ runner.arch }}-${{ steps.vtest-sha.outputs.sha }}
|
|
||||||
|
|
||||||
- name: Install VTest
|
|
||||||
if: ${{ steps.cache-vtest.outputs.cache-hit != 'true' }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
DESTDIR=${{ github.workspace }}/vtest scripts/build-vtest.sh
|
|
||||||
|
|
||||||
- name: Install problem matcher for VTest
|
|
||||||
shell: bash
|
|
||||||
# This allows one to more easily see which tests fail.
|
|
||||||
run: echo "::add-matcher::.github/vtest.json"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2
.github/h2spec.config
vendored
2
.github/h2spec.config
vendored
|
|
@ -19,7 +19,7 @@ defaults
|
||||||
|
|
||||||
frontend h2
|
frontend h2
|
||||||
mode http
|
mode http
|
||||||
bind 127.0.0.1:8443 ssl crt reg-tests/ssl/certs/common.pem alpn h2,http/1.1
|
bind 127.0.0.1:8443 ssl crt reg-tests/ssl/common.pem alpn h2,http/1.1
|
||||||
default_backend h2b
|
default_backend h2b
|
||||||
|
|
||||||
backend h2b
|
backend h2b
|
||||||
|
|
|
||||||
167
.github/matrix.py
vendored
167
.github/matrix.py
vendored
|
|
@ -12,7 +12,6 @@ import functools
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import urllib.error
|
|
||||||
import urllib.request
|
import urllib.request
|
||||||
from os import environ
|
from os import environ
|
||||||
from packaging import version
|
from packaging import version
|
||||||
|
|
@ -20,10 +19,9 @@ from packaging import version
|
||||||
#
|
#
|
||||||
# this CI is used for both development and stable branches of HAProxy
|
# this CI is used for both development and stable branches of HAProxy
|
||||||
#
|
#
|
||||||
# naming convention used, if branch/tag name matches:
|
# naming convention used, if branch name matches:
|
||||||
#
|
#
|
||||||
# "haproxy-" - stable branches
|
# "haproxy-" - stable branches
|
||||||
# "vX.Y.Z" - release tags
|
|
||||||
# otherwise - development branch (i.e. "latest" ssl variants, "latest" github images)
|
# otherwise - development branch (i.e. "latest" ssl variants, "latest" github images)
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
@ -34,24 +32,13 @@ def get_all_github_tags(url):
|
||||||
headers = {}
|
headers = {}
|
||||||
if environ.get("GITHUB_TOKEN") is not None:
|
if environ.get("GITHUB_TOKEN") is not None:
|
||||||
headers["Authorization"] = "token {}".format(environ.get("GITHUB_TOKEN"))
|
headers["Authorization"] = "token {}".format(environ.get("GITHUB_TOKEN"))
|
||||||
all_tags = []
|
request = urllib.request.Request(url, headers=headers)
|
||||||
page = 1
|
try:
|
||||||
sep = "&" if "?" in url else "?"
|
tags = urllib.request.urlopen(request)
|
||||||
while True:
|
except:
|
||||||
paginated_url = "{}{}per_page=100&page={}".format(url, sep, page)
|
return None
|
||||||
request = urllib.request.Request(paginated_url, headers=headers)
|
tags = json.loads(tags.read().decode("utf-8"))
|
||||||
try:
|
return [tag['name'] for tag in tags]
|
||||||
response = urllib.request.urlopen(request)
|
|
||||||
except urllib.error.URLError:
|
|
||||||
return all_tags if all_tags else None
|
|
||||||
tags = json.loads(response.read().decode("utf-8"))
|
|
||||||
if not tags:
|
|
||||||
break
|
|
||||||
all_tags.extend([tag['name'] for tag in tags])
|
|
||||||
if len(tags) < 100:
|
|
||||||
break
|
|
||||||
page += 1
|
|
||||||
return all_tags if all_tags else None
|
|
||||||
|
|
||||||
@functools.lru_cache(5)
|
@functools.lru_cache(5)
|
||||||
def determine_latest_openssl(ssl):
|
def determine_latest_openssl(ssl):
|
||||||
|
|
@ -69,7 +56,7 @@ def aws_lc_version_string_to_num(version_string):
|
||||||
return tuple(map(int, version_string[1:].split('.')))
|
return tuple(map(int, version_string[1:].split('.')))
|
||||||
|
|
||||||
def aws_lc_version_valid(version_string):
|
def aws_lc_version_valid(version_string):
|
||||||
return re.match(r'^v[0-9]+(\.[0-9]+)*$', version_string)
|
return re.match('^v[0-9]+(\.[0-9]+)*$', version_string)
|
||||||
|
|
||||||
@functools.lru_cache(5)
|
@functools.lru_cache(5)
|
||||||
def determine_latest_aws_lc(ssl):
|
def determine_latest_aws_lc(ssl):
|
||||||
|
|
@ -77,8 +64,6 @@ def determine_latest_aws_lc(ssl):
|
||||||
if not tags:
|
if not tags:
|
||||||
return "AWS_LC_VERSION=failed_to_detect"
|
return "AWS_LC_VERSION=failed_to_detect"
|
||||||
valid_tags = list(filter(aws_lc_version_valid, tags))
|
valid_tags = list(filter(aws_lc_version_valid, tags))
|
||||||
if not valid_tags:
|
|
||||||
return "AWS_LC_VERSION=failed_to_detect"
|
|
||||||
latest_tag = max(valid_tags, key=aws_lc_version_string_to_num)
|
latest_tag = max(valid_tags, key=aws_lc_version_string_to_num)
|
||||||
return "AWS_LC_VERSION={}".format(latest_tag[1:])
|
return "AWS_LC_VERSION={}".format(latest_tag[1:])
|
||||||
|
|
||||||
|
|
@ -86,16 +71,15 @@ def aws_lc_fips_version_string_to_num(version_string):
|
||||||
return tuple(map(int, version_string[12:].split('.')))
|
return tuple(map(int, version_string[12:].split('.')))
|
||||||
|
|
||||||
def aws_lc_fips_version_valid(version_string):
|
def aws_lc_fips_version_valid(version_string):
|
||||||
return re.match(r'^AWS-LC-FIPS-[0-9]+(\.[0-9]+)*$', version_string)
|
return re.match('^AWS-LC-FIPS-[0-9]+(\.[0-9]+)*$', version_string)
|
||||||
|
|
||||||
@functools.lru_cache(5)
|
@functools.lru_cache(5)
|
||||||
def determine_latest_aws_lc_fips(ssl):
|
def determine_latest_aws_lc_fips(ssl):
|
||||||
tags = get_all_github_tags("https://api.github.com/repos/aws/aws-lc/tags")
|
# the AWS-LC-FIPS tags are at the end of the list, so let's get a lot
|
||||||
|
tags = get_all_github_tags("https://api.github.com/repos/aws/aws-lc/tags?per_page=200")
|
||||||
if not tags:
|
if not tags:
|
||||||
return "AWS_LC_FIPS_VERSION=failed_to_detect"
|
return "AWS_LC_FIPS_VERSION=failed_to_detect"
|
||||||
valid_tags = list(filter(aws_lc_fips_version_valid, tags))
|
valid_tags = list(filter(aws_lc_fips_version_valid, tags))
|
||||||
if not valid_tags:
|
|
||||||
return "AWS_LC_FIPS_VERSION=failed_to_detect"
|
|
||||||
latest_tag = max(valid_tags, key=aws_lc_fips_version_string_to_num)
|
latest_tag = max(valid_tags, key=aws_lc_fips_version_string_to_num)
|
||||||
return "AWS_LC_FIPS_VERSION={}".format(latest_tag[12:])
|
return "AWS_LC_FIPS_VERSION={}".format(latest_tag[12:])
|
||||||
|
|
||||||
|
|
@ -103,7 +87,7 @@ def wolfssl_version_string_to_num(version_string):
|
||||||
return tuple(map(int, version_string[1:].removesuffix('-stable').split('.')))
|
return tuple(map(int, version_string[1:].removesuffix('-stable').split('.')))
|
||||||
|
|
||||||
def wolfssl_version_valid(version_string):
|
def wolfssl_version_valid(version_string):
|
||||||
return re.match(r'^v[0-9]+(\.[0-9]+)*-stable$', version_string)
|
return re.match('^v[0-9]+(\.[0-9]+)*-stable$', version_string)
|
||||||
|
|
||||||
@functools.lru_cache(5)
|
@functools.lru_cache(5)
|
||||||
def determine_latest_wolfssl(ssl):
|
def determine_latest_wolfssl(ssl):
|
||||||
|
|
@ -136,18 +120,14 @@ def clean_compression(compression):
|
||||||
def main(ref_name):
|
def main(ref_name):
|
||||||
print("Generating matrix for branch '{}'.".format(ref_name))
|
print("Generating matrix for branch '{}'.".format(ref_name))
|
||||||
|
|
||||||
is_stable = "haproxy-" in ref_name or re.match(r'^v\d+\.\d+\.\d+$', ref_name)
|
|
||||||
|
|
||||||
matrix = []
|
matrix = []
|
||||||
|
|
||||||
# Ubuntu
|
# Ubuntu
|
||||||
|
|
||||||
if is_stable:
|
if "haproxy-" in ref_name:
|
||||||
os = "ubuntu-24.04" # stable branch
|
os = "ubuntu-22.04" # stable branch
|
||||||
os_arm = "ubuntu-24.04-arm" # stable branch
|
|
||||||
else:
|
else:
|
||||||
os = "ubuntu-24.04" # development branch
|
os = "ubuntu-24.04" # development branch
|
||||||
os_arm = "ubuntu-24.04-arm" # development branch
|
|
||||||
|
|
||||||
TARGET = "linux-glibc"
|
TARGET = "linux-glibc"
|
||||||
for CC in ["gcc", "clang"]:
|
for CC in ["gcc", "clang"]:
|
||||||
|
|
@ -192,37 +172,36 @@ def main(ref_name):
|
||||||
|
|
||||||
# ASAN
|
# ASAN
|
||||||
|
|
||||||
for os_asan in [os, os_arm]:
|
matrix.append(
|
||||||
matrix.append(
|
{
|
||||||
{
|
"name": "{}, {}, ASAN, all features".format(os, CC),
|
||||||
"name": "{}, {}, ASAN, all features".format(os_asan, CC),
|
"os": os,
|
||||||
"os": os_asan,
|
"TARGET": TARGET,
|
||||||
"TARGET": TARGET,
|
"CC": CC,
|
||||||
"CC": CC,
|
"FLAGS": [
|
||||||
"FLAGS": [
|
"USE_OBSOLETE_LINKER=1",
|
||||||
"USE_OBSOLETE_LINKER=1",
|
'ARCH_FLAGS="-g -fsanitize=address"',
|
||||||
'ARCH_FLAGS="-g -fsanitize=address"',
|
'OPT_CFLAGS="-O1"',
|
||||||
'OPT_CFLAGS="-O1"',
|
"USE_ZLIB=1",
|
||||||
"USE_ZLIB=1",
|
"USE_OT=1",
|
||||||
"USE_OT=1",
|
"OT_INC=${HOME}/opt-ot/include",
|
||||||
"OT_INC=${HOME}/opt-ot/include",
|
"OT_LIB=${HOME}/opt-ot/lib",
|
||||||
"OT_LIB=${HOME}/opt-ot/lib",
|
"OT_RUNPATH=1",
|
||||||
"OT_RUNPATH=1",
|
"USE_PCRE2=1",
|
||||||
"USE_PCRE2=1",
|
"USE_PCRE2_JIT=1",
|
||||||
"USE_PCRE2_JIT=1",
|
"USE_LUA=1",
|
||||||
"USE_LUA=1",
|
"USE_OPENSSL=1",
|
||||||
"USE_OPENSSL=1",
|
"USE_WURFL=1",
|
||||||
"USE_WURFL=1",
|
"WURFL_INC=addons/wurfl/dummy",
|
||||||
"WURFL_INC=addons/wurfl/dummy",
|
"WURFL_LIB=addons/wurfl/dummy",
|
||||||
"WURFL_LIB=addons/wurfl/dummy",
|
"USE_DEVICEATLAS=1",
|
||||||
"USE_DEVICEATLAS=1",
|
"DEVICEATLAS_SRC=addons/deviceatlas/dummy",
|
||||||
"DEVICEATLAS_SRC=addons/deviceatlas/dummy",
|
"USE_PROMEX=1",
|
||||||
"USE_PROMEX=1",
|
"USE_51DEGREES=1",
|
||||||
"USE_51DEGREES=1",
|
"51DEGREES_SRC=addons/51degrees/dummy/pattern",
|
||||||
"51DEGREES_SRC=addons/51degrees/dummy/pattern",
|
],
|
||||||
],
|
}
|
||||||
}
|
)
|
||||||
)
|
|
||||||
|
|
||||||
for compression in ["USE_ZLIB=1"]:
|
for compression in ["USE_ZLIB=1"]:
|
||||||
matrix.append(
|
matrix.append(
|
||||||
|
|
@ -239,14 +218,13 @@ def main(ref_name):
|
||||||
"stock",
|
"stock",
|
||||||
"OPENSSL_VERSION=1.0.2u",
|
"OPENSSL_VERSION=1.0.2u",
|
||||||
"OPENSSL_VERSION=1.1.1s",
|
"OPENSSL_VERSION=1.1.1s",
|
||||||
"OPENSSL_VERSION=3.5.1",
|
"QUICTLS=yes",
|
||||||
"QUICTLS_VERSION=OpenSSL_1_1_1w-quic1",
|
|
||||||
"WOLFSSL_VERSION=5.7.0",
|
"WOLFSSL_VERSION=5.7.0",
|
||||||
"AWS_LC_VERSION=1.39.0",
|
"AWS_LC_VERSION=1.39.0",
|
||||||
# "BORINGSSL=yes",
|
# "BORINGSSL=yes",
|
||||||
]
|
]
|
||||||
|
|
||||||
if not is_stable: # development branch
|
if "haproxy-" not in ref_name: # development branch
|
||||||
ssl_versions = ssl_versions + [
|
ssl_versions = ssl_versions + [
|
||||||
"OPENSSL_VERSION=latest",
|
"OPENSSL_VERSION=latest",
|
||||||
"LIBRESSL_VERSION=latest",
|
"LIBRESSL_VERSION=latest",
|
||||||
|
|
@ -254,7 +232,8 @@ def main(ref_name):
|
||||||
|
|
||||||
for ssl in ssl_versions:
|
for ssl in ssl_versions:
|
||||||
flags = ["USE_OPENSSL=1"]
|
flags = ["USE_OPENSSL=1"]
|
||||||
skipdup=0
|
if ssl == "BORINGSSL=yes" or ssl == "QUICTLS=yes" or "LIBRESSL" in ssl or "WOLFSSL" in ssl or "AWS_LC" in ssl:
|
||||||
|
flags.append("USE_QUIC=1")
|
||||||
if "WOLFSSL" in ssl:
|
if "WOLFSSL" in ssl:
|
||||||
flags.append("USE_OPENSSL_WOLFSSL=1")
|
flags.append("USE_OPENSSL_WOLFSSL=1")
|
||||||
if "AWS_LC" in ssl:
|
if "AWS_LC" in ssl:
|
||||||
|
|
@ -264,23 +243,8 @@ def main(ref_name):
|
||||||
flags.append("SSL_INC=${HOME}/opt/include")
|
flags.append("SSL_INC=${HOME}/opt/include")
|
||||||
if "LIBRESSL" in ssl and "latest" in ssl:
|
if "LIBRESSL" in ssl and "latest" in ssl:
|
||||||
ssl = determine_latest_libressl(ssl)
|
ssl = determine_latest_libressl(ssl)
|
||||||
skipdup=1
|
|
||||||
if "OPENSSL" in ssl and "latest" in ssl:
|
if "OPENSSL" in ssl and "latest" in ssl:
|
||||||
ssl = determine_latest_openssl(ssl)
|
ssl = determine_latest_openssl(ssl)
|
||||||
skipdup=1
|
|
||||||
|
|
||||||
# if "latest" equals a version already in the list
|
|
||||||
if ssl in ssl_versions and skipdup == 1:
|
|
||||||
continue
|
|
||||||
|
|
||||||
openssl_supports_quic = False
|
|
||||||
try:
|
|
||||||
openssl_supports_quic = version.Version(ssl.split("OPENSSL_VERSION=",1)[1]) >= version.Version("3.5.0")
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if ssl == "BORINGSSL=yes" or "QUICTLS" in ssl or "LIBRESSL" in ssl or "WOLFSSL" in ssl or "AWS_LC" in ssl or openssl_supports_quic:
|
|
||||||
flags.append("USE_QUIC=1")
|
|
||||||
|
|
||||||
matrix.append(
|
matrix.append(
|
||||||
{
|
{
|
||||||
|
|
@ -293,21 +257,24 @@ def main(ref_name):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# macOS on dev branches
|
# macOS
|
||||||
if not is_stable:
|
|
||||||
os = "macos-26" # development branch
|
|
||||||
|
|
||||||
TARGET = "osx"
|
if "haproxy-" in ref_name:
|
||||||
for CC in ["clang"]:
|
os = "macos-13" # stable branch
|
||||||
matrix.append(
|
else:
|
||||||
{
|
os = "macos-15" # development branch
|
||||||
"name": "{}, {}, no features".format(os, CC),
|
|
||||||
"os": os,
|
TARGET = "osx"
|
||||||
"TARGET": TARGET,
|
for CC in ["clang"]:
|
||||||
"CC": CC,
|
matrix.append(
|
||||||
"FLAGS": [],
|
{
|
||||||
}
|
"name": "{}, {}, no features".format(os, CC),
|
||||||
)
|
"os": os,
|
||||||
|
"TARGET": TARGET,
|
||||||
|
"CC": CC,
|
||||||
|
"FLAGS": [],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Print matrix
|
# Print matrix
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
name: openssl master
|
name: AWS-LC-FIPS
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 3 * * *"
|
- cron: "0 0 * * 4"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
|
@ -13,19 +13,33 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install VTest
|
||||||
|
run: |
|
||||||
|
scripts/build-vtest.sh
|
||||||
|
- name: Determine latest AWS-LC release
|
||||||
|
id: get_aws_lc_release
|
||||||
|
run: |
|
||||||
|
result=$(cd .github && python3 -c "from matrix import determine_latest_aws_lc_fips; print(determine_latest_aws_lc_fips(''))")
|
||||||
|
echo $result
|
||||||
|
echo "result=$result" >> $GITHUB_OUTPUT
|
||||||
|
- name: Cache AWS-LC
|
||||||
|
id: cache_aws_lc
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: '~/opt/'
|
||||||
|
key: ssl-${{ steps.get_aws_lc_release.outputs.result }}-Ubuntu-latest-gcc
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
|
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
|
||||||
sudo apt-get --no-install-recommends -y install socat gdb
|
sudo apt-get --no-install-recommends -y install socat gdb jose
|
||||||
sudo apt-get --no-install-recommends -y install libpsl-dev
|
- name: Install AWS-LC
|
||||||
- uses: ./.github/actions/setup-vtest
|
if: ${{ steps.cache_ssl.outputs.cache-hit != 'true' }}
|
||||||
- name: Install OpenSSL master
|
run: env ${{ steps.get_aws_lc_release.outputs.result }} scripts/build-ssl.sh
|
||||||
run: env OPENSSL_VERSION="git-master" GIT_TYPE="branch" scripts/build-ssl.sh
|
|
||||||
- name: Compile HAProxy
|
- name: Compile HAProxy
|
||||||
run: |
|
run: |
|
||||||
make -j$(nproc) ERR=1 CC=gcc TARGET=linux-glibc \
|
make -j$(nproc) ERR=1 CC=gcc TARGET=linux-glibc \
|
||||||
USE_QUIC=1 USE_OPENSSL=1 \
|
USE_OPENSSL_AWSLC=1 USE_QUIC=1 \
|
||||||
SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include \
|
SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include \
|
||||||
DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" \
|
DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" \
|
||||||
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/"
|
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/"
|
||||||
|
|
@ -35,7 +49,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
ldd $(which haproxy)
|
ldd $(which haproxy)
|
||||||
haproxy -vv
|
haproxy -vv
|
||||||
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
||||||
- name: Install problem matcher for VTest
|
- name: Install problem matcher for VTest
|
||||||
run: echo "::add-matcher::.github/vtest.json"
|
run: echo "::add-matcher::.github/vtest.json"
|
||||||
- name: Run VTest for HAProxy
|
- name: Run VTest for HAProxy
|
||||||
|
|
@ -46,7 +60,11 @@ jobs:
|
||||||
ulimit -n 65536
|
ulimit -n 65536
|
||||||
# allow to catch coredumps
|
# allow to catch coredumps
|
||||||
ulimit -c unlimited
|
ulimit -c unlimited
|
||||||
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
|
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||||
|
- name: Run Unit tests
|
||||||
|
id: unittests
|
||||||
|
run: |
|
||||||
|
make unit-tests
|
||||||
- name: Show VTest results
|
- name: Show VTest results
|
||||||
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -57,10 +75,6 @@ jobs:
|
||||||
echo "::endgroup::"
|
echo "::endgroup::"
|
||||||
done
|
done
|
||||||
exit 1
|
exit 1
|
||||||
- name: Run Unit tests
|
|
||||||
id: unittests
|
|
||||||
run: |
|
|
||||||
make unit-tests
|
|
||||||
- name: Show coredumps
|
- name: Show coredumps
|
||||||
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -75,3 +89,13 @@ jobs:
|
||||||
if [ "$failed" = true ]; then
|
if [ "$failed" = true ]; then
|
||||||
exit 1;
|
exit 1;
|
||||||
fi
|
fi
|
||||||
|
- name: Show Unit-Tests results
|
||||||
|
if: ${{ failure() && steps.unittests.outcome == 'failure' }}
|
||||||
|
run: |
|
||||||
|
for result in ${TMPDIR:-/tmp}/ha-unittests-*/results/res.*; do
|
||||||
|
printf "::group::"
|
||||||
|
cat $result
|
||||||
|
echo "::endgroup::"
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
|
||||||
32
.github/workflows/aws-lc.yml
vendored
32
.github/workflows/aws-lc.yml
vendored
|
|
@ -9,28 +9,23 @@ permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Test:
|
test:
|
||||||
name: ${{ matrix.name }}
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- name: AWS-LC
|
|
||||||
command: "from matrix import determine_latest_aws_lc; print(determine_latest_aws_lc(''))"
|
|
||||||
- name: AWS-LC (FIPS)
|
|
||||||
command: "from matrix import determine_latest_aws_lc_fips; print(determine_latest_aws_lc_fips(''))"
|
|
||||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install VTest
|
||||||
|
run: |
|
||||||
|
scripts/build-vtest.sh
|
||||||
- name: Determine latest AWS-LC release
|
- name: Determine latest AWS-LC release
|
||||||
id: get_aws_lc_release
|
id: get_aws_lc_release
|
||||||
run: |
|
run: |
|
||||||
result=$(cd .github && python3 -c "${{ matrix.command }}")
|
result=$(cd .github && python3 -c "from matrix import determine_latest_aws_lc; print(determine_latest_aws_lc(''))")
|
||||||
echo $result
|
echo $result
|
||||||
echo "result=$result" >> $GITHUB_OUTPUT
|
echo "result=$result" >> $GITHUB_OUTPUT
|
||||||
- name: Cache AWS-LC
|
- name: Cache AWS-LC
|
||||||
id: cache_aws_lc
|
id: cache_aws_lc
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: '~/opt/'
|
path: '~/opt/'
|
||||||
key: ssl-${{ steps.get_aws_lc_release.outputs.result }}-Ubuntu-latest-gcc
|
key: ssl-${{ steps.get_aws_lc_release.outputs.result }}-Ubuntu-latest-gcc
|
||||||
|
|
@ -54,12 +49,18 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
ldd $(which haproxy)
|
ldd $(which haproxy)
|
||||||
haproxy -vv
|
haproxy -vv
|
||||||
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
||||||
- uses: ./.github/actions/setup-vtest
|
- name: Install problem matcher for VTest
|
||||||
|
run: echo "::add-matcher::.github/vtest.json"
|
||||||
- name: Run VTest for HAProxy
|
- name: Run VTest for HAProxy
|
||||||
id: vtest
|
id: vtest
|
||||||
run: |
|
run: |
|
||||||
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
|
# This is required for macOS which does not actually allow to increase
|
||||||
|
# the '-n' soft limit to the hard limit, thus failing to run.
|
||||||
|
ulimit -n 65536
|
||||||
|
# allow to catch coredumps
|
||||||
|
ulimit -c unlimited
|
||||||
|
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||||
- name: Run Unit tests
|
- name: Run Unit tests
|
||||||
id: unittests
|
id: unittests
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -97,3 +98,4 @@ jobs:
|
||||||
echo "::endgroup::"
|
echo "::endgroup::"
|
||||||
done
|
done
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
|
|
|
||||||
2
.github/workflows/codespell.yml
vendored
2
.github/workflows/codespell.yml
vendored
|
|
@ -13,7 +13,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: codespell-project/codespell-problem-matcher@v1.2.0
|
- uses: codespell-project/codespell-problem-matcher@v1.2.0
|
||||||
- uses: codespell-project/actions-codespell@master
|
- uses: codespell-project/actions-codespell@master
|
||||||
with:
|
with:
|
||||||
|
|
|
||||||
4
.github/workflows/compliance.yml
vendored
4
.github/workflows/compliance.yml
vendored
|
|
@ -14,7 +14,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- name: Install h2spec
|
- name: Install h2spec
|
||||||
id: install-h2spec
|
id: install-h2spec
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -45,7 +45,7 @@ jobs:
|
||||||
fi
|
fi
|
||||||
echo "::endgroup::"
|
echo "::endgroup::"
|
||||||
haproxy -vv
|
haproxy -vv
|
||||||
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
||||||
- name: Launch HAProxy ${{ steps.show-version.outputs.version }}
|
- name: Launch HAProxy ${{ steps.show-version.outputs.version }}
|
||||||
run: haproxy -f .github/h2spec.config -D
|
run: haproxy -f .github/h2spec.config -D
|
||||||
- name: Run h2spec ${{ steps.install-h2spec.outputs.version }}
|
- name: Run h2spec ${{ steps.install-h2spec.outputs.version }}
|
||||||
|
|
|
||||||
5
.github/workflows/contrib.yml
vendored
5
.github/workflows/contrib.yml
vendored
|
|
@ -10,7 +10,10 @@ jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
- name: Compile admin/halog/halog
|
||||||
|
run: |
|
||||||
|
make admin/halog/halog
|
||||||
- name: Compile dev/flags/flags
|
- name: Compile dev/flags/flags
|
||||||
run: |
|
run: |
|
||||||
make dev/flags/flags
|
make dev/flags/flags
|
||||||
|
|
|
||||||
6
.github/workflows/coverity.yml
vendored
6
.github/workflows/coverity.yml
vendored
|
|
@ -17,7 +17,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
|
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
|
||||||
|
|
@ -27,7 +27,7 @@ jobs:
|
||||||
libsystemd-dev
|
libsystemd-dev
|
||||||
- name: Install QUICTLS
|
- name: Install QUICTLS
|
||||||
run: |
|
run: |
|
||||||
QUICTLS_VERSION=OpenSSL_1_1_1w-quic1 scripts/build-ssl.sh
|
QUICTLS=yes scripts/build-ssl.sh
|
||||||
- name: Download Coverity build tool
|
- name: Download Coverity build tool
|
||||||
run: |
|
run: |
|
||||||
wget -c -N https://scan.coverity.com/download/linux64 --post-data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=Haproxy" -O coverity_tool.tar.gz
|
wget -c -N https://scan.coverity.com/download/linux64 --post-data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=Haproxy" -O coverity_tool.tar.gz
|
||||||
|
|
@ -38,7 +38,7 @@ jobs:
|
||||||
- name: Build with Coverity build tool
|
- name: Build with Coverity build tool
|
||||||
run: |
|
run: |
|
||||||
export PATH=`pwd`/coverity_tool/bin:$PATH
|
export PATH=`pwd`/coverity_tool/bin:$PATH
|
||||||
cov-build --dir cov-int make CC=clang TARGET=linux-glibc USE_ZLIB=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_LUA=1 USE_OPENSSL=1 USE_QUIC=1 USE_WURFL=1 WURFL_INC=addons/wurfl/dummy WURFL_LIB=addons/wurfl/dummy USE_DEVICEATLAS=1 DEVICEATLAS_SRC=addons/deviceatlas/dummy USE_51DEGREES=1 51DEGREES_SRC=addons/51degrees/dummy/pattern ADDLIB=\"-Wl,-rpath,$HOME/opt/lib/\" SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include DEBUG+=-DDEBUG_STRICT=2 DEBUG+=-DDEBUG_USE_ABORT=1
|
cov-build --dir cov-int make CC=clang TARGET=linux-glibc USE_ZLIB=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_LUA=1 USE_OPENSSL=1 USE_QUIC=1 USE_WURFL=1 WURFL_INC=addons/wurfl/dummy WURFL_LIB=addons/wurfl/dummy USE_DEVICEATLAS=1 DEVICEATLAS_SRC=addons/deviceatlas/dummy USE_51DEGREES=1 51DEGREES_SRC=addons/51degrees/dummy/pattern ADDLIB=\"-Wl,-rpath,$HOME/opt/lib/\" SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include DEBUG+=-DDEBUG_STRICT=1 DEBUG+=-DDEBUG_USE_ABORT=1
|
||||||
- name: Submit build result to Coverity Scan
|
- name: Submit build result to Coverity Scan
|
||||||
run: |
|
run: |
|
||||||
tar czvf cov.tar.gz cov-int
|
tar czvf cov.tar.gz cov-int
|
||||||
|
|
|
||||||
4
.github/workflows/cross-zoo.yml
vendored
4
.github/workflows/cross-zoo.yml
vendored
|
|
@ -99,12 +99,12 @@ jobs:
|
||||||
sudo apt-get -yq --force-yes install \
|
sudo apt-get -yq --force-yes install \
|
||||||
gcc-${{ matrix.platform.arch }} \
|
gcc-${{ matrix.platform.arch }} \
|
||||||
${{ matrix.platform.libs }}
|
${{ matrix.platform.libs }}
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
|
||||||
- name: install quictls
|
- name: install quictls
|
||||||
run: |
|
run: |
|
||||||
QUICTLS_EXTRA_ARGS="--cross-compile-prefix=${{ matrix.platform.arch }}- ${{ matrix.platform.target }}" QUICTLS_VERSION=OpenSSL_1_1_1w-quic1 scripts/build-ssl.sh
|
QUICTLS_EXTRA_ARGS="--cross-compile-prefix=${{ matrix.platform.arch }}- ${{ matrix.platform.target }}" QUICTLS=yes scripts/build-ssl.sh
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
|
|
|
||||||
37
.github/workflows/fedora-rawhide.yml
vendored
37
.github/workflows/fedora-rawhide.yml
vendored
|
|
@ -1,4 +1,4 @@
|
||||||
name: Fedora/Rawhide/OpenSSL
|
name: Fedora/Rawhide/QuicTLS
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
|
|
@ -13,24 +13,26 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
platform: [
|
platform: [
|
||||||
{ name: x64, cc: gcc, ADDLIB_ATOMIC: "", ARCH_FLAGS: "" },
|
{ name: x64, cc: gcc, QUICTLS_EXTRA_ARGS: "", ADDLIB_ATOMIC: "", ARCH_FLAGS: "" },
|
||||||
{ name: x64, cc: clang, ADDLIB_ATOMIC: "", ARCH_FLAGS: "" },
|
{ name: x64, cc: clang, QUICTLS_EXTRA_ARGS: "", ADDLIB_ATOMIC: "", ARCH_FLAGS: "" },
|
||||||
{ name: x86, cc: gcc, ADDLIB_ATOMIC: "-latomic", ARCH_FLAGS: "-m32" },
|
{ name: x86, cc: gcc, QUICTLS_EXTRA_ARGS: "-m32 linux-generic32", ADDLIB_ATOMIC: "-latomic", ARCH_FLAGS: "-m32" },
|
||||||
{ name: x86, cc: clang, ADDLIB_ATOMIC: "-latomic", ARCH_FLAGS: "-m32" }
|
{ name: x86, cc: clang, QUICTLS_EXTRA_ARGS: "-m32 linux-generic32", ADDLIB_ATOMIC: "-latomic", ARCH_FLAGS: "-m32" }
|
||||||
]
|
]
|
||||||
fail-fast: false
|
|
||||||
name: ${{ matrix.platform.cc }}.${{ matrix.platform.name }}
|
name: ${{ matrix.platform.cc }}.${{ matrix.platform.name }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||||
container:
|
container:
|
||||||
image: fedora:rawhide
|
image: fedora:rawhide
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
dnf -y install awk diffutils git pcre-devel zlib-devel pcre2-devel 'perl(FindBin)' perl-IPC-Cmd 'perl(File::Copy)' 'perl(File::Compare)' lua-devel socat findutils systemd-devel clang openssl-devel.x86_64
|
dnf -y install awk diffutils git pcre-devel zlib-devel pcre2-devel 'perl(FindBin)' perl-IPC-Cmd 'perl(File::Copy)' 'perl(File::Compare)' lua-devel socat findutils systemd-devel clang
|
||||||
dnf -y install 'perl(FindBin)' 'perl(File::Compare)' perl-IPC-Cmd 'perl(File::Copy)' glibc-devel.i686 lua-devel.i686 lua-devel.x86_64 systemd-devel.i686 zlib-ng-compat-devel.i686 pcre-devel.i686 libatomic.i686 openssl-devel.i686
|
dnf -y install 'perl(FindBin)' 'perl(File::Compare)' perl-IPC-Cmd 'perl(File::Copy)' glibc-devel.i686 lua-devel.i686 lua-devel.x86_64 systemd-devel.i686 zlib-ng-compat-devel.i686 pcre-devel.i686 libatomic.i686
|
||||||
- uses: ./.github/actions/setup-vtest
|
- name: Install VTest
|
||||||
|
run: scripts/build-vtest.sh
|
||||||
|
- name: Install QuicTLS
|
||||||
|
run: QUICTLS=yes QUICTLS_EXTRA_ARGS="${{ matrix.platform.QUICTLS_EXTRA_ARGS }}" scripts/build-ssl.sh
|
||||||
- name: Build contrib tools
|
- name: Build contrib tools
|
||||||
run: |
|
run: |
|
||||||
make admin/halog/halog
|
make admin/halog/halog
|
||||||
|
|
@ -39,7 +41,7 @@ jobs:
|
||||||
make dev/hpack/decode dev/hpack/gen-enc dev/hpack/gen-rht
|
make dev/hpack/decode dev/hpack/gen-enc dev/hpack/gen-rht
|
||||||
- name: Compile HAProxy with ${{ matrix.platform.cc }}
|
- name: Compile HAProxy with ${{ matrix.platform.cc }}
|
||||||
run: |
|
run: |
|
||||||
make -j3 CC=${{ matrix.platform.cc }} V=1 ERR=1 TARGET=linux-glibc DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" USE_PROMEX=1 USE_OPENSSL=1 USE_QUIC=1 USE_ZLIB=1 USE_PCRE=1 USE_PCRE_JIT=1 USE_LUA=1 ADDLIB="${{ matrix.platform.ADDLIB_ATOMIC }}" ARCH_FLAGS="${{ matrix.platform.ARCH_FLAGS }}"
|
make -j3 CC=${{ matrix.platform.cc }} V=1 ERR=1 TARGET=linux-glibc DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" USE_OPENSSL=1 USE_QUIC=1 USE_ZLIB=1 USE_PCRE=1 USE_PCRE_JIT=1 USE_LUA=1 ADDLIB="${{ matrix.platform.ADDLIB_ATOMIC }} -Wl,-rpath,${HOME}/opt/lib" SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include ARCH_FLAGS="${{ matrix.platform.ARCH_FLAGS }}"
|
||||||
make install
|
make install
|
||||||
- name: Show HAProxy version
|
- name: Show HAProxy version
|
||||||
id: show-version
|
id: show-version
|
||||||
|
|
@ -48,18 +50,11 @@ jobs:
|
||||||
ldd $(command -v haproxy)
|
ldd $(command -v haproxy)
|
||||||
echo "::endgroup::"
|
echo "::endgroup::"
|
||||||
haproxy -vv
|
haproxy -vv
|
||||||
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
||||||
#
|
|
||||||
# TODO: review this workaround later
|
|
||||||
- name: relax crypto policies
|
|
||||||
run: |
|
|
||||||
dnf -y install crypto-policies-scripts
|
|
||||||
echo LEGACY > /etc/crypto-policies/config
|
|
||||||
update-crypto-policies
|
|
||||||
- name: Run VTest for HAProxy ${{ steps.show-version.outputs.version }}
|
- name: Run VTest for HAProxy ${{ steps.show-version.outputs.version }}
|
||||||
id: vtest
|
id: vtest
|
||||||
run: |
|
run: |
|
||||||
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
|
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||||
- name: Show VTest results
|
- name: Show VTest results
|
||||||
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -72,4 +67,4 @@ jobs:
|
||||||
- name: Run Unit tests
|
- name: Run Unit tests
|
||||||
id: unittests
|
id: unittests
|
||||||
run: |
|
run: |
|
||||||
make unit-tests
|
make unit-tests
|
||||||
7
.github/workflows/illumos.yml
vendored
7
.github/workflows/illumos.yml
vendored
|
|
@ -5,16 +5,15 @@ on:
|
||||||
- cron: "0 0 25 * *"
|
- cron: "0 0 25 * *"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
gcc:
|
gcc:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout repository"
|
- name: "Checkout repository"
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: "Build on VM"
|
- name: "Build on VM"
|
||||||
uses: vmactions/solaris-vm@v1
|
uses: vmactions/solaris-vm@v1
|
||||||
|
|
|
||||||
7
.github/workflows/musl.yml
vendored
7
.github/workflows/musl.yml
vendored
|
|
@ -20,10 +20,11 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
ulimit -c unlimited
|
ulimit -c unlimited
|
||||||
echo '/tmp/core/core.%h.%e.%t' > /proc/sys/kernel/core_pattern
|
echo '/tmp/core/core.%h.%e.%t' > /proc/sys/kernel/core_pattern
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: apk add gcc gdb make tar git python3 libc-dev linux-headers pcre-dev pcre2-dev openssl-dev lua5.3-dev grep socat curl musl-dbg lua5.3-dbg jose
|
run: apk add gcc gdb make tar git python3 libc-dev linux-headers pcre-dev pcre2-dev openssl-dev lua5.3-dev grep socat curl musl-dbg lua5.3-dbg jose
|
||||||
- uses: ./.github/actions/setup-vtest
|
- name: Install VTest
|
||||||
|
run: scripts/build-vtest.sh
|
||||||
- name: Build
|
- name: Build
|
||||||
run: make -j$(nproc) TARGET=linux-musl DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" ARCH_FLAGS='-ggdb3' CC=cc V=1 USE_LUA=1 LUA_INC=/usr/include/lua5.3 LUA_LIB=/usr/lib/lua5.3 USE_OPENSSL=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_PROMEX=1
|
run: make -j$(nproc) TARGET=linux-musl DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" ARCH_FLAGS='-ggdb3' CC=cc V=1 USE_LUA=1 LUA_INC=/usr/include/lua5.3 LUA_LIB=/usr/lib/lua5.3 USE_OPENSSL=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_PROMEX=1
|
||||||
- name: Show version
|
- name: Show version
|
||||||
|
|
@ -35,7 +36,7 @@ jobs:
|
||||||
run: echo "::add-matcher::.github/vtest.json"
|
run: echo "::add-matcher::.github/vtest.json"
|
||||||
- name: Run VTest
|
- name: Run VTest
|
||||||
id: vtest
|
id: vtest
|
||||||
run: make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
|
run: make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||||
- name: Run Unit tests
|
- name: Run Unit tests
|
||||||
id: unittests
|
id: unittests
|
||||||
run: |
|
run: |
|
||||||
|
|
|
||||||
7
.github/workflows/netbsd.yml
vendored
7
.github/workflows/netbsd.yml
vendored
|
|
@ -5,16 +5,15 @@ on:
|
||||||
- cron: "0 0 25 * *"
|
- cron: "0 0 25 * *"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
gcc:
|
gcc:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout repository"
|
- name: "Checkout repository"
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: "Build on VM"
|
- name: "Build on VM"
|
||||||
uses: vmactions/netbsd-vm@v1
|
uses: vmactions/netbsd-vm@v1
|
||||||
|
|
|
||||||
80
.github/workflows/openssl-ech.yml
vendored
80
.github/workflows/openssl-ech.yml
vendored
|
|
@ -1,80 +0,0 @@
|
||||||
name: openssl ECH
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 3 * * *"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
- name: Install apt dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
|
|
||||||
sudo apt-get --no-install-recommends -y install socat gdb
|
|
||||||
sudo apt-get --no-install-recommends -y install libpsl-dev
|
|
||||||
- uses: ./.github/actions/setup-vtest
|
|
||||||
- name: Install OpenSSL+ECH
|
|
||||||
run: env OPENSSL_VERSION="git-feature/ech" GIT_TYPE="branch" scripts/build-ssl.sh
|
|
||||||
- name: Install curl+ECH
|
|
||||||
run: env SSL_LIB=${HOME}/opt/ scripts/build-curl.sh
|
|
||||||
- name: Compile HAProxy
|
|
||||||
run: |
|
|
||||||
make -j$(nproc) CC=gcc TARGET=linux-glibc \
|
|
||||||
USE_QUIC=1 USE_OPENSSL=1 USE_ECH=1 \
|
|
||||||
SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include \
|
|
||||||
DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" \
|
|
||||||
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/" \
|
|
||||||
ARCH_FLAGS="-ggdb3 -fsanitize=address"
|
|
||||||
sudo make install
|
|
||||||
- name: Show HAProxy version
|
|
||||||
id: show-version
|
|
||||||
run: |
|
|
||||||
ldd $(which haproxy)
|
|
||||||
haproxy -vv
|
|
||||||
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
|
||||||
- name: Install problem matcher for VTest
|
|
||||||
run: echo "::add-matcher::.github/vtest.json"
|
|
||||||
- name: Run VTest for HAProxy
|
|
||||||
id: vtest
|
|
||||||
run: |
|
|
||||||
# This is required for macOS which does not actually allow to increase
|
|
||||||
# the '-n' soft limit to the hard limit, thus failing to run.
|
|
||||||
ulimit -n 65536
|
|
||||||
# allow to catch coredumps
|
|
||||||
ulimit -c unlimited
|
|
||||||
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
|
|
||||||
- name: Show VTest results
|
|
||||||
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
|
||||||
run: |
|
|
||||||
for folder in ${TMPDIR:-/tmp}/haregtests-*/vtc.*; do
|
|
||||||
printf "::group::"
|
|
||||||
cat $folder/INFO
|
|
||||||
cat $folder/LOG
|
|
||||||
echo "::endgroup::"
|
|
||||||
done
|
|
||||||
exit 1
|
|
||||||
- name: Run Unit tests
|
|
||||||
id: unittests
|
|
||||||
run: |
|
|
||||||
make unit-tests
|
|
||||||
- name: Show coredumps
|
|
||||||
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
|
||||||
run: |
|
|
||||||
failed=false
|
|
||||||
shopt -s nullglob
|
|
||||||
for file in /tmp/core.*; do
|
|
||||||
failed=true
|
|
||||||
printf "::group::"
|
|
||||||
gdb -ex 'thread apply all bt full' ./haproxy $file
|
|
||||||
echo "::endgroup::"
|
|
||||||
done
|
|
||||||
if [ "$failed" = true ]; then
|
|
||||||
exit 1;
|
|
||||||
fi
|
|
||||||
34
.github/workflows/openssl-nodeprecated.yml
vendored
Normal file
34
.github/workflows/openssl-nodeprecated.yml
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#
|
||||||
|
# special purpose CI: test against OpenSSL built in "no-deprecated" mode
|
||||||
|
# let us run those builds weekly
|
||||||
|
#
|
||||||
|
# for example, OpenWRT uses such OpenSSL builds (those builds are smaller)
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# some details might be found at NL: https://www.mail-archive.com/haproxy@formilux.org/msg35759.html
|
||||||
|
# GH: https://github.com/haproxy/haproxy/issues/367
|
||||||
|
|
||||||
|
name: openssl no-deprecated
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0 * * 4"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install VTest
|
||||||
|
run: |
|
||||||
|
scripts/build-vtest.sh
|
||||||
|
- name: Compile HAProxy
|
||||||
|
run: |
|
||||||
|
make DEFINE="-DOPENSSL_API_COMPAT=0x10100000L -DOPENSSL_NO_DEPRECATED" -j3 CC=gcc ERR=1 TARGET=linux-glibc USE_OPENSSL=1
|
||||||
|
- name: Run VTest
|
||||||
|
run: |
|
||||||
|
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||||
94
.github/workflows/quic-interop-aws-lc.yml
vendored
94
.github/workflows/quic-interop-aws-lc.yml
vendored
|
|
@ -9,58 +9,96 @@ on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 0 * * 2"
|
- cron: "0 0 * * 2"
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
combined-build-and-run:
|
build:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Log in to the Container registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
id: push
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: https://github.com/haproxytech/haproxy-qns.git
|
||||||
|
push: true
|
||||||
|
build-args: |
|
||||||
|
SSLLIB: AWS-LC
|
||||||
|
tags: ghcr.io/${{ github.repository }}:aws-lc
|
||||||
|
|
||||||
|
- name: Cleanup registry
|
||||||
|
uses: actions/delete-package-versions@v5
|
||||||
|
with:
|
||||||
|
owner: ${{ github.repository_owner }}
|
||||||
|
package-name: 'haproxy'
|
||||||
|
package-type: container
|
||||||
|
min-versions-to-keep: 1
|
||||||
|
delete-only-untagged-versions: 'true'
|
||||||
|
|
||||||
|
run:
|
||||||
|
needs: build
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
suite: [
|
||||||
|
{ client: chrome, tests: "http3" },
|
||||||
|
{ client: picoquic, tests: "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" },
|
||||||
|
{ client: quic-go, tests: "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" },
|
||||||
|
{ client: ngtcp2, tests: "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" }
|
||||||
|
]
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
name: ${{ matrix.suite.client }}
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Update Docker to the latest
|
- name: Log in to the Container registry
|
||||||
uses: docker/setup-docker-action@v4
|
uses: docker/login-action@v3
|
||||||
|
|
||||||
- name: Build Docker image
|
|
||||||
id: push
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
with:
|
||||||
context: https://github.com/haproxytech/haproxy-qns.git
|
registry: ghcr.io
|
||||||
platforms: linux/amd64
|
username: ${{ github.actor }}
|
||||||
build-args: |
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
SSLLIB=AWS-LC
|
|
||||||
tags: local:aws-lc
|
|
||||||
|
|
||||||
- name: Install tshark
|
- name: Install tshark
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get -y install tshark
|
sudo apt-get -y install tshark
|
||||||
|
|
||||||
|
- name: Pull image
|
||||||
|
run: |
|
||||||
|
docker pull ghcr.io/${{ github.repository }}:aws-lc
|
||||||
|
|
||||||
- name: Run
|
- name: Run
|
||||||
run: |
|
run: |
|
||||||
git clone https://github.com/quic-interop/quic-interop-runner
|
git clone https://github.com/quic-interop/quic-interop-runner
|
||||||
cd quic-interop-runner
|
cd quic-interop-runner
|
||||||
pip install -r requirements.txt --break-system-packages
|
pip install -r requirements.txt --break-system-packages
|
||||||
python run.py -j result.json -l logs-chrome -r haproxy=local:aws-lc -t "http3" -c chrome -s haproxy
|
python run.py -j result.json -l logs -r haproxy=ghcr.io/${{ github.repository }}:aws-lc -t ${{ matrix.suite.tests }} -c ${{ matrix.suite.client }} -s haproxy
|
||||||
python run.py -j result.json -l logs-picoquic -r haproxy=local:aws-lc -t "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" -c picoquic -s haproxy
|
|
||||||
python run.py -j result.json -l logs-quic-go -r haproxy=local:aws-lc -t "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" -c quic-go -s haproxy
|
|
||||||
python run.py -j result.json -l logs-ngtcp2 -r haproxy=local:aws-lc -t "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" -c ngtcp2 -s haproxy
|
|
||||||
|
|
||||||
- name: Delete succeeded logs
|
- name: Delete succeeded logs
|
||||||
if: ${{ failure() }}
|
if: failure()
|
||||||
run: |
|
run: |
|
||||||
for client in chrome picoquic quic-go ngtcp2; do
|
cd quic-interop-runner/logs/haproxy_${{ matrix.suite.client }}
|
||||||
pushd quic-interop-runner/logs-${client}/haproxy_${client}
|
cat ../../result.json | jq -r '.results[][] | select(.result=="succeeded") | .name' | xargs rm -rf
|
||||||
cat ../../result.json | jq -r '.results[][] | select(.result=="succeeded") | .name' | xargs rm -rf
|
|
||||||
popd
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Logs upload
|
- name: Logs upload
|
||||||
if: ${{ failure() }}
|
if: failure()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: logs
|
name: logs-${{ matrix.suite.client }}
|
||||||
path: quic-interop-runner/logs*/
|
path: quic-interop-runner/logs/
|
||||||
retention-days: 6
|
retention-days: 6
|
||||||
|
|
|
||||||
90
.github/workflows/quic-interop-libressl.yml
vendored
90
.github/workflows/quic-interop-libressl.yml
vendored
|
|
@ -9,56 +9,94 @@ on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 0 * * 2"
|
- cron: "0 0 * * 2"
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
combined-build-and-run:
|
build:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Log in to the Container registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
id: push
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: https://github.com/haproxytech/haproxy-qns.git
|
||||||
|
push: true
|
||||||
|
build-args: |
|
||||||
|
SSLLIB: LibreSSL
|
||||||
|
tags: ghcr.io/${{ github.repository }}:libressl
|
||||||
|
|
||||||
|
- name: Cleanup registry
|
||||||
|
uses: actions/delete-package-versions@v5
|
||||||
|
with:
|
||||||
|
owner: ${{ github.repository_owner }}
|
||||||
|
package-name: 'haproxy'
|
||||||
|
package-type: container
|
||||||
|
min-versions-to-keep: 1
|
||||||
|
delete-only-untagged-versions: 'true'
|
||||||
|
|
||||||
|
run:
|
||||||
|
needs: build
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
suite: [
|
||||||
|
{ client: picoquic, tests: "handshake,transfer,longrtt,chacha20,multiplexing,retry,http3,blackhole,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,v2" },
|
||||||
|
{ client: quic-go, tests: "handshake,transfer,longrtt,chacha20,multiplexing,retry,http3,blackhole,amplificationlimit,transferloss,transfercorruption,v2" }
|
||||||
|
]
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
name: ${{ matrix.suite.client }}
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Update Docker to the latest
|
- name: Log in to the Container registry
|
||||||
uses: docker/setup-docker-action@v4
|
uses: docker/login-action@v3
|
||||||
|
|
||||||
- name: Build Docker image
|
|
||||||
id: push
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
with:
|
||||||
context: https://github.com/haproxytech/haproxy-qns.git
|
registry: ghcr.io
|
||||||
platforms: linux/amd64
|
username: ${{ github.actor }}
|
||||||
build-args: |
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
SSLLIB=LibreSSL
|
|
||||||
tags: local:libressl
|
|
||||||
|
|
||||||
- name: Install tshark
|
- name: Install tshark
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get -y install tshark
|
sudo apt-get -y install tshark
|
||||||
|
|
||||||
|
- name: Pull image
|
||||||
|
run: |
|
||||||
|
docker pull ghcr.io/${{ github.repository }}:libressl
|
||||||
|
|
||||||
- name: Run
|
- name: Run
|
||||||
run: |
|
run: |
|
||||||
git clone https://github.com/quic-interop/quic-interop-runner
|
git clone https://github.com/quic-interop/quic-interop-runner
|
||||||
cd quic-interop-runner
|
cd quic-interop-runner
|
||||||
pip install -r requirements.txt --break-system-packages
|
pip install -r requirements.txt --break-system-packages
|
||||||
python run.py -j result.json -l logs-picoquic -r haproxy=local:libressl -t "handshake,transfer,longrtt,chacha20,multiplexing,retry,http3,blackhole,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,v2" -c picoquic -s haproxy
|
python run.py -j result.json -l logs -r haproxy=ghcr.io/${{ github.repository }}:libressl -t ${{ matrix.suite.tests }} -c ${{ matrix.suite.client }} -s haproxy
|
||||||
python run.py -j result.json -l logs-quic-go -r haproxy=local:libressl -t "handshake,transfer,longrtt,chacha20,multiplexing,retry,http3,blackhole,amplificationlimit,transferloss,transfercorruption,v2" -c quic-go -s haproxy
|
|
||||||
|
|
||||||
- name: Delete succeeded logs
|
- name: Delete succeeded logs
|
||||||
if: ${{ failure() }}
|
if: failure()
|
||||||
run: |
|
run: |
|
||||||
for client in picoquic quic-go; do
|
cd quic-interop-runner/logs/haproxy_${{ matrix.suite.client }}
|
||||||
pushd quic-interop-runner/logs-${client}/haproxy_${client}
|
cat ../../result.json | jq -r '.results[][] | select(.result=="succeeded") | .name' | xargs rm -rf
|
||||||
cat ../../result.json | jq -r '.results[][] | select(.result=="succeeded") | .name' | xargs rm -rf
|
|
||||||
popd
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Logs upload
|
- name: Logs upload
|
||||||
if: ${{ failure() }}
|
if: failure()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: logs
|
name: logs-${{ matrix.suite.client }}
|
||||||
path: quic-interop-runner/logs*/
|
path: quic-interop-runner/logs/
|
||||||
retention-days: 6
|
retention-days: 6
|
||||||
|
|
|
||||||
19
.github/workflows/quictls.yml
vendored
19
.github/workflows/quictls.yml
vendored
|
|
@ -17,13 +17,16 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install VTest
|
||||||
|
run: |
|
||||||
|
scripts/build-vtest.sh
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
|
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
|
||||||
sudo apt-get --no-install-recommends -y install socat gdb
|
sudo apt-get --no-install-recommends -y install socat gdb
|
||||||
- name: Install QuicTLS
|
- name: Install QuicTLS
|
||||||
run: env QUICTLS_VERSION=main QUICTLS_URL=https://github.com/quictls/quictls scripts/build-ssl.sh
|
run: env QUICTLS=yes QUICTLS_URL=https://github.com/quictls/quictls scripts/build-ssl.sh
|
||||||
- name: Compile HAProxy
|
- name: Compile HAProxy
|
||||||
run: |
|
run: |
|
||||||
make -j$(nproc) ERR=1 CC=gcc TARGET=linux-glibc \
|
make -j$(nproc) ERR=1 CC=gcc TARGET=linux-glibc \
|
||||||
|
|
@ -38,12 +41,18 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
ldd $(which haproxy)
|
ldd $(which haproxy)
|
||||||
haproxy -vv
|
haproxy -vv
|
||||||
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
||||||
- uses: ./.github/actions/setup-vtest
|
- name: Install problem matcher for VTest
|
||||||
|
run: echo "::add-matcher::.github/vtest.json"
|
||||||
- name: Run VTest for HAProxy
|
- name: Run VTest for HAProxy
|
||||||
id: vtest
|
id: vtest
|
||||||
run: |
|
run: |
|
||||||
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
|
# This is required for macOS which does not actually allow to increase
|
||||||
|
# the '-n' soft limit to the hard limit, thus failing to run.
|
||||||
|
ulimit -n 65536
|
||||||
|
# allow to catch coredumps
|
||||||
|
ulimit -c unlimited
|
||||||
|
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||||
- name: Show VTest results
|
- name: Show VTest results
|
||||||
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
||||||
run: |
|
run: |
|
||||||
|
|
|
||||||
44
.github/workflows/vtest.yml
vendored
44
.github/workflows/vtest.yml
vendored
|
|
@ -23,7 +23,7 @@ jobs:
|
||||||
outputs:
|
outputs:
|
||||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- name: Generate Build Matrix
|
- name: Generate Build Matrix
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
@ -44,10 +44,16 @@ jobs:
|
||||||
TMPDIR: /tmp
|
TMPDIR: /tmp
|
||||||
OT_CPP_VERSION: 1.6.0
|
OT_CPP_VERSION: 1.6.0
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 100
|
fetch-depth: 100
|
||||||
|
|
||||||
|
- name: Setup coredumps
|
||||||
|
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
|
||||||
|
run: |
|
||||||
|
sudo sysctl -w fs.suid_dumpable=1
|
||||||
|
sudo sysctl kernel.core_pattern=/tmp/core.%h.%e.%t
|
||||||
|
|
||||||
#
|
#
|
||||||
# Github Action cache key cannot contain comma, so we calculate it based on job name
|
# Github Action cache key cannot contain comma, so we calculate it based on job name
|
||||||
#
|
#
|
||||||
|
|
@ -57,9 +63,9 @@ jobs:
|
||||||
echo "key=$(echo ${{ matrix.name }} | sha256sum | awk '{print $1}')" >> $GITHUB_OUTPUT
|
echo "key=$(echo ${{ matrix.name }} | sha256sum | awk '{print $1}')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Cache SSL libs
|
- name: Cache SSL libs
|
||||||
if: ${{ matrix.ssl && matrix.ssl != 'stock' && matrix.ssl != 'BORINGSSL=yes' && !contains(matrix.ssl, 'QUICTLS') }}
|
if: ${{ matrix.ssl && matrix.ssl != 'stock' && matrix.ssl != 'BORINGSSL=yes' && matrix.ssl != 'QUICTLS=yes' }}
|
||||||
id: cache_ssl
|
id: cache_ssl
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: '~/opt/'
|
path: '~/opt/'
|
||||||
key: ssl-${{ steps.generate-cache-key.outputs.key }}
|
key: ssl-${{ steps.generate-cache-key.outputs.key }}
|
||||||
|
|
@ -67,10 +73,10 @@ jobs:
|
||||||
- name: Cache OpenTracing
|
- name: Cache OpenTracing
|
||||||
if: ${{ contains(matrix.FLAGS, 'USE_OT=1') }}
|
if: ${{ contains(matrix.FLAGS, 'USE_OT=1') }}
|
||||||
id: cache_ot
|
id: cache_ot
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: '~/opt-ot/'
|
path: '~/opt-ot/'
|
||||||
key: ${{ matrix.os }}-ot-${{ matrix.CC }}-${{ env.OT_CPP_VERSION }}-${{ contains(matrix.name, 'ASAN') }}
|
key: ot-${{ matrix.CC }}-${{ env.OT_CPP_VERSION }}-${{ contains(matrix.name, 'ASAN') }}
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
|
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -87,7 +93,9 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
brew install socat
|
brew install socat
|
||||||
brew install lua
|
brew install lua
|
||||||
- uses: ./.github/actions/setup-vtest
|
- name: Install VTest
|
||||||
|
run: |
|
||||||
|
scripts/build-vtest.sh
|
||||||
- name: Install SSL ${{ matrix.ssl }}
|
- name: Install SSL ${{ matrix.ssl }}
|
||||||
if: ${{ matrix.ssl && matrix.ssl != 'stock' && steps.cache_ssl.outputs.cache-hit != 'true' }}
|
if: ${{ matrix.ssl && matrix.ssl != 'stock' && steps.cache_ssl.outputs.cache-hit != 'true' }}
|
||||||
run: env ${{ matrix.ssl }} scripts/build-ssl.sh
|
run: env ${{ matrix.ssl }} scripts/build-ssl.sh
|
||||||
|
|
@ -113,16 +121,7 @@ jobs:
|
||||||
DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" \
|
DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" \
|
||||||
${{ join(matrix.FLAGS, ' ') }} \
|
${{ join(matrix.FLAGS, ' ') }} \
|
||||||
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/"
|
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/"
|
||||||
sudo make install-bin
|
sudo make install
|
||||||
- name: Compile admin/halog/halog
|
|
||||||
run: |
|
|
||||||
make -j$(nproc) admin/halog/halog \
|
|
||||||
ERR=1 \
|
|
||||||
TARGET=${{ matrix.TARGET }} \
|
|
||||||
CC=${{ matrix.CC }} \
|
|
||||||
DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" \
|
|
||||||
${{ join(matrix.FLAGS, ' ') }} \
|
|
||||||
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/"
|
|
||||||
- name: Show HAProxy version
|
- name: Show HAProxy version
|
||||||
id: show-version
|
id: show-version
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -136,11 +135,18 @@ jobs:
|
||||||
fi
|
fi
|
||||||
echo "::endgroup::"
|
echo "::endgroup::"
|
||||||
haproxy -vv
|
haproxy -vv
|
||||||
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
||||||
|
- name: Install problem matcher for VTest
|
||||||
|
# This allows one to more easily see which tests fail.
|
||||||
|
run: echo "::add-matcher::.github/vtest.json"
|
||||||
- name: Run VTest for HAProxy ${{ steps.show-version.outputs.version }}
|
- name: Run VTest for HAProxy ${{ steps.show-version.outputs.version }}
|
||||||
id: vtest
|
id: vtest
|
||||||
run: |
|
run: |
|
||||||
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
|
# This is required for macOS which does not actually allow to increase
|
||||||
|
# the '-n' soft limit to the hard limit, thus failing to run.
|
||||||
|
ulimit -n 65536
|
||||||
|
ulimit -c unlimited
|
||||||
|
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||||
- name: Show VTest results
|
- name: Show VTest results
|
||||||
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
|
||||||
run: |
|
run: |
|
||||||
|
|
|
||||||
3
.github/workflows/windows.yml
vendored
3
.github/workflows/windows.yml
vendored
|
|
@ -18,7 +18,6 @@ jobs:
|
||||||
msys2:
|
msys2:
|
||||||
name: ${{ matrix.name }}
|
name: ${{ matrix.name }}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
|
|
@ -36,7 +35,7 @@ jobs:
|
||||||
- USE_THREAD=1
|
- USE_THREAD=1
|
||||||
- USE_ZLIB=1
|
- USE_ZLIB=1
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
- uses: msys2/setup-msys2@v2
|
- uses: msys2/setup-msys2@v2
|
||||||
with:
|
with:
|
||||||
install: >-
|
install: >-
|
||||||
|
|
|
||||||
17
.github/workflows/wolfssl.yml
vendored
17
.github/workflows/wolfssl.yml
vendored
|
|
@ -13,7 +13,10 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install VTest
|
||||||
|
run: |
|
||||||
|
scripts/build-vtest.sh
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
|
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
|
||||||
|
|
@ -34,12 +37,18 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
ldd $(which haproxy)
|
ldd $(which haproxy)
|
||||||
haproxy -vv
|
haproxy -vv
|
||||||
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
||||||
- uses: ./.github/actions/setup-vtest
|
- name: Install problem matcher for VTest
|
||||||
|
run: echo "::add-matcher::.github/vtest.json"
|
||||||
- name: Run VTest for HAProxy
|
- name: Run VTest for HAProxy
|
||||||
id: vtest
|
id: vtest
|
||||||
run: |
|
run: |
|
||||||
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
|
# This is required for macOS which does not actually allow to increase
|
||||||
|
# the '-n' soft limit to the hard limit, thus failing to run.
|
||||||
|
ulimit -n 65536
|
||||||
|
# allow to catch coredumps
|
||||||
|
ulimit -c unlimited
|
||||||
|
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||||
- name: Run Unit tests
|
- name: Run Unit tests
|
||||||
id: unittests
|
id: unittests
|
||||||
run: |
|
run: |
|
||||||
|
|
|
||||||
12
BRANCHES
12
BRANCHES
|
|
@ -171,17 +171,7 @@ feedback for developers:
|
||||||
as the previous releases that had 6 months to stabilize. In terms of
|
as the previous releases that had 6 months to stabilize. In terms of
|
||||||
stability it really means that the point zero version already accumulated
|
stability it really means that the point zero version already accumulated
|
||||||
6 months of fixes and that it is much safer to use even just after it is
|
6 months of fixes and that it is much safer to use even just after it is
|
||||||
released. There is one exception though, features marked as "experimental"
|
released.
|
||||||
are not guaranteed to be maintained beyond the release of the next LTS
|
|
||||||
branch. The rationale here is that the experimental status is made to
|
|
||||||
expose an early preview of a feature, that is often incomplete, not always
|
|
||||||
in its definitive form regarding configuration, and for which developers
|
|
||||||
are seeking feedback from the users. It is even possible that changes will
|
|
||||||
be brought within the stable branch and it may happen that the feature
|
|
||||||
breaks. It is not imaginable to always be able to backport bug fixes too
|
|
||||||
far in this context since the code and configuration may change quite a
|
|
||||||
bit. Users who want to try experimental features are expected to upgrade
|
|
||||||
quickly to benefit from the improvements made to that feature.
|
|
||||||
|
|
||||||
- for developers, given that the odd versions are solely used by highly
|
- for developers, given that the odd versions are solely used by highly
|
||||||
skilled users, it's easier to get advanced traces and captures, and there
|
skilled users, it's easier to get advanced traces and captures, and there
|
||||||
|
|
|
||||||
45
INSTALL
45
INSTALL
|
|
@ -111,7 +111,7 @@ HAProxy requires a working GCC or Clang toolchain and GNU make :
|
||||||
may want to retry with "gmake" which is the name commonly used for GNU make
|
may want to retry with "gmake" which is the name commonly used for GNU make
|
||||||
on BSD systems.
|
on BSD systems.
|
||||||
|
|
||||||
- GCC >= 4.7 (up to 15 tested). Older versions are no longer supported due to
|
- GCC >= 4.7 (up to 14 tested). Older versions are no longer supported due to
|
||||||
the latest mt_list update which only uses c11-like atomics. Newer versions
|
the latest mt_list update which only uses c11-like atomics. Newer versions
|
||||||
may sometimes break due to compiler regressions or behaviour changes. The
|
may sometimes break due to compiler regressions or behaviour changes. The
|
||||||
version shipped with your operating system is very likely to work with no
|
version shipped with your operating system is very likely to work with no
|
||||||
|
|
@ -237,7 +237,7 @@ to forcefully enable it using "USE_LIBCRYPT=1".
|
||||||
-----------------
|
-----------------
|
||||||
For SSL/TLS, it is necessary to use a cryptography library. HAProxy currently
|
For SSL/TLS, it is necessary to use a cryptography library. HAProxy currently
|
||||||
supports the OpenSSL library, and is known to build and work with branches
|
supports the OpenSSL library, and is known to build and work with branches
|
||||||
1.0.0, 1.0.1, 1.0.2, 1.1.0, 1.1.1, and 3.0 to 3.6. It is recommended to use
|
1.0.0, 1.0.1, 1.0.2, 1.1.0, 1.1.1, and 3.0 to 3.4. It is recommended to use
|
||||||
at least OpenSSL 1.1.1 to have support for all SSL keywords and configuration
|
at least OpenSSL 1.1.1 to have support for all SSL keywords and configuration
|
||||||
in HAProxy. OpenSSL follows a long-term support cycle similar to HAProxy's,
|
in HAProxy. OpenSSL follows a long-term support cycle similar to HAProxy's,
|
||||||
and each of the branches above receives its own fixes, without forcing you to
|
and each of the branches above receives its own fixes, without forcing you to
|
||||||
|
|
@ -259,15 +259,11 @@ reported to work as well. While there are some efforts from the community to
|
||||||
ensure they work well, OpenSSL remains the primary target and this means that
|
ensure they work well, OpenSSL remains the primary target and this means that
|
||||||
in case of conflicting choices, OpenSSL support will be favored over other
|
in case of conflicting choices, OpenSSL support will be favored over other
|
||||||
options. Note that QUIC is not fully supported when haproxy is built with
|
options. Note that QUIC is not fully supported when haproxy is built with
|
||||||
OpenSSL < 3.5.2 version. In this case, QUICTLS or AWS-LC are the preferred
|
OpenSSL. In this case, QUICTLS is the preferred alternative. As of writing
|
||||||
alternatives. As of writing this, the QuicTLS project follows OpenSSL very
|
this, the QuicTLS project follows OpenSSL very closely and provides update
|
||||||
closely and provides update simultaneously, but being a volunteer-driven
|
simultaneously, but being a volunteer-driven project, its long-term future does
|
||||||
project, its long-term future does not look certain enough to convince
|
not look certain enough to convince operating systems to package it, so it
|
||||||
operating systems to package it, so it needs to be build locally. Recent
|
needs to be build locally. See the section about QUIC in this document.
|
||||||
versions of AWS-LC (>= 1.22 and the FIPS branches) are pretty complete and
|
|
||||||
generally more performant than other OpenSSL derivatives, but may behave
|
|
||||||
slightly differently, particularly when dealing with outdated setups. See
|
|
||||||
the section about QUIC in this document.
|
|
||||||
|
|
||||||
A fifth option is wolfSSL (https://github.com/wolfSSL/wolfssl). It is the only
|
A fifth option is wolfSSL (https://github.com/wolfSSL/wolfssl). It is the only
|
||||||
supported alternative stack not based on OpenSSL, yet which implements almost
|
supported alternative stack not based on OpenSSL, yet which implements almost
|
||||||
|
|
@ -504,11 +500,10 @@ QUIC is the new transport layer protocol and is required for HTTP/3. This
|
||||||
protocol stack is currently supported as an experimental feature in haproxy on
|
protocol stack is currently supported as an experimental feature in haproxy on
|
||||||
the frontend side. In order to enable it, use "USE_QUIC=1 USE_OPENSSL=1".
|
the frontend side. In order to enable it, use "USE_QUIC=1 USE_OPENSSL=1".
|
||||||
|
|
||||||
Note that QUIC is not always fully supported by the OpenSSL library depending on
|
Note that QUIC is not fully supported by the OpenSSL library. Indeed QUIC 0-RTT
|
||||||
its version. Indeed QUIC 0-RTT cannot be supported by OpenSSL for versions before
|
cannot be supported by OpenSSL contrary to others libraries with full QUIC
|
||||||
3.5 contrary to others libraries with full QUIC support. The preferred option is
|
support. The preferred option is to use QUICTLS. This is a fork of OpenSSL with
|
||||||
to use QUICTLS. This is a fork of OpenSSL with a QUIC-compatible API. Its
|
a QUIC-compatible API. Its repository is available at this location:
|
||||||
repository is available at this location:
|
|
||||||
|
|
||||||
https://github.com/quictls/openssl
|
https://github.com/quictls/openssl
|
||||||
|
|
||||||
|
|
@ -536,18 +531,14 @@ way assuming that wolfSSL was installed in /opt/wolfssl-5.6.0 as shown in 4.5:
|
||||||
SSL_INC=/opt/wolfssl-5.6.0/include SSL_LIB=/opt/wolfssl-5.6.0/lib
|
SSL_INC=/opt/wolfssl-5.6.0/include SSL_LIB=/opt/wolfssl-5.6.0/lib
|
||||||
LDFLAGS="-Wl,-rpath,/opt/wolfssl-5.6.0/lib"
|
LDFLAGS="-Wl,-rpath,/opt/wolfssl-5.6.0/lib"
|
||||||
|
|
||||||
As last resort, haproxy may be compiled against OpenSSL as follows from 3.5
|
As last resort, haproxy may be compiled against OpenSSL as follows:
|
||||||
version with 0-RTT support:
|
|
||||||
|
|
||||||
$ make TARGET=generic USE_OPENSSL=1 USE_QUIC=1
|
|
||||||
|
|
||||||
or as follows for all OpenSSL versions but without O-RTT support:
|
|
||||||
|
|
||||||
$ make TARGET=generic USE_OPENSSL=1 USE_QUIC=1 USE_QUIC_OPENSSL_COMPAT=1
|
$ make TARGET=generic USE_OPENSSL=1 USE_QUIC=1 USE_QUIC_OPENSSL_COMPAT=1
|
||||||
|
|
||||||
In addition to this requirements, the QUIC listener bindings must be explicitly
|
Note that QUIC 0-RTT is not supported by haproxy QUIC stack when built against
|
||||||
enabled with a specific QUIC tuning parameter. (see "limited-quic" global
|
OpenSSL. In addition to this compilation requirements, the QUIC listener
|
||||||
parameter of haproxy Configuration Manual).
|
bindings must be explicitly enabled with a specific QUIC tuning parameter.
|
||||||
|
(see "limited-quic" global parameter of haproxy Configuration Manual).
|
||||||
|
|
||||||
|
|
||||||
5) How to build HAProxy
|
5) How to build HAProxy
|
||||||
|
|
@ -563,9 +554,9 @@ It goes into more details with the main options.
|
||||||
To build haproxy, you have to choose your target OS amongst the following ones
|
To build haproxy, you have to choose your target OS amongst the following ones
|
||||||
and assign it to the TARGET variable :
|
and assign it to the TARGET variable :
|
||||||
|
|
||||||
- linux-glibc for Linux kernel 4.17 and above
|
- linux-glibc for Linux kernel 2.6.28 and above
|
||||||
- linux-glibc-legacy for Linux kernel 2.6.28 and above without new features
|
- linux-glibc-legacy for Linux kernel 2.6.28 and above without new features
|
||||||
- linux-musl for Linux kernel 4.17 and above with musl libc
|
- linux-musl for Linux kernel 2.6.28 and above with musl libc
|
||||||
- solaris for Solaris 10 and above
|
- solaris for Solaris 10 and above
|
||||||
- freebsd for FreeBSD 10 and above
|
- freebsd for FreeBSD 10 and above
|
||||||
- dragonfly for DragonFlyBSD 4.3 and above
|
- dragonfly for DragonFlyBSD 4.3 and above
|
||||||
|
|
|
||||||
87
Makefile
87
Makefile
|
|
@ -35,7 +35,6 @@
|
||||||
# USE_OPENSSL : enable use of OpenSSL. Recommended, but see below.
|
# USE_OPENSSL : enable use of OpenSSL. Recommended, but see below.
|
||||||
# USE_OPENSSL_AWSLC : enable use of AWS-LC
|
# USE_OPENSSL_AWSLC : enable use of AWS-LC
|
||||||
# USE_OPENSSL_WOLFSSL : enable use of wolfSSL with the OpenSSL API
|
# USE_OPENSSL_WOLFSSL : enable use of wolfSSL with the OpenSSL API
|
||||||
# USE_ECH : enable use of ECH with the OpenSSL API
|
|
||||||
# USE_QUIC : enable use of QUIC with the quictls API (quictls, libressl, boringssl)
|
# USE_QUIC : enable use of QUIC with the quictls API (quictls, libressl, boringssl)
|
||||||
# USE_QUIC_OPENSSL_COMPAT : enable use of QUIC with the standard openssl API (limited features)
|
# USE_QUIC_OPENSSL_COMPAT : enable use of QUIC with the standard openssl API (limited features)
|
||||||
# USE_ENGINE : enable use of OpenSSL Engine.
|
# USE_ENGINE : enable use of OpenSSL Engine.
|
||||||
|
|
@ -60,12 +59,9 @@
|
||||||
# USE_OBSOLETE_LINKER : use when the linker fails to emit __start_init/__stop_init
|
# USE_OBSOLETE_LINKER : use when the linker fails to emit __start_init/__stop_init
|
||||||
# USE_THREAD_DUMP : use the more advanced thread state dump system. Automatic.
|
# USE_THREAD_DUMP : use the more advanced thread state dump system. Automatic.
|
||||||
# USE_OT : enable the OpenTracing filter
|
# USE_OT : enable the OpenTracing filter
|
||||||
# USE_OTEL : enable the OpenTelemetry filter
|
|
||||||
# USE_MEMORY_PROFILING : enable the memory profiler. Linux-glibc only.
|
# USE_MEMORY_PROFILING : enable the memory profiler. Linux-glibc only.
|
||||||
# USE_LIBATOMIC : force to link with/without libatomic. Automatic.
|
# USE_LIBATOMIC : force to link with/without libatomic. Automatic.
|
||||||
# USE_PTHREAD_EMULATION : replace pthread's rwlocks with ours
|
# USE_PTHREAD_EMULATION : replace pthread's rwlocks with ours
|
||||||
# USE_SHM_OPEN : use shm_open() for features that can make use of shared memory
|
|
||||||
# USE_KTLS : use kTLS.(requires at least Linux 4.17).
|
|
||||||
#
|
#
|
||||||
# Options can be forced by specifying "USE_xxx=1" or can be disabled by using
|
# Options can be forced by specifying "USE_xxx=1" or can be disabled by using
|
||||||
# "USE_xxx=" (empty string). The list of enabled and disabled options for a
|
# "USE_xxx=" (empty string). The list of enabled and disabled options for a
|
||||||
|
|
@ -129,11 +125,6 @@
|
||||||
# OT_LIB : force the lib path to libopentracing-c-wrapper
|
# OT_LIB : force the lib path to libopentracing-c-wrapper
|
||||||
# OT_RUNPATH : add RUNPATH for libopentracing-c-wrapper to haproxy executable
|
# OT_RUNPATH : add RUNPATH for libopentracing-c-wrapper to haproxy executable
|
||||||
# OT_USE_VARS : allows the use of variables for the OpenTracing context
|
# OT_USE_VARS : allows the use of variables for the OpenTracing context
|
||||||
# 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 RUNPATH for libopentelemetry-c-wrapper to haproxy executable
|
|
||||||
# OTEL_USE_VARS : allows the use of variables for the OpenTelemetry context
|
|
||||||
# IGNOREGIT : ignore GIT commit versions if set.
|
# IGNOREGIT : ignore GIT commit versions if set.
|
||||||
# VERSION : force haproxy version reporting.
|
# VERSION : force haproxy version reporting.
|
||||||
# SUBVERS : add a sub-version (eg: platform, model, ...).
|
# SUBVERS : add a sub-version (eg: platform, model, ...).
|
||||||
|
|
@ -220,8 +211,7 @@ UNIT_TEST_SCRIPT=./scripts/run-unittests.sh
|
||||||
# undefined behavior to silently produce invalid code. For this reason we have
|
# undefined behavior to silently produce invalid code. For this reason we have
|
||||||
# to use -fwrapv or -fno-strict-overflow to guarantee the intended behavior.
|
# to use -fwrapv or -fno-strict-overflow to guarantee the intended behavior.
|
||||||
# It is preferable not to change this option in order to avoid breakage.
|
# It is preferable not to change this option in order to avoid breakage.
|
||||||
STD_CFLAGS := $(call cc-opt-alt,-fwrapv,-fno-strict-overflow) \
|
STD_CFLAGS := $(call cc-opt-alt,-fwrapv,-fno-strict-overflow)
|
||||||
$(call cc-opt,-fvect-cost-model=very-cheap)
|
|
||||||
|
|
||||||
#### Compiler-specific flags to enable certain classes of warnings.
|
#### Compiler-specific flags to enable certain classes of warnings.
|
||||||
# Some are hard-coded, others are enabled only if supported.
|
# Some are hard-coded, others are enabled only if supported.
|
||||||
|
|
@ -348,16 +338,14 @@ use_opts = USE_EPOLL USE_KQUEUE USE_NETFILTER USE_POLL \
|
||||||
USE_TPROXY USE_LINUX_TPROXY USE_LINUX_CAP \
|
USE_TPROXY USE_LINUX_TPROXY USE_LINUX_CAP \
|
||||||
USE_LINUX_SPLICE USE_LIBCRYPT USE_CRYPT_H USE_ENGINE \
|
USE_LINUX_SPLICE USE_LIBCRYPT USE_CRYPT_H USE_ENGINE \
|
||||||
USE_GETADDRINFO USE_OPENSSL USE_OPENSSL_WOLFSSL USE_OPENSSL_AWSLC \
|
USE_GETADDRINFO USE_OPENSSL USE_OPENSSL_WOLFSSL USE_OPENSSL_AWSLC \
|
||||||
USE_ECH \
|
|
||||||
USE_SSL USE_LUA USE_ACCEPT4 USE_CLOSEFROM USE_ZLIB USE_SLZ \
|
USE_SSL USE_LUA USE_ACCEPT4 USE_CLOSEFROM USE_ZLIB USE_SLZ \
|
||||||
USE_CPU_AFFINITY USE_TFO USE_NS USE_DL USE_RT USE_LIBATOMIC \
|
USE_CPU_AFFINITY USE_TFO USE_NS USE_DL USE_RT USE_LIBATOMIC \
|
||||||
USE_MATH USE_DEVICEATLAS USE_51DEGREES \
|
USE_MATH USE_DEVICEATLAS USE_51DEGREES \
|
||||||
USE_WURFL USE_OBSOLETE_LINKER USE_PRCTL USE_PROCCTL \
|
USE_WURFL USE_OBSOLETE_LINKER USE_PRCTL USE_PROCCTL \
|
||||||
USE_THREAD_DUMP USE_EVPORTS USE_OT USE_OTEL USE_QUIC USE_PROMEX \
|
USE_THREAD_DUMP USE_EVPORTS USE_OT USE_QUIC USE_PROMEX \
|
||||||
USE_MEMORY_PROFILING USE_SHM_OPEN \
|
USE_MEMORY_PROFILING \
|
||||||
USE_STATIC_PCRE USE_STATIC_PCRE2 \
|
USE_STATIC_PCRE USE_STATIC_PCRE2 \
|
||||||
USE_PCRE USE_PCRE_JIT USE_PCRE2 USE_PCRE2_JIT \
|
USE_PCRE USE_PCRE_JIT USE_PCRE2 USE_PCRE2_JIT USE_QUIC_OPENSSL_COMPAT
|
||||||
USE_QUIC_OPENSSL_COMPAT USE_KTLS
|
|
||||||
|
|
||||||
# preset all variables for all supported build options among use_opts
|
# preset all variables for all supported build options among use_opts
|
||||||
$(reset_opts_vars)
|
$(reset_opts_vars)
|
||||||
|
|
@ -388,13 +376,13 @@ ifeq ($(TARGET),haiku)
|
||||||
set_target_defaults = $(call default_opts,USE_POLL USE_TPROXY USE_OBSOLETE_LINKER)
|
set_target_defaults = $(call default_opts,USE_POLL USE_TPROXY USE_OBSOLETE_LINKER)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# For linux >= 4.17 and glibc
|
# For linux >= 2.6.28 and glibc
|
||||||
ifeq ($(TARGET),linux-glibc)
|
ifeq ($(TARGET),linux-glibc)
|
||||||
set_target_defaults = $(call default_opts, \
|
set_target_defaults = $(call default_opts, \
|
||||||
USE_POLL USE_TPROXY USE_LIBCRYPT USE_DL USE_RT USE_CRYPT_H USE_NETFILTER \
|
USE_POLL USE_TPROXY USE_LIBCRYPT USE_DL USE_RT USE_CRYPT_H USE_NETFILTER \
|
||||||
USE_CPU_AFFINITY USE_THREAD USE_EPOLL USE_LINUX_TPROXY USE_LINUX_CAP \
|
USE_CPU_AFFINITY USE_THREAD USE_EPOLL USE_LINUX_TPROXY USE_LINUX_CAP \
|
||||||
USE_ACCEPT4 USE_LINUX_SPLICE USE_PRCTL USE_THREAD_DUMP USE_NS USE_TFO \
|
USE_ACCEPT4 USE_LINUX_SPLICE USE_PRCTL USE_THREAD_DUMP USE_NS USE_TFO \
|
||||||
USE_GETADDRINFO USE_BACKTRACE USE_SHM_OPEN USE_KTLS)
|
USE_GETADDRINFO USE_BACKTRACE)
|
||||||
INSTALL = install -v
|
INSTALL = install -v
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
@ -407,13 +395,13 @@ ifeq ($(TARGET),linux-glibc-legacy)
|
||||||
INSTALL = install -v
|
INSTALL = install -v
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# For linux >= 4.17 and musl
|
# For linux >= 2.6.28 and musl
|
||||||
ifeq ($(TARGET),linux-musl)
|
ifeq ($(TARGET),linux-musl)
|
||||||
set_target_defaults = $(call default_opts, \
|
set_target_defaults = $(call default_opts, \
|
||||||
USE_POLL USE_TPROXY USE_LIBCRYPT USE_DL USE_RT USE_CRYPT_H USE_NETFILTER \
|
USE_POLL USE_TPROXY USE_LIBCRYPT USE_DL USE_RT USE_CRYPT_H USE_NETFILTER \
|
||||||
USE_CPU_AFFINITY USE_THREAD USE_EPOLL USE_LINUX_TPROXY USE_LINUX_CAP \
|
USE_CPU_AFFINITY USE_THREAD USE_EPOLL USE_LINUX_TPROXY USE_LINUX_CAP \
|
||||||
USE_ACCEPT4 USE_LINUX_SPLICE USE_PRCTL USE_THREAD_DUMP USE_NS USE_TFO \
|
USE_ACCEPT4 USE_LINUX_SPLICE USE_PRCTL USE_THREAD_DUMP USE_NS USE_TFO \
|
||||||
USE_GETADDRINFO USE_BACKTRACE USE_SHM_OPEN USE_KTLS)
|
USE_GETADDRINFO USE_BACKTRACE)
|
||||||
INSTALL = install -v
|
INSTALL = install -v
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
@ -607,10 +595,6 @@ ifneq ($(USE_BACKTRACE:0=),)
|
||||||
BACKTRACE_CFLAGS = -fno-omit-frame-pointer
|
BACKTRACE_CFLAGS = -fno-omit-frame-pointer
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(USE_MEMORY_PROFILING:0=),)
|
|
||||||
MEMORY_PROFILING_CFLAGS = -fno-optimize-sibling-calls
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq ($(USE_CPU_AFFINITY:0=),)
|
ifneq ($(USE_CPU_AFFINITY:0=),)
|
||||||
OPTIONS_OBJS += src/cpuset.o
|
OPTIONS_OBJS += src/cpuset.o
|
||||||
OPTIONS_OBJS += src/cpu_topo.o
|
OPTIONS_OBJS += src/cpu_topo.o
|
||||||
|
|
@ -649,7 +633,7 @@ ifneq ($(USE_OPENSSL:0=),)
|
||||||
OPTIONS_OBJS += src/ssl_sock.o src/ssl_ckch.o src/ssl_ocsp.o src/ssl_crtlist.o \
|
OPTIONS_OBJS += src/ssl_sock.o src/ssl_ckch.o src/ssl_ocsp.o src/ssl_crtlist.o \
|
||||||
src/ssl_sample.o src/cfgparse-ssl.o src/ssl_gencert.o \
|
src/ssl_sample.o src/cfgparse-ssl.o src/ssl_gencert.o \
|
||||||
src/ssl_utils.o src/jwt.o src/ssl_clienthello.o src/jws.o src/acme.o \
|
src/ssl_utils.o src/jwt.o src/ssl_clienthello.o src/jws.o src/acme.o \
|
||||||
src/acme_resolvers.o src/ssl_trace.o src/jwe.o
|
src/ssl_trace.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(USE_ENGINE:0=),)
|
ifneq ($(USE_ENGINE:0=),)
|
||||||
|
|
@ -676,7 +660,7 @@ OPTIONS_OBJS += src/mux_quic.o src/h3.o src/quic_rx.o src/quic_tx.o \
|
||||||
src/quic_cc_nocc.o src/quic_cc.o src/quic_pacing.o \
|
src/quic_cc_nocc.o src/quic_cc.o src/quic_pacing.o \
|
||||||
src/h3_stats.o src/quic_stats.o src/qpack-enc.o \
|
src/h3_stats.o src/quic_stats.o src/qpack-enc.o \
|
||||||
src/qpack-tbl.o src/quic_cc_drs.o src/quic_fctl.o \
|
src/qpack-tbl.o src/quic_cc_drs.o src/quic_fctl.o \
|
||||||
src/quic_enc.o src/mux_quic_qstrm.o src/xprt_qstrm.o
|
src/cbuf.o src/quic_enc.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(USE_QUIC_OPENSSL_COMPAT:0=),)
|
ifneq ($(USE_QUIC_OPENSSL_COMPAT:0=),)
|
||||||
|
|
@ -868,10 +852,6 @@ ifneq ($(USE_OT:0=),)
|
||||||
include addons/ot/Makefile
|
include addons/ot/Makefile
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(USE_OTEL:0=),)
|
|
||||||
include addons/otel/Makefile
|
|
||||||
endif
|
|
||||||
|
|
||||||
# better keep this one close to the end, as several libs above may need it
|
# better keep this one close to the end, as several libs above may need it
|
||||||
ifneq ($(USE_DL:0=),)
|
ifneq ($(USE_DL:0=),)
|
||||||
DL_LDFLAGS = -ldl
|
DL_LDFLAGS = -ldl
|
||||||
|
|
@ -966,7 +946,6 @@ endif # obsolete targets
|
||||||
endif # TARGET
|
endif # TARGET
|
||||||
|
|
||||||
OBJS =
|
OBJS =
|
||||||
HATERM_OBJS =
|
|
||||||
|
|
||||||
ifneq ($(EXTRA_OBJS),)
|
ifneq ($(EXTRA_OBJS),)
|
||||||
OBJS += $(EXTRA_OBJS)
|
OBJS += $(EXTRA_OBJS)
|
||||||
|
|
@ -980,15 +959,15 @@ OBJS += src/mux_h2.o src/mux_h1.o src/mux_fcgi.o src/log.o \
|
||||||
src/cache.o src/stconn.o src/http_htx.o src/debug.o \
|
src/cache.o src/stconn.o src/http_htx.o src/debug.o \
|
||||||
src/check.o src/stats-html.o src/haproxy.o src/listener.o \
|
src/check.o src/stats-html.o src/haproxy.o src/listener.o \
|
||||||
src/applet.o src/pattern.o src/cfgparse-listen.o \
|
src/applet.o src/pattern.o src/cfgparse-listen.o \
|
||||||
src/flt_spoe.o src/cebis_tree.o src/http_ext.o \
|
src/flt_spoe.o src/cebuis_tree.o src/http_ext.o \
|
||||||
src/http_act.o src/http_fetch.o src/cebs_tree.o \
|
src/http_act.o src/http_fetch.o src/cebus_tree.o \
|
||||||
src/cebib_tree.o src/http_client.o src/dns.o \
|
src/cebuib_tree.o src/http_client.o src/dns.o \
|
||||||
src/cebb_tree.o src/vars.o src/event_hdl.o src/tcp_rules.o \
|
src/cebub_tree.o src/vars.o src/event_hdl.o src/tcp_rules.o \
|
||||||
src/trace.o src/stats-proxy.o src/pool.o src/stats.o \
|
src/trace.o src/stats-proxy.o src/pool.o src/stats.o \
|
||||||
src/cfgparse-global.o src/filters.o src/mux_pt.o \
|
src/cfgparse-global.o src/filters.o src/mux_pt.o \
|
||||||
src/flt_http_comp.o src/sock.o src/h1.o src/sink.o \
|
src/flt_http_comp.o src/sock.o src/h1.o src/sink.o \
|
||||||
src/ceba_tree.o src/session.o src/payload.o src/htx.o \
|
src/cebua_tree.o src/session.o src/payload.o src/htx.o \
|
||||||
src/cebl_tree.o src/ceb32_tree.o src/ceb64_tree.o \
|
src/cebul_tree.o src/cebu32_tree.o src/cebu64_tree.o \
|
||||||
src/server_state.o src/proto_rhttp.o src/flt_trace.o src/fd.o \
|
src/server_state.o src/proto_rhttp.o src/flt_trace.o src/fd.o \
|
||||||
src/task.o src/map.o src/fcgi-app.o src/h2.o src/mworker.o \
|
src/task.o src/map.o src/fcgi-app.o src/h2.o src/mworker.o \
|
||||||
src/tcp_sample.o src/mjson.o src/h1_htx.o src/tcp_act.o \
|
src/tcp_sample.o src/mjson.o src/h1_htx.o src/tcp_act.o \
|
||||||
|
|
@ -1003,9 +982,9 @@ OBJS += src/mux_h2.o src/mux_h1.o src/mux_fcgi.o src/log.o \
|
||||||
src/cfgcond.o src/proto_udp.o src/lb_fwlc.o src/ebmbtree.o \
|
src/cfgcond.o src/proto_udp.o src/lb_fwlc.o src/ebmbtree.o \
|
||||||
src/proto_uxdg.o src/cfgdiag.o src/sock_unix.o src/sha1.o \
|
src/proto_uxdg.o src/cfgdiag.o src/sock_unix.o src/sha1.o \
|
||||||
src/lb_fas.o src/clock.o src/sock_inet.o src/ev_select.o \
|
src/lb_fas.o src/clock.o src/sock_inet.o src/ev_select.o \
|
||||||
src/lb_map.o src/shctx.o src/hpack-dec.o src/net_helper.o \
|
src/lb_map.o src/shctx.o src/mworker-prog.o src/hpack-dec.o \
|
||||||
src/arg.o src/signal.o src/fix.o src/dynbuf.o src/guid.o \
|
src/arg.o src/signal.o src/fix.o src/dynbuf.o src/guid.o \
|
||||||
src/cfgparse-tcp.o src/lb_ss.o src/chunk.o src/counters.o \
|
src/cfgparse-tcp.o src/lb_ss.o src/chunk.o \
|
||||||
src/cfgparse-unix.o src/regex.o src/fcgi.o src/uri_auth.o \
|
src/cfgparse-unix.o src/regex.o src/fcgi.o src/uri_auth.o \
|
||||||
src/eb64tree.o src/eb32tree.o src/eb32sctree.o src/lru.o \
|
src/eb64tree.o src/eb32tree.o src/eb32sctree.o src/lru.o \
|
||||||
src/limits.o src/ebimtree.o src/wdt.o src/hpack-tbl.o \
|
src/limits.o src/ebimtree.o src/wdt.o src/hpack-tbl.o \
|
||||||
|
|
@ -1013,15 +992,12 @@ OBJS += src/mux_h2.o src/mux_h1.o src/mux_fcgi.o src/log.o \
|
||||||
src/ebsttree.o src/freq_ctr.o src/systemd.o src/init.o \
|
src/ebsttree.o src/freq_ctr.o src/systemd.o src/init.o \
|
||||||
src/http_acl.o src/dict.o src/dgram.o src/pipe.o \
|
src/http_acl.o src/dict.o src/dgram.o src/pipe.o \
|
||||||
src/hpack-huff.o src/hpack-enc.o src/ebtree.o src/hash.o \
|
src/hpack-huff.o src/hpack-enc.o src/ebtree.o src/hash.o \
|
||||||
src/httpclient_cli.o src/version.o src/ncbmbuf.o src/ech.o \
|
src/version.o
|
||||||
src/cfgparse-peers.o src/haterm.o
|
|
||||||
|
|
||||||
ifneq ($(TRACE),)
|
ifneq ($(TRACE),)
|
||||||
OBJS += src/calltrace.o
|
OBJS += src/calltrace.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
HATERM_OBJS += $(OBJS) src/haterm_init.o
|
|
||||||
|
|
||||||
# Used only for forced dependency checking. May be cleared during development.
|
# Used only for forced dependency checking. May be cleared during development.
|
||||||
INCLUDES = $(wildcard include/*/*.h)
|
INCLUDES = $(wildcard include/*/*.h)
|
||||||
DEP = $(INCLUDES) .build_opts
|
DEP = $(INCLUDES) .build_opts
|
||||||
|
|
@ -1053,7 +1029,7 @@ IGNORE_OPTS=help install install-man install-doc install-bin \
|
||||||
uninstall clean tags cscope tar git-tar version update-version \
|
uninstall clean tags cscope tar git-tar version update-version \
|
||||||
opts reg-tests reg-tests-help unit-tests admin/halog/halog dev/flags/flags \
|
opts reg-tests reg-tests-help unit-tests admin/halog/halog dev/flags/flags \
|
||||||
dev/haring/haring dev/ncpu/ncpu dev/poll/poll dev/tcploop/tcploop \
|
dev/haring/haring dev/ncpu/ncpu dev/poll/poll dev/tcploop/tcploop \
|
||||||
dev/term_events/term_events dev/gdb/pm-from-core dev/gdb/libs-from-core
|
dev/term_events/term_events
|
||||||
|
|
||||||
ifneq ($(TARGET),)
|
ifneq ($(TARGET),)
|
||||||
ifeq ($(filter $(firstword $(MAKECMDGOALS)),$(IGNORE_OPTS)),)
|
ifeq ($(filter $(firstword $(MAKECMDGOALS)),$(IGNORE_OPTS)),)
|
||||||
|
|
@ -1069,9 +1045,6 @@ endif # non-empty target
|
||||||
haproxy: $(OPTIONS_OBJS) $(OBJS)
|
haproxy: $(OPTIONS_OBJS) $(OBJS)
|
||||||
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
||||||
|
|
||||||
haterm: $(OPTIONS_OBJS) $(HATERM_OBJS)
|
|
||||||
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
|
||||||
|
|
||||||
objsize: haproxy
|
objsize: haproxy
|
||||||
$(Q)objdump -t $^|grep ' g '|grep -F '.text'|awk '{print $$5 FS $$6}'|sort
|
$(Q)objdump -t $^|grep ' g '|grep -F '.text'|awk '{print $$5 FS $$6}'|sort
|
||||||
|
|
||||||
|
|
@ -1087,12 +1060,6 @@ admin/dyncookie/dyncookie: admin/dyncookie/dyncookie.o
|
||||||
dev/flags/flags: dev/flags/flags.o
|
dev/flags/flags: dev/flags/flags.o
|
||||||
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
||||||
|
|
||||||
dev/gdb/libs-from-core: dev/gdb/libs-from-core.o
|
|
||||||
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
|
||||||
|
|
||||||
dev/gdb/pm-from-core: dev/gdb/pm-from-core.o
|
|
||||||
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
|
||||||
|
|
||||||
dev/haring/haring: dev/haring/haring.o
|
dev/haring/haring: dev/haring/haring.o
|
||||||
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
|
||||||
|
|
||||||
|
|
@ -1146,11 +1113,6 @@ install-doc:
|
||||||
$(INSTALL) -m 644 doc/$$x.txt "$(DESTDIR)$(DOCDIR)" ; \
|
$(INSTALL) -m 644 doc/$$x.txt "$(DESTDIR)$(DOCDIR)" ; \
|
||||||
done
|
done
|
||||||
|
|
||||||
install-admin:
|
|
||||||
$(Q)$(INSTALL) -d "$(DESTDIR)$(SBINDIR)"
|
|
||||||
$(Q)$(INSTALL) admin/cli/haproxy-dump-certs "$(DESTDIR)$(SBINDIR)"
|
|
||||||
$(Q)$(INSTALL) admin/cli/haproxy-reload "$(DESTDIR)$(SBINDIR)"
|
|
||||||
|
|
||||||
install-bin:
|
install-bin:
|
||||||
$(Q)for i in haproxy $(EXTRA); do \
|
$(Q)for i in haproxy $(EXTRA); do \
|
||||||
if ! [ -e "$$i" ]; then \
|
if ! [ -e "$$i" ]; then \
|
||||||
|
|
@ -1161,7 +1123,7 @@ install-bin:
|
||||||
$(Q)$(INSTALL) -d "$(DESTDIR)$(SBINDIR)"
|
$(Q)$(INSTALL) -d "$(DESTDIR)$(SBINDIR)"
|
||||||
$(Q)$(INSTALL) haproxy $(EXTRA) "$(DESTDIR)$(SBINDIR)"
|
$(Q)$(INSTALL) haproxy $(EXTRA) "$(DESTDIR)$(SBINDIR)"
|
||||||
|
|
||||||
install: install-bin install-admin install-man install-doc
|
install: install-bin install-man install-doc
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
$(Q)rm -f "$(DESTDIR)$(MANDIR)"/man1/haproxy.1
|
$(Q)rm -f "$(DESTDIR)$(MANDIR)"/man1/haproxy.1
|
||||||
|
|
@ -1180,7 +1142,6 @@ clean:
|
||||||
$(Q)rm -f addons/51degrees/*.[oas] addons/51degrees/dummy/*.[oas] addons/51degrees/dummy/*/*.[oas]
|
$(Q)rm -f addons/51degrees/*.[oas] addons/51degrees/dummy/*.[oas] addons/51degrees/dummy/*/*.[oas]
|
||||||
$(Q)rm -f addons/deviceatlas/*.[oas] addons/deviceatlas/dummy/*.[oas] addons/deviceatlas/dummy/*.o
|
$(Q)rm -f addons/deviceatlas/*.[oas] addons/deviceatlas/dummy/*.[oas] addons/deviceatlas/dummy/*.o
|
||||||
$(Q)rm -f addons/deviceatlas/dummy/Os/*.o
|
$(Q)rm -f addons/deviceatlas/dummy/Os/*.o
|
||||||
$(Q)rm -f addons/otel/src/*.[oas]
|
|
||||||
$(Q)rm -f addons/ot/src/*.[oas]
|
$(Q)rm -f addons/ot/src/*.[oas]
|
||||||
$(Q)rm -f addons/wurfl/*.[oas] addons/wurfl/dummy/*.[oas]
|
$(Q)rm -f addons/wurfl/*.[oas] addons/wurfl/dummy/*.[oas]
|
||||||
$(Q)rm -f admin/*/*.[oas] admin/*/*/*.[oas]
|
$(Q)rm -f admin/*/*.[oas] admin/*/*/*.[oas]
|
||||||
|
|
@ -1192,7 +1153,7 @@ distclean: clean
|
||||||
$(Q)rm -f admin/dyncookie/dyncookie
|
$(Q)rm -f admin/dyncookie/dyncookie
|
||||||
$(Q)rm -f dev/haring/haring dev/ncpu/ncpu{,.so} dev/poll/poll dev/tcploop/tcploop
|
$(Q)rm -f dev/haring/haring dev/ncpu/ncpu{,.so} dev/poll/poll dev/tcploop/tcploop
|
||||||
$(Q)rm -f dev/hpack/decode dev/hpack/gen-enc dev/hpack/gen-rht
|
$(Q)rm -f dev/hpack/decode dev/hpack/gen-enc dev/hpack/gen-rht
|
||||||
$(Q)rm -f dev/qpack/decode dev/gdb/pm-from-core dev/gdb/libs-from-core
|
$(Q)rm -f dev/qpack/decode
|
||||||
|
|
||||||
tags:
|
tags:
|
||||||
$(Q)find src include \( -name '*.c' -o -name '*.h' \) -print0 | \
|
$(Q)find src include \( -name '*.c' -o -name '*.h' \) -print0 | \
|
||||||
|
|
@ -1319,8 +1280,6 @@ unit-tests:
|
||||||
# options for all commits within RANGE. RANGE may be either a git range
|
# options for all commits within RANGE. RANGE may be either a git range
|
||||||
# such as ref1..ref2 or a single commit, in which case all commits from
|
# such as ref1..ref2 or a single commit, in which case all commits from
|
||||||
# the master branch to this one will be tested.
|
# the master branch to this one will be tested.
|
||||||
# Will execute TEST_CMD for each commit if defined, and will stop in case of
|
|
||||||
# failure.
|
|
||||||
|
|
||||||
range:
|
range:
|
||||||
$(Q)[ -d .git/. ] || { echo "## Fatal: \"make $@\" may only be used inside a Git repository."; exit 1; }
|
$(Q)[ -d .git/. ] || { echo "## Fatal: \"make $@\" may only be used inside a Git repository."; exit 1; }
|
||||||
|
|
@ -1346,8 +1305,6 @@ range:
|
||||||
echo "[ $$index/$$count ] $$commit #############################"; \
|
echo "[ $$index/$$count ] $$commit #############################"; \
|
||||||
git checkout -q $$commit || die 1; \
|
git checkout -q $$commit || die 1; \
|
||||||
$(MAKE) all || die 1; \
|
$(MAKE) all || die 1; \
|
||||||
set -- $(TEST_CMD); \
|
|
||||||
[ "$$#" -eq 0 ] || "$$@" || die 1; \
|
|
||||||
index=$$((index + 1)); \
|
index=$$((index + 1)); \
|
||||||
done; \
|
done; \
|
||||||
echo;echo "Done! $${count} commit(s) built successfully for RANGE $${RANGE}" ; \
|
echo;echo "Done! $${count} commit(s) built successfully for RANGE $${RANGE}" ; \
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
[](https://github.com/haproxy/haproxy/actions/workflows/musl.yml)
|
[](https://github.com/haproxy/haproxy/actions/workflows/musl.yml)
|
||||||
[](https://github.com/haproxy/haproxy/actions/workflows/aws-lc.yml)
|
[](https://github.com/haproxy/haproxy/actions/workflows/aws-lc.yml)
|
||||||
|
[](https://github.com/haproxy/haproxy/actions/workflows/openssl-nodeprecated.yml)
|
||||||
[](https://github.com/haproxy/haproxy/actions/workflows/illumos.yml)
|
[](https://github.com/haproxy/haproxy/actions/workflows/illumos.yml)
|
||||||
[](https://github.com/haproxy/haproxy/actions/workflows/netbsd.yml)
|
[](https://github.com/haproxy/haproxy/actions/workflows/netbsd.yml)
|
||||||
[](https://cirrus-ci.com/github/haproxy/haproxy/)
|
[](https://cirrus-ci.com/github/haproxy/haproxy/)
|
||||||
|
|
|
||||||
2
VERDATE
2
VERDATE
|
|
@ -1,2 +1,2 @@
|
||||||
$Format:%ci$
|
$Format:%ci$
|
||||||
2026/04/03
|
2025/05/14
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
3.4-dev8
|
3.2-dev16
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@ CXX := c++
|
||||||
CXXLIB := -lstdc++
|
CXXLIB := -lstdc++
|
||||||
|
|
||||||
ifeq ($(DEVICEATLAS_SRC),)
|
ifeq ($(DEVICEATLAS_SRC),)
|
||||||
OPTIONS_CFLAGS += -I$(DEVICEATLAS_INC)
|
OPTIONS_LDFLAGS += -lda
|
||||||
OPTIONS_LDFLAGS += -Wl,-rpath,$(DEVICEATLAS_LIB) -L$(DEVICEATLAS_LIB) -lda
|
|
||||||
else
|
else
|
||||||
DEVICEATLAS_INC = $(DEVICEATLAS_SRC)
|
DEVICEATLAS_INC = $(DEVICEATLAS_SRC)
|
||||||
DEVICEATLAS_LIB = $(DEVICEATLAS_SRC)
|
DEVICEATLAS_LIB = $(DEVICEATLAS_SRC)
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ static struct {
|
||||||
da_atlas_t atlas;
|
da_atlas_t atlas;
|
||||||
da_evidence_id_t useragentid;
|
da_evidence_id_t useragentid;
|
||||||
da_severity_t loglevel;
|
da_severity_t loglevel;
|
||||||
size_t maxhdrlen;
|
|
||||||
char separator;
|
char separator;
|
||||||
unsigned char daset:1;
|
unsigned char daset:1;
|
||||||
} global_deviceatlas = {
|
} global_deviceatlas = {
|
||||||
|
|
@ -43,7 +42,6 @@ static struct {
|
||||||
.atlasmap = NULL,
|
.atlasmap = NULL,
|
||||||
.atlasfd = -1,
|
.atlasfd = -1,
|
||||||
.useragentid = 0,
|
.useragentid = 0,
|
||||||
.maxhdrlen = 0,
|
|
||||||
.daset = 0,
|
.daset = 0,
|
||||||
.separator = '|',
|
.separator = '|',
|
||||||
};
|
};
|
||||||
|
|
@ -59,10 +57,6 @@ static int da_json_file(char **args, int section_type, struct proxy *curpx,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
global_deviceatlas.jsonpath = strdup(args[1]);
|
global_deviceatlas.jsonpath = strdup(args[1]);
|
||||||
if (unlikely(global_deviceatlas.jsonpath == NULL)) {
|
|
||||||
memprintf(err, "deviceatlas json file : out of memory.\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,7 +73,6 @@ static int da_log_level(char **args, int section_type, struct proxy *curpx,
|
||||||
loglevel = atol(args[1]);
|
loglevel = atol(args[1]);
|
||||||
if (loglevel < 0 || loglevel > 3) {
|
if (loglevel < 0 || loglevel > 3) {
|
||||||
memprintf(err, "deviceatlas log level : expects a log level between 0 and 3, %s given.\n", args[1]);
|
memprintf(err, "deviceatlas log level : expects a log level between 0 and 3, %s given.\n", args[1]);
|
||||||
return -1;
|
|
||||||
} else {
|
} else {
|
||||||
global_deviceatlas.loglevel = (da_severity_t)loglevel;
|
global_deviceatlas.loglevel = (da_severity_t)loglevel;
|
||||||
}
|
}
|
||||||
|
|
@ -108,10 +101,6 @@ static int da_properties_cookie(char **args, int section_type, struct proxy *cur
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
global_deviceatlas.cookiename = strdup(args[1]);
|
global_deviceatlas.cookiename = strdup(args[1]);
|
||||||
if (unlikely(global_deviceatlas.cookiename == NULL)) {
|
|
||||||
memprintf(err, "deviceatlas cookie name : out of memory.\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
global_deviceatlas.cookienamelen = strlen(global_deviceatlas.cookiename);
|
global_deviceatlas.cookienamelen = strlen(global_deviceatlas.cookiename);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -130,7 +119,6 @@ static int da_cache_size(char **args, int section_type, struct proxy *curpx,
|
||||||
cachesize = atol(args[1]);
|
cachesize = atol(args[1]);
|
||||||
if (cachesize < 0 || cachesize > DA_CACHE_MAX) {
|
if (cachesize < 0 || cachesize > DA_CACHE_MAX) {
|
||||||
memprintf(err, "deviceatlas cache size : expects a cache size between 0 and %d, %s given.\n", DA_CACHE_MAX, args[1]);
|
memprintf(err, "deviceatlas cache size : expects a cache size between 0 and %d, %s given.\n", DA_CACHE_MAX, args[1]);
|
||||||
return -1;
|
|
||||||
} else {
|
} else {
|
||||||
#ifdef APINOCACHE
|
#ifdef APINOCACHE
|
||||||
fprintf(stdout, "deviceatlas cache size : no-op, its support is disabled.\n");
|
fprintf(stdout, "deviceatlas cache size : no-op, its support is disabled.\n");
|
||||||
|
|
@ -177,7 +165,7 @@ static int init_deviceatlas(void)
|
||||||
da_status_t status;
|
da_status_t status;
|
||||||
|
|
||||||
jsonp = fopen(global_deviceatlas.jsonpath, "r");
|
jsonp = fopen(global_deviceatlas.jsonpath, "r");
|
||||||
if (unlikely(jsonp == 0)) {
|
if (jsonp == 0) {
|
||||||
ha_alert("deviceatlas : '%s' json file has invalid path or is not readable.\n",
|
ha_alert("deviceatlas : '%s' json file has invalid path or is not readable.\n",
|
||||||
global_deviceatlas.jsonpath);
|
global_deviceatlas.jsonpath);
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
|
@ -189,11 +177,9 @@ static int init_deviceatlas(void)
|
||||||
status = da_atlas_compile(jsonp, da_haproxy_read, da_haproxy_seek,
|
status = da_atlas_compile(jsonp, da_haproxy_read, da_haproxy_seek,
|
||||||
&global_deviceatlas.atlasimgptr, &atlasimglen);
|
&global_deviceatlas.atlasimgptr, &atlasimglen);
|
||||||
fclose(jsonp);
|
fclose(jsonp);
|
||||||
if (unlikely(status != DA_OK)) {
|
if (status != DA_OK) {
|
||||||
ha_alert("deviceatlas : '%s' json file is invalid.\n",
|
ha_alert("deviceatlas : '%s' json file is invalid.\n",
|
||||||
global_deviceatlas.jsonpath);
|
global_deviceatlas.jsonpath);
|
||||||
free(global_deviceatlas.atlasimgptr);
|
|
||||||
da_fini();
|
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
@ -201,10 +187,8 @@ static int init_deviceatlas(void)
|
||||||
status = da_atlas_open(&global_deviceatlas.atlas, extraprops,
|
status = da_atlas_open(&global_deviceatlas.atlas, extraprops,
|
||||||
global_deviceatlas.atlasimgptr, atlasimglen);
|
global_deviceatlas.atlasimgptr, atlasimglen);
|
||||||
|
|
||||||
if (unlikely(status != DA_OK)) {
|
if (status != DA_OK) {
|
||||||
ha_alert("deviceatlas : data could not be compiled.\n");
|
ha_alert("deviceatlas : data could not be compiled.\n");
|
||||||
free(global_deviceatlas.atlasimgptr);
|
|
||||||
da_fini();
|
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
@ -213,28 +197,11 @@ static int init_deviceatlas(void)
|
||||||
|
|
||||||
if (global_deviceatlas.cookiename == 0) {
|
if (global_deviceatlas.cookiename == 0) {
|
||||||
global_deviceatlas.cookiename = strdup(DA_COOKIENAME_DEFAULT);
|
global_deviceatlas.cookiename = strdup(DA_COOKIENAME_DEFAULT);
|
||||||
if (unlikely(global_deviceatlas.cookiename == NULL)) {
|
|
||||||
ha_alert("deviceatlas : out of memory.\n");
|
|
||||||
da_atlas_close(&global_deviceatlas.atlas);
|
|
||||||
free(global_deviceatlas.atlasimgptr);
|
|
||||||
da_fini();
|
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
global_deviceatlas.cookienamelen = strlen(global_deviceatlas.cookiename);
|
global_deviceatlas.cookienamelen = strlen(global_deviceatlas.cookiename);
|
||||||
}
|
}
|
||||||
|
|
||||||
global_deviceatlas.useragentid = da_atlas_header_evidence_id(&global_deviceatlas.atlas,
|
global_deviceatlas.useragentid = da_atlas_header_evidence_id(&global_deviceatlas.atlas,
|
||||||
"user-agent");
|
"user-agent");
|
||||||
{
|
|
||||||
size_t hi;
|
|
||||||
global_deviceatlas.maxhdrlen = 16;
|
|
||||||
for (hi = 0; hi < global_deviceatlas.atlas.header_evidence_count; hi++) {
|
|
||||||
size_t nl = strlen(global_deviceatlas.atlas.header_priorities[hi].name);
|
|
||||||
if (nl > global_deviceatlas.maxhdrlen)
|
|
||||||
global_deviceatlas.maxhdrlen = nl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((global_deviceatlas.atlasfd = shm_open(ATLASMAPNM, O_RDWR, 0660)) != -1) {
|
if ((global_deviceatlas.atlasfd = shm_open(ATLASMAPNM, O_RDWR, 0660)) != -1) {
|
||||||
global_deviceatlas.atlasmap = mmap(NULL, ATLASTOKSZ, PROT_READ | PROT_WRITE, MAP_SHARED, global_deviceatlas.atlasfd, 0);
|
global_deviceatlas.atlasmap = mmap(NULL, ATLASTOKSZ, PROT_READ | PROT_WRITE, MAP_SHARED, global_deviceatlas.atlasfd, 0);
|
||||||
if (global_deviceatlas.atlasmap == MAP_FAILED) {
|
if (global_deviceatlas.atlasmap == MAP_FAILED) {
|
||||||
|
|
@ -264,22 +231,24 @@ static void deinit_deviceatlas(void)
|
||||||
free(global_deviceatlas.cookiename);
|
free(global_deviceatlas.cookiename);
|
||||||
da_atlas_close(&global_deviceatlas.atlas);
|
da_atlas_close(&global_deviceatlas.atlas);
|
||||||
free(global_deviceatlas.atlasimgptr);
|
free(global_deviceatlas.atlasimgptr);
|
||||||
da_fini();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (global_deviceatlas.atlasfd != -1) {
|
if (global_deviceatlas.atlasfd != -1) {
|
||||||
munmap(global_deviceatlas.atlasmap, ATLASTOKSZ);
|
munmap(global_deviceatlas.atlasmap, ATLASTOKSZ);
|
||||||
close(global_deviceatlas.atlasfd);
|
close(global_deviceatlas.atlasfd);
|
||||||
|
shm_unlink(ATLASMAPNM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
da_fini();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void da_haproxy_checkinst(void)
|
static void da_haproxy_checkinst(void)
|
||||||
{
|
{
|
||||||
if (global_deviceatlas.atlasmap != 0) {
|
if (global_deviceatlas.atlasmap != 0) {
|
||||||
char *base;
|
char *base;
|
||||||
base = (char *)global_deviceatlas.atlasmap;
|
base = (char *)global_deviceatlas.atlasmap;
|
||||||
|
|
||||||
if (base[0] != 0) {
|
if (base[0] != 0) {
|
||||||
FILE *jsonp;
|
FILE *jsonp;
|
||||||
void *cnew;
|
void *cnew;
|
||||||
da_status_t status;
|
da_status_t status;
|
||||||
|
|
@ -289,10 +258,6 @@ static void da_haproxy_checkinst(void)
|
||||||
da_property_decl_t extraprops[1] = {{NULL, 0}};
|
da_property_decl_t extraprops[1] = {{NULL, 0}};
|
||||||
#ifdef USE_THREAD
|
#ifdef USE_THREAD
|
||||||
HA_SPIN_LOCK(OTHER_LOCK, &dadwsch_lock);
|
HA_SPIN_LOCK(OTHER_LOCK, &dadwsch_lock);
|
||||||
if (base[0] == 0) {
|
|
||||||
HA_SPIN_UNLOCK(OTHER_LOCK, &dadwsch_lock);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
strlcpy2(atlasp, base + sizeof(char), sizeof(atlasp));
|
strlcpy2(atlasp, base + sizeof(char), sizeof(atlasp));
|
||||||
jsonp = fopen(atlasp, "r");
|
jsonp = fopen(atlasp, "r");
|
||||||
|
|
@ -310,20 +275,10 @@ static void da_haproxy_checkinst(void)
|
||||||
fclose(jsonp);
|
fclose(jsonp);
|
||||||
if (status == DA_OK) {
|
if (status == DA_OK) {
|
||||||
if (da_atlas_open(&inst, extraprops, cnew, atlassz) == DA_OK) {
|
if (da_atlas_open(&inst, extraprops, cnew, atlassz) == DA_OK) {
|
||||||
inst.config.cache_size = global_deviceatlas.cachesize;
|
|
||||||
da_atlas_close(&global_deviceatlas.atlas);
|
da_atlas_close(&global_deviceatlas.atlas);
|
||||||
free(global_deviceatlas.atlasimgptr);
|
free(global_deviceatlas.atlasimgptr);
|
||||||
global_deviceatlas.atlasimgptr = cnew;
|
global_deviceatlas.atlasimgptr = cnew;
|
||||||
global_deviceatlas.atlas = inst;
|
global_deviceatlas.atlas = inst;
|
||||||
{
|
|
||||||
size_t hi;
|
|
||||||
global_deviceatlas.maxhdrlen = 16;
|
|
||||||
for (hi = 0; hi < inst.header_evidence_count; hi++) {
|
|
||||||
size_t nl = strlen(inst.header_priorities[hi].name);
|
|
||||||
if (nl > global_deviceatlas.maxhdrlen)
|
|
||||||
global_deviceatlas.maxhdrlen = nl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
base[0] = 0;
|
base[0] = 0;
|
||||||
ha_notice("deviceatlas : new instance, data file date `%s`.\n",
|
ha_notice("deviceatlas : new instance, data file date `%s`.\n",
|
||||||
da_getdatacreationiso8601(&global_deviceatlas.atlas));
|
da_getdatacreationiso8601(&global_deviceatlas.atlas));
|
||||||
|
|
@ -331,8 +286,6 @@ static void da_haproxy_checkinst(void)
|
||||||
ha_alert("deviceatlas : instance update failed.\n");
|
ha_alert("deviceatlas : instance update failed.\n");
|
||||||
free(cnew);
|
free(cnew);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
free(cnew);
|
|
||||||
}
|
}
|
||||||
#ifdef USE_THREAD
|
#ifdef USE_THREAD
|
||||||
HA_SPIN_UNLOCK(OTHER_LOCK, &dadwsch_lock);
|
HA_SPIN_UNLOCK(OTHER_LOCK, &dadwsch_lock);
|
||||||
|
|
@ -344,7 +297,7 @@ static void da_haproxy_checkinst(void)
|
||||||
static int da_haproxy(const struct arg *args, struct sample *smp, da_deviceinfo_t *devinfo)
|
static int da_haproxy(const struct arg *args, struct sample *smp, da_deviceinfo_t *devinfo)
|
||||||
{
|
{
|
||||||
struct buffer *tmp;
|
struct buffer *tmp;
|
||||||
da_propid_t prop;
|
da_propid_t prop, *pprop;
|
||||||
da_status_t status;
|
da_status_t status;
|
||||||
da_type_t proptype;
|
da_type_t proptype;
|
||||||
const char *propname;
|
const char *propname;
|
||||||
|
|
@ -364,15 +317,13 @@ static int da_haproxy(const struct arg *args, struct sample *smp, da_deviceinfo_
|
||||||
chunk_appendf(tmp, "%c", global_deviceatlas.separator);
|
chunk_appendf(tmp, "%c", global_deviceatlas.separator);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (unlikely(da_atlas_getproptype(&global_deviceatlas.atlas, prop, &proptype) != DA_OK)) {
|
pprop = ∝
|
||||||
chunk_appendf(tmp, "%c", global_deviceatlas.separator);
|
da_atlas_getproptype(&global_deviceatlas.atlas, *pprop, &proptype);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (proptype) {
|
switch (proptype) {
|
||||||
case DA_TYPE_BOOLEAN: {
|
case DA_TYPE_BOOLEAN: {
|
||||||
bool val;
|
bool val;
|
||||||
status = da_getpropboolean(devinfo, prop, &val);
|
status = da_getpropboolean(devinfo, *pprop, &val);
|
||||||
if (status == DA_OK) {
|
if (status == DA_OK) {
|
||||||
chunk_appendf(tmp, "%d", val);
|
chunk_appendf(tmp, "%d", val);
|
||||||
}
|
}
|
||||||
|
|
@ -381,7 +332,7 @@ static int da_haproxy(const struct arg *args, struct sample *smp, da_deviceinfo_
|
||||||
case DA_TYPE_INTEGER:
|
case DA_TYPE_INTEGER:
|
||||||
case DA_TYPE_NUMBER: {
|
case DA_TYPE_NUMBER: {
|
||||||
long val;
|
long val;
|
||||||
status = da_getpropinteger(devinfo, prop, &val);
|
status = da_getpropinteger(devinfo, *pprop, &val);
|
||||||
if (status == DA_OK) {
|
if (status == DA_OK) {
|
||||||
chunk_appendf(tmp, "%ld", val);
|
chunk_appendf(tmp, "%ld", val);
|
||||||
}
|
}
|
||||||
|
|
@ -389,7 +340,7 @@ static int da_haproxy(const struct arg *args, struct sample *smp, da_deviceinfo_
|
||||||
}
|
}
|
||||||
case DA_TYPE_STRING: {
|
case DA_TYPE_STRING: {
|
||||||
const char *val;
|
const char *val;
|
||||||
status = da_getpropstring(devinfo, prop, &val);
|
status = da_getpropstring(devinfo, *pprop, &val);
|
||||||
if (status == DA_OK) {
|
if (status == DA_OK) {
|
||||||
chunk_appendf(tmp, "%s", val);
|
chunk_appendf(tmp, "%s", val);
|
||||||
}
|
}
|
||||||
|
|
@ -420,26 +371,29 @@ static int da_haproxy_conv(const struct arg *args, struct sample *smp, void *pri
|
||||||
{
|
{
|
||||||
da_deviceinfo_t devinfo;
|
da_deviceinfo_t devinfo;
|
||||||
da_status_t status;
|
da_status_t status;
|
||||||
char useragentbuf[1024];
|
const char *useragent;
|
||||||
|
char useragentbuf[1024] = { 0 };
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (unlikely(global_deviceatlas.daset == 0) || smp->data.u.str.data == 0) {
|
if (global_deviceatlas.daset == 0 || smp->data.u.str.data == 0) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
da_haproxy_checkinst();
|
da_haproxy_checkinst();
|
||||||
|
|
||||||
i = smp->data.u.str.data > sizeof(useragentbuf) - 1 ? sizeof(useragentbuf) - 1 : smp->data.u.str.data;
|
i = smp->data.u.str.data > sizeof(useragentbuf) ? sizeof(useragentbuf) : smp->data.u.str.data;
|
||||||
memcpy(useragentbuf, smp->data.u.str.area, i);
|
memcpy(useragentbuf, smp->data.u.str.area, i - 1);
|
||||||
useragentbuf[i] = 0;
|
useragentbuf[i - 1] = 0;
|
||||||
|
|
||||||
|
useragent = (const char *)useragentbuf;
|
||||||
|
|
||||||
status = da_search(&global_deviceatlas.atlas, &devinfo,
|
status = da_search(&global_deviceatlas.atlas, &devinfo,
|
||||||
global_deviceatlas.useragentid, useragentbuf, 0);
|
global_deviceatlas.useragentid, useragent, 0);
|
||||||
|
|
||||||
return status != DA_OK ? 0 : da_haproxy(args, smp, &devinfo);
|
return status != DA_OK ? 0 : da_haproxy(args, smp, &devinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DA_MAX_HEADERS 32
|
#define DA_MAX_HEADERS 24
|
||||||
|
|
||||||
static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
||||||
{
|
{
|
||||||
|
|
@ -449,10 +403,10 @@ static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const ch
|
||||||
struct channel *chn;
|
struct channel *chn;
|
||||||
struct htx *htx;
|
struct htx *htx;
|
||||||
struct htx_blk *blk;
|
struct htx_blk *blk;
|
||||||
char vbuf[DA_MAX_HEADERS][1024];
|
char vbuf[DA_MAX_HEADERS][1024] = {{ 0 }};
|
||||||
int i, nbh = 0;
|
int i, nbh = 0;
|
||||||
|
|
||||||
if (unlikely(global_deviceatlas.daset == 0)) {
|
if (global_deviceatlas.daset == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -460,17 +414,18 @@ static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const ch
|
||||||
|
|
||||||
chn = (smp->strm ? &smp->strm->req : NULL);
|
chn = (smp->strm ? &smp->strm->req : NULL);
|
||||||
htx = smp_prefetch_htx(smp, chn, NULL, 1);
|
htx = smp_prefetch_htx(smp, chn, NULL, 1);
|
||||||
if (unlikely(!htx))
|
if (!htx)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
for (blk = htx_get_first_blk(htx); nbh < DA_MAX_HEADERS && blk; blk = htx_get_next_blk(htx, blk)) {
|
for (blk = htx_get_first_blk(htx); nbh < DA_MAX_HEADERS && blk; blk = htx_get_next_blk(htx, blk)) {
|
||||||
size_t vlen;
|
size_t vlen;
|
||||||
char *pval;
|
char *pval;
|
||||||
da_evidence_id_t evid;
|
da_evidence_id_t evid;
|
||||||
enum htx_blk_type type;
|
enum htx_blk_type type;
|
||||||
struct ist n, v;
|
struct ist n, v;
|
||||||
char hbuf[64];
|
char hbuf[24] = { 0 };
|
||||||
char tval[1024];
|
char tval[1024] = { 0 };
|
||||||
|
|
||||||
type = htx_get_blk_type(blk);
|
type = htx_get_blk_type(blk);
|
||||||
|
|
||||||
|
|
@ -483,18 +438,20 @@ static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const ch
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n.len > global_deviceatlas.maxhdrlen || n.len >= sizeof(hbuf)) {
|
/* The HTTP headers used by the DeviceAtlas API are not longer */
|
||||||
|
if (n.len >= sizeof(hbuf)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(hbuf, n.ptr, n.len);
|
memcpy(hbuf, n.ptr, n.len);
|
||||||
hbuf[n.len] = 0;
|
hbuf[n.len] = 0;
|
||||||
|
pval = v.ptr;
|
||||||
|
vlen = v.len;
|
||||||
evid = -1;
|
evid = -1;
|
||||||
i = v.len > sizeof(tval) - 1 ? sizeof(tval) - 1 : v.len;
|
i = v.len > sizeof(tval) - 1 ? sizeof(tval) - 1 : v.len;
|
||||||
memcpy(tval, v.ptr, i);
|
memcpy(tval, v.ptr, i);
|
||||||
tval[i] = 0;
|
tval[i] = 0;
|
||||||
pval = tval;
|
pval = tval;
|
||||||
vlen = i;
|
|
||||||
|
|
||||||
if (strcasecmp(hbuf, "Accept-Language") == 0) {
|
if (strcasecmp(hbuf, "Accept-Language") == 0) {
|
||||||
evid = da_atlas_accept_language_evidence_id(&global_deviceatlas.atlas);
|
evid = da_atlas_accept_language_evidence_id(&global_deviceatlas.atlas);
|
||||||
|
|
@ -512,7 +469,7 @@ static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const ch
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
vlen = pl;
|
vlen -= global_deviceatlas.cookienamelen - 1;
|
||||||
pval = p;
|
pval = p;
|
||||||
evid = da_atlas_clientprop_evidence_id(&global_deviceatlas.atlas);
|
evid = da_atlas_clientprop_evidence_id(&global_deviceatlas.atlas);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -141,11 +141,6 @@ enum {
|
||||||
DA_INITIAL_MEMORY_ESTIMATE = 1024 * 1024 * 14
|
DA_INITIAL_MEMORY_ESTIMATE = 1024 * 1024 * 14
|
||||||
};
|
};
|
||||||
|
|
||||||
struct header_evidence_entry {
|
|
||||||
const char *name;
|
|
||||||
da_evidence_id_t id;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct da_config {
|
struct da_config {
|
||||||
unsigned int cache_size;
|
unsigned int cache_size;
|
||||||
unsigned int __reserved[15]; /* enough reserved keywords for future use */
|
unsigned int __reserved[15]; /* enough reserved keywords for future use */
|
||||||
|
|
|
||||||
|
|
@ -70,4 +70,4 @@ OPTIONS_OBJS += \
|
||||||
addons/ot/src/vars.o
|
addons/ot/src/vars.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
OT_CFLAGS := $(OT_CFLAGS) $(OT_DEFINE)
|
OT_CFLAGS := $(OT_CFLAGS) -Iaddons/ot/include $(OT_DEFINE)
|
||||||
|
|
|
||||||
|
|
@ -35,11 +35,11 @@
|
||||||
do { \
|
do { \
|
||||||
if (!(l) || (flt_ot_debug.level & (1 << (l)))) \
|
if (!(l) || (flt_ot_debug.level & (1 << (l)))) \
|
||||||
(void)fprintf(stderr, FLT_OT_DBG_FMT("%.*s" f "\n"), \
|
(void)fprintf(stderr, FLT_OT_DBG_FMT("%.*s" f "\n"), \
|
||||||
flt_ot_dbg_indent_level, FLT_OT_DBG_INDENT, ##__VA_ARGS__); \
|
dbg_indent_level, FLT_OT_DBG_INDENT, ##__VA_ARGS__); \
|
||||||
} while (0)
|
} while (0)
|
||||||
# define FLT_OT_FUNC(f, ...) do { FLT_OT_DBG(1, "%s(" f ") {", __func__, ##__VA_ARGS__); flt_ot_dbg_indent_level += 3; } while (0)
|
# define FLT_OT_FUNC(f, ...) do { FLT_OT_DBG(1, "%s(" f ") {", __func__, ##__VA_ARGS__); dbg_indent_level += 3; } while (0)
|
||||||
# define FLT_OT_RETURN(a) do { flt_ot_dbg_indent_level -= 3; FLT_OT_DBG(1, "}"); return a; } while (0)
|
# define FLT_OT_RETURN(a) do { dbg_indent_level -= 3; FLT_OT_DBG(1, "}"); return a; } while (0)
|
||||||
# define FLT_OT_RETURN_EX(a,t,f) do { flt_ot_dbg_indent_level -= 3; { t _r = (a); FLT_OT_DBG(1, "} = " f, _r); return _r; } } while (0)
|
# define FLT_OT_RETURN_EX(a,t,f) do { dbg_indent_level -= 3; { t _r = (a); FLT_OT_DBG(1, "} = " f, _r); return _r; } } while (0)
|
||||||
# define FLT_OT_RETURN_INT(a) FLT_OT_RETURN_EX((a), int, "%d")
|
# define FLT_OT_RETURN_INT(a) FLT_OT_RETURN_EX((a), int, "%d")
|
||||||
# define FLT_OT_RETURN_PTR(a) FLT_OT_RETURN_EX((a), void *, "%p")
|
# define FLT_OT_RETURN_PTR(a) FLT_OT_RETURN_EX((a), void *, "%p")
|
||||||
# define FLT_OT_DBG_IFDEF(a,b) a
|
# define FLT_OT_DBG_IFDEF(a,b) a
|
||||||
|
|
@ -54,7 +54,7 @@ struct flt_ot_debug {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
extern THREAD_LOCAL int flt_ot_dbg_indent_level;
|
extern THREAD_LOCAL int dbg_indent_level;
|
||||||
extern struct flt_ot_debug flt_ot_debug;
|
extern struct flt_ot_debug flt_ot_debug;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
#include "../include/include.h"
|
#include "include.h"
|
||||||
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
#include "../include/include.h"
|
#include "include.h"
|
||||||
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
#include "../include/include.h"
|
#include "include.h"
|
||||||
|
|
||||||
|
|
||||||
#define FLT_OT_EVENT_DEF(a,b,c,d,e,f) { AN_##b##_##a, SMP_OPT_DIR_##b, SMP_VAL_FE_##c, SMP_VAL_BE_##d, e, f },
|
#define FLT_OT_EVENT_DEF(a,b,c,d,e,f) { AN_##b##_##a, SMP_OPT_DIR_##b, SMP_VAL_FE_##c, SMP_VAL_BE_##d, e, f },
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
#include "../include/include.h"
|
#include "include.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
#include "../include/include.h"
|
#include "include.h"
|
||||||
|
|
||||||
|
|
||||||
#define FLT_OT_GROUP_DEF(a,b,c) { a, b, c },
|
#define FLT_OT_GROUP_DEF(a,b,c) { a, b, c },
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
#include "../include/include.h"
|
#include "include.h"
|
||||||
|
|
||||||
|
|
||||||
#ifdef DEBUG_OT
|
#ifdef DEBUG_OT
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
#include "../include/include.h"
|
#include "include.h"
|
||||||
|
|
||||||
|
|
||||||
static struct pool_head *pool_head_ot_span_context __read_mostly = NULL;
|
static struct pool_head *pool_head_ot_span_context __read_mostly = NULL;
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,12 @@
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
#include "../include/include.h"
|
#include "include.h"
|
||||||
|
|
||||||
|
|
||||||
#ifdef DEBUG_OT
|
#ifdef DEBUG_OT
|
||||||
struct flt_ot_debug flt_ot_debug;
|
struct flt_ot_debug flt_ot_debug;
|
||||||
THREAD_LOCAL int flt_ot_dbg_indent_level = 0;
|
THREAD_LOCAL int dbg_indent_level = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef OTC_DBG_MEM
|
#ifdef OTC_DBG_MEM
|
||||||
|
|
@ -359,7 +359,8 @@ static int flt_ot_parse_cfg_sample(const char *file, int linenum, char **args, s
|
||||||
*/
|
*/
|
||||||
static int flt_ot_parse_cfg_str(const char *file, int linenum, char **args, struct list *head, char **err)
|
static int flt_ot_parse_cfg_str(const char *file, int linenum, char **args, struct list *head, char **err)
|
||||||
{
|
{
|
||||||
int i, retval = ERR_NONE;
|
struct flt_ot_conf_str *str = NULL;
|
||||||
|
int i, retval = ERR_NONE;
|
||||||
|
|
||||||
FLT_OT_FUNC("\"%s\", %d, %p, %p, %p:%p", file, linenum, args, head, FLT_OT_DPTR_ARGS(err));
|
FLT_OT_FUNC("\"%s\", %d, %p, %p, %p:%p", file, linenum, args, head, FLT_OT_DPTR_ARGS(err));
|
||||||
|
|
||||||
|
|
@ -367,6 +368,9 @@ static int flt_ot_parse_cfg_str(const char *file, int linenum, char **args, stru
|
||||||
if (flt_ot_conf_str_init(args[i], linenum, head, err) == NULL)
|
if (flt_ot_conf_str_init(args[i], linenum, head, err) == NULL)
|
||||||
retval |= ERR_ABORT | ERR_ALERT;
|
retval |= ERR_ABORT | ERR_ALERT;
|
||||||
|
|
||||||
|
if (retval & ERR_CODE)
|
||||||
|
flt_ot_conf_str_free(&str);
|
||||||
|
|
||||||
FLT_OT_RETURN_INT(retval);
|
FLT_OT_RETURN_INT(retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -640,7 +644,7 @@ static int flt_ot_parse_cfg_group(const char *file, int linenum, char **args, in
|
||||||
|
|
||||||
if (pdata->keyword == FLT_OT_PARSE_GROUP_ID) {
|
if (pdata->keyword == FLT_OT_PARSE_GROUP_ID) {
|
||||||
flt_ot_current_group = flt_ot_conf_group_init(args[1], linenum, &(flt_ot_current_config->groups), &err);
|
flt_ot_current_group = flt_ot_conf_group_init(args[1], linenum, &(flt_ot_current_config->groups), &err);
|
||||||
if (flt_ot_current_group == NULL)
|
if (flt_ot_current_config == NULL)
|
||||||
retval |= ERR_ABORT | ERR_ALERT;
|
retval |= ERR_ABORT | ERR_ALERT;
|
||||||
}
|
}
|
||||||
else if (pdata->keyword == FLT_OT_PARSE_GROUP_SCOPES) {
|
else if (pdata->keyword == FLT_OT_PARSE_GROUP_SCOPES) {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
#include "../include/include.h"
|
#include "include.h"
|
||||||
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
#include "../include/include.h"
|
#include "include.h"
|
||||||
|
|
||||||
|
|
||||||
static struct pool_head *pool_head_ot_scope_span __read_mostly = NULL;
|
static struct pool_head *pool_head_ot_scope_span __read_mostly = NULL;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
#include "../include/include.h"
|
#include "include.h"
|
||||||
|
|
||||||
|
|
||||||
#ifdef DEBUG_OT
|
#ifdef DEBUG_OT
|
||||||
|
|
@ -41,7 +41,7 @@ void flt_ot_args_dump(char **args)
|
||||||
|
|
||||||
argc = flt_ot_args_count(args);
|
argc = flt_ot_args_count(args);
|
||||||
|
|
||||||
(void)fprintf(stderr, FLT_OT_DBG_FMT("%.*sargs[%d]: { '%s' "), flt_ot_dbg_indent_level, FLT_OT_DBG_INDENT, argc, args[0]);
|
(void)fprintf(stderr, FLT_OT_DBG_FMT("%.*sargs[%d]: { '%s' "), dbg_indent_level, FLT_OT_DBG_INDENT, argc, args[0]);
|
||||||
|
|
||||||
for (i = 1; i < argc; i++)
|
for (i = 1; i < argc; i++)
|
||||||
(void)fprintf(stderr, "'%s' ", args[i]);
|
(void)fprintf(stderr, "'%s' ", args[i]);
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
#include "../include/include.h"
|
#include "include.h"
|
||||||
|
|
||||||
|
|
||||||
#ifdef DEBUG_OT
|
#ifdef DEBUG_OT
|
||||||
|
|
@ -46,10 +46,10 @@ static void flt_ot_vars_scope_dump(struct vars *vars, const char *scope)
|
||||||
|
|
||||||
vars_rdlock(vars);
|
vars_rdlock(vars);
|
||||||
for (i = 0; i < VAR_NAME_ROOTS; i++) {
|
for (i = 0; i < VAR_NAME_ROOTS; i++) {
|
||||||
struct ceb_node *node = cebu64_imm_first(&(vars->name_root[i]));
|
struct ceb_node *node = cebu64_first(&(vars->name_root[i]));
|
||||||
|
|
||||||
for ( ; node != NULL; node = cebu64_imm_next(&(vars->name_root[i]), node)) {
|
for ( ; node != NULL; node = cebu64_next(&(vars->name_root[i]), node)) {
|
||||||
struct var *var = container_of(node, struct var, name_node);
|
struct var *var = container_of(node, struct var, node);
|
||||||
|
|
||||||
FLT_OT_DBG(2, "'%s.%016" PRIx64 "' -> '%.*s'", scope, var->name_hash, (int)b_data(&(var->data.u.str)), b_orig(&(var->data.u.str)));
|
FLT_OT_DBG(2, "'%s.%016" PRIx64 "' -> '%.*s'", scope, var->name_hash, (int)b_data(&(var->data.u.str)), b_orig(&(var->data.u.str)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Miroslav Zagorac <mzagorac@haproxy.com>
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Miroslav Zagorac <mzagorac@haproxy.com>
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
# 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)
|
|
||||||
1182
addons/otel/README
1182
addons/otel/README
File diff suppressed because it is too large
Load diff
|
|
@ -1,456 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -1,951 +0,0 @@
|
||||||
-----------------------------------------
|
|
||||||
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 OTel filter requires the 'insecure-fork-wanted' keyword in the HAProxy
|
|
||||||
'global' section. This is necessary because the OpenTelemetry C++ SDK creates
|
|
||||||
background threads for data export and batch processing. HAProxy will refuse
|
|
||||||
to load the configuration if this keyword is missing.
|
|
||||||
|
|
||||||
global
|
|
||||||
insecure-fork-wanted
|
|
||||||
...
|
|
||||||
|
|
||||||
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)
|
|
||||||
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.
|
|
||||||
|
|
@ -1,725 +0,0 @@
|
||||||
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).
|
|
||||||
|
|
@ -1,723 +0,0 @@
|
||||||
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/nolognorm).
|
|
||||||
|
|
||||||
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.
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,101 +0,0 @@
|
||||||
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.
|
|
||||||
|
|
@ -1,288 +0,0 @@
|
||||||
## 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
|
|
||||||
|
|
||||||
The OTel filter requires the `insecure-fork-wanted` keyword in the HAProxy
|
|
||||||
`global` section. This is necessary because the OpenTelemetry C++ SDK creates
|
|
||||||
background threads for data export and batch processing. HAProxy will refuse
|
|
||||||
to load the configuration if this keyword is missing.
|
|
||||||
|
|
||||||
```
|
|
||||||
global
|
|
||||||
insecure-fork-wanted
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
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>
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,313 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,128 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,152 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
/* 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>
|
|
||||||
|
|
||||||
#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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,221 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,174 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,457 +0,0 @@
|
||||||
/* 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 "nolognorm"
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,885 +0,0 @@
|
||||||
/* 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 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 links,
|
|
||||||
* 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, spans_to_finish, and instruments. 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,849 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,378 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,324 +0,0 @@
|
||||||
/* 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) {
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,289 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,385 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
|
|
@ -1,745 +0,0 @@
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,85 +0,0 @@
|
||||||
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.
|
|
||||||
|
|
@ -1,149 +0,0 @@
|
||||||
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.
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
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.
|
|
||||||
|
|
@ -1,158 +0,0 @@
|
||||||
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.
|
|
||||||
|
|
@ -1,134 +0,0 @@
|
||||||
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.
|
|
||||||
|
|
@ -1,144 +0,0 @@
|
||||||
--- 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
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
@ -1,144 +0,0 @@
|
||||||
--- 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
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
@ -1,144 +0,0 @@
|
||||||
--- 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
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
@ -1,144 +0,0 @@
|
||||||
--- 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
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
@ -1,319 +0,0 @@
|
||||||
-----------------------------------------
|
|
||||||
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.
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
[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
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue