Skip to content

Commit 78f887e

Browse files
authored
Better file naming for AWS XRay Propagator + Add NathanielRN as Propagator owner (open-telemetry#729)
1 parent e8af7a3 commit 78f887e

File tree

10 files changed

+350
-322
lines changed

10 files changed

+350
-322
lines changed

.github/component_owners.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ components:
33
instrumentation/opentelemetry-instrumentation-pika:
44
- oxeye-nikolay
55
- nikosokolik
6-
6+
7+
propagator/opentelemetry-propagator-aws-xray:
8+
- NathanielRN
9+
710
sdk-extension/opentelemetry-sdk-extension-aws:
811
- NathanielRN

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
([#720](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/720))
1515
- `opentelemetry-instrumentation-sqlalchemy` Respect provided tracer provider when instrumenting SQLAlchemy
1616
([#728](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/728))
17+
- `opentelemetry-propagators-aws-xray` Rename `AwsXRayFormat` to `AwsXRayPropagator`
18+
([#729](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/729))
1719

1820

1921
### Changed

CONTRIBUTING.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def test_simple_start_span(benchmark):
7272
Make sure the test file is under the `tests/performance/benchmarks/` folder of
7373
the package it is benchmarking and further has a path that corresponds to the
7474
file in the package it is testing. Make sure that the file name begins with
75-
`test_benchmark_`. (e.g. `sdk-extension/opentelemetry-sdk-extension-aws/tests/performance/benchmarks/trace/propagation/test_benchmark_aws_xray_format.py`)
75+
`test_benchmark_`. (e.g. `propagator/opentelemetry-propagator-aws-xray/tests/performance/benchmarks/trace/propagation/test_benchmark_aws_xray_propagator.py`)
7676

7777
## Pull Requests
7878

docs/nitpick-exceptions.ini

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@ class_references=
55
opentelemetry.propagators.textmap.Setter
66
opentelemetry.propagators.textmap.Getter
77
opentelemetry.propagators.textmap.TextMapPropagator
8-
; - AwsXRayFormat
8+
; - AwsXRayPropagator
99
opentelemetry.propagators.textmap.DefaultGetter
1010
; API
1111
opentelemetry.propagators.textmap.Getter
1212
; - DatadogFormat
13-
; - AWSXRayFormat
13+
; - AWSXRayPropagator
1414
opentelemetry.sdk.trace.id_generator.IdGenerator
1515
; - AwsXRayIdGenerator
1616
TextMapPropagator
1717
CarrierT
1818
Setter
1919
Getter
20-
; - AwsXRayFormat.extract
20+
; - AwsXRayPropagator.extract
2121
; httpx changes __module__ causing Sphinx to error and no Sphinx site is available
2222
httpx.Client
2323
httpx.AsyncClient
@@ -29,7 +29,7 @@ class_references=
2929
anys=
3030
; API
3131
opentelemetry.propagators.textmap.TextMapPropagator.fields
32-
; - AWSXRayFormat
32+
; - AWSXRayPropagator
3333
TraceId
3434
; - AwsXRayIdGenerator
3535
TraceIdRatioBased

propagator/opentelemetry-propagator-aws-xray/README.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ Or by setting this propagator in your instrumented application:
4141
.. code-block:: python
4242
4343
from opentelemetry.propagate import set_global_textmap
44-
from opentelemetry.propagators.aws import AwsXRayFormat
44+
from opentelemetry.propagators.aws import AwsXRayPropagator
4545
46-
set_global_textmap(AwsXRayFormat())
46+
set_global_textmap(AwsXRayPropagator())
4747
4848
4949
References

propagator/opentelemetry-propagator-aws-xray/setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ install_requires =
4242

4343
[options.entry_points]
4444
opentelemetry_propagator =
45-
xray = opentelemetry.propagators.aws:AwsXRayFormat
45+
xray = opentelemetry.propagators.aws:AwsXRayPropagator
4646

4747
[options.extras_require]
4848
test =

propagator/opentelemetry-propagator-aws-xray/src/opentelemetry/propagators/aws/__init__.py

+2-308
Original file line numberDiff line numberDiff line change
@@ -12,312 +12,6 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
"""
16-
AWS X-Ray Propagator
17-
--------------------
15+
from opentelemetry.propagators.aws.aws_xray_propagator import AwsXRayPropagator
1816

19-
The **AWS X-Ray Propagator** provides a propagator that when used, adds a `trace
20-
header`_ to outgoing traces that is compatible with the AWS X-Ray backend service.
21-
This allows the trace context to be propagated when a trace spans multiple AWS
22-
services.
23-
24-
The same propagator setup is used to extract a context sent by external systems
25-
so that child span have the correct parent context.
26-
27-
**NOTE**: Because the parent context parsed from the ``X-Amzn-Trace-Id`` header
28-
assumes the context is _not_ sampled by default, users should make sure to add
29-
``Sampled=1`` to their ``X-Amzn-Trace-Id`` headers so that the child spans are
30-
sampled.
31-
32-
Usage
33-
-----
34-
35-
Use the provided AWS X-Ray Propagator to inject the necessary context into
36-
traces sent to external systems.
37-
38-
This can be done by either setting this environment variable:
39-
40-
::
41-
42-
export OTEL_PROPAGATORS = xray
43-
44-
45-
Or by setting this propagator in your instrumented application:
46-
47-
.. code-block:: python
48-
49-
from opentelemetry.propagate import set_global_textmap
50-
from opentelemetry.propagators.aws import AwsXRayFormat
51-
52-
set_global_textmap(AwsXRayFormat())
53-
54-
API
55-
---
56-
.. _trace header: https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-tracingheader
57-
"""
58-
59-
import logging
60-
import typing
61-
62-
from opentelemetry import trace
63-
from opentelemetry.context import Context
64-
from opentelemetry.propagators.textmap import (
65-
CarrierT,
66-
Getter,
67-
Setter,
68-
TextMapPropagator,
69-
default_getter,
70-
default_setter,
71-
)
72-
73-
TRACE_HEADER_KEY = "X-Amzn-Trace-Id"
74-
KV_PAIR_DELIMITER = ";"
75-
KEY_AND_VALUE_DELIMITER = "="
76-
77-
TRACE_ID_KEY = "Root"
78-
TRACE_ID_LENGTH = 35
79-
TRACE_ID_VERSION = "1"
80-
TRACE_ID_DELIMITER = "-"
81-
TRACE_ID_DELIMITER_INDEX_1 = 1
82-
TRACE_ID_DELIMITER_INDEX_2 = 10
83-
TRACE_ID_FIRST_PART_LENGTH = 8
84-
85-
PARENT_ID_KEY = "Parent"
86-
PARENT_ID_LENGTH = 16
87-
88-
SAMPLED_FLAG_KEY = "Sampled"
89-
SAMPLED_FLAG_LENGTH = 1
90-
IS_SAMPLED = "1"
91-
NOT_SAMPLED = "0"
92-
93-
94-
_logger = logging.getLogger(__name__)
95-
96-
97-
class AwsParseTraceHeaderError(Exception):
98-
def __init__(self, message):
99-
super().__init__()
100-
self.message = message
101-
102-
103-
class AwsXRayFormat(TextMapPropagator):
104-
"""Propagator for the AWS X-Ray Trace Header propagation protocol.
105-
106-
See: https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-tracingheader
107-
"""
108-
109-
# AWS
110-
111-
def extract(
112-
self,
113-
carrier: CarrierT,
114-
context: typing.Optional[Context] = None,
115-
getter: Getter = default_getter,
116-
) -> Context:
117-
if context is None:
118-
context = Context()
119-
120-
trace_header_list = getter.get(carrier, TRACE_HEADER_KEY)
121-
122-
if not trace_header_list or len(trace_header_list) != 1:
123-
return context
124-
125-
trace_header = trace_header_list[0]
126-
127-
if not trace_header:
128-
return context
129-
130-
try:
131-
(
132-
trace_id,
133-
span_id,
134-
sampled,
135-
) = AwsXRayFormat._extract_span_properties(trace_header)
136-
except AwsParseTraceHeaderError as err:
137-
_logger.debug(err.message)
138-
return context
139-
140-
options = 0
141-
if sampled:
142-
options |= trace.TraceFlags.SAMPLED
143-
144-
span_context = trace.SpanContext(
145-
trace_id=trace_id,
146-
span_id=span_id,
147-
is_remote=True,
148-
trace_flags=trace.TraceFlags(options),
149-
trace_state=trace.TraceState(),
150-
)
151-
152-
if not span_context.is_valid:
153-
_logger.debug(
154-
"Invalid Span Extracted. Inserting INVALID span into provided context."
155-
)
156-
return context
157-
158-
return trace.set_span_in_context(
159-
trace.NonRecordingSpan(span_context), context=context
160-
)
161-
162-
@staticmethod
163-
def _extract_span_properties(trace_header):
164-
trace_id = trace.INVALID_TRACE_ID
165-
span_id = trace.INVALID_SPAN_ID
166-
sampled = False
167-
168-
for kv_pair_str in trace_header.split(KV_PAIR_DELIMITER):
169-
try:
170-
key_str, value_str = kv_pair_str.split(KEY_AND_VALUE_DELIMITER)
171-
key, value = key_str.strip(), value_str.strip()
172-
except ValueError as ex:
173-
raise AwsParseTraceHeaderError(
174-
(
175-
"Error parsing X-Ray trace header. Invalid key value pair: %s. Returning INVALID span context.",
176-
kv_pair_str,
177-
)
178-
) from ex
179-
if key == TRACE_ID_KEY:
180-
if not AwsXRayFormat._validate_trace_id(value):
181-
raise AwsParseTraceHeaderError(
182-
(
183-
"Invalid TraceId in X-Ray trace header: '%s' with value '%s'. Returning INVALID span context.",
184-
TRACE_HEADER_KEY,
185-
trace_header,
186-
)
187-
)
188-
189-
try:
190-
trace_id = AwsXRayFormat._parse_trace_id(value)
191-
except ValueError as ex:
192-
raise AwsParseTraceHeaderError(
193-
(
194-
"Invalid TraceId in X-Ray trace header: '%s' with value '%s'. Returning INVALID span context.",
195-
TRACE_HEADER_KEY,
196-
trace_header,
197-
)
198-
) from ex
199-
elif key == PARENT_ID_KEY:
200-
if not AwsXRayFormat._validate_span_id(value):
201-
raise AwsParseTraceHeaderError(
202-
(
203-
"Invalid ParentId in X-Ray trace header: '%s' with value '%s'. Returning INVALID span context.",
204-
TRACE_HEADER_KEY,
205-
trace_header,
206-
)
207-
)
208-
209-
try:
210-
span_id = AwsXRayFormat._parse_span_id(value)
211-
except ValueError as ex:
212-
raise AwsParseTraceHeaderError(
213-
(
214-
"Invalid TraceId in X-Ray trace header: '%s' with value '%s'. Returning INVALID span context.",
215-
TRACE_HEADER_KEY,
216-
trace_header,
217-
)
218-
) from ex
219-
elif key == SAMPLED_FLAG_KEY:
220-
if not AwsXRayFormat._validate_sampled_flag(value):
221-
raise AwsParseTraceHeaderError(
222-
(
223-
"Invalid Sampling flag in X-Ray trace header: '%s' with value '%s'. Returning INVALID span context.",
224-
TRACE_HEADER_KEY,
225-
trace_header,
226-
)
227-
)
228-
229-
sampled = AwsXRayFormat._parse_sampled_flag(value)
230-
231-
return trace_id, span_id, sampled
232-
233-
@staticmethod
234-
def _validate_trace_id(trace_id_str):
235-
return (
236-
len(trace_id_str) == TRACE_ID_LENGTH
237-
and trace_id_str.startswith(TRACE_ID_VERSION)
238-
and trace_id_str[TRACE_ID_DELIMITER_INDEX_1] == TRACE_ID_DELIMITER
239-
and trace_id_str[TRACE_ID_DELIMITER_INDEX_2] == TRACE_ID_DELIMITER
240-
)
241-
242-
@staticmethod
243-
def _parse_trace_id(trace_id_str):
244-
timestamp_subset = trace_id_str[
245-
TRACE_ID_DELIMITER_INDEX_1 + 1 : TRACE_ID_DELIMITER_INDEX_2
246-
]
247-
unique_id_subset = trace_id_str[
248-
TRACE_ID_DELIMITER_INDEX_2 + 1 : TRACE_ID_LENGTH
249-
]
250-
return int(timestamp_subset + unique_id_subset, 16)
251-
252-
@staticmethod
253-
def _validate_span_id(span_id_str):
254-
return len(span_id_str) == PARENT_ID_LENGTH
255-
256-
@staticmethod
257-
def _parse_span_id(span_id_str):
258-
return int(span_id_str, 16)
259-
260-
@staticmethod
261-
def _validate_sampled_flag(sampled_flag_str):
262-
return len(
263-
sampled_flag_str
264-
) == SAMPLED_FLAG_LENGTH and sampled_flag_str in (
265-
IS_SAMPLED,
266-
NOT_SAMPLED,
267-
)
268-
269-
@staticmethod
270-
def _parse_sampled_flag(sampled_flag_str):
271-
return sampled_flag_str[0] == IS_SAMPLED
272-
273-
def inject(
274-
self,
275-
carrier: CarrierT,
276-
context: typing.Optional[Context] = None,
277-
setter: Setter = default_setter,
278-
) -> None:
279-
span = trace.get_current_span(context=context)
280-
281-
span_context = span.get_span_context()
282-
if not span_context.is_valid:
283-
return
284-
285-
otel_trace_id = f"{span_context.trace_id:032x}"
286-
xray_trace_id = TRACE_ID_DELIMITER.join(
287-
[
288-
TRACE_ID_VERSION,
289-
otel_trace_id[:TRACE_ID_FIRST_PART_LENGTH],
290-
otel_trace_id[TRACE_ID_FIRST_PART_LENGTH:],
291-
]
292-
)
293-
294-
parent_id = f"{span_context.span_id:016x}"
295-
296-
sampling_flag = (
297-
IS_SAMPLED
298-
if span_context.trace_flags & trace.TraceFlags.SAMPLED
299-
else NOT_SAMPLED
300-
)
301-
302-
# TODO: Add OT trace state to the X-Ray trace header
303-
304-
trace_header = KV_PAIR_DELIMITER.join(
305-
[
306-
KEY_AND_VALUE_DELIMITER.join([key, value])
307-
for key, value in [
308-
(TRACE_ID_KEY, xray_trace_id),
309-
(PARENT_ID_KEY, parent_id),
310-
(SAMPLED_FLAG_KEY, sampling_flag),
311-
]
312-
]
313-
)
314-
315-
setter.set(
316-
carrier, TRACE_HEADER_KEY, trace_header,
317-
)
318-
319-
@property
320-
def fields(self):
321-
"""Returns a set with the fields set in `inject`."""
322-
323-
return {TRACE_HEADER_KEY}
17+
__all__ = ["AwsXRayPropagator"]

0 commit comments

Comments
 (0)