Skip to content

Commit 5ce4162

Browse files
authored
Temporarily Default to XRay Remote Sampler is Sampler is not Specified (#64)
*Issue #, if available:* Use X-Ray default sampler as default sampler if user has not specified one Requires #55 to be merged first before this PR can work. *Description of changes:* Use X-Ray default sampler as default sampler if user has not specified one *Testing:* Assume #55 is merged 1. Enabled span_metrics_processor and added debug statement to print out Sampled status of a span in `on_start()` ``` # aws_span_metrics_processor.py if span.get_span_context().trace_flags.sampled: print("sampled") ``` 2. Replaced resource detectors and manually set resource with service.name=test-service-name and build ADOT SDK 3. Setup OTel collector with XRay proxy for sampling and AWS credentials 4. In AWS account, create sampling rule to match service_name=test-service-name 5. Setup sample app in `sample-applications/simple-client-server/server_automatic_s3client.py` 6. Update `sample-applications/simple-client-server/client.py` to call server/sample-app a variable number of times to verify sampling rule is applied 7. Repeat 6 after changing sampling rule rate/reservoir 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 453a3d5 commit 5ce4162

File tree

2 files changed

+137
-4
lines changed

2 files changed

+137
-4
lines changed

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

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
AwsMetricAttributesSpanExporterBuilder,
1515
)
1616
from amazon.opentelemetry.distro.aws_span_metrics_processor_builder import AwsSpanMetricsProcessorBuilder
17+
from amazon.opentelemetry.distro.sampler.aws_xray_remote_sampler import AwsXRayRemoteSampler
1718
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
1819
from opentelemetry.sdk._configuration import (
1920
_get_exporter_names,
@@ -26,7 +27,10 @@
2627
_init_metrics,
2728
_OTelSDKConfigurator,
2829
)
29-
from opentelemetry.sdk.environment_variables import _OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED
30+
from opentelemetry.sdk.environment_variables import (
31+
_OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED,
32+
OTEL_TRACES_SAMPLER_ARG,
33+
)
3034
from opentelemetry.sdk.extension.aws.resource.ec2 import AwsEc2ResourceDetector
3135
from opentelemetry.sdk.extension.aws.resource.ecs import AwsEcsResourceDetector
3236
from opentelemetry.sdk.extension.aws.resource.eks import AwsEksResourceDetector
@@ -81,8 +85,7 @@ def _initialize_components(auto_instrumentation_version):
8185
_get_exporter_names("metrics"),
8286
_get_exporter_names("logs"),
8387
)
84-
sampler_name = _get_sampler()
85-
sampler = _import_sampler(sampler_name)
88+
8689
id_generator_name = _get_id_generator()
8790
id_generator = _import_id_generator(id_generator_name)
8891
# if env var OTEL_RESOURCE_ATTRIBUTES is given, it will read the service_name
@@ -100,6 +103,9 @@ def _initialize_components(auto_instrumentation_version):
100103
]
101104
).merge(Resource.create(auto_resource))
102105

106+
sampler_name = _get_sampler()
107+
sampler = _custom_import_sampler(sampler_name, resource)
108+
103109
_init_tracing(
104110
exporters=trace_exporters,
105111
id_generator=id_generator,
@@ -137,6 +143,36 @@ def _init_tracing(
137143
set_tracer_provider(trace_provider)
138144

139145

146+
def _custom_import_sampler(sampler_name: str, resource: Resource) -> Sampler:
147+
# TODO: Remove `sampler_name is None` condition when xray sampler is configured here:
148+
# https://github.com/aws/amazon-cloudwatch-agent-operator/blob/main/pkg/instrumentation/defaultinstrumentation.go#L90
149+
if sampler_name is None or sampler_name == "xray":
150+
# Example env var value
151+
# OTEL_TRACES_SAMPLER_ARG=endpoint=http://localhost:2000,polling_interval=360
152+
sampler_argument_env: str = os.getenv(OTEL_TRACES_SAMPLER_ARG, None)
153+
endpoint: str = None
154+
polling_interval: int = None
155+
156+
if sampler_argument_env is not None:
157+
args = sampler_argument_env.split(",")
158+
for arg in args:
159+
key_value = arg.split("=", 1)
160+
if len(key_value) != 2:
161+
continue
162+
if key_value[0] == "endpoint":
163+
endpoint = key_value[1]
164+
elif key_value[0] == "polling_interval":
165+
try:
166+
polling_interval = int(key_value[1])
167+
except ValueError as error:
168+
_logger.error("polling_interval in OTEL_TRACES_SAMPLER_ARG must be a number: %s", error)
169+
170+
_logger.debug("XRay Sampler Endpoint: %s", str(endpoint))
171+
_logger.debug("XRay Sampler Polling Interval: %s", str(polling_interval))
172+
return AwsXRayRemoteSampler(resource=resource, endpoint=endpoint, polling_interval=polling_interval)
173+
return _import_sampler(sampler_name)
174+
175+
140176
def _customize_sampler(sampler: Sampler) -> Sampler:
141177
if not is_smp_enabled():
142178
return sampler

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

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,19 @@
33
import os
44
import time
55
from unittest import TestCase
6+
from unittest.mock import patch
67

7-
from amazon.opentelemetry.distro.aws_opentelemetry_configurator import AwsOpenTelemetryConfigurator
8+
from amazon.opentelemetry.distro.aws_opentelemetry_configurator import (
9+
AwsOpenTelemetryConfigurator,
10+
_custom_import_sampler,
11+
)
812
from amazon.opentelemetry.distro.aws_opentelemetry_distro import AwsOpenTelemetryDistro
13+
from amazon.opentelemetry.distro.sampler._aws_xray_sampling_client import _AwsXRaySamplingClient
14+
from amazon.opentelemetry.distro.sampler.aws_xray_remote_sampler import AwsXRayRemoteSampler
915
from opentelemetry.environment_variables import OTEL_LOGS_EXPORTER, OTEL_METRICS_EXPORTER, OTEL_TRACES_EXPORTER
1016
from opentelemetry.sdk.environment_variables import OTEL_TRACES_SAMPLER, OTEL_TRACES_SAMPLER_ARG
1117
from opentelemetry.sdk.trace import Span, Tracer, TracerProvider
18+
from opentelemetry.sdk.trace.sampling import Sampler
1219
from opentelemetry.trace import get_tracer_provider
1320

1421

@@ -52,3 +59,93 @@ def test_trace_id_ratio_sampler(self):
5259
span.end()
5360
# Configured for 1%, confirm there are at most 5% to account for randomness and reduce test flakiness.
5461
self.assertGreater(0.05, num_sampled / num_spans)
62+
63+
# Test method for importing xray sampler
64+
# Cannot test this logic via `aws_otel_configurator.configure()` because that will
65+
# attempt to setup tracer provider again, which can be only be done once (already done)
66+
@patch.object(AwsXRayRemoteSampler, "_AwsXRayRemoteSampler__start_sampling_rule_poller", lambda x: None)
67+
@patch.object(AwsXRayRemoteSampler, "_AwsXRayRemoteSampler__start_sampling_target_poller", lambda x: None)
68+
def test_import_xray_sampler_without_environment_arguments(self):
69+
os.environ.pop(OTEL_TRACES_SAMPLER_ARG, None)
70+
71+
# May log http request error as xray sampler will attempt to fetch rules
72+
xray_sampler: Sampler = _custom_import_sampler(None, resource=None)
73+
xray_client: _AwsXRaySamplingClient = xray_sampler._AwsXRayRemoteSampler__xray_client
74+
self.assertEqual(xray_sampler._AwsXRayRemoteSampler__polling_interval, 300)
75+
self.assertEqual(
76+
xray_client._AwsXRaySamplingClient__get_sampling_rules_endpoint, "http://127.0.0.1:2000/GetSamplingRules"
77+
)
78+
79+
@patch.object(AwsXRayRemoteSampler, "_AwsXRayRemoteSampler__start_sampling_rule_poller", lambda x: None)
80+
@patch.object(AwsXRayRemoteSampler, "_AwsXRayRemoteSampler__start_sampling_target_poller", lambda x: None)
81+
def test_import_xray_sampler_with_valid_environment_arguments(self):
82+
os.environ.pop(OTEL_TRACES_SAMPLER_ARG, None)
83+
os.environ.setdefault(OTEL_TRACES_SAMPLER_ARG, "endpoint=http://localhost:2000,polling_interval=600")
84+
85+
# May log http request error as xray sampler will attempt to fetch rules
86+
xray_sampler: Sampler = _custom_import_sampler(None, resource=None)
87+
xray_client: _AwsXRaySamplingClient = xray_sampler._AwsXRayRemoteSampler__xray_client
88+
self.assertEqual(xray_sampler._AwsXRayRemoteSampler__polling_interval, 600)
89+
self.assertEqual(
90+
xray_client._AwsXRaySamplingClient__get_sampling_rules_endpoint, "http://localhost:2000/GetSamplingRules"
91+
)
92+
93+
os.environ.pop(OTEL_TRACES_SAMPLER_ARG, None)
94+
os.environ.setdefault(OTEL_TRACES_SAMPLER_ARG, "polling_interval=123")
95+
96+
# May log http request error as xray sampler will attempt to fetch rules
97+
xray_sampler: Sampler = _custom_import_sampler(None, resource=None)
98+
xray_client: _AwsXRaySamplingClient = xray_sampler._AwsXRayRemoteSampler__xray_client
99+
self.assertEqual(xray_sampler._AwsXRayRemoteSampler__polling_interval, 123)
100+
self.assertEqual(
101+
xray_client._AwsXRaySamplingClient__get_sampling_rules_endpoint, "http://127.0.0.1:2000/GetSamplingRules"
102+
)
103+
104+
os.environ.pop(OTEL_TRACES_SAMPLER_ARG, None)
105+
os.environ.setdefault(OTEL_TRACES_SAMPLER_ARG, "endpoint=http://cloudwatch-agent.amazon-cloudwatch:2000")
106+
107+
# May log http request error as xray sampler will attempt to fetch rules
108+
xray_sampler: Sampler = _custom_import_sampler(None, resource=None)
109+
xray_client: _AwsXRaySamplingClient = xray_sampler._AwsXRayRemoteSampler__xray_client
110+
self.assertEqual(xray_sampler._AwsXRayRemoteSampler__polling_interval, 300)
111+
self.assertEqual(
112+
xray_client._AwsXRaySamplingClient__get_sampling_rules_endpoint,
113+
"http://cloudwatch-agent.amazon-cloudwatch:2000/GetSamplingRules",
114+
)
115+
116+
@patch.object(AwsXRayRemoteSampler, "_AwsXRayRemoteSampler__start_sampling_rule_poller", lambda x: None)
117+
@patch.object(AwsXRayRemoteSampler, "_AwsXRayRemoteSampler__start_sampling_target_poller", lambda x: None)
118+
def test_import_xray_sampler_with_invalid_environment_arguments(self):
119+
os.environ.pop(OTEL_TRACES_SAMPLER_ARG, None)
120+
os.environ.setdefault(OTEL_TRACES_SAMPLER_ARG, "endpoint=h=tt=p://=loca=lho=st:2000,polling_interval=FOOBAR")
121+
122+
# May log http request error as xray sampler will attempt to fetch rules
123+
xray_sampler: Sampler = _custom_import_sampler(None, resource=None)
124+
xray_client: _AwsXRaySamplingClient = xray_sampler._AwsXRayRemoteSampler__xray_client
125+
self.assertEqual(xray_sampler._AwsXRayRemoteSampler__polling_interval, 300)
126+
self.assertEqual(
127+
xray_client._AwsXRaySamplingClient__get_sampling_rules_endpoint,
128+
"h=tt=p://=loca=lho=st:2000/GetSamplingRules",
129+
)
130+
131+
os.environ.pop(OTEL_TRACES_SAMPLER_ARG, None)
132+
os.environ.setdefault(OTEL_TRACES_SAMPLER_ARG, ",,=,==,,===,")
133+
134+
# May log http request error as xray sampler will attempt to fetch rules
135+
xray_sampler: Sampler = _custom_import_sampler(None, resource=None)
136+
xray_client: _AwsXRaySamplingClient = xray_sampler._AwsXRayRemoteSampler__xray_client
137+
self.assertEqual(xray_sampler._AwsXRayRemoteSampler__polling_interval, 300)
138+
self.assertEqual(
139+
xray_client._AwsXRaySamplingClient__get_sampling_rules_endpoint, "http://127.0.0.1:2000/GetSamplingRules"
140+
)
141+
142+
os.environ.pop(OTEL_TRACES_SAMPLER_ARG, None)
143+
os.environ.setdefault(OTEL_TRACES_SAMPLER_ARG, "endpoint,polling_interval")
144+
145+
# May log http request error as xray sampler will attempt to fetch rules
146+
xray_sampler: Sampler = _custom_import_sampler(None, resource=None)
147+
xray_client: _AwsXRaySamplingClient = xray_sampler._AwsXRayRemoteSampler__xray_client
148+
self.assertEqual(xray_sampler._AwsXRayRemoteSampler__polling_interval, 300)
149+
self.assertEqual(
150+
xray_client._AwsXRaySamplingClient__get_sampling_rules_endpoint, "http://127.0.0.1:2000/GetSamplingRules"
151+
)

0 commit comments

Comments
 (0)