Skip to content

Commit 3f5301c

Browse files
authored
Set up UDP Release Workflow (#333)
**Description of changes:** Setting up the validation steps for the GitHub workflow we intend to use to manually trigger a UDP exporter release to PyPI. At a high-level, this validation step will: - Build the UDP exporter. - Set up and run an instance of X-Ray daemon. - Run a validation app to send traces to X-Ray daemon via UDP exporter. - Validation step to verify that daemon received the traces. TODO: - Add a publish to PyPI step to release workflow. **Test plan:** Triggered workflow in feature branch: https://github.com/aws-observability/aws-otel-python-instrumentation/actions/runs/13663595075 By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
1 parent 5d41039 commit 3f5301c

File tree

3 files changed

+129
-4
lines changed

3 files changed

+129
-4
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
name: Release ADOT OTLP UDP Exporter
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: 'Version number for deployment e.g. 0.1.0'
8+
required: true
9+
type: string
10+
11+
jobs:
12+
build-test-publish:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v3
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v4
19+
with:
20+
python-version: '3.10'
21+
22+
- name: Install dependencies
23+
run: |
24+
python -m pip install --upgrade pip
25+
pip install hatch pytest flask
26+
27+
- name: Build package
28+
working-directory: exporters/aws-otel-otlp-udp-exporter
29+
run: hatch build
30+
31+
- name: Download and run X-Ray Daemon
32+
run: |
33+
mkdir xray-daemon
34+
cd xray-daemon
35+
wget https://s3.us-west-2.amazonaws.com/aws-xray-assets.us-west-2/xray-daemon/aws-xray-daemon-linux-3.x.zip
36+
unzip aws-xray-daemon-linux-3.x.zip
37+
./xray -o -n us-west-2 -f ./daemon-logs.log --log-level debug &
38+
39+
- name: Install UDP Exporter
40+
run: |
41+
pip install ./exporters/aws-otel-otlp-udp-exporter/dist/*.whl
42+
43+
- name: Ensure Unit Tests are passing
44+
run: |
45+
pytest exporters/aws-otel-otlp-udp-exporter/tests/
46+
47+
- name: Run Sample App in Background
48+
working-directory: sample-applications/integ-test-app
49+
run: |
50+
# Start validation app
51+
python udp_exporter_validation_app.py &
52+
# Wait for validation app to initialize
53+
sleep 5
54+
55+
- name: Call Sample App Endpoint
56+
run: |
57+
echo "traceId=$(curl localhost:8080/test)" >> $GITHUB_OUTPUT
58+
59+
- name: Verify X-Ray daemon received traces
60+
run: |
61+
sleep 10
62+
echo "X-Ray daemon logs:"
63+
cat xray-daemon/daemon-logs.log
64+
65+
# Check if the daemon received and processed some data
66+
if grep -q "sending.*batch" xray-daemon/daemon-logs.log; then
67+
echo "✅ X-Ray daemon processed trace data (AWS upload errors are expected)"
68+
exit 0
69+
elif grep -q "processor:.*segment" xray-daemon/daemon-logs.log; then
70+
echo "✅ X-Ray daemon processed segment data (AWS upload errors are expected)"
71+
exit 0
72+
else
73+
echo "❌ No evidence of traces being received by X-Ray daemon"
74+
exit 1
75+
fi
76+
77+
# TODO: Steps to publish to PyPI

exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/udp/exporter.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@
2323

2424
class UdpExporter:
2525
def __init__(self, endpoint: Optional[str] = None):
26-
if endpoint is None and "AWS_LAMBDA_FUNCTION_NAME" in os.environ:
27-
# If in an AWS Lambda Environment, `AWS_XRAY_DAEMON_ADDRESS` will be defined
28-
endpoint = os.environ.get("AWS_XRAY_DAEMON_ADDRESS", DEFAULT_ENDPOINT)
29-
3026
self._endpoint = endpoint or DEFAULT_ENDPOINT
3127
self._host, self._port = self._parse_endpoint(self._endpoint)
3228
self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
@@ -61,6 +57,10 @@ def _parse_endpoint(self, endpoint: str) -> Tuple[str, int]:
6157

6258
class OTLPUdpSpanExporter(SpanExporter):
6359
def __init__(self, endpoint: Optional[str] = None, sampled: bool = True):
60+
if endpoint is None and "AWS_LAMBDA_FUNCTION_NAME" in os.environ:
61+
# If in an AWS Lambda Environment, `AWS_XRAY_DAEMON_ADDRESS` will be defined
62+
endpoint = os.environ.get("AWS_XRAY_DAEMON_ADDRESS", DEFAULT_ENDPOINT)
63+
6464
self._udp_exporter = UdpExporter(endpoint=endpoint)
6565
self._sampled = sampled
6666

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from flask import Flask
2+
3+
from amazon.opentelemetry.exporters.otlp.udp import OTLPUdpSpanExporter
4+
from opentelemetry import trace
5+
from opentelemetry.sdk.trace import TracerProvider
6+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
7+
8+
app = Flask(__name__)
9+
10+
# Set up tracer provider
11+
tracer_provider = TracerProvider()
12+
trace.set_tracer_provider(tracer_provider)
13+
14+
# Set up UDP exporter with batch processor
15+
exporter = OTLPUdpSpanExporter(endpoint="127.0.0.1:2000")
16+
span_processor = BatchSpanProcessor(exporter)
17+
tracer_provider.add_span_processor(span_processor)
18+
19+
# Get tracer
20+
tracer = trace.get_tracer(__name__)
21+
22+
23+
@app.route("/test", methods=["GET"])
24+
def create_trace():
25+
# Create a span for testing with various attributes
26+
tracer = trace.get_tracer(__name__)
27+
with tracer.start_as_current_span("test_parent_span") as parent:
28+
parent.set_attribute("service.name", "validation-app")
29+
parent.set_attribute("test.attribute", "test_value")
30+
parent.add_event("test-event", {"event.data": "some data"})
31+
32+
# Get the trace ID
33+
trace_id = format(parent.get_span_context().trace_id, "032x")
34+
35+
# Add a child span
36+
with tracer.start_as_current_span("test_child_span") as child:
37+
child.set_attribute("child.attribute", "child_value")
38+
print("Created spans with attributes and events")
39+
40+
# Force flush to ensure spans are exported immediately
41+
success = tracer_provider.force_flush()
42+
print(f"Force flush {'succeeded' if success else 'failed'}")
43+
44+
return trace_id
45+
46+
47+
if __name__ == "__main__":
48+
app.run(port=8080)

0 commit comments

Comments
 (0)