Skip to content

Commit b981ee4

Browse files
authored
Merge branch 'main' into boto-normalization
2 parents fcf8b18 + 5fa9864 commit b981ee4

File tree

27 files changed

+507
-165
lines changed

27 files changed

+507
-165
lines changed

.github/actions/artifacts_build/action.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ runs:
4444
run_unit_tests: true
4545

4646
- name: Configure AWS Credentials
47+
if: inputs.push_image == true
4748
uses: aws-actions/configure-aws-credentials@v4
4849
with:
4950
role-to-assume: ${{ inputs.snapshot-ecr-role }}
@@ -65,6 +66,7 @@ runs:
6566
uses: docker/setup-buildx-action@v3
6667

6768
- name: Login to AWS ECR
69+
if: inputs.push_image == true
6870
uses: docker/login-action@v3
6971
with:
7072
registry: ${{ inputs.image_registry }}

aws-opentelemetry-distro/src/amazon/opentelemetry/distro/aws_opentelemetry_configurator.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,22 @@ def _init_tracing(
146146
set_tracer_provider(trace_provider)
147147

148148

149+
def _exclude_urls_for_instrumentations():
150+
urls_to_exclude_instr = "SamplingTargets,GetSamplingRules"
151+
requests_excluded_urls = os.environ.pop("OTEL_PYTHON_REQUESTS_EXCLUDED_URLS", "")
152+
urllib3_excluded_urls = os.environ.pop("OTEL_PYTHON_URLLIB3_EXCLUDED_URLS", "")
153+
if len(requests_excluded_urls) > 0:
154+
requests_excluded_urls = ",".join([requests_excluded_urls, urls_to_exclude_instr])
155+
else:
156+
requests_excluded_urls = urls_to_exclude_instr
157+
if len(urllib3_excluded_urls) > 0:
158+
urllib3_excluded_urls = ",".join([urllib3_excluded_urls, urls_to_exclude_instr])
159+
else:
160+
urllib3_excluded_urls = urls_to_exclude_instr
161+
os.environ.setdefault("OTEL_PYTHON_URLLIB3_EXCLUDED_URLS", requests_excluded_urls)
162+
os.environ.setdefault("OTEL_PYTHON_REQUESTS_EXCLUDED_URLS", urllib3_excluded_urls)
163+
164+
149165
def _custom_import_sampler(sampler_name: str, resource: Resource) -> Sampler:
150166
if sampler_name == "xray":
151167
# Example env var value
@@ -167,6 +183,11 @@ def _custom_import_sampler(sampler_name: str, resource: Resource) -> Sampler:
167183
polling_interval = int(key_value[1])
168184
except ValueError as error:
169185
_logger.error("polling_interval in OTEL_TRACES_SAMPLER_ARG must be a number: %s", error)
186+
# Until `suppress_instrumentation` is available in next OTEL Python version (>=1.23.0/0.44b0),
187+
# suppress recording of X-Ray sampler's Request POST calls via setting `exclude urls` Env Vars. This
188+
# should be done in this class's `_configure()` method which is run before any instrumentation is loaded
189+
# TODO: Replace usage of `exclude urls` by wrapping X-Ray sampler POST calls with `suppress_instrumentation`
190+
_exclude_urls_for_instrumentations()
170191

171192
_logger.debug("XRay Sampler Endpoint: %s", str(endpoint))
172193
_logger.debug("XRay Sampler Polling Interval: %s", str(polling_interval))

aws-opentelemetry-distro/tests/amazon/opentelemetry/distro/sampler/test_aws_xray_sampling_client.py

Lines changed: 109 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
# SPDX-License-Identifier: Apache-2.0
33
import json
4+
import logging
45
import os
6+
from importlib import reload
57
from logging import getLogger
68
from unittest import TestCase
79
from unittest.mock import patch
810

11+
import requests
12+
13+
import opentelemetry.instrumentation.requests as requests_instrumentation
14+
import opentelemetry.instrumentation.urllib3 as urllib3_instrumentation
915
from amazon.opentelemetry.distro.sampler._aws_xray_sampling_client import _AwsXRaySamplingClient
16+
from opentelemetry import trace as trace_api
17+
from opentelemetry.instrumentation.requests import RequestsInstrumentor
18+
from opentelemetry.instrumentation.urllib3 import URLLib3Instrumentor
19+
from opentelemetry.sdk.trace import TracerProvider, export
20+
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
21+
from opentelemetry.sdk.trace.sampling import ALWAYS_ON
22+
from opentelemetry.util._once import Once
1023

1124
SAMPLING_CLIENT_LOGGER_NAME = "amazon.opentelemetry.distro.sampler._aws_xray_sampling_client"
12-
_logger = getLogger(SAMPLING_CLIENT_LOGGER_NAME)
25+
_sampling_client_logger = getLogger(SAMPLING_CLIENT_LOGGER_NAME)
26+
_logger = getLogger(__name__)
1327

1428
TEST_DIR = os.path.dirname(os.path.realpath(__file__))
1529
DATA_DIR = os.path.join(TEST_DIR, "data")
@@ -27,15 +41,15 @@ def test_get_no_sampling_rules(self, mock_post=None):
2741
def test_get_invalid_responses(self, mock_post=None):
2842
mock_post.return_value.configure_mock(**{"json.return_value": {}})
2943
client = _AwsXRaySamplingClient("http://127.0.0.1:2000")
30-
with self.assertLogs(_logger, level="ERROR"):
44+
with self.assertLogs(_sampling_client_logger, level="ERROR"):
3145
sampling_rules = client.get_sampling_rules()
3246
self.assertTrue(len(sampling_rules) == 0)
3347

3448
@patch("requests.Session.post")
3549
def test_get_sampling_rule_missing_in_records(self, mock_post=None):
3650
mock_post.return_value.configure_mock(**{"json.return_value": {"SamplingRuleRecords": [{}]}})
3751
client = _AwsXRaySamplingClient("http://127.0.0.1:2000")
38-
with self.assertLogs(_logger, level="ERROR"):
52+
with self.assertLogs(_sampling_client_logger, level="ERROR"):
3953
sampling_rules = client.get_sampling_rules()
4054
self.assertTrue(len(sampling_rules) == 0)
4155

@@ -132,3 +146,95 @@ def test_get_invalid_sampling_targets(self, mock_post=None):
132146
self.assertEqual(sampling_targets_response.SamplingTargetDocuments, [])
133147
self.assertEqual(sampling_targets_response.UnprocessedStatistics, [])
134148
self.assertEqual(sampling_targets_response.LastRuleModification, 0.0)
149+
150+
# pylint: disable=too-many-statements
151+
def test_urls_excluded_from_sampling(self):
152+
"""
153+
This test case needs the following trace_api configurations since
154+
TestAwsOpenTelemetryConfigurator has already set tracer_provider.
155+
156+
See `reset_trace_globals()`:
157+
https://github.com/open-telemetry/opentelemetry-python/blob/main/tests/opentelemetry-test-utils/src/opentelemetry/test/globals_test.py
158+
"""
159+
trace_api._TRACER_PROVIDER_SET_ONCE = Once()
160+
trace_api._TRACER_PROVIDER = None
161+
trace_api._PROXY_TRACER_PROVIDER = trace_api.ProxyTracerProvider()
162+
163+
tracer_provider = TracerProvider(sampler=ALWAYS_ON)
164+
memory_exporter = InMemorySpanExporter()
165+
span_processor = export.SimpleSpanProcessor(memory_exporter)
166+
tracer_provider.add_span_processor(span_processor)
167+
trace_api.set_tracer_provider(tracer_provider)
168+
169+
# Reload instrumentors, where Sampling calls are instrumented for requests/urllib3
170+
RequestsInstrumentor().uninstrument()
171+
URLLib3Instrumentor().uninstrument()
172+
os.environ.pop("OTEL_PYTHON_REQUESTS_EXCLUDED_URLS", None)
173+
os.environ.pop("OTEL_PYTHON_URLLIB3_EXCLUDED_URLS", None)
174+
reload(requests_instrumentation)
175+
reload(urllib3_instrumentation)
176+
RequestsInstrumentor().instrument()
177+
URLLib3Instrumentor().instrument()
178+
179+
client = _AwsXRaySamplingClient("http://this_is_a_fake_url:3849", log_level=logging.CRITICAL)
180+
181+
span_list = memory_exporter.get_finished_spans()
182+
self.assertEqual(0, len(span_list))
183+
184+
try:
185+
client.get_sampling_rules()
186+
except requests.exceptions.RequestException:
187+
pass
188+
189+
span_list = memory_exporter.get_finished_spans()
190+
self.assertEqual(1, len(span_list))
191+
span_http_url = span_list[0].attributes.get("http.url")
192+
self.assertEqual(span_http_url, "http://this_is_a_fake_url:3849/GetSamplingRules")
193+
194+
try:
195+
client.get_sampling_targets([])
196+
except requests.exceptions.RequestException:
197+
pass
198+
199+
span_list = memory_exporter.get_finished_spans()
200+
self.assertEqual(2, len(span_list))
201+
span_http_url = span_list[1].attributes.get("http.url")
202+
self.assertEqual(span_http_url, "http://this_is_a_fake_url:3849/SamplingTargets")
203+
204+
# Reload instrumentors, this time with Env Vars to exclude Sampling URLs for requests/urllib3
205+
urls_to_exclude_instr = (
206+
",,,SamplingTargets,,endpoint1,endpoint2,,,GetSamplingRules,,SamplingTargets,GetSamplingRules"
207+
)
208+
209+
memory_exporter.clear()
210+
URLLib3Instrumentor().uninstrument()
211+
RequestsInstrumentor().uninstrument()
212+
os.environ.pop("OTEL_PYTHON_REQUESTS_EXCLUDED_URLS", None)
213+
os.environ.pop("OTEL_PYTHON_URLLIB3_EXCLUDED_URLS", None)
214+
os.environ.setdefault("OTEL_PYTHON_REQUESTS_EXCLUDED_URLS", urls_to_exclude_instr)
215+
os.environ.setdefault("OTEL_PYTHON_URLLIB3_EXCLUDED_URLS", urls_to_exclude_instr)
216+
reload(requests_instrumentation)
217+
reload(urllib3_instrumentation)
218+
RequestsInstrumentor().instrument()
219+
URLLib3Instrumentor().instrument()
220+
221+
client = _AwsXRaySamplingClient("http://this_is_a_fake_url:3849", log_level=logging.CRITICAL)
222+
223+
try:
224+
client.get_sampling_rules()
225+
except requests.exceptions.RequestException:
226+
pass
227+
228+
span_list = memory_exporter.get_finished_spans()
229+
self.assertEqual(0, len(span_list))
230+
231+
try:
232+
client.get_sampling_targets([])
233+
except requests.exceptions.RequestException:
234+
pass
235+
236+
span_list = memory_exporter.get_finished_spans()
237+
self.assertEqual(0, len(span_list))
238+
239+
URLLib3Instrumentor().uninstrument()
240+
RequestsInstrumentor().uninstrument()

aws-opentelemetry-distro/tests/amazon/opentelemetry/distro/test_aws_opentelementry_configurator.py

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,8 @@ def test_trace_id_ratio_sampler(self):
6666
@patch.object(AwsXRayRemoteSampler, "_AwsXRayRemoteSampler__start_sampling_rule_poller", lambda x: None)
6767
@patch.object(AwsXRayRemoteSampler, "_AwsXRayRemoteSampler__start_sampling_target_poller", lambda x: None)
6868
def test_import_xray_sampler_without_environment_arguments(self):
69-
os.environ.pop(OTEL_TRACES_SAMPLER_ARG)
69+
os.environ.pop(OTEL_TRACES_SAMPLER_ARG, None)
7070

71-
# May log http request error as xray sampler will attempt to fetch rules
7271
xray_sampler: Sampler = _custom_import_sampler("xray", resource=None)
7372
xray_client: _AwsXRaySamplingClient = xray_sampler._AwsXRayRemoteSampler__xray_client
7473
self.assertEqual(xray_sampler._AwsXRayRemoteSampler__polling_interval, 300)
@@ -79,32 +78,29 @@ def test_import_xray_sampler_without_environment_arguments(self):
7978
@patch.object(AwsXRayRemoteSampler, "_AwsXRayRemoteSampler__start_sampling_rule_poller", lambda x: None)
8079
@patch.object(AwsXRayRemoteSampler, "_AwsXRayRemoteSampler__start_sampling_target_poller", lambda x: None)
8180
def test_import_xray_sampler_with_valid_environment_arguments(self):
82-
os.environ.pop(OTEL_TRACES_SAMPLER_ARG)
81+
os.environ.pop(OTEL_TRACES_SAMPLER_ARG, None)
8382
os.environ.setdefault(OTEL_TRACES_SAMPLER_ARG, "endpoint=http://localhost:2000,polling_interval=600")
8483

85-
# May log http request error as xray sampler will attempt to fetch rules
8684
xray_sampler: Sampler = _custom_import_sampler("xray", resource=None)
8785
xray_client: _AwsXRaySamplingClient = xray_sampler._AwsXRayRemoteSampler__xray_client
8886
self.assertEqual(xray_sampler._AwsXRayRemoteSampler__polling_interval, 600)
8987
self.assertEqual(
9088
xray_client._AwsXRaySamplingClient__get_sampling_rules_endpoint, "http://localhost:2000/GetSamplingRules"
9189
)
9290

93-
os.environ.pop(OTEL_TRACES_SAMPLER_ARG)
91+
os.environ.pop(OTEL_TRACES_SAMPLER_ARG, None)
9492
os.environ.setdefault(OTEL_TRACES_SAMPLER_ARG, "polling_interval=123")
9593

96-
# May log http request error as xray sampler will attempt to fetch rules
9794
xray_sampler: Sampler = _custom_import_sampler("xray", resource=None)
9895
xray_client: _AwsXRaySamplingClient = xray_sampler._AwsXRayRemoteSampler__xray_client
9996
self.assertEqual(xray_sampler._AwsXRayRemoteSampler__polling_interval, 123)
10097
self.assertEqual(
10198
xray_client._AwsXRaySamplingClient__get_sampling_rules_endpoint, "http://127.0.0.1:2000/GetSamplingRules"
10299
)
103100

104-
os.environ.pop(OTEL_TRACES_SAMPLER_ARG)
101+
os.environ.pop(OTEL_TRACES_SAMPLER_ARG, None)
105102
os.environ.setdefault(OTEL_TRACES_SAMPLER_ARG, "endpoint=http://cloudwatch-agent.amazon-cloudwatch:2000")
106103

107-
# May log http request error as xray sampler will attempt to fetch rules
108104
xray_sampler: Sampler = _custom_import_sampler("xray", resource=None)
109105
xray_client: _AwsXRaySamplingClient = xray_sampler._AwsXRayRemoteSampler__xray_client
110106
self.assertEqual(xray_sampler._AwsXRayRemoteSampler__polling_interval, 300)
@@ -116,10 +112,9 @@ def test_import_xray_sampler_with_valid_environment_arguments(self):
116112
@patch.object(AwsXRayRemoteSampler, "_AwsXRayRemoteSampler__start_sampling_rule_poller", lambda x: None)
117113
@patch.object(AwsXRayRemoteSampler, "_AwsXRayRemoteSampler__start_sampling_target_poller", lambda x: None)
118114
def test_import_xray_sampler_with_invalid_environment_arguments(self):
119-
os.environ.pop(OTEL_TRACES_SAMPLER_ARG)
115+
os.environ.pop(OTEL_TRACES_SAMPLER_ARG, None)
120116
os.environ.setdefault(OTEL_TRACES_SAMPLER_ARG, "endpoint=h=tt=p://=loca=lho=st:2000,polling_interval=FOOBAR")
121117

122-
# May log http request error as xray sampler will attempt to fetch rules
123118
xray_sampler: Sampler = _custom_import_sampler("xray", resource=None)
124119
xray_client: _AwsXRaySamplingClient = xray_sampler._AwsXRayRemoteSampler__xray_client
125120
self.assertEqual(xray_sampler._AwsXRayRemoteSampler__polling_interval, 300)
@@ -128,24 +123,63 @@ def test_import_xray_sampler_with_invalid_environment_arguments(self):
128123
"h=tt=p://=loca=lho=st:2000/GetSamplingRules",
129124
)
130125

131-
os.environ.pop(OTEL_TRACES_SAMPLER_ARG)
126+
os.environ.pop(OTEL_TRACES_SAMPLER_ARG, None)
132127
os.environ.setdefault(OTEL_TRACES_SAMPLER_ARG, ",,=,==,,===,")
133128

134-
# May log http request error as xray sampler will attempt to fetch rules
135129
xray_sampler: Sampler = _custom_import_sampler("xray", resource=None)
136130
xray_client: _AwsXRaySamplingClient = xray_sampler._AwsXRayRemoteSampler__xray_client
137131
self.assertEqual(xray_sampler._AwsXRayRemoteSampler__polling_interval, 300)
138132
self.assertEqual(
139133
xray_client._AwsXRaySamplingClient__get_sampling_rules_endpoint, "http://127.0.0.1:2000/GetSamplingRules"
140134
)
141135

142-
os.environ.pop(OTEL_TRACES_SAMPLER_ARG)
136+
os.environ.pop(OTEL_TRACES_SAMPLER_ARG, None)
143137
os.environ.setdefault(OTEL_TRACES_SAMPLER_ARG, "endpoint,polling_interval")
144138

145-
# May log http request error as xray sampler will attempt to fetch rules
146139
xray_sampler: Sampler = _custom_import_sampler("xray", resource=None)
147140
xray_client: _AwsXRaySamplingClient = xray_sampler._AwsXRayRemoteSampler__xray_client
148141
self.assertEqual(xray_sampler._AwsXRayRemoteSampler__polling_interval, 300)
149142
self.assertEqual(
150143
xray_client._AwsXRaySamplingClient__get_sampling_rules_endpoint, "http://127.0.0.1:2000/GetSamplingRules"
151144
)
145+
146+
def test_using_xray_sampler_sets_url_exclusion_env_vars(self):
147+
targets_to_exclude = "SamplingTargets,GetSamplingRules"
148+
os.environ.pop("OTEL_PYTHON_REQUESTS_EXCLUDED_URLS", None)
149+
os.environ.pop("OTEL_PYTHON_URLLIB3_EXCLUDED_URLS", None)
150+
self.assertEqual(os.environ.get("OTEL_PYTHON_REQUESTS_EXCLUDED_URLS", None), None)
151+
self.assertEqual(os.environ.get("OTEL_PYTHON_URLLIB3_EXCLUDED_URLS", None), None)
152+
153+
_: Sampler = _custom_import_sampler("xray", resource=None)
154+
self.assertEqual(os.environ.get("OTEL_PYTHON_REQUESTS_EXCLUDED_URLS", None), targets_to_exclude)
155+
self.assertEqual(os.environ.get("OTEL_PYTHON_URLLIB3_EXCLUDED_URLS", None), targets_to_exclude)
156+
157+
def test_using_xray_sampler_appends_url_exclusion_env_vars(self):
158+
targets_to_exclude = "SamplingTargets,GetSamplingRules"
159+
os.environ.pop("OTEL_PYTHON_REQUESTS_EXCLUDED_URLS", None)
160+
os.environ.pop("OTEL_PYTHON_URLLIB3_EXCLUDED_URLS", None)
161+
self.assertEqual(os.environ.get("OTEL_PYTHON_REQUESTS_EXCLUDED_URLS", None), None)
162+
self.assertEqual(os.environ.get("OTEL_PYTHON_URLLIB3_EXCLUDED_URLS", None), None)
163+
os.environ.setdefault("OTEL_PYTHON_REQUESTS_EXCLUDED_URLS", ",,,target_A,target_B,,,")
164+
os.environ.setdefault("OTEL_PYTHON_URLLIB3_EXCLUDED_URLS", "target_C,target_D")
165+
166+
_: Sampler = _custom_import_sampler("xray", resource=None)
167+
self.assertTrue(targets_to_exclude in os.environ.get("OTEL_PYTHON_REQUESTS_EXCLUDED_URLS", None))
168+
self.assertTrue(targets_to_exclude in os.environ.get("OTEL_PYTHON_URLLIB3_EXCLUDED_URLS", None))
169+
170+
def test_not_using_xray_sampler_does_not_modify_url_exclusion_env_vars(self):
171+
os.environ.pop("OTEL_PYTHON_REQUESTS_EXCLUDED_URLS", None)
172+
os.environ.pop("OTEL_PYTHON_URLLIB3_EXCLUDED_URLS", None)
173+
self.assertEqual(os.environ.get("OTEL_PYTHON_REQUESTS_EXCLUDED_URLS", None), None)
174+
self.assertEqual(os.environ.get("OTEL_PYTHON_URLLIB3_EXCLUDED_URLS", None), None)
175+
176+
_: Sampler = _custom_import_sampler("traceidratio", resource=None)
177+
self.assertEqual(os.environ.get("OTEL_PYTHON_REQUESTS_EXCLUDED_URLS", None), None)
178+
self.assertEqual(os.environ.get("OTEL_PYTHON_URLLIB3_EXCLUDED_URLS", None), None)
179+
180+
os.environ.setdefault("OTEL_PYTHON_REQUESTS_EXCLUDED_URLS", ",,,target_A,target_B,,,")
181+
os.environ.setdefault("OTEL_PYTHON_URLLIB3_EXCLUDED_URLS", "target_C,target_D")
182+
183+
_: Sampler = _custom_import_sampler("traceidratio", resource=None)
184+
self.assertEqual(os.environ.get("OTEL_PYTHON_REQUESTS_EXCLUDED_URLS", None), ",,,target_A,target_B,,,")
185+
self.assertEqual(os.environ.get("OTEL_PYTHON_URLLIB3_EXCLUDED_URLS", None), "target_C,target_D")

0 commit comments

Comments
 (0)