Skip to content

Commit bba778d

Browse files
committed
addressing comments
1 parent 4a46f99 commit bba778d

File tree

5 files changed

+87
-113
lines changed

5 files changed

+87
-113
lines changed

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

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,34 @@
22
# SPDX-License-Identifier: Apache-2.0
33

44
import re
5+
import sys
6+
from logging import Logger, getLogger
57

8+
import pkg_resources
9+
10+
_logger: Logger = getLogger(__name__)
11+
12+
XRAY_OTLP_ENDPOINT_PATTERN = r"https://xray\.([a-z0-9-]+)\.amazonaws\.com/v1/traces$"
13+
14+
15+
def is_xray_otlp_endpoint(otlp_endpoint: str = None) -> bool:
16+
"""Is the given endpoint the XRay OTLP endpoint?"""
617

7-
def is_otlp_endpoint_cloudwatch(otlp_endpoint=None):
8-
# Detects if it's the OTLP endpoint in CloudWatchs
918
if not otlp_endpoint:
1019
return False
1120

12-
pattern = r"https://xray\.([a-z0-9-]+)\.amazonaws\.com/v1/traces$"
21+
return bool(re.match(XRAY_OTLP_ENDPOINT_PATTERN, otlp_endpoint.lower()))
1322

14-
return bool(re.match(pattern, otlp_endpoint.lower()))
23+
24+
def is_installed(req: str) -> bool:
25+
"""Is the given required package installed?"""
26+
27+
if req in sys.modules and sys.modules.get(req) != None:
28+
return True
29+
30+
try:
31+
pkg_resources.get_distribution(req)
32+
except Exception as exc: # pylint: disable=broad-except
33+
_logger.debug("Skipping instrumentation patch: package %s, exception: %s", req, exc)
34+
return False
35+
return True

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from amazon.opentelemetry.distro._aws_attribute_keys import AWS_LOCAL_SERVICE
1212
from amazon.opentelemetry.distro._aws_resource_attribute_configurator import get_service_attribute
13-
from amazon.opentelemetry.distro._utils import is_otlp_endpoint_cloudwatch
13+
from amazon.opentelemetry.distro._utils import is_xray_otlp_endpoint
1414
from amazon.opentelemetry.distro.always_record_sampler import AlwaysRecordSampler
1515
from amazon.opentelemetry.distro.attribute_propagating_span_processor_builder import (
1616
AttributePropagatingSpanProcessorBuilder,
@@ -20,7 +20,7 @@
2020
AwsMetricAttributesSpanExporterBuilder,
2121
)
2222
from amazon.opentelemetry.distro.aws_span_metrics_processor_builder import AwsSpanMetricsProcessorBuilder
23-
from amazon.opentelemetry.distro.otlp_sigv4_exporter import OTLPAwsSigV4Exporter
23+
from amazon.opentelemetry.distro.otlp_aws_span_exporter import OTLPAwsSpanExporter
2424
from amazon.opentelemetry.distro.otlp_udp_exporter import OTLPUdpSpanExporter
2525
from amazon.opentelemetry.distro.sampler.aws_xray_remote_sampler import AwsXRayRemoteSampler
2626
from amazon.opentelemetry.distro.scope_based_exporter import ScopeBasedPeriodicExportingMetricReader
@@ -317,8 +317,10 @@ def _customize_exporter(span_exporter: SpanExporter, resource: Resource) -> Span
317317
traces_endpoint = os.environ.get(AWS_XRAY_DAEMON_ADDRESS_CONFIG, "127.0.0.1:2000")
318318
span_exporter = OTLPUdpSpanExporter(endpoint=traces_endpoint)
319319

320-
if is_otlp_endpoint_cloudwatch(os.environ.get(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT)):
321-
span_exporter = OTLPAwsSigV4Exporter(endpoint=os.getenv(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT))
320+
if isinstance(span_exporter, OTLPSpanExporter) and is_xray_otlp_endpoint(
321+
os.environ.get(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT)
322+
):
323+
span_exporter = OTLPAwsSpanExporter(endpoint=os.getenv(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT))
322324

323325
if not _is_application_signals_enabled():
324326
return span_exporter
@@ -333,8 +335,8 @@ def _customize_span_processors(provider: TracerProvider, resource: Resource) ->
333335
# Construct and set local and remote attributes span processor
334336
provider.add_span_processor(AttributePropagatingSpanProcessorBuilder().build())
335337

336-
# Do not export metrics if it's CloudWatch OTLP endpoint
337-
if is_otlp_endpoint_cloudwatch():
338+
# Do not export Application-Signals metrics if it's XRay OTLP endpoint
339+
if is_xray_otlp_endpoint():
338340
return
339341

340342
# Export 100% spans and not export Application-Signals metrics if on Lambda.

aws-opentelemetry-distro/src/amazon/opentelemetry/distro/otlp_sigv4_exporter.py renamed to aws-opentelemetry-distro/src/amazon/opentelemetry/distro/otlp_aws_span_exporter.py

Lines changed: 13 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,23 @@
44
from typing import Dict, Optional
55

66
import requests
7-
from grpc import Compression
87

9-
from amazon.opentelemetry.distro._utils import is_otlp_endpoint_cloudwatch
8+
from amazon.opentelemetry.distro._utils import is_installed, is_xray_otlp_endpoint
9+
from opentelemetry.exporter.otlp.proto.http import Compression
1010
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
1111

1212
AWS_SERVICE = "xray"
1313
_logger = logging.getLogger(__name__)
1414

15-
"""The OTLPAwsSigV4Exporter extends the functionality of the OTLPSpanExporter to allow SigV4 authentication if the
16-
configured traces endpoint is a CloudWatch OTLP endpoint https://xray.[AWSRegion].amazonaws.com/v1/traces"""
1715

16+
class OTLPAwsSpanExporter(OTLPSpanExporter):
17+
"""
18+
This exporter extends the functionality of the OTLPSpanExporter to allow spans to be exported to the
19+
XRay OTLP endpoint https://xray.[AWSRegion].amazonaws.com/v1/traces. Utilizes the botocore
20+
library to sign and directly inject SigV4 Authentication to the exported request's headers.
1821
19-
class OTLPAwsSigV4Exporter(OTLPSpanExporter):
22+
https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-OTLPEndpoint.html
23+
"""
2024

2125
def __init__(
2226
self,
@@ -35,34 +39,23 @@ def __init__(
3539

3640
self._aws_region = None
3741

38-
if endpoint and is_otlp_endpoint_cloudwatch(endpoint):
42+
if endpoint and is_xray_otlp_endpoint(endpoint):
3943

40-
# Defensive check to verify that the application being auto instrumented has
41-
# botocore installed.
42-
try:
44+
if is_installed("botocore"):
4345
# pylint: disable=import-outside-toplevel
4446
from botocore import auth, awsrequest, session
4547

4648
self.boto_auth = auth
4749
self.boto_aws_request = awsrequest
4850
self.boto_session = session.Session()
49-
self._aws_region = self._validate_exporter_endpoint(endpoint)
51+
self._aws_region = endpoint.split(".")[1]
5052

51-
except ImportError:
53+
else:
5254
_logger.error(
5355
"botocore is required to export traces to %s. Please install it using `pip install botocore`",
5456
endpoint,
5557
)
5658

57-
else:
58-
_logger.error(
59-
"Invalid XRay traces endpoint: %s. Resolving to OTLPSpanExporter to handle exporting. "
60-
"The traces endpoint follows the pattern https://xray.[AWSRegion].amazonaws.com/v1/traces. "
61-
"For example, for the US West (Oregon) (us-west-2) Region, the endpoint will be "
62-
"https://xray.us-west-2.amazonaws.com/v1/traces.",
63-
endpoint,
64-
)
65-
6659
super().__init__(
6760
endpoint=endpoint,
6861
certificate_file=certificate_file,
@@ -99,20 +92,3 @@ def _export(self, serialized_data: bytes):
9992
_logger.error("Failed to get credentials to export span to OTLP CloudWatch endpoint")
10093

10194
return super()._export(serialized_data)
102-
103-
def _validate_exporter_endpoint(self, endpoint: str) -> Optional[str]:
104-
if not endpoint:
105-
return None
106-
107-
region = endpoint.split(".")[1]
108-
xray_regions = self.boto_session.get_available_regions(AWS_SERVICE)
109-
110-
if region not in xray_regions:
111-
112-
_logger.error(
113-
"Invalid AWS region: %s. Valid regions are %s. Resolving to default endpoint.", region, xray_regions
114-
)
115-
116-
return None
117-
118-
return region

aws-opentelemetry-distro/src/amazon/opentelemetry/distro/patches/_instrumentation_patch.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import pkg_resources
99

10+
from amazon.opentelemetry.distro._utils import is_installed
1011
from amazon.opentelemetry.distro.patches._resource_detector_patches import _apply_resource_detector_patches
1112

1213
# Env variable for determining whether we want to monkey patch gevent modules. Possible values are 'all', 'none', and
@@ -25,7 +26,7 @@ def apply_instrumentation_patches() -> None:
2526
2627
Where possible, automated testing should be run to catch upstream changes resulting in broken patches
2728
"""
28-
if _is_installed("gevent"):
29+
if is_installed("gevent"):
2930
try:
3031
gevent_patch_module = os.environ.get(AWS_GEVENT_PATCH_MODULES, "all")
3132

@@ -56,7 +57,7 @@ def apply_instrumentation_patches() -> None:
5657
except Exception as exc: # pylint: disable=broad-except
5758
_logger.info("Failed to monkey patch gevent, exception: %s", exc)
5859

59-
if _is_installed("botocore ~= 1.0"):
60+
if is_installed("botocore ~= 1.0"):
6061
# pylint: disable=import-outside-toplevel
6162
# Delay import to only occur if patches is safe to apply (e.g. the instrumented library is installed).
6263
from amazon.opentelemetry.distro.patches._botocore_patches import _apply_botocore_instrumentation_patches
@@ -66,15 +67,3 @@ def apply_instrumentation_patches() -> None:
6667
# No need to check if library is installed as this patches opentelemetry.sdk,
6768
# which must be installed for the distro to work at all.
6869
_apply_resource_detector_patches()
69-
70-
71-
def _is_installed(req: str) -> bool:
72-
if req in sys.modules:
73-
return True
74-
75-
try:
76-
pkg_resources.get_distribution(req)
77-
except Exception as exc: # pylint: disable=broad-except
78-
_logger.debug("Skipping instrumentation patch: package %s, exception: %s", req, exc)
79-
return False
80-
return True

0 commit comments

Comments
 (0)