mirror of
https://github.com/haproxy/haproxy.git
synced 2026-04-13 04:46:15 -04:00
Compare commits
263 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
175717f5be | ||
|
|
6e67b59aca | ||
|
|
a7c2cf9274 | ||
|
|
5ea919fa7c | ||
|
|
86430ab5a4 | ||
|
|
c6b9ba80ae | ||
|
|
abcf2d757d | ||
|
|
5d6a09580a | ||
|
|
a4737cca08 | ||
|
|
991d5dabe0 | ||
|
|
40d4fa29fd | ||
|
|
f2629342b2 | ||
|
|
124e32ad78 | ||
|
|
9fa34e592a | ||
|
|
de41121c0c | ||
|
|
dae6fa9c6f | ||
|
|
7c66bb5497 | ||
|
|
651e9fd8a7 | ||
|
|
d96ce16cef | ||
|
|
20c740db8c | ||
|
|
bb2c512d29 | ||
|
|
081a8fd5cf | ||
|
|
8d8d7e4602 | ||
|
|
579d1247f2 | ||
|
|
6f177cd01e | ||
|
|
bf05a014db | ||
|
|
eaa05d2af3 | ||
|
|
d8e69f07dc | ||
|
|
bc6402f609 | ||
|
|
40ac0fd932 | ||
|
|
cb4cb1065c | ||
|
|
b1498910ec | ||
|
|
2d6a15ac00 | ||
|
|
8effcac6f5 | ||
|
|
ea9d05de02 | ||
|
|
bab0ea7b77 | ||
|
|
3184470339 | ||
|
|
2e962a5443 | ||
|
|
f05a6735b1 | ||
|
|
c0fd39457f | ||
|
|
2d56399b0c | ||
|
|
8126fd569b | ||
|
|
cd14abf9f3 | ||
|
|
b8145fa5d4 | ||
|
|
b5624a6365 | ||
|
|
fb82dece47 | ||
|
|
313121639e | ||
|
|
63febbace7 | ||
|
|
ec552b0cc2 | ||
|
|
a993f0c503 | ||
|
|
792e055c7c | ||
|
|
5271cdaca3 | ||
|
|
10f2867dc2 | ||
|
|
47199ce895 | ||
|
|
fb3b268747 | ||
|
|
890831f292 | ||
|
|
c63e6ecd4b | ||
|
|
90d0e8a948 | ||
|
|
4ad200f276 | ||
|
|
0aeae23056 | ||
|
|
e6c3660327 | ||
|
|
b0a9216ca5 | ||
|
|
265be7e8cb | ||
|
|
0cde3cd4df | ||
|
|
bd03f05007 | ||
|
|
b63cae7f9b | ||
|
|
0e18e1cc77 | ||
|
|
591a85e29e | ||
|
|
3020fde525 | ||
|
|
d759e60a32 | ||
|
|
2140249c18 | ||
|
|
2eefd489c2 | ||
|
|
052feec33f | ||
|
|
8745d2cf8e | ||
|
|
923b4c3a19 | ||
|
|
4111cf3e0e | ||
|
|
b7add82f92 | ||
|
|
accc9003e8 | ||
|
|
06673291d7 | ||
|
|
782a1b5888 | ||
|
|
f712841cf0 | ||
|
|
d6284470e4 | ||
|
|
2db801c635 | ||
|
|
a03120e228 | ||
|
|
5161415653 | ||
|
|
648b0e7bea | ||
|
|
717e9aec5f | ||
|
|
ed267f9bc5 | ||
|
|
41bded8952 | ||
|
|
ca53ee17b6 | ||
|
|
774d0dfe14 | ||
|
|
09c37fb6bd | ||
|
|
6ed656d691 | ||
|
|
063dfd9ff3 | ||
|
|
2147e8e368 | ||
|
|
c010c3924a | ||
|
|
8056117e98 | ||
|
|
ed208b7e0f | ||
|
|
910a182f05 | ||
|
|
3eebd5f67d | ||
|
|
8d28c0e37b | ||
|
|
629a5ae531 | ||
|
|
882176a602 | ||
|
|
f2e362ab22 | ||
|
|
e42f381bfc | ||
|
|
f1bf8dd148 | ||
|
|
3ab5044c1c | ||
|
|
f349d0b113 | ||
|
|
70a7f8dce0 | ||
|
|
ecf36f2ca8 | ||
|
|
2a1afcf39d | ||
|
|
d89ae36adc | ||
|
|
efb1ab57be | ||
|
|
f9ba750fd9 | ||
|
|
bf04e64f2c | ||
|
|
3608374d6d | ||
|
|
6df3662077 | ||
|
|
6ca83eb731 | ||
|
|
22b7da1464 | ||
|
|
3636ebd062 | ||
|
|
253bf8cbae | ||
|
|
6228ec6a81 | ||
|
|
2457701299 | ||
|
|
490b465fd1 | ||
|
|
b26178396a | ||
|
|
3c42a7e9ac | ||
|
|
f1ed1de317 | ||
|
|
531a2b9f1a | ||
|
|
91ea5809e9 | ||
|
|
4dd224b5ef | ||
|
|
782894f5b8 | ||
|
|
621f21f6fd | ||
|
|
e8d9eb4f7a | ||
|
|
0f0574ee96 | ||
|
|
81f22cd68a | ||
|
|
068baf4ddf | ||
|
|
6ae22a50e5 | ||
|
|
f16c851625 | ||
|
|
ce4aab4fdb | ||
|
|
afa17f68a9 | ||
|
|
9d8f7a4459 | ||
|
|
3078a63335 | ||
|
|
10094fdd00 | ||
|
|
62fcc48bcf | ||
|
|
ea5cb23307 | ||
|
|
9a2db73e32 | ||
|
|
967228c211 | ||
|
|
b72bfedd68 | ||
|
|
011b085803 | ||
|
|
48e41e4ce0 | ||
|
|
1e08247961 | ||
|
|
7c3fe4d0c0 | ||
|
|
10ce550b47 | ||
|
|
7f6999b764 | ||
|
|
c49facbabe | ||
|
|
6fbccae1ab | ||
|
|
44c02854ca | ||
|
|
275bd9ec03 | ||
|
|
9e92352967 | ||
|
|
954e87ee01 | ||
|
|
51e1562a0d | ||
|
|
3e8b8aa6aa | ||
|
|
64e3029e8b | ||
|
|
b58f567ff3 | ||
|
|
978119caa6 | ||
|
|
dc7c8bd2f8 | ||
|
|
949aa36820 | ||
|
|
8c00df7448 | ||
|
|
2adcdbacc2 | ||
|
|
e4b8531d5a | ||
|
|
6a862009be | ||
|
|
c8bfd06b57 | ||
|
|
e264523112 | ||
|
|
eaf42ee886 | ||
|
|
397530b1e9 | ||
|
|
7c73b08a98 | ||
|
|
ee95a7539e | ||
|
|
daf378d2b4 | ||
|
|
b134065ea8 | ||
|
|
94d2f69b93 | ||
|
|
66965a60ba | ||
|
|
453a01387b | ||
|
|
25366f6dc1 | ||
|
|
d47415624b | ||
|
|
14a4168a84 | ||
|
|
226bb4bd28 | ||
|
|
ad87ab1f2e | ||
|
|
2b0c510aff | ||
|
|
631fd5f99b | ||
|
|
5dcfbc5fad | ||
|
|
e418e828aa | ||
|
|
50446c35a7 | ||
|
|
a336c467a0 | ||
|
|
e375f1061a | ||
|
|
cf3173d92b | ||
|
|
5280130343 | ||
|
|
d4eee1f206 | ||
|
|
08cc37a554 | ||
|
|
b7d1c2f91d | ||
|
|
20ae1eb79d | ||
|
|
4fd5cafe27 | ||
|
|
d26bd9f978 | ||
|
|
506cfcb5d4 | ||
|
|
418f0c0bbe | ||
|
|
27d7c69e87 | ||
|
|
d1c7e56585 | ||
|
|
4e99cddde4 | ||
|
|
0e36267aac | ||
|
|
1b0dfff552 | ||
|
|
d3ad730d5f | ||
|
|
cca9245416 | ||
|
|
07edaed191 | ||
|
|
1c1d9d2500 | ||
|
|
47987ccbd9 | ||
|
|
33041fe91f | ||
|
|
8e250bba8f | ||
|
|
c7564c19a2 | ||
|
|
efbf0f8ed1 | ||
|
|
52d8ee85e7 | ||
|
|
38a7d8599d | ||
|
|
82afd36b6c | ||
|
|
ada33006ef | ||
|
|
163eba5c8c | ||
|
|
61d68f14b2 | ||
|
|
125cbecfa9 | ||
|
|
a61ea0f414 | ||
|
|
cd363e0246 | ||
|
|
d257dd4563 | ||
|
|
39121ceca6 | ||
|
|
c9a9fa813b | ||
|
|
181cd8ba8a | ||
|
|
5acdda4eed | ||
|
|
92a24a4e87 | ||
|
|
467f911cea | ||
|
|
0213dd70c9 | ||
|
|
5ead611cc2 | ||
|
|
41c89e4fb6 | ||
|
|
b71f70d548 | ||
|
|
01b9b67d5c | ||
|
|
f8c96bf9cb | ||
|
|
4d6cba03f2 | ||
|
|
1c379cad88 | ||
|
|
3d9865a12c | ||
|
|
d72be950bd | ||
|
|
5a0fbbf1ca | ||
|
|
c6fc53aa99 | ||
|
|
2ca7601c2d | ||
|
|
d250b381dc | ||
|
|
5b184e4178 | ||
|
|
fedaf054c4 | ||
|
|
8e469ebf2e | ||
|
|
ff7b06badb | ||
|
|
5d0f5f8168 | ||
|
|
ed6a4bc807 | ||
|
|
282b9b7d16 | ||
|
|
6982c2539f | ||
|
|
9852d5be26 | ||
|
|
7d40b3134a | ||
|
|
8f6cb8f452 | ||
|
|
60c9e2975b | ||
|
|
5617e47f91 | ||
|
|
042b7ab763 | ||
|
|
7466f64c56 |
260 changed files with 29302 additions and 1815 deletions
18
.github/actions/setup-vtest/action.yml
vendored
18
.github/actions/setup-vtest/action.yml
vendored
|
|
@ -20,10 +20,24 @@ runs:
|
||||||
ulimit -n 65536
|
ulimit -n 65536
|
||||||
ulimit -c unlimited
|
ulimit -c unlimited
|
||||||
|
|
||||||
- name: Install VTest
|
- name: Get VTest latest commit SHA
|
||||||
|
id: vtest-sha
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
scripts/build-vtest.sh
|
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
|
- name: Install problem matcher for VTest
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
|
||||||
39
.github/matrix.py
vendored
39
.github/matrix.py
vendored
|
|
@ -12,6 +12,7 @@ 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
|
||||||
|
|
@ -33,13 +34,24 @@ 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"))
|
||||||
request = urllib.request.Request(url, headers=headers)
|
all_tags = []
|
||||||
try:
|
page = 1
|
||||||
tags = urllib.request.urlopen(request)
|
sep = "&" if "?" in url else "?"
|
||||||
except:
|
while True:
|
||||||
return None
|
paginated_url = "{}{}per_page=100&page={}".format(url, sep, page)
|
||||||
tags = json.loads(tags.read().decode("utf-8"))
|
request = urllib.request.Request(paginated_url, headers=headers)
|
||||||
return [tag['name'] for tag in tags]
|
try:
|
||||||
|
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):
|
||||||
|
|
@ -57,7 +69,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('^v[0-9]+(\.[0-9]+)*$', version_string)
|
return re.match(r'^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):
|
||||||
|
|
@ -65,6 +77,8 @@ 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:])
|
||||||
|
|
||||||
|
|
@ -72,15 +86,16 @@ 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('^AWS-LC-FIPS-[0-9]+(\.[0-9]+)*$', version_string)
|
return re.match(r'^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):
|
||||||
# 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")
|
||||||
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:])
|
||||||
|
|
||||||
|
|
@ -88,7 +103,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('^v[0-9]+(\.[0-9]+)*-stable$', version_string)
|
return re.match(r'^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):
|
||||||
|
|
|
||||||
12
.github/workflows/aws-lc-fips.yml
vendored
12
.github/workflows/aws-lc-fips.yml
vendored
|
|
@ -1,12 +0,0 @@
|
||||||
name: AWS-LC-FIPS
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 0 * * 4"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
uses: ./.github/workflows/aws-lc-template.yml
|
|
||||||
with:
|
|
||||||
command: "from matrix import determine_latest_aws_lc_fips; print(determine_latest_aws_lc_fips(''))"
|
|
||||||
94
.github/workflows/aws-lc-template.yml
vendored
94
.github/workflows/aws-lc-template.yml
vendored
|
|
@ -1,94 +0,0 @@
|
||||||
name: AWS-LC template
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
command:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v5
|
|
||||||
- name: Determine latest AWS-LC release
|
|
||||||
id: get_aws_lc_release
|
|
||||||
run: |
|
|
||||||
result=$(cd .github && python3 -c "${{ inputs.command }}")
|
|
||||||
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
|
|
||||||
run: |
|
|
||||||
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
|
|
||||||
sudo apt-get --no-install-recommends -y install socat gdb jose
|
|
||||||
- name: Install AWS-LC
|
|
||||||
if: ${{ steps.cache_ssl.outputs.cache-hit != 'true' }}
|
|
||||||
run: env ${{ steps.get_aws_lc_release.outputs.result }} scripts/build-ssl.sh
|
|
||||||
- name: Compile HAProxy
|
|
||||||
run: |
|
|
||||||
make -j$(nproc) ERR=1 CC=gcc TARGET=linux-glibc \
|
|
||||||
USE_OPENSSL_AWSLC=1 USE_QUIC=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/"
|
|
||||||
sudo make install
|
|
||||||
- name: Show HAProxy version
|
|
||||||
id: show-version
|
|
||||||
run: |
|
|
||||||
ldd $(which haproxy)
|
|
||||||
haproxy -vv
|
|
||||||
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
|
||||||
- uses: ./.github/actions/setup-vtest
|
|
||||||
- name: Run VTest for HAProxy
|
|
||||||
id: vtest
|
|
||||||
run: |
|
|
||||||
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
|
|
||||||
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: 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
|
|
||||||
- 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
|
|
||||||
|
|
||||||
95
.github/workflows/aws-lc.yml
vendored
95
.github/workflows/aws-lc.yml
vendored
|
|
@ -5,8 +5,95 @@ on:
|
||||||
- cron: "0 0 * * 4"
|
- cron: "0 0 * * 4"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
Test:
|
||||||
uses: ./.github/workflows/aws-lc-template.yml
|
name: ${{ matrix.name }}
|
||||||
with:
|
runs-on: ubuntu-latest
|
||||||
command: "from matrix import determine_latest_aws_lc; print(determine_latest_aws_lc(''))"
|
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' }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Determine latest AWS-LC release
|
||||||
|
id: get_aws_lc_release
|
||||||
|
run: |
|
||||||
|
result=$(cd .github && python3 -c "${{ matrix.command }}")
|
||||||
|
echo $result
|
||||||
|
echo "result=$result" >> $GITHUB_OUTPUT
|
||||||
|
- name: Cache AWS-LC
|
||||||
|
id: cache_aws_lc
|
||||||
|
uses: actions/cache@v5
|
||||||
|
with:
|
||||||
|
path: '~/opt/'
|
||||||
|
key: ssl-${{ steps.get_aws_lc_release.outputs.result }}-Ubuntu-latest-gcc
|
||||||
|
- 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 jose
|
||||||
|
- name: Install AWS-LC
|
||||||
|
if: ${{ steps.cache_ssl.outputs.cache-hit != 'true' }}
|
||||||
|
run: env ${{ steps.get_aws_lc_release.outputs.result }} scripts/build-ssl.sh
|
||||||
|
- name: Compile HAProxy
|
||||||
|
run: |
|
||||||
|
make -j$(nproc) ERR=1 CC=gcc TARGET=linux-glibc \
|
||||||
|
USE_OPENSSL_AWSLC=1 USE_QUIC=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/"
|
||||||
|
sudo make install
|
||||||
|
- name: Show HAProxy version
|
||||||
|
id: show-version
|
||||||
|
run: |
|
||||||
|
ldd $(which haproxy)
|
||||||
|
haproxy -vv
|
||||||
|
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
||||||
|
- uses: ./.github/actions/setup-vtest
|
||||||
|
- name: Run VTest for HAProxy
|
||||||
|
id: vtest
|
||||||
|
run: |
|
||||||
|
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||||
|
- name: Run Unit tests
|
||||||
|
id: unittests
|
||||||
|
run: |
|
||||||
|
make unit-tests
|
||||||
|
- 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: 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
|
||||||
|
- 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
|
||||||
|
|
|
||||||
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@v5
|
- uses: actions/checkout@v6
|
||||||
- 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@v5
|
- uses: actions/checkout@v6
|
||||||
- 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 -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
echo "version=$(haproxy -vq)" >> $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 }}
|
||||||
|
|
|
||||||
2
.github/workflows/contrib.yml
vendored
2
.github/workflows/contrib.yml
vendored
|
|
@ -10,7 +10,7 @@ jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
- name: Compile dev/flags/flags
|
- name: Compile dev/flags/flags
|
||||||
run: |
|
run: |
|
||||||
make dev/flags/flags
|
make dev/flags/flags
|
||||||
|
|
|
||||||
2
.github/workflows/coverity.yml
vendored
2
.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@v5
|
- uses: actions/checkout@v6
|
||||||
- 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
|
||||||
|
|
|
||||||
2
.github/workflows/cross-zoo.yml
vendored
2
.github/workflows/cross-zoo.yml
vendored
|
|
@ -99,7 +99,7 @@ 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@v5
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
|
||||||
- name: install quictls
|
- name: install quictls
|
||||||
|
|
|
||||||
6
.github/workflows/fedora-rawhide.yml
vendored
6
.github/workflows/fedora-rawhide.yml
vendored
|
|
@ -25,7 +25,7 @@ jobs:
|
||||||
container:
|
container:
|
||||||
image: fedora:rawhide
|
image: fedora:rawhide
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
- 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 openssl-devel.x86_64
|
||||||
|
|
@ -48,7 +48,7 @@ jobs:
|
||||||
ldd $(command -v haproxy)
|
ldd $(command -v haproxy)
|
||||||
echo "::endgroup::"
|
echo "::endgroup::"
|
||||||
haproxy -vv
|
haproxy -vv
|
||||||
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
||||||
#
|
#
|
||||||
# TODO: review this workaround later
|
# TODO: review this workaround later
|
||||||
- name: relax crypto policies
|
- name: relax crypto policies
|
||||||
|
|
@ -59,7 +59,7 @@ jobs:
|
||||||
- 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=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/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: |
|
||||||
|
|
|
||||||
7
.github/workflows/illumos.yml
vendored
7
.github/workflows/illumos.yml
vendored
|
|
@ -5,15 +5,16 @@ 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@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- 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,11 +20,10 @@ 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@v5
|
- uses: actions/checkout@v6
|
||||||
- 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
|
||||||
- name: Install VTest
|
- uses: ./.github/actions/setup-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
|
||||||
|
|
@ -36,7 +35,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=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
run: make reg-tests VTEST_PROGRAM=${{ github.workspace }}/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,15 +5,16 @@ 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@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: "Build on VM"
|
- name: "Build on VM"
|
||||||
uses: vmactions/netbsd-vm@v1
|
uses: vmactions/netbsd-vm@v1
|
||||||
|
|
|
||||||
10
.github/workflows/openssl-ech.yml
vendored
10
.github/workflows/openssl-ech.yml
vendored
|
|
@ -13,15 +13,13 @@ 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@v5
|
- uses: actions/checkout@v6
|
||||||
- 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
|
||||||
sudo apt-get --no-install-recommends -y install libpsl-dev
|
sudo apt-get --no-install-recommends -y install libpsl-dev
|
||||||
|
- uses: ./.github/actions/setup-vtest
|
||||||
- name: Install OpenSSL+ECH
|
- name: Install OpenSSL+ECH
|
||||||
run: env OPENSSL_VERSION="git-feature/ech" GIT_TYPE="branch" scripts/build-ssl.sh
|
run: env OPENSSL_VERSION="git-feature/ech" GIT_TYPE="branch" scripts/build-ssl.sh
|
||||||
- name: Install curl+ECH
|
- name: Install curl+ECH
|
||||||
|
|
@ -40,7 +38,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
ldd $(which haproxy)
|
ldd $(which haproxy)
|
||||||
haproxy -vv
|
haproxy -vv
|
||||||
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
echo "version=$(haproxy -vq)" >> $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
|
||||||
|
|
@ -51,7 +49,7 @@ 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=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/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: |
|
||||||
|
|
|
||||||
6
.github/workflows/openssl-master.yml
vendored
6
.github/workflows/openssl-master.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@v5
|
- uses: actions/checkout@v6
|
||||||
- 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
|
||||||
|
|
@ -35,7 +35,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
ldd $(which haproxy)
|
ldd $(which haproxy)
|
||||||
haproxy -vv
|
haproxy -vv
|
||||||
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
echo "version=$(haproxy -vq)" >> $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 +46,7 @@ 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=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/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: |
|
||||||
|
|
|
||||||
11
.github/workflows/quic-interop-aws-lc.yml
vendored
11
.github/workflows/quic-interop-aws-lc.yml
vendored
|
|
@ -9,17 +9,16 @@ on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 0 * * 2"
|
- cron: "0 0 * * 2"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
combined-build-and-run:
|
combined-build-and-run:
|
||||||
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' }}
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Update Docker to the latest
|
- name: Update Docker to the latest
|
||||||
uses: docker/setup-docker-action@v4
|
uses: docker/setup-docker-action@v4
|
||||||
|
|
@ -50,7 +49,7 @@ jobs:
|
||||||
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
|
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
|
for client in chrome picoquic quic-go ngtcp2; do
|
||||||
pushd quic-interop-runner/logs-${client}/haproxy_${client}
|
pushd quic-interop-runner/logs-${client}/haproxy_${client}
|
||||||
|
|
@ -59,7 +58,7 @@ jobs:
|
||||||
done
|
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
|
||||||
|
|
|
||||||
11
.github/workflows/quic-interop-libressl.yml
vendored
11
.github/workflows/quic-interop-libressl.yml
vendored
|
|
@ -9,17 +9,16 @@ on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 0 * * 2"
|
- cron: "0 0 * * 2"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
combined-build-and-run:
|
combined-build-and-run:
|
||||||
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' }}
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Update Docker to the latest
|
- name: Update Docker to the latest
|
||||||
uses: docker/setup-docker-action@v4
|
uses: docker/setup-docker-action@v4
|
||||||
|
|
@ -48,7 +47,7 @@ jobs:
|
||||||
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
|
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
|
for client in picoquic quic-go; do
|
||||||
pushd quic-interop-runner/logs-${client}/haproxy_${client}
|
pushd quic-interop-runner/logs-${client}/haproxy_${client}
|
||||||
|
|
@ -57,7 +56,7 @@ jobs:
|
||||||
done
|
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
|
||||||
|
|
|
||||||
6
.github/workflows/quictls.yml
vendored
6
.github/workflows/quictls.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@v5
|
- uses: actions/checkout@v6
|
||||||
- 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
|
||||||
|
|
@ -38,12 +38,12 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
ldd $(which haproxy)
|
ldd $(which haproxy)
|
||||||
haproxy -vv
|
haproxy -vv
|
||||||
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
||||||
- uses: ./.github/actions/setup-vtest
|
- uses: ./.github/actions/setup-vtest
|
||||||
- name: Run VTest for HAProxy
|
- name: Run VTest for HAProxy
|
||||||
id: vtest
|
id: vtest
|
||||||
run: |
|
run: |
|
||||||
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/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: |
|
||||||
|
|
|
||||||
12
.github/workflows/vtest.yml
vendored
12
.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@v5
|
- uses: actions/checkout@v6
|
||||||
- name: Generate Build Matrix
|
- name: Generate Build Matrix
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
@ -44,7 +44,7 @@ jobs:
|
||||||
TMPDIR: /tmp
|
TMPDIR: /tmp
|
||||||
OT_CPP_VERSION: 1.6.0
|
OT_CPP_VERSION: 1.6.0
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 100
|
fetch-depth: 100
|
||||||
|
|
||||||
|
|
@ -59,7 +59,7 @@ jobs:
|
||||||
- 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' && !contains(matrix.ssl, 'QUICTLS') }}
|
||||||
id: cache_ssl
|
id: cache_ssl
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: '~/opt/'
|
path: '~/opt/'
|
||||||
key: ssl-${{ steps.generate-cache-key.outputs.key }}
|
key: ssl-${{ steps.generate-cache-key.outputs.key }}
|
||||||
|
|
@ -67,7 +67,7 @@ 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@v4
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: '~/opt-ot/'
|
path: '~/opt-ot/'
|
||||||
key: ${{ matrix.os }}-ot-${{ matrix.CC }}-${{ env.OT_CPP_VERSION }}-${{ contains(matrix.name, 'ASAN') }}
|
key: ${{ matrix.os }}-ot-${{ matrix.CC }}-${{ env.OT_CPP_VERSION }}-${{ contains(matrix.name, 'ASAN') }}
|
||||||
|
|
@ -136,11 +136,11 @@ jobs:
|
||||||
fi
|
fi
|
||||||
echo "::endgroup::"
|
echo "::endgroup::"
|
||||||
haproxy -vv
|
haproxy -vv
|
||||||
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
||||||
- 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=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/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: |
|
||||||
|
|
|
||||||
2
.github/workflows/windows.yml
vendored
2
.github/workflows/windows.yml
vendored
|
|
@ -36,7 +36,7 @@ jobs:
|
||||||
- USE_THREAD=1
|
- USE_THREAD=1
|
||||||
- USE_ZLIB=1
|
- USE_ZLIB=1
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
- uses: msys2/setup-msys2@v2
|
- uses: msys2/setup-msys2@v2
|
||||||
with:
|
with:
|
||||||
install: >-
|
install: >-
|
||||||
|
|
|
||||||
6
.github/workflows/wolfssl.yml
vendored
6
.github/workflows/wolfssl.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@v5
|
- uses: actions/checkout@v6
|
||||||
- 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 +34,12 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
ldd $(which haproxy)
|
ldd $(which haproxy)
|
||||||
haproxy -vv
|
haproxy -vv
|
||||||
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
|
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
|
||||||
- uses: ./.github/actions/setup-vtest
|
- uses: ./.github/actions/setup-vtest
|
||||||
- name: Run VTest for HAProxy
|
- name: Run VTest for HAProxy
|
||||||
id: vtest
|
id: vtest
|
||||||
run: |
|
run: |
|
||||||
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
|
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
|
||||||
- name: Run Unit tests
|
- name: Run Unit tests
|
||||||
id: unittests
|
id: unittests
|
||||||
run: |
|
run: |
|
||||||
|
|
|
||||||
161
CHANGELOG
161
CHANGELOG
|
|
@ -1,6 +1,167 @@
|
||||||
ChangeLog :
|
ChangeLog :
|
||||||
===========
|
===========
|
||||||
|
|
||||||
|
2026/04/03 : 3.4-dev8
|
||||||
|
- MINOR: log: split do_log() in do_log() + do_log_ctx()
|
||||||
|
- MINOR: log: provide a way to override logger->profile from process_send_log_ctx
|
||||||
|
- MINOR: log: support optional 'profile <log_profile_name>' argument to do-log action
|
||||||
|
- BUG/MINOR: sock: adjust accept() error messages for ENFILE and ENOMEM
|
||||||
|
- BUG/MINOR: qpack: fix 62-bit overflow and 1-byte OOB reads in decoding
|
||||||
|
- MEDIUM: sched: do not run a same task multiple times in series
|
||||||
|
- MINOR: sched: do not requeue a tasklet into the current queue
|
||||||
|
- MINOR: sched: do not punish self-waking tasklets anymore
|
||||||
|
- MEDIUM: sched: do not punish self-waking tasklets if TASK_WOKEN_ANY
|
||||||
|
- MEDIUM: sched: change scheduler budgets to lower TL_BULK
|
||||||
|
- MINOR: mux-h2: assign a limited frames processing budget
|
||||||
|
- BUILD: sched: fix leftover of debugging test in single-run changes
|
||||||
|
- BUG/MEDIUM: acme: fix multiple resource leaks in acme_x509_req()
|
||||||
|
- MINOR: http_htx: use enum for arbitrary values in conf_errors
|
||||||
|
- MINOR: http_htx: rename fields in struct conf_errors
|
||||||
|
- MINOR: http_htx: split check/init of http_errors
|
||||||
|
- MINOR/OPTIM: http_htx: lookup once http_errors section on check/init
|
||||||
|
- MEDIUM: proxy: remove http-errors limitation for dynamic backends
|
||||||
|
- BUG/MINOR: acme: leak of ext_san upon insertion error
|
||||||
|
- BUG/MINOR: acme: wrong error when checking for duplicate section
|
||||||
|
- BUG/MINOR: acme/cli: wrong argument check in 'acme renew'
|
||||||
|
- BUG/MINOR: http_htx: fix null deref in http-errors config check
|
||||||
|
- MINOR: buffers: Move small buffers management from quic to dynbuf part
|
||||||
|
- MINOR: dynbuf: Add helper functions to alloc large and small buffers
|
||||||
|
- MINOR: quic: Use b_alloc_small() to allocate a small buffer
|
||||||
|
- MINOR: config: Relax tests on the configured size of small buffers
|
||||||
|
- MINOR: config: Report the warning when invalid large buffer size is set
|
||||||
|
- MEDIUM: htx: Add htx_xfer function to replace htx_xfer_blks
|
||||||
|
- MINOR: htx: Add helper functions to xfer a message to smaller or larger one
|
||||||
|
- MINOR: http-ana: Use HTX API to move to a large buffer
|
||||||
|
- MEDIUM: chunk: Add support for small chunks
|
||||||
|
- MEDIUM: stream: Try to use a small buffer for HTTP request on queuing
|
||||||
|
- MEDIUM: stream: Try to use small buffer when TCP stream is queued
|
||||||
|
- MEDIUM: stconn: Use a small buffer if possible for L7 retries
|
||||||
|
- MEDIUM: tree-wide: Rely on htx_xfer() instead of htx_xfer_blks()
|
||||||
|
- Revert "BUG/MEDIUM: mux-h2: make sure to always report pending errors to the stream"
|
||||||
|
- MEDIUM: mux-h2: Stop dealing with HTX flags transfer in h2_rcv_buf()
|
||||||
|
- MEDIUM: tcpcheck: Use small buffer if possible for healthchecks
|
||||||
|
- MINOR: proxy: Review options flags used to configure healthchecks
|
||||||
|
- DOC: config: Fix alphabetical ordering of proxy options
|
||||||
|
- DOC: config: Fix alphabetical ordering of external-check directives
|
||||||
|
- MINOR: proxy: Add use-small-buffers option to set where to use small buffers
|
||||||
|
- DOC: config: Add missing 'status-code' param for 'http-check expect' directive
|
||||||
|
- DOC: config: Reorder params for 'tcp-check expect' directive
|
||||||
|
- BUG/MINOR: acme: NULL check on my_strndup()
|
||||||
|
- BUG/MINOR: acme: free() DER buffer on a2base64url error path
|
||||||
|
- BUG/MINOR: acme: replace atol with len-bounded __strl2uic() for retry-after
|
||||||
|
- BUG/MINOR: acme/cli: fix argument check and error in 'acme challenge_ready'
|
||||||
|
- BUILD: tools: potential null pointer dereference in dl_collect_libs_cb
|
||||||
|
- BUG/MINOR: ech: permission checks on the CLI
|
||||||
|
- BUG/MINOR: acme: permission checks on the CLI
|
||||||
|
- BUG/MEDIUM: check: Don't reuse the server xprt if we should not
|
||||||
|
- MINOR: checks: Store the protocol to be used in struct check
|
||||||
|
- MINOR: protocols: Add a new proto_is_quic() function
|
||||||
|
- MEDIUM: connections: Enforce mux protocol requirements
|
||||||
|
- MEDIUM: server: remove a useless memset() in srv_update_check_addr_port.
|
||||||
|
- BUG/MINOR: config: Warn only if warnif_cond_conflicts report a conflict
|
||||||
|
- BUG/MINOR: config: Properly test warnif_misplaced_* return values
|
||||||
|
- BUG/MINOR: http-ana: Only consider client abort for abortonclose
|
||||||
|
- BUG/MEDIUM: acme: skip doing challenge if it is already valid
|
||||||
|
- MINOR: connections: Enhance tune.idle-pool.shared
|
||||||
|
- BUG/MINOR: acme: fix task allocation leaked upon error
|
||||||
|
- BUG/MEDIUM: htx: Fix htx_xfer() to consume more data than expected
|
||||||
|
- CI: github: fix tag listing by implementing proper API pagination
|
||||||
|
- CLEANUP: fix typos and spelling in comments and documentation
|
||||||
|
- BUG/MINOR: quic: close conn on packet reception with incompatible frame
|
||||||
|
- CLEANUP: stconn: Remove usless sc_new_from_haterm() declaration
|
||||||
|
- BUG/MINOR: stconn: Always declare the SC created from healthchecks as a back SC
|
||||||
|
- MINOR: stconn: flag the stream endpoint descriptor when the app has started
|
||||||
|
- MINOR: mux-h2: report glitches on early RST_STREAM
|
||||||
|
- BUG/MINOR: net_helper: fix length controls on ip.fp tcp options parsing
|
||||||
|
- BUILD: net_helper: fix unterminated comment that broke the build
|
||||||
|
- MINOR: resolvers: basic TXT record implementation
|
||||||
|
- MINOR: acme: store the TXT record in auth->token
|
||||||
|
- MEDIUM: acme: add dns-01 DNS propagation pre-check
|
||||||
|
- MEDIUM: acme: new 'challenge-ready' option
|
||||||
|
- DOC: configuration: document challenge-ready and dns-delay options for ACME
|
||||||
|
- SCRIPTS: git-show-backports: list new commits and how to review them with -L
|
||||||
|
- BUG/MEDIUM: ssl/cli: tls-keys commands warn when accessed without admin level
|
||||||
|
- BUG/MEDIUM: ssl/ocsp: ocsp commands warn when accessed without admin level
|
||||||
|
- BUG/MEDIUM: map/cli: map/acl commands warn when accessed without admin level
|
||||||
|
- BUG/MEDIUM: ssl/cli: tls-keys commands are missing permission checks
|
||||||
|
- BUG/MEDIUM: ssl/ocsp: ocsp commands are missing permission checks
|
||||||
|
- BUG/MEDIUM: map/cli: CLI commands lack admin permission checks
|
||||||
|
- DOC: configuration: mention QUIC server support
|
||||||
|
- MEDIUM: Add set-headers-bin, add-headers-bin and del-headers-bin actions
|
||||||
|
- BUG/MEDIUM: mux-h1: Don't set MSG_MORE on bodyless responses forwarded to client
|
||||||
|
- BUG/MINOR: http_act: Properly handle decoding errors in *-headers-bin actions
|
||||||
|
- MEDIUM: stats: Hide the version by default and add stats-showversion
|
||||||
|
- MINOR: backends: Don't update last_sess if it did not change
|
||||||
|
- MINOR: servers: Don't update last_sess if it did not change
|
||||||
|
- MINOR: ssl/log: add keylog format variables and env vars
|
||||||
|
- DOC: configuration: update tune.ssl.keylog URL to IETF draft
|
||||||
|
- BUG/MINOR: http_act: Make set/add-headers-bin compatible with ACL conditions
|
||||||
|
- MINOR: action: Add a sample expression field in arguments used by HTTP actions
|
||||||
|
- MEDIUM: http_act: Rework *-headers-bin actions
|
||||||
|
- BUG/MINOR: tcpcheck: Remove unexpected flag on tcpcheck rules for httchck option
|
||||||
|
- MEDIUM: tcpcheck: Refactor how tcp-check rulesets are stored
|
||||||
|
- MINOR: tcpcheck: Deal with disable-on-404 and send-state in the tcp-check itself
|
||||||
|
- BUG/MINOR: tcpcheck: Don't enable http_needed when parsing HTTP samples
|
||||||
|
- MINOR: tcpcheck: Use tcpcheck flags to know a healthcheck uses SSL connections
|
||||||
|
- BUG/MINOR: tcpcheck: Use tcpcheck context for expressions parsing
|
||||||
|
- CLEANUP: tcpcheck: Don't needlessly expose proxy_parse_tcpcheck()
|
||||||
|
- MINOR: tcpcheck: Add a function to stringify the healthcheck type
|
||||||
|
- MEDIUM: tcpcheck: Split parsing functions to prepare healthcheck sections parsing
|
||||||
|
- MEDIUM: tcpcheck: Add parsing support for healthcheck sections
|
||||||
|
- MINOR: tcpcheck: Extract tcpheck ruleset post-config in a dedicated function
|
||||||
|
- MEDIUM: tcpcheck/server: Add healthcheck server keyword
|
||||||
|
- REGTESTS: tcpcheck: Add a script to check healthcheck section
|
||||||
|
- MINOR: acme: add 'dns-timeout' keyword for dns-01 challenge
|
||||||
|
- CLEANUP: net_helper: fix typo in comment
|
||||||
|
- MINOR: acme: set the default dns-delay to 30s
|
||||||
|
- MINOR: connection: add function to identify a QUIC connection
|
||||||
|
- MINOR: quic: refactor frame parsing
|
||||||
|
- MINOR: quic: refactor frame encoding
|
||||||
|
- BUG/MINOR: quic: fix documentation for transport params decoding
|
||||||
|
- MINOR: quic: split transport params decoding/check
|
||||||
|
- MINOR: quic: remove useless quic_tp_dec_err type
|
||||||
|
- MINOR: quic: define QMux transport parameters frame type
|
||||||
|
- MINOR: quic: implement QMux transport params frame parser/builder
|
||||||
|
- MINOR: mux-quic: move qcs stream member into tx inner struct
|
||||||
|
- MINOR: mux-quic: prepare Tx support for QMux
|
||||||
|
- MINOR: mux-quic: convert init/closure for QMux compatibility
|
||||||
|
- MINOR: mux-quic: protect qcc_io_process for QMux
|
||||||
|
- MINOR: mux-quic: prepare traces support for QMux
|
||||||
|
- MINOR: quic: abstract stream type in qf_stream frame
|
||||||
|
- MEDIUM: mux-quic: implement QMux receive
|
||||||
|
- MINOR: mux-quic: handle flow-control frame on qstream read
|
||||||
|
- MINOR: mux-quic: define Rx connection buffer for QMux
|
||||||
|
- MINOR: mux_quic: implement qstrm rx buffer realign
|
||||||
|
- MEDIUM: mux-quic: implement QMux send
|
||||||
|
- MINOR: mux-quic: implement qstream send callback
|
||||||
|
- MINOR: mux-quic: define Tx connection buffer for QMux
|
||||||
|
- MINOR: xprt_qstrm: define new xprt module for QMux protocol
|
||||||
|
- MINOR: xprt_qstrm: define callback for ALPN retrieval
|
||||||
|
- MINOR: xprt_qstrm: implement reception of transport parameters
|
||||||
|
- MINOR: xprt_qstrm: implement sending of transport parameters
|
||||||
|
- MEDIUM: ssl: load xprt_qstrm after handshake completion
|
||||||
|
- MINOR: mux-quic: use QMux transport parameters from qstrm xprt
|
||||||
|
- MAJOR: mux-quic: activate QMux for frontend side
|
||||||
|
- MAJOR: mux-quic: activate QMux on the backend side
|
||||||
|
- MINOR: acme: split the CLI wait from the resolve wait
|
||||||
|
- MEDIUM: acme: initialize the dns timer starting from the first DNS request
|
||||||
|
- DEBUG: connection/flags: add QSTRM flags for the decoder
|
||||||
|
- BUG/MINOR: mux_quic: fix uninit for QMux emission
|
||||||
|
- MINOR: acme: remove remaining CLI wait in ACME_RSLV_TRIGGER
|
||||||
|
- MEDIUM: acme: split the initial delay from the retry DNS delay
|
||||||
|
- BUG/MINOR: cfgcond: properly set the error pointer on evaluation error
|
||||||
|
- BUG/MINOR: cfgcond: always set the error string on openssl_version checks
|
||||||
|
- BUG/MINOR: cfgcond: always set the error string on awslc_api checks
|
||||||
|
- BUG/MINOR: cfgcond: fail cleanly on missing argument for "feature"
|
||||||
|
- MINOR: ssl: add the ssl_fc_crtname sample fetch
|
||||||
|
- MINOR: hasterm: Change hstream_add_data() to prepare zero-copy data forwarding
|
||||||
|
- MEDIUM: haterm: Add support for 0-copy data forwading and option to disable it
|
||||||
|
- MEDIUM: haterm: Prepare support for splicing by initializing a master pipe
|
||||||
|
- MEDIUM: haterm: Add support for splicing and option to disable it
|
||||||
|
- MINOR: haterm: Handle boolean request options as flags
|
||||||
|
- MINOR: haterm: Add an request option to disable splicing
|
||||||
|
- BUG/MINOR: ssl: fix memory leak in ssl_fc_crtname by using SSL_CTX ex_data index
|
||||||
|
|
||||||
2026/03/20 : 3.4-dev7
|
2026/03/20 : 3.4-dev7
|
||||||
- BUG/MINOR: stconn: Increase SC bytes_out value in se_done_ff()
|
- BUG/MINOR: stconn: Increase SC bytes_out value in se_done_ff()
|
||||||
- BUG/MINOR: ssl-sample: Fix sample_conv_sha2() by checking EVP_Digest* failures
|
- BUG/MINOR: ssl-sample: Fix sample_conv_sha2() by checking EVP_Digest* failures
|
||||||
|
|
|
||||||
17
Makefile
17
Makefile
|
|
@ -60,6 +60,7 @@
|
||||||
# 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
|
||||||
|
|
@ -128,6 +129,11 @@
|
||||||
# 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, ...).
|
||||||
|
|
@ -347,7 +353,7 @@ use_opts = USE_EPOLL USE_KQUEUE USE_NETFILTER USE_POLL \
|
||||||
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_QUIC USE_PROMEX \
|
USE_THREAD_DUMP USE_EVPORTS USE_OT USE_OTEL USE_QUIC USE_PROMEX \
|
||||||
USE_MEMORY_PROFILING USE_SHM_OPEN \
|
USE_MEMORY_PROFILING USE_SHM_OPEN \
|
||||||
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 \
|
||||||
|
|
@ -643,7 +649,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/ssl_trace.o src/jwe.o
|
src/acme_resolvers.o src/ssl_trace.o src/jwe.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(USE_ENGINE:0=),)
|
ifneq ($(USE_ENGINE:0=),)
|
||||||
|
|
@ -670,7 +676,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/quic_enc.o src/mux_quic_qstrm.o src/xprt_qstrm.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(USE_QUIC_OPENSSL_COMPAT:0=),)
|
ifneq ($(USE_QUIC_OPENSSL_COMPAT:0=),)
|
||||||
|
|
@ -862,6 +868,10 @@ 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
|
||||||
|
|
@ -1170,6 +1180,7 @@ 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]
|
||||||
|
|
|
||||||
2
VERDATE
2
VERDATE
|
|
@ -1,2 +1,2 @@
|
||||||
$Format:%ci$
|
$Format:%ci$
|
||||||
2026/03/20
|
2026/04/03
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
3.4-dev7
|
3.4-dev8
|
||||||
|
|
|
||||||
|
|
@ -70,4 +70,4 @@ OPTIONS_OBJS += \
|
||||||
addons/ot/src/vars.o
|
addons/ot/src/vars.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
OT_CFLAGS := $(OT_CFLAGS) -Iaddons/ot/include $(OT_DEFINE)
|
OT_CFLAGS := $(OT_CFLAGS) $(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"), \
|
||||||
dbg_indent_level, FLT_OT_DBG_INDENT, ##__VA_ARGS__); \
|
flt_ot_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__); dbg_indent_level += 3; } 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_RETURN(a) do { dbg_indent_level -= 3; FLT_OT_DBG(1, "}"); return a; } 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_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_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_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 dbg_indent_level;
|
extern THREAD_LOCAL int flt_ot_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.h"
|
#include "../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.h"
|
#include "../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.h"
|
#include "../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.h"
|
#include "../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.h"
|
#include "../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.h"
|
#include "../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.h"
|
#include "../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.h"
|
#include "../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 dbg_indent_level = 0;
|
THREAD_LOCAL int flt_ot_dbg_indent_level = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef OTC_DBG_MEM
|
#ifdef OTC_DBG_MEM
|
||||||
|
|
@ -359,8 +359,7 @@ 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)
|
||||||
{
|
{
|
||||||
struct flt_ot_conf_str *str = NULL;
|
int i, retval = ERR_NONE;
|
||||||
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));
|
||||||
|
|
||||||
|
|
@ -368,9 +367,6 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -644,7 +640,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_config == NULL)
|
if (flt_ot_current_group == 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.h"
|
#include "../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.h"
|
#include "../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.h"
|
#include "../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' "), dbg_indent_level, FLT_OT_DBG_INDENT, argc, args[0]);
|
(void)fprintf(stderr, FLT_OT_DBG_FMT("%.*sargs[%d]: { '%s' "), flt_ot_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.h"
|
#include "../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_first(&(vars->name_root[i]));
|
struct ceb_node *node = cebu64_imm_first(&(vars->name_root[i]));
|
||||||
|
|
||||||
for ( ; node != NULL; node = cebu64_next(&(vars->name_root[i]), node)) {
|
for ( ; node != NULL; node = cebu64_imm_next(&(vars->name_root[i]), node)) {
|
||||||
struct var *var = container_of(node, struct var, node);
|
struct var *var = container_of(node, struct var, name_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
addons/otel/AUTHORS
Normal file
1
addons/otel/AUTHORS
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Miroslav Zagorac <mzagorac@haproxy.com>
|
||||||
1
addons/otel/MAINTAINERS
Normal file
1
addons/otel/MAINTAINERS
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Miroslav Zagorac <mzagorac@haproxy.com>
|
||||||
80
addons/otel/Makefile
Normal file
80
addons/otel/Makefile
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
# 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
Normal file
1182
addons/otel/README
Normal file
File diff suppressed because it is too large
Load diff
456
addons/otel/README-conf
Normal file
456
addons/otel/README-conf
Normal file
|
|
@ -0,0 +1,456 @@
|
||||||
|
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
|
||||||
951
addons/otel/README-configuration
Normal file
951
addons/otel/README-configuration
Normal file
|
|
@ -0,0 +1,951 @@
|
||||||
|
-----------------------------------------
|
||||||
|
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.
|
||||||
725
addons/otel/README-design
Normal file
725
addons/otel/README-design
Normal file
|
|
@ -0,0 +1,725 @@
|
||||||
|
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).
|
||||||
723
addons/otel/README-func
Normal file
723
addons/otel/README-func
Normal file
|
|
@ -0,0 +1,723 @@
|
||||||
|
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.
|
||||||
1224
addons/otel/README-implementation
Normal file
1224
addons/otel/README-implementation
Normal file
File diff suppressed because it is too large
Load diff
101
addons/otel/README-misc
Normal file
101
addons/otel/README-misc
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
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.
|
||||||
288
addons/otel/README.md
Normal file
288
addons/otel/README.md
Normal file
|
|
@ -0,0 +1,288 @@
|
||||||
|
## 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>
|
||||||
28
addons/otel/include/cli.h
Normal file
28
addons/otel/include/cli.h
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
313
addons/otel/include/conf.h
Normal file
313
addons/otel/include/conf.h
Normal file
|
|
@ -0,0 +1,313 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
128
addons/otel/include/conf_funcs.h
Normal file
128
addons/otel/include/conf_funcs.h
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
34
addons/otel/include/config.h
Normal file
34
addons/otel/include/config.h
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
55
addons/otel/include/debug.h
Normal file
55
addons/otel/include/debug.h
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
98
addons/otel/include/define.h
Normal file
98
addons/otel/include/define.h
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
152
addons/otel/include/event.h
Normal file
152
addons/otel/include/event.h
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
55
addons/otel/include/filter.h
Normal file
55
addons/otel/include/filter.h
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
46
addons/otel/include/group.h
Normal file
46
addons/otel/include/group.h
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
31
addons/otel/include/http.h
Normal file
31
addons/otel/include/http.h
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
54
addons/otel/include/include.h
Normal file
54
addons/otel/include/include.h
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
27
addons/otel/include/otelc.h
Normal file
27
addons/otel/include/otelc.h
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
221
addons/otel/include/parser.h
Normal file
221
addons/otel/include/parser.h
Normal file
|
|
@ -0,0 +1,221 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
71
addons/otel/include/pool.h
Normal file
71
addons/otel/include/pool.h
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
174
addons/otel/include/scope.h
Normal file
174
addons/otel/include/scope.h
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
104
addons/otel/include/util.h
Normal file
104
addons/otel/include/util.h
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
52
addons/otel/include/vars.h
Normal file
52
addons/otel/include/vars.h
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
457
addons/otel/src/cli.c
Normal file
457
addons/otel/src/cli.c
Normal file
|
|
@ -0,0 +1,457 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
885
addons/otel/src/conf.c
Normal file
885
addons/otel/src/conf.c
Normal file
|
|
@ -0,0 +1,885 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
849
addons/otel/src/event.c
Normal file
849
addons/otel/src/event.c
Normal file
|
|
@ -0,0 +1,849 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
1838
addons/otel/src/filter.c
Normal file
1838
addons/otel/src/filter.c
Normal file
File diff suppressed because it is too large
Load diff
378
addons/otel/src/group.c
Normal file
378
addons/otel/src/group.c
Normal file
|
|
@ -0,0 +1,378 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
324
addons/otel/src/http.c
Normal file
324
addons/otel/src/http.c
Normal file
|
|
@ -0,0 +1,324 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
289
addons/otel/src/otelc.c
Normal file
289
addons/otel/src/otelc.c
Normal file
|
|
@ -0,0 +1,289 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
1828
addons/otel/src/parser.c
Normal file
1828
addons/otel/src/parser.c
Normal file
File diff suppressed because it is too large
Load diff
385
addons/otel/src/pool.c
Normal file
385
addons/otel/src/pool.c
Normal file
|
|
@ -0,0 +1,385 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
745
addons/otel/src/scope.c
Normal file
745
addons/otel/src/scope.c
Normal file
|
|
@ -0,0 +1,745 @@
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
1087
addons/otel/src/util.c
Normal file
1087
addons/otel/src/util.c
Normal file
File diff suppressed because it is too large
Load diff
1175
addons/otel/src/vars.c
Normal file
1175
addons/otel/src/vars.c
Normal file
File diff suppressed because it is too large
Load diff
85
addons/otel/test/README-cmp
Normal file
85
addons/otel/test/README-cmp
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
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.
|
||||||
149
addons/otel/test/README-ctx
Normal file
149
addons/otel/test/README-ctx
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
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.
|
||||||
53
addons/otel/test/README-empty
Normal file
53
addons/otel/test/README-empty
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
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
|
||||||
124
addons/otel/test/README-fe-be
Normal file
124
addons/otel/test/README-fe-be
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
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.
|
||||||
158
addons/otel/test/README-full
Normal file
158
addons/otel/test/README-full
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
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.
|
||||||
134
addons/otel/test/README-sa
Normal file
134
addons/otel/test/README-sa
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
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.
|
||||||
144
addons/otel/test/README-speed-cmp
Normal file
144
addons/otel/test/README-speed-cmp
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
--- 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
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
144
addons/otel/test/README-speed-ctx
Normal file
144
addons/otel/test/README-speed-ctx
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
--- 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
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
144
addons/otel/test/README-speed-fe-be
Normal file
144
addons/otel/test/README-speed-fe-be
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
--- 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
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
144
addons/otel/test/README-speed-sa
Normal file
144
addons/otel/test/README-speed-sa
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
--- 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
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
319
addons/otel/test/README-test-speed
Normal file
319
addons/otel/test/README-test-speed
Normal file
|
|
@ -0,0 +1,319 @@
|
||||||
|
-----------------------------------------
|
||||||
|
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.
|
||||||
19
addons/otel/test/be/haproxy.cfg
Normal file
19
addons/otel/test/be/haproxy.cfg
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
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
|
||||||
61
addons/otel/test/be/otel.cfg
Normal file
61
addons/otel/test/be/otel.cfg
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
[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
|
||||||
246
addons/otel/test/be/otel.yml
Normal file
246
addons/otel/test/be/otel.yml
Normal file
|
|
@ -0,0 +1,246 @@
|
||||||
|
exporters:
|
||||||
|
exporter_traces_otlp_file:
|
||||||
|
type: otlp_file
|
||||||
|
thread_name: "OTLP/file trace"
|
||||||
|
file_pattern: "__be_traces_log-%F-%N"
|
||||||
|
alias_pattern: "__traces_log-latest"
|
||||||
|
flush_interval: 30000000
|
||||||
|
flush_count: 256
|
||||||
|
file_size: 134217728
|
||||||
|
rotate_size: 5
|
||||||
|
|
||||||
|
exporter_traces_otlp_grpc:
|
||||||
|
type: otlp_grpc
|
||||||
|
thread_name: "OTLP/gRPC trace"
|
||||||
|
endpoint: "http://localhost:4317/v1/traces"
|
||||||
|
use_ssl_credentials: false
|
||||||
|
# ssl_credentials_cacert_path: ""
|
||||||
|
# ssl_credentials_cacert_as_string: ""
|
||||||
|
# ssl_client_key_path: ""
|
||||||
|
# ssl_client_key_string: ""
|
||||||
|
# ssl_client_cert_path: ""
|
||||||
|
# ssl_client_cert_string: ""
|
||||||
|
# timeout: 10
|
||||||
|
# user_agent: ""
|
||||||
|
# max_threads: 0
|
||||||
|
# compression: ""
|
||||||
|
# max_concurrent_requests: 0
|
||||||
|
|
||||||
|
exporter_traces_otlp_http:
|
||||||
|
type: otlp_http
|
||||||
|
thread_name: "OTLP/HTTP trace"
|
||||||
|
endpoint: "http://localhost:4318/v1/traces"
|
||||||
|
content_type: json
|
||||||
|
json_bytes_mapping: hexid
|
||||||
|
debug: false
|
||||||
|
timeout: 10
|
||||||
|
http_headers:
|
||||||
|
- X-OTel-Header-1: "OTLP HTTP traces test header #1"
|
||||||
|
- X-OTel-Header-2: "OTLP HTTP traces test header #2"
|
||||||
|
max_concurrent_requests: 64
|
||||||
|
max_requests_per_connection: 8
|
||||||
|
ssl_insecure_skip_verify: true
|
||||||
|
# ssl_ca_cert_path: ""
|
||||||
|
# ssl_ca_cert_string: ""
|
||||||
|
# ssl_client_key_path: ""
|
||||||
|
# ssl_client_key_string: ""
|
||||||
|
# ssl_client_cert_path: ""
|
||||||
|
# ssl_client_cert_string: ""
|
||||||
|
# ssl_min_tls: ""
|
||||||
|
# ssl_max_tls: ""
|
||||||
|
# ssl_cipher: ""
|
||||||
|
# ssl_cipher_suite: ""
|
||||||
|
# compression: ""
|
||||||
|
|
||||||
|
exporter_traces_dev_null:
|
||||||
|
type: ostream
|
||||||
|
filename: /dev/null
|
||||||
|
|
||||||
|
exporter_traces_ostream:
|
||||||
|
type: ostream
|
||||||
|
filename: __be_traces
|
||||||
|
|
||||||
|
exporter_traces_memory:
|
||||||
|
type: memory
|
||||||
|
buffer_size: 256
|
||||||
|
|
||||||
|
exporter_traces_zipkin:
|
||||||
|
type: zipkin
|
||||||
|
endpoint: "http://localhost:9411/api/v2/spans"
|
||||||
|
format: json
|
||||||
|
service_name: "zipkin-service"
|
||||||
|
# ipv4: ""
|
||||||
|
# ipv6: ""
|
||||||
|
|
||||||
|
exporter_metrics_otlp_file:
|
||||||
|
type: otlp_file
|
||||||
|
thread_name: "OTLP/file metr"
|
||||||
|
file_pattern: "__be_metrics_log-%F-%N"
|
||||||
|
alias_pattern: "__metrics_log-latest"
|
||||||
|
flush_interval: 30000000
|
||||||
|
flush_count: 256
|
||||||
|
file_size: 134217728
|
||||||
|
rotate_size: 5
|
||||||
|
|
||||||
|
exporter_metrics_otlp_grpc:
|
||||||
|
type: otlp_grpc
|
||||||
|
thread_name: "OTLP/gRPC metr"
|
||||||
|
endpoint: "http://localhost:4317/v1/metrics"
|
||||||
|
use_ssl_credentials: false
|
||||||
|
|
||||||
|
exporter_metrics_otlp_http:
|
||||||
|
type: otlp_http
|
||||||
|
thread_name: "OTLP/HTTP metr"
|
||||||
|
endpoint: "http://localhost:4318/v1/metrics"
|
||||||
|
content_type: json
|
||||||
|
debug: false
|
||||||
|
timeout: 10
|
||||||
|
http_headers:
|
||||||
|
- X-OTel-Header-1: "OTLP HTTP metrics test header #1"
|
||||||
|
- X-OTel-Header-2: "OTLP HTTP metrics test header #2"
|
||||||
|
max_concurrent_requests: 64
|
||||||
|
max_requests_per_connection: 8
|
||||||
|
ssl_insecure_skip_verify: true
|
||||||
|
|
||||||
|
exporter_metrics_dev_null:
|
||||||
|
type: ostream
|
||||||
|
filename: /dev/null
|
||||||
|
|
||||||
|
exporter_metrics_ostream:
|
||||||
|
type: ostream
|
||||||
|
filename: __be_metrics
|
||||||
|
|
||||||
|
exporter_metrics_memory:
|
||||||
|
type: memory
|
||||||
|
buffer_size: 256
|
||||||
|
|
||||||
|
exporter_logs_otlp_file:
|
||||||
|
type: otlp_file
|
||||||
|
thread_name: "OTLP/file logs"
|
||||||
|
file_pattern: "__be_logs_log-%F-%N"
|
||||||
|
alias_pattern: "__logs_log-latest"
|
||||||
|
flush_interval: 30000000
|
||||||
|
flush_count: 256
|
||||||
|
file_size: 134217728
|
||||||
|
rotate_size: 5
|
||||||
|
|
||||||
|
exporter_logs_otlp_grpc:
|
||||||
|
type: otlp_grpc
|
||||||
|
thread_name: "OTLP/gRPC logs"
|
||||||
|
endpoint: "http://localhost:4317/v1/logs"
|
||||||
|
use_ssl_credentials: false
|
||||||
|
|
||||||
|
exporter_logs_otlp_http:
|
||||||
|
type: otlp_http
|
||||||
|
thread_name: "OTLP/HTTP logs"
|
||||||
|
endpoint: "http://localhost:4318/v1/logs"
|
||||||
|
content_type: json
|
||||||
|
debug: false
|
||||||
|
timeout: 10
|
||||||
|
http_headers:
|
||||||
|
- X-OTel-Header-1: "OTLP HTTP logs test header #1"
|
||||||
|
- X-OTel-Header-2: "OTLP HTTP logs test header #2"
|
||||||
|
max_concurrent_requests: 64
|
||||||
|
max_requests_per_connection: 8
|
||||||
|
ssl_insecure_skip_verify: true
|
||||||
|
|
||||||
|
exporter_logs_dev_null:
|
||||||
|
type: ostream
|
||||||
|
filename: /dev/null
|
||||||
|
|
||||||
|
exporter_logs_ostream:
|
||||||
|
type: ostream
|
||||||
|
filename: __be_logs
|
||||||
|
|
||||||
|
exporter_logs_elasticsearch:
|
||||||
|
type: elasticsearch
|
||||||
|
host: localhost
|
||||||
|
port: 9200
|
||||||
|
index: logs
|
||||||
|
response_timeout: 30
|
||||||
|
debug: false
|
||||||
|
http_headers:
|
||||||
|
- X-OTel-Header-1: "Elasticsearch logs test header #1"
|
||||||
|
- X-OTel-Header-2: "Elasticsearch logs test header #2"
|
||||||
|
|
||||||
|
readers:
|
||||||
|
reader_metrics:
|
||||||
|
thread_name: "reader metr"
|
||||||
|
export_interval: 10000
|
||||||
|
export_timeout: 5000
|
||||||
|
|
||||||
|
samplers:
|
||||||
|
sampler_traces:
|
||||||
|
# type: always_on
|
||||||
|
# type: always_off
|
||||||
|
# type: trace_id_ratio_based
|
||||||
|
# ratio: 1.0
|
||||||
|
type: parent_based
|
||||||
|
delegate: always_on
|
||||||
|
|
||||||
|
processors:
|
||||||
|
processor_traces_batch:
|
||||||
|
type: batch
|
||||||
|
thread_name: "proc/batch trac"
|
||||||
|
# Note: when the queue is half full, a preemptive notification is sent
|
||||||
|
# to start the export call.
|
||||||
|
max_queue_size: 2048
|
||||||
|
# Time interval (in ms) between two consecutive exports
|
||||||
|
schedule_delay: 5000
|
||||||
|
# Export 'max_export_batch_size' after every `schedule_delay' milliseconds.
|
||||||
|
max_export_batch_size: 512
|
||||||
|
|
||||||
|
processor_traces_single:
|
||||||
|
type: single
|
||||||
|
|
||||||
|
processor_logs_batch:
|
||||||
|
type: batch
|
||||||
|
thread_name: "proc/batch logs"
|
||||||
|
max_queue_size: 2048
|
||||||
|
schedule_delay: 5000
|
||||||
|
max_export_batch_size: 512
|
||||||
|
|
||||||
|
processor_logs_single:
|
||||||
|
type: single
|
||||||
|
|
||||||
|
providers:
|
||||||
|
provider_traces:
|
||||||
|
resources:
|
||||||
|
- service.version: "1.0.0"
|
||||||
|
- service.instance.id: "id-be"
|
||||||
|
- service.name: "be"
|
||||||
|
- service.namespace: "HAProxy traces test"
|
||||||
|
|
||||||
|
provider_metrics:
|
||||||
|
resources:
|
||||||
|
- service.version: "1.0.0"
|
||||||
|
- service.instance.id: "id-be"
|
||||||
|
- service.name: "be"
|
||||||
|
- service.namespace: "HAProxy metrics test"
|
||||||
|
|
||||||
|
provider_logs:
|
||||||
|
resources:
|
||||||
|
- service.version: "1.0.0"
|
||||||
|
- service.instance.id: "id-be"
|
||||||
|
- service.name: "be"
|
||||||
|
- service.namespace: "HAProxy logs test"
|
||||||
|
|
||||||
|
signals:
|
||||||
|
traces:
|
||||||
|
scope_name: "HAProxy OTEL - traces"
|
||||||
|
exporters: exporter_traces_otlp_http
|
||||||
|
samplers: sampler_traces
|
||||||
|
processors: processor_traces_batch
|
||||||
|
providers: provider_traces
|
||||||
|
|
||||||
|
metrics:
|
||||||
|
scope_name: "HAProxy OTEL - metrics"
|
||||||
|
exporters: exporter_metrics_otlp_http
|
||||||
|
readers: reader_metrics
|
||||||
|
providers: provider_metrics
|
||||||
|
|
||||||
|
logs:
|
||||||
|
scope_name: "HAProxy OTEL - logs"
|
||||||
|
exporters: exporter_logs_otlp_http
|
||||||
|
processors: processor_logs_batch
|
||||||
|
providers: provider_logs
|
||||||
22
addons/otel/test/cmp/haproxy.cfg
Normal file
22
addons/otel/test/cmp/haproxy.cfg
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
global
|
||||||
|
stats socket /tmp/haproxy.sock mode 666 level admin
|
||||||
|
|
||||||
|
listen stats
|
||||||
|
mode http
|
||||||
|
bind *:8001
|
||||||
|
stats uri /
|
||||||
|
stats admin if TRUE
|
||||||
|
stats refresh 10s
|
||||||
|
|
||||||
|
frontend otel-test-cmp-frontend
|
||||||
|
bind *:10080
|
||||||
|
default_backend servers-backend
|
||||||
|
|
||||||
|
# ACL used to distinguish successful from error responses
|
||||||
|
acl acl-http-status-ok status 100:399
|
||||||
|
|
||||||
|
# OTel filter
|
||||||
|
filter opentelemetry id otel-test-cmp config cmp/otel.cfg
|
||||||
|
|
||||||
|
backend servers-backend
|
||||||
|
server server-1 127.0.0.1:8000
|
||||||
81
addons/otel/test/cmp/otel.cfg
Normal file
81
addons/otel/test/cmp/otel.cfg
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
[otel-test-cmp]
|
||||||
|
otel-instrumentation otel-test-instrumentation
|
||||||
|
config cmp/otel.yml
|
||||||
|
# log localhost:514 local7 debug
|
||||||
|
option dontlog-normal
|
||||||
|
option hard-errors
|
||||||
|
no option disabled
|
||||||
|
rate-limit 100.0
|
||||||
|
|
||||||
|
scopes client_session_start
|
||||||
|
scopes frontend_tcp_request
|
||||||
|
scopes frontend_http_request
|
||||||
|
scopes backend_tcp_request
|
||||||
|
scopes backend_http_request
|
||||||
|
scopes server_unavailable
|
||||||
|
|
||||||
|
scopes server_session_start
|
||||||
|
scopes tcp_response
|
||||||
|
scopes http_response http_response-error server_session_end client_session_end
|
||||||
|
|
||||||
|
otel-scope client_session_start
|
||||||
|
span "HAProxy session" root
|
||||||
|
baggage "haproxy_id" var(sess.otel.uuid)
|
||||||
|
span "Client session" parent "HAProxy session"
|
||||||
|
otel-event on-client-session-start
|
||||||
|
|
||||||
|
otel-scope frontend_tcp_request
|
||||||
|
span "Frontend TCP request" parent "Client session"
|
||||||
|
otel-event on-frontend-tcp-request
|
||||||
|
|
||||||
|
otel-scope frontend_http_request
|
||||||
|
span "Frontend HTTP request" parent "Frontend TCP request"
|
||||||
|
attribute "http.method" method
|
||||||
|
attribute "http.url" url
|
||||||
|
attribute "http.version" str("HTTP/") req.ver
|
||||||
|
finish "Frontend TCP request"
|
||||||
|
otel-event on-frontend-http-request
|
||||||
|
|
||||||
|
otel-scope backend_tcp_request
|
||||||
|
span "Backend TCP request" parent "Frontend HTTP request"
|
||||||
|
finish "Frontend HTTP request"
|
||||||
|
otel-event on-backend-tcp-request
|
||||||
|
|
||||||
|
otel-scope backend_http_request
|
||||||
|
span "Backend HTTP request" parent "Backend TCP request"
|
||||||
|
finish "Backend TCP request"
|
||||||
|
otel-event on-backend-http-request
|
||||||
|
|
||||||
|
otel-scope server_unavailable
|
||||||
|
span "HAProxy session"
|
||||||
|
status "error" str("503 Service Unavailable")
|
||||||
|
finish *
|
||||||
|
otel-event on-server-unavailable
|
||||||
|
|
||||||
|
otel-scope server_session_start
|
||||||
|
span "Server session" parent "HAProxy session"
|
||||||
|
finish "Backend HTTP request"
|
||||||
|
otel-event on-server-session-start
|
||||||
|
|
||||||
|
otel-scope tcp_response
|
||||||
|
span "TCP response" parent "Server session"
|
||||||
|
otel-event on-tcp-response
|
||||||
|
|
||||||
|
otel-scope http_response
|
||||||
|
span "HTTP response" parent "TCP response"
|
||||||
|
attribute "http.status_code" status
|
||||||
|
finish "TCP response"
|
||||||
|
otel-event on-http-response
|
||||||
|
|
||||||
|
otel-scope http_response-error
|
||||||
|
span "HTTP response"
|
||||||
|
status "error" str("!acl-http-status-ok")
|
||||||
|
otel-event on-http-response if !acl-http-status-ok
|
||||||
|
|
||||||
|
otel-scope server_session_end
|
||||||
|
finish "HTTP response" "Server session"
|
||||||
|
otel-event on-http-response
|
||||||
|
|
||||||
|
otel-scope client_session_end
|
||||||
|
finish "*"
|
||||||
|
otel-event on-http-response
|
||||||
246
addons/otel/test/cmp/otel.yml
Normal file
246
addons/otel/test/cmp/otel.yml
Normal file
|
|
@ -0,0 +1,246 @@
|
||||||
|
exporters:
|
||||||
|
exporter_traces_otlp_file:
|
||||||
|
type: otlp_file
|
||||||
|
thread_name: "OTLP/file trace"
|
||||||
|
file_pattern: "__cmp_traces_log-%F-%N"
|
||||||
|
alias_pattern: "__traces_log-latest"
|
||||||
|
flush_interval: 30000000
|
||||||
|
flush_count: 256
|
||||||
|
file_size: 134217728
|
||||||
|
rotate_size: 5
|
||||||
|
|
||||||
|
exporter_traces_otlp_grpc:
|
||||||
|
type: otlp_grpc
|
||||||
|
thread_name: "OTLP/gRPC trace"
|
||||||
|
endpoint: "http://localhost:4317/v1/traces"
|
||||||
|
use_ssl_credentials: false
|
||||||
|
# ssl_credentials_cacert_path: ""
|
||||||
|
# ssl_credentials_cacert_as_string: ""
|
||||||
|
# ssl_client_key_path: ""
|
||||||
|
# ssl_client_key_string: ""
|
||||||
|
# ssl_client_cert_path: ""
|
||||||
|
# ssl_client_cert_string: ""
|
||||||
|
# timeout: 10
|
||||||
|
# user_agent: ""
|
||||||
|
# max_threads: 0
|
||||||
|
# compression: ""
|
||||||
|
# max_concurrent_requests: 0
|
||||||
|
|
||||||
|
exporter_traces_otlp_http:
|
||||||
|
type: otlp_http
|
||||||
|
thread_name: "OTLP/HTTP trace"
|
||||||
|
endpoint: "http://localhost:4318/v1/traces"
|
||||||
|
content_type: json
|
||||||
|
json_bytes_mapping: hexid
|
||||||
|
debug: false
|
||||||
|
timeout: 10
|
||||||
|
http_headers:
|
||||||
|
- X-OTel-Header-1: "OTLP HTTP traces test header #1"
|
||||||
|
- X-OTel-Header-2: "OTLP HTTP traces test header #2"
|
||||||
|
max_concurrent_requests: 64
|
||||||
|
max_requests_per_connection: 8
|
||||||
|
ssl_insecure_skip_verify: true
|
||||||
|
# ssl_ca_cert_path: ""
|
||||||
|
# ssl_ca_cert_string: ""
|
||||||
|
# ssl_client_key_path: ""
|
||||||
|
# ssl_client_key_string: ""
|
||||||
|
# ssl_client_cert_path: ""
|
||||||
|
# ssl_client_cert_string: ""
|
||||||
|
# ssl_min_tls: ""
|
||||||
|
# ssl_max_tls: ""
|
||||||
|
# ssl_cipher: ""
|
||||||
|
# ssl_cipher_suite: ""
|
||||||
|
# compression: ""
|
||||||
|
|
||||||
|
exporter_traces_dev_null:
|
||||||
|
type: ostream
|
||||||
|
filename: /dev/null
|
||||||
|
|
||||||
|
exporter_traces_ostream:
|
||||||
|
type: ostream
|
||||||
|
filename: __cmp_traces
|
||||||
|
|
||||||
|
exporter_traces_memory:
|
||||||
|
type: memory
|
||||||
|
buffer_size: 256
|
||||||
|
|
||||||
|
exporter_traces_zipkin:
|
||||||
|
type: zipkin
|
||||||
|
endpoint: "http://localhost:9411/api/v2/spans"
|
||||||
|
format: json
|
||||||
|
service_name: "zipkin-service"
|
||||||
|
# ipv4: ""
|
||||||
|
# ipv6: ""
|
||||||
|
|
||||||
|
exporter_metrics_otlp_file:
|
||||||
|
type: otlp_file
|
||||||
|
thread_name: "OTLP/file metr"
|
||||||
|
file_pattern: "__cmp_metrics_log-%F-%N"
|
||||||
|
alias_pattern: "__metrics_log-latest"
|
||||||
|
flush_interval: 30000000
|
||||||
|
flush_count: 256
|
||||||
|
file_size: 134217728
|
||||||
|
rotate_size: 5
|
||||||
|
|
||||||
|
exporter_metrics_otlp_grpc:
|
||||||
|
type: otlp_grpc
|
||||||
|
thread_name: "OTLP/gRPC metr"
|
||||||
|
endpoint: "http://localhost:4317/v1/metrics"
|
||||||
|
use_ssl_credentials: false
|
||||||
|
|
||||||
|
exporter_metrics_otlp_http:
|
||||||
|
type: otlp_http
|
||||||
|
thread_name: "OTLP/HTTP metr"
|
||||||
|
endpoint: "http://localhost:4318/v1/metrics"
|
||||||
|
content_type: json
|
||||||
|
debug: false
|
||||||
|
timeout: 10
|
||||||
|
http_headers:
|
||||||
|
- X-OTel-Header-1: "OTLP HTTP metrics test header #1"
|
||||||
|
- X-OTel-Header-2: "OTLP HTTP metrics test header #2"
|
||||||
|
max_concurrent_requests: 64
|
||||||
|
max_requests_per_connection: 8
|
||||||
|
ssl_insecure_skip_verify: true
|
||||||
|
|
||||||
|
exporter_metrics_dev_null:
|
||||||
|
type: ostream
|
||||||
|
filename: /dev/null
|
||||||
|
|
||||||
|
exporter_metrics_ostream:
|
||||||
|
type: ostream
|
||||||
|
filename: __cmp_metrics
|
||||||
|
|
||||||
|
exporter_metrics_memory:
|
||||||
|
type: memory
|
||||||
|
buffer_size: 256
|
||||||
|
|
||||||
|
exporter_logs_otlp_file:
|
||||||
|
type: otlp_file
|
||||||
|
thread_name: "OTLP/file logs"
|
||||||
|
file_pattern: "__cmp_logs_log-%F-%N"
|
||||||
|
alias_pattern: "__logs_log-latest"
|
||||||
|
flush_interval: 30000000
|
||||||
|
flush_count: 256
|
||||||
|
file_size: 134217728
|
||||||
|
rotate_size: 5
|
||||||
|
|
||||||
|
exporter_logs_otlp_grpc:
|
||||||
|
type: otlp_grpc
|
||||||
|
thread_name: "OTLP/gRPC logs"
|
||||||
|
endpoint: "http://localhost:4317/v1/logs"
|
||||||
|
use_ssl_credentials: false
|
||||||
|
|
||||||
|
exporter_logs_otlp_http:
|
||||||
|
type: otlp_http
|
||||||
|
thread_name: "OTLP/HTTP logs"
|
||||||
|
endpoint: "http://localhost:4318/v1/logs"
|
||||||
|
content_type: json
|
||||||
|
debug: false
|
||||||
|
timeout: 10
|
||||||
|
http_headers:
|
||||||
|
- X-OTel-Header-1: "OTLP HTTP logs test header #1"
|
||||||
|
- X-OTel-Header-2: "OTLP HTTP logs test header #2"
|
||||||
|
max_concurrent_requests: 64
|
||||||
|
max_requests_per_connection: 8
|
||||||
|
ssl_insecure_skip_verify: true
|
||||||
|
|
||||||
|
exporter_logs_dev_null:
|
||||||
|
type: ostream
|
||||||
|
filename: /dev/null
|
||||||
|
|
||||||
|
exporter_logs_ostream:
|
||||||
|
type: ostream
|
||||||
|
filename: __cmp_logs
|
||||||
|
|
||||||
|
exporter_logs_elasticsearch:
|
||||||
|
type: elasticsearch
|
||||||
|
host: localhost
|
||||||
|
port: 9200
|
||||||
|
index: logs
|
||||||
|
response_timeout: 30
|
||||||
|
debug: false
|
||||||
|
http_headers:
|
||||||
|
- X-OTel-Header-1: "Elasticsearch logs test header #1"
|
||||||
|
- X-OTel-Header-2: "Elasticsearch logs test header #2"
|
||||||
|
|
||||||
|
readers:
|
||||||
|
reader_metrics:
|
||||||
|
thread_name: "reader metr"
|
||||||
|
export_interval: 10000
|
||||||
|
export_timeout: 5000
|
||||||
|
|
||||||
|
samplers:
|
||||||
|
sampler_traces:
|
||||||
|
# type: always_on
|
||||||
|
# type: always_off
|
||||||
|
# type: trace_id_ratio_based
|
||||||
|
# ratio: 1.0
|
||||||
|
type: parent_based
|
||||||
|
delegate: always_on
|
||||||
|
|
||||||
|
processors:
|
||||||
|
processor_traces_batch:
|
||||||
|
type: batch
|
||||||
|
thread_name: "proc/batch trac"
|
||||||
|
# Note: when the queue is half full, a preemptive notification is sent
|
||||||
|
# to start the export call.
|
||||||
|
max_queue_size: 2048
|
||||||
|
# Time interval (in ms) between two consecutive exports
|
||||||
|
schedule_delay: 5000
|
||||||
|
# Export 'max_export_batch_size' after every `schedule_delay' milliseconds.
|
||||||
|
max_export_batch_size: 512
|
||||||
|
|
||||||
|
processor_traces_single:
|
||||||
|
type: single
|
||||||
|
|
||||||
|
processor_logs_batch:
|
||||||
|
type: batch
|
||||||
|
thread_name: "proc/batch logs"
|
||||||
|
max_queue_size: 2048
|
||||||
|
schedule_delay: 5000
|
||||||
|
max_export_batch_size: 512
|
||||||
|
|
||||||
|
processor_logs_single:
|
||||||
|
type: single
|
||||||
|
|
||||||
|
providers:
|
||||||
|
provider_traces:
|
||||||
|
resources:
|
||||||
|
- service.version: "1.0.0"
|
||||||
|
- service.instance.id: "id-cmp"
|
||||||
|
- service.name: "cmp"
|
||||||
|
- service.namespace: "HAProxy traces test"
|
||||||
|
|
||||||
|
provider_metrics:
|
||||||
|
resources:
|
||||||
|
- service.version: "1.0.0"
|
||||||
|
- service.instance.id: "id-cmp"
|
||||||
|
- service.name: "cmp"
|
||||||
|
- service.namespace: "HAProxy metrics test"
|
||||||
|
|
||||||
|
provider_logs:
|
||||||
|
resources:
|
||||||
|
- service.version: "1.0.0"
|
||||||
|
- service.instance.id: "id-cmp"
|
||||||
|
- service.name: "cmp"
|
||||||
|
- service.namespace: "HAProxy logs test"
|
||||||
|
|
||||||
|
signals:
|
||||||
|
traces:
|
||||||
|
scope_name: "HAProxy OTEL - traces"
|
||||||
|
exporters: exporter_traces_otlp_http
|
||||||
|
samplers: sampler_traces
|
||||||
|
processors: processor_traces_batch
|
||||||
|
providers: provider_traces
|
||||||
|
|
||||||
|
metrics:
|
||||||
|
scope_name: "HAProxy OTEL - metrics"
|
||||||
|
exporters: exporter_metrics_otlp_http
|
||||||
|
readers: reader_metrics
|
||||||
|
providers: provider_metrics
|
||||||
|
|
||||||
|
logs:
|
||||||
|
scope_name: "HAProxy OTEL - logs"
|
||||||
|
exporters: exporter_logs_otlp_http
|
||||||
|
processors: processor_logs_batch
|
||||||
|
providers: provider_logs
|
||||||
24
addons/otel/test/copy-yml.sh
Executable file
24
addons/otel/test/copy-yml.sh
Executable file
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/bin/sh -u
|
||||||
|
#
|
||||||
|
# Copyright 2026 HAProxy Technologies, Miroslav Zagorac <mzagorac@haproxy.com>
|
||||||
|
#
|
||||||
|
SH_FILE="${1:-}"
|
||||||
|
SH_EXT="${2:-}"
|
||||||
|
|
||||||
|
|
||||||
|
if test ${#} -ne 2; then
|
||||||
|
echo
|
||||||
|
echo "usage: $(basename "${0}") input-file test-name"
|
||||||
|
echo
|
||||||
|
exit 64
|
||||||
|
fi
|
||||||
|
|
||||||
|
sed '
|
||||||
|
s/^\( *\)\(filename:\)\( *\)_\(_[a-z]*\)/\1\2\3__'"${SH_EXT}"'\4/g
|
||||||
|
s/^\( *\)\(file_pattern:\)\( *\)"_\(_[a-z]*_[^"]*\)"/\1\2\3"__'"${SH_EXT}"'\4"/g
|
||||||
|
s/^\( *\)\(- service.instance.id:\)\( *\).*/\1\2\3"id-'"${SH_EXT}"'"/g
|
||||||
|
s/^\( *\)\(- service.name:\)\( *\).*/\1\2\3"'"${SH_EXT}"'"/g
|
||||||
|
s/^\( *\)\(- service.namespace:\)\( *\)\("otelc\)/\1\2\3"HAProxy/g
|
||||||
|
s/^\( *\)\(scope_name:\)\( *\)"OTEL C wrapper /\1\2 "HAProxy OTEL /g
|
||||||
|
s/^\( *\)\(exporters:\)\( *\)\(exporter_[a-z]*_\).*/\1\2\3\4otlp_http/g
|
||||||
|
' "${SH_FILE}"
|
||||||
28
addons/otel/test/ctx/haproxy.cfg
Normal file
28
addons/otel/test/ctx/haproxy.cfg
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
global
|
||||||
|
stats socket /tmp/haproxy.sock mode 666 level admin
|
||||||
|
|
||||||
|
listen stats
|
||||||
|
mode http
|
||||||
|
bind *:8001
|
||||||
|
stats uri /
|
||||||
|
stats admin if TRUE
|
||||||
|
stats refresh 10s
|
||||||
|
|
||||||
|
frontend otel-test-ctx-frontend
|
||||||
|
bind *:10080
|
||||||
|
default_backend servers-backend
|
||||||
|
|
||||||
|
# ACL used to distinguish successful from error responses
|
||||||
|
acl acl-http-status-ok status 100:399
|
||||||
|
|
||||||
|
# OTel filter
|
||||||
|
filter opentelemetry id otel-test-ctx config ctx/otel.cfg
|
||||||
|
|
||||||
|
# run response scopes for successful responses
|
||||||
|
http-response otel-group otel-test-ctx http_response_group if acl-http-status-ok
|
||||||
|
|
||||||
|
# run after-response scopes for error responses
|
||||||
|
http-after-response otel-group otel-test-ctx http_after_response_group if !acl-http-status-ok
|
||||||
|
|
||||||
|
backend servers-backend
|
||||||
|
server server-1 127.0.0.1:8000
|
||||||
196
addons/otel/test/ctx/otel.cfg
Normal file
196
addons/otel/test/ctx/otel.cfg
Normal file
|
|
@ -0,0 +1,196 @@
|
||||||
|
[otel-test-ctx]
|
||||||
|
otel-instrumentation otel-test-instrumentation
|
||||||
|
debug-level 0x77f
|
||||||
|
log localhost:514 local7 debug
|
||||||
|
config ctx/otel.yml
|
||||||
|
option dontlog-normal
|
||||||
|
option hard-errors
|
||||||
|
no option disabled
|
||||||
|
rate-limit 100.0
|
||||||
|
|
||||||
|
groups http_response_group
|
||||||
|
groups http_after_response_group
|
||||||
|
|
||||||
|
scopes client_session_start_1
|
||||||
|
scopes client_session_start_2
|
||||||
|
scopes frontend_tcp_request
|
||||||
|
scopes http_wait_request
|
||||||
|
scopes http_body_request
|
||||||
|
scopes frontend_http_request
|
||||||
|
scopes switching_rules_request
|
||||||
|
scopes backend_tcp_request
|
||||||
|
scopes backend_http_request
|
||||||
|
scopes process_server_rules_request
|
||||||
|
scopes http_process_request
|
||||||
|
scopes tcp_rdp_cookie_request
|
||||||
|
scopes process_sticking_rules_request
|
||||||
|
scopes client_session_end
|
||||||
|
scopes server_unavailable
|
||||||
|
|
||||||
|
scopes server_session_start
|
||||||
|
scopes tcp_response
|
||||||
|
scopes http_wait_response
|
||||||
|
scopes process_store_rules_response
|
||||||
|
scopes http_response http_response-error
|
||||||
|
scopes server_session_end
|
||||||
|
|
||||||
|
otel-group http_response_group
|
||||||
|
scopes http_response_1
|
||||||
|
scopes http_response_2
|
||||||
|
|
||||||
|
otel-scope http_response_1
|
||||||
|
span "HTTP response"
|
||||||
|
event "event_1" "hdr.content" res.hdr("content-type") str("; length: ") res.hdr("content-length") str(" bytes")
|
||||||
|
|
||||||
|
otel-scope http_response_2
|
||||||
|
span "HTTP response"
|
||||||
|
event "event_2" "hdr.date" res.hdr("date") str(" / ") res.hdr("last-modified")
|
||||||
|
|
||||||
|
otel-group http_after_response_group
|
||||||
|
scopes http_after_response
|
||||||
|
|
||||||
|
otel-scope http_after_response
|
||||||
|
span "HAProxy response" parent "HAProxy session"
|
||||||
|
status "error" str("http.status_code") status
|
||||||
|
|
||||||
|
otel-scope client_session_start_1
|
||||||
|
span "HAProxy session" root
|
||||||
|
inject "otel_ctx_1" use-headers use-vars
|
||||||
|
baggage "haproxy_id" var(sess.otel.uuid)
|
||||||
|
otel-event on-client-session-start
|
||||||
|
|
||||||
|
otel-scope client_session_start_2
|
||||||
|
extract "otel_ctx_1" use-vars
|
||||||
|
span "Client session" parent "otel_ctx_1"
|
||||||
|
inject "otel_ctx_2" use-headers use-vars
|
||||||
|
otel-event on-client-session-start
|
||||||
|
|
||||||
|
otel-scope frontend_tcp_request
|
||||||
|
extract "otel_ctx_2" use-vars
|
||||||
|
span "Frontend TCP request" parent "otel_ctx_2"
|
||||||
|
inject "otel_ctx_3" use-headers use-vars
|
||||||
|
otel-event on-frontend-tcp-request
|
||||||
|
|
||||||
|
otel-scope http_wait_request
|
||||||
|
extract "otel_ctx_3" use-vars
|
||||||
|
span "HTTP wait request" parent "otel_ctx_3"
|
||||||
|
inject "otel_ctx_4" use-headers use-vars
|
||||||
|
finish "Frontend TCP request" "otel_ctx_3"
|
||||||
|
otel-event on-http-wait-request
|
||||||
|
|
||||||
|
otel-scope http_body_request
|
||||||
|
extract "otel_ctx_4" use-vars
|
||||||
|
span "HTTP body request" parent "otel_ctx_4"
|
||||||
|
inject "otel_ctx_5" use-headers use-vars
|
||||||
|
finish "HTTP wait request" "otel_ctx_4"
|
||||||
|
otel-event on-http-body-request
|
||||||
|
|
||||||
|
otel-scope frontend_http_request
|
||||||
|
extract "otel_ctx_5" use-vars
|
||||||
|
span "Frontend HTTP request" parent "otel_ctx_5"
|
||||||
|
attribute "http.method" method
|
||||||
|
attribute "http.url" url
|
||||||
|
attribute "http.version" str("HTTP/") req.ver
|
||||||
|
inject "otel_ctx_6" use-headers use-vars
|
||||||
|
finish "HTTP body request" "otel_ctx_5"
|
||||||
|
otel-event on-frontend-http-request
|
||||||
|
|
||||||
|
otel-scope switching_rules_request
|
||||||
|
extract "otel_ctx_6" use-vars
|
||||||
|
span "Switching rules request" parent "otel_ctx_6"
|
||||||
|
inject "otel_ctx_7" use-headers use-vars
|
||||||
|
finish "Frontend HTTP request" "otel_ctx_6"
|
||||||
|
otel-event on-switching-rules-request
|
||||||
|
|
||||||
|
otel-scope backend_tcp_request
|
||||||
|
extract "otel_ctx_7" use-vars
|
||||||
|
span "Backend TCP request" parent "otel_ctx_7"
|
||||||
|
inject "otel_ctx_8" use-headers use-vars
|
||||||
|
finish "Switching rules request" "otel_ctx_7"
|
||||||
|
otel-event on-backend-tcp-request
|
||||||
|
|
||||||
|
otel-scope backend_http_request
|
||||||
|
extract "otel_ctx_8" use-vars
|
||||||
|
span "Backend HTTP request" parent "otel_ctx_8"
|
||||||
|
inject "otel_ctx_9" use-headers use-vars
|
||||||
|
finish "Backend TCP request" "otel_ctx_8"
|
||||||
|
otel-event on-backend-http-request
|
||||||
|
|
||||||
|
otel-scope process_server_rules_request
|
||||||
|
extract "otel_ctx_9" use-vars
|
||||||
|
span "Process server rules request" parent "otel_ctx_9"
|
||||||
|
inject "otel_ctx_10" use-headers use-vars
|
||||||
|
finish "Backend HTTP request" "otel_ctx_9"
|
||||||
|
otel-event on-process-server-rules-request
|
||||||
|
|
||||||
|
otel-scope http_process_request
|
||||||
|
extract "otel_ctx_10" use-vars
|
||||||
|
span "HTTP process request" parent "otel_ctx_10"
|
||||||
|
inject "otel_ctx_11" use-headers use-vars
|
||||||
|
finish "Process server rules request" "otel_ctx_10"
|
||||||
|
otel-event on-http-process-request
|
||||||
|
|
||||||
|
otel-scope tcp_rdp_cookie_request
|
||||||
|
extract "otel_ctx_11" use-vars
|
||||||
|
span "TCP RDP cookie request" parent "otel_ctx_11"
|
||||||
|
inject "otel_ctx_12" use-headers use-vars
|
||||||
|
finish "HTTP process request" "otel_ctx_11"
|
||||||
|
otel-event on-tcp-rdp-cookie-request
|
||||||
|
|
||||||
|
otel-scope process_sticking_rules_request
|
||||||
|
extract "otel_ctx_12" use-vars
|
||||||
|
span "Process sticking rules request" parent "otel_ctx_12"
|
||||||
|
inject "otel_ctx_13" use-headers use-vars
|
||||||
|
finish "TCP RDP cookie request" "otel_ctx_12"
|
||||||
|
otel-event on-process-sticking-rules-request
|
||||||
|
|
||||||
|
otel-scope client_session_end
|
||||||
|
finish "Client session" "otel_ctx_2"
|
||||||
|
otel-event on-client-session-end
|
||||||
|
|
||||||
|
otel-scope server_unavailable
|
||||||
|
finish *
|
||||||
|
otel-event on-server-unavailable
|
||||||
|
|
||||||
|
otel-scope server_session_start
|
||||||
|
span "Server session" parent "otel_ctx_1"
|
||||||
|
inject "otel_ctx_14" use-vars
|
||||||
|
extract "otel_ctx_13" use-vars
|
||||||
|
finish "Process sticking rules request" "otel_ctx_13"
|
||||||
|
otel-event on-server-session-start
|
||||||
|
|
||||||
|
otel-scope tcp_response
|
||||||
|
extract "otel_ctx_14" use-vars
|
||||||
|
span "TCP response" parent "otel_ctx_14"
|
||||||
|
inject "otel_ctx_15" use-vars
|
||||||
|
otel-event on-tcp-response
|
||||||
|
|
||||||
|
otel-scope http_wait_response
|
||||||
|
extract "otel_ctx_15" use-vars
|
||||||
|
span "HTTP wait response" parent "otel_ctx_15"
|
||||||
|
inject "otel_ctx_16" use-headers use-vars
|
||||||
|
finish "TCP response" "otel_ctx_15"
|
||||||
|
otel-event on-http-wait-response
|
||||||
|
|
||||||
|
otel-scope process_store_rules_response
|
||||||
|
extract "otel_ctx_16" use-vars
|
||||||
|
span "Process store rules response" parent "otel_ctx_16"
|
||||||
|
inject "otel_ctx_17" use-headers use-vars
|
||||||
|
finish "HTTP wait response" "otel_ctx_16"
|
||||||
|
otel-event on-process-store-rules-response
|
||||||
|
|
||||||
|
otel-scope http_response
|
||||||
|
extract "otel_ctx_17" use-vars
|
||||||
|
span "HTTP response" parent "otel_ctx_17"
|
||||||
|
attribute "http.status_code" status
|
||||||
|
finish "Process store rules response" "otel_ctx_17"
|
||||||
|
otel-event on-http-response
|
||||||
|
|
||||||
|
otel-scope http_response-error
|
||||||
|
span "HTTP response"
|
||||||
|
status "error" str("!acl-http-status-ok")
|
||||||
|
otel-event on-http-response if !acl-http-status-ok
|
||||||
|
|
||||||
|
otel-scope server_session_end
|
||||||
|
finish *
|
||||||
|
otel-event on-server-session-end
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue