Skip to content

Commit 1b9e3b5

Browse files
authored
Merge branch 'main' into remote-sampler
2 parents 14d7aad + 8e3078b commit 1b9e3b5

32 files changed

+1817
-112
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def _is_sqs_receive_message_consumer_span(span: ReadableSpan) -> bool:
102102
instrumentation_scope: InstrumentationScope = span.instrumentation_scope
103103

104104
return (
105-
_SQS_RECEIVE_MESSAGE_SPAN_NAME.casefold() == span.name.casefold()
105+
(span.name is not None and _SQS_RECEIVE_MESSAGE_SPAN_NAME.casefold() == span.name.casefold())
106106
and SpanKind.CONSUMER == span.kind
107107
and instrumentation_scope is not None
108108
and instrumentation_scope.name.startswith(_AWS_SDK_INSTRUMENTATION_SCOPE_PREFIX)

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
# SPDX-License-Identifier: Apache-2.0
3+
import copy
34
from typing import List, Sequence, TypeVar
45

56
from typing_extensions import override
@@ -102,12 +103,11 @@ def copy_attributes_with_local_root(attributes: BoundedAttributes) -> BoundedAtt
102103
)
103104

104105

105-
# TODO: AwsMetricAttributesSpanExporter depends on internal ReadableSpan method _attributes.
106-
# This is a bit risky but is required for our implementation.
107-
# The risk is that the implementation of _attributes changes in the future.
108-
# We need tests that thoroughly test this behaviour to make sure it does not change upstream.
109106
def wrap_span_with_attributes(span: ReadableSpan, attributes: BoundedAttributes) -> ReadableSpan:
110-
original_attributes: AttributesT = span.attributes
107+
# To make sure we create a new span without influence original span's Attributes
108+
# We have to create a deepcopy for it
109+
new_span = copy.deepcopy(span)
110+
original_attributes: AttributesT = new_span.attributes
111111
update_attributes: types.Attributes = {}
112112
# Copy all attribute in span into update_attributes
113113
for key, value in original_attributes.items():
@@ -117,12 +117,12 @@ def wrap_span_with_attributes(span: ReadableSpan, attributes: BoundedAttributes)
117117
update_attributes[key] = value
118118

119119
if isinstance(original_attributes, BoundedAttributes):
120-
span._attributes = BoundedAttributes(
120+
new_span._attributes = BoundedAttributes(
121121
maxlen=original_attributes.maxlen,
122122
attributes=update_attributes,
123123
immutable=original_attributes._immutable,
124124
max_value_len=original_attributes.max_value_len,
125125
)
126126
else:
127-
span._attributes = update_attributes
128-
return span
127+
new_span._attributes = update_attributes
128+
return new_span
Lines changed: 190 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,198 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
# SPDX-License-Identifier: Apache-2.0
3+
from typing import Callable, Optional
34
from unittest import TestCase
45

6+
from amazon.opentelemetry.distro._aws_attribute_keys import AWS_CONSUMER_PARENT_SPAN_KIND, AWS_SDK_DESCENDANT
7+
from amazon.opentelemetry.distro._aws_span_processing_util import get_ingress_operation
58
from amazon.opentelemetry.distro.attribute_propagating_span_processor import AttributePropagatingSpanProcessor
9+
from opentelemetry.sdk.trace import ReadableSpan, Span, Tracer, TracerProvider
10+
from opentelemetry.semconv.trace import MessagingOperationValues, SpanAttributes
11+
from opentelemetry.trace import SpanContext, SpanKind, TraceFlags, TraceState, set_span_in_context, set_tracer_provider
12+
13+
14+
def _get_ingress_operation(span: Span):
15+
return get_ingress_operation(None, span)
16+
17+
18+
_SPAN_NAME_EXTRACTOR: Callable[[ReadableSpan], str] = _get_ingress_operation
19+
_SPAN_NAME_KEY: str = "span_name"
20+
_TEST_KEY_1: str = "key1"
21+
_TEST_KEY_2: str = "key2"
622

723

824
class TestAttributePropagatingSpanProcessor(TestCase):
9-
def test_basic(self):
10-
processor: AttributePropagatingSpanProcessor = AttributePropagatingSpanProcessor(None, None, None)
11-
self.assertTrue(processor.force_flush)
25+
def setUp(self):
26+
self.processor: AttributePropagatingSpanProcessor = AttributePropagatingSpanProcessor(
27+
_SPAN_NAME_EXTRACTOR,
28+
_SPAN_NAME_KEY,
29+
(
30+
_TEST_KEY_1,
31+
_TEST_KEY_2,
32+
),
33+
)
34+
self.provider: TracerProvider = TracerProvider(
35+
id_generator=None,
36+
sampler=None,
37+
resource=None,
38+
)
39+
set_tracer_provider(self.provider)
40+
self.provider.add_span_processor(self.processor)
41+
self.tracer: Tracer = self.provider.get_tracer("awsxray")
42+
43+
def test_attributes_propagation_by_spankind(self):
44+
for span_kind in SpanKind:
45+
span_with_app_only: Span = self.tracer.start_span(
46+
name="parent", kind=span_kind, attributes={_TEST_KEY_1: "TestValue1"}
47+
)
48+
span_with_op_only: Span = self.tracer.start_span(
49+
name="parent", kind=span_kind, attributes={_TEST_KEY_2: "TestValue2"}
50+
)
51+
span_with_app_and_op: Span = self.tracer.start_span(
52+
name="parent", kind=span_kind, attributes={_TEST_KEY_1: "TestValue1", _TEST_KEY_2: "TestValue2"}
53+
)
54+
55+
if span_kind == SpanKind.SERVER:
56+
self._validate_span_attributes_inheritance(span_with_app_only, "parent", None, None)
57+
self._validate_span_attributes_inheritance(span_with_op_only, "parent", None, None)
58+
self._validate_span_attributes_inheritance(span_with_app_and_op, "parent", None, None)
59+
elif span_kind == SpanKind.INTERNAL:
60+
self._validate_span_attributes_inheritance(span_with_app_only, "InternalOperation", "TestValue1", None)
61+
self._validate_span_attributes_inheritance(span_with_op_only, "InternalOperation", None, "TestValue2")
62+
self._validate_span_attributes_inheritance(
63+
span_with_app_and_op, "InternalOperation", "TestValue1", "TestValue2"
64+
)
65+
else:
66+
self._validate_span_attributes_inheritance(span_with_app_only, "InternalOperation", None, None)
67+
self._validate_span_attributes_inheritance(span_with_op_only, "InternalOperation", None, None)
68+
self._validate_span_attributes_inheritance(span_with_app_and_op, "InternalOperation", None, None)
69+
70+
def test_attributes_propagation_with_internal_kinds(self):
71+
grand_parent_span: Span = self.tracer.start_span(
72+
name="grandparent", kind=SpanKind.INTERNAL, attributes={_TEST_KEY_1: "testValue1"}
73+
)
74+
parent_span: Span = self.tracer.start_span(
75+
name="parent",
76+
kind=SpanKind.INTERNAL,
77+
attributes={_TEST_KEY_2: "testValue2"},
78+
context=set_span_in_context(grand_parent_span),
79+
)
80+
child_span: Span = self.tracer.start_span(
81+
name="child", kind=SpanKind.CLIENT, context=set_span_in_context(parent_span)
82+
)
83+
grand_child_span: Span = self.tracer.start_span(
84+
name="child", kind=SpanKind.INTERNAL, context=set_span_in_context(child_span)
85+
)
86+
87+
self.assertEqual(grand_parent_span.attributes.get(_TEST_KEY_1), "testValue1")
88+
self.assertIsNone(grand_parent_span.attributes.get(_TEST_KEY_2))
89+
self.assertEqual(parent_span.attributes.get(_TEST_KEY_1), "testValue1")
90+
self.assertEqual(parent_span.attributes.get(_TEST_KEY_2), "testValue2")
91+
self.assertEqual(child_span.attributes.get(_TEST_KEY_1), "testValue1")
92+
self.assertEqual(child_span.attributes.get(_TEST_KEY_2), "testValue2")
93+
self.assertIsNone(grand_child_span.attributes.get(_TEST_KEY_1))
94+
self.assertIsNone(grand_child_span.attributes.get(_TEST_KEY_2))
95+
96+
def test_override_attributes(self):
97+
parent_span: Span = self.tracer.start_span(name="parent", kind=SpanKind.SERVER)
98+
parent_span.set_attribute(_TEST_KEY_1, "testValue1")
99+
parent_span.set_attribute(_TEST_KEY_2, "testValue2")
100+
101+
transmit_spans_1: Span = self._create_nested_span(parent_span, 2)
102+
103+
child_span: Span = self.tracer.start_span(name="child:1", context=set_span_in_context(transmit_spans_1))
104+
105+
child_span.set_attribute(_TEST_KEY_2, "testValue3")
106+
107+
transmit_spans_2: Span = self._create_nested_span(child_span, 2)
108+
109+
self.assertEqual(transmit_spans_2.attributes.get(_TEST_KEY_2), "testValue3")
110+
111+
def test_span_name_propagation_by_span_kind(self):
112+
for value in SpanKind:
113+
span: Span = self.tracer.start_span(name="parent", kind=value)
114+
if value == SpanKind.SERVER:
115+
self._validate_span_attributes_inheritance(span, "parent", None, None)
116+
else:
117+
self._validate_span_attributes_inheritance(span, "InternalOperation", None, None)
118+
119+
def test_span_name_propagation_with_remote_parent_span(self):
120+
remote_parent_context: SpanContext = SpanContext(1, 2, True, TraceFlags.SAMPLED, TraceState.get_default())
121+
# Don't have a Span.Wrap(SpanContext) like method from Java, create a readable span instead
122+
remote_parent_span: Span = ReadableSpan(remote_parent_context)
123+
span: Span = self.tracer.start_span(
124+
name="parent", kind=SpanKind.SERVER, context=set_span_in_context(remote_parent_span)
125+
)
126+
self._validate_span_attributes_inheritance(span, "parent", None, None)
127+
128+
def test_aws_sdk_descendant_span(self):
129+
aws_sdk_span: Span = self.tracer.start_span(
130+
name="parent", kind=SpanKind.CLIENT, attributes={SpanAttributes.RPC_SYSTEM: "aws-api"}
131+
)
132+
self.assertIsNone(aws_sdk_span.attributes.get(AWS_SDK_DESCENDANT))
133+
child_span: Span = self._create_nested_span(aws_sdk_span, 1)
134+
self.assertIsNotNone(child_span.attributes.get(AWS_SDK_DESCENDANT))
135+
self.assertEqual(child_span.attributes.get(AWS_SDK_DESCENDANT), "true")
136+
137+
def test_consumer_parent_span_kind_attribute_propagation(self):
138+
grand_parent_span: Span = self.tracer.start_span(name="grandparent", kind=SpanKind.CONSUMER)
139+
parent_span: Span = self.tracer.start_span(
140+
name="parent", kind=SpanKind.INTERNAL, context=set_span_in_context(grand_parent_span)
141+
)
142+
child_span: Span = self.tracer.start_span(
143+
name="child",
144+
kind=SpanKind.CONSUMER,
145+
attributes={SpanAttributes.MESSAGING_OPERATION: MessagingOperationValues.PROCESS},
146+
context=set_span_in_context(parent_span),
147+
)
148+
self.assertIsNone(parent_span.attributes.get(AWS_CONSUMER_PARENT_SPAN_KIND))
149+
self.assertIsNone(child_span.attributes.get(AWS_CONSUMER_PARENT_SPAN_KIND))
150+
151+
def test_no_consumer_parent_span_kind_attribute_with_consumer_process(self):
152+
parent_span: Span = self.tracer.start_span(name="parent", kind=SpanKind.SERVER)
153+
child_span: Span = self.tracer.start_span(
154+
name="child",
155+
kind=SpanKind.CONSUMER,
156+
attributes={SpanAttributes.MESSAGING_OPERATION: MessagingOperationValues.PROCESS},
157+
context=set_span_in_context(parent_span),
158+
)
159+
self.assertIsNone(child_span.attributes.get(AWS_CONSUMER_PARENT_SPAN_KIND))
160+
161+
def test_consumer_parent_span_kind_attribute_with_consumer_parent(self):
162+
parent_span: Span = self.tracer.start_span(name="parent", kind=SpanKind.CONSUMER)
163+
child_span: Span = self.tracer.start_span(
164+
name="parent", kind=SpanKind.CONSUMER, context=set_span_in_context(parent_span)
165+
)
166+
self.assertEqual(child_span.attributes.get(AWS_CONSUMER_PARENT_SPAN_KIND), SpanKind.CONSUMER.name)
167+
168+
def _create_nested_span(self, parent_span: Span, depth: int) -> Span:
169+
if depth == 0:
170+
return parent_span
171+
child_span: Span = self.tracer.start_span(name="child:" + str(depth), context=set_span_in_context(parent_span))
172+
try:
173+
return self._create_nested_span(child_span, depth - 1)
174+
finally:
175+
child_span.end()
176+
177+
def _validate_span_attributes_inheritance(
178+
self,
179+
parent_span: Span,
180+
propagated_name: Optional[str] = None,
181+
propagation_value1: Optional[str] = None,
182+
propagation_value2: Optional[str] = None,
183+
):
184+
leaf_span: ReadableSpan = self._create_nested_span(parent_span, 10)
185+
self.assertIsNotNone(leaf_span.parent)
186+
self.assertEqual(leaf_span.name, "child:1")
187+
if propagated_name is not None:
188+
self.assertEqual(propagated_name, leaf_span.attributes.get(_SPAN_NAME_KEY))
189+
else:
190+
self.assertIsNone(leaf_span.attributes.get(_SPAN_NAME_KEY))
191+
if propagation_value1 is not None:
192+
self.assertEqual(propagation_value1, leaf_span.attributes.get(_TEST_KEY_1))
193+
else:
194+
self.assertIsNone(leaf_span.attributes.get(_TEST_KEY_1))
195+
if propagation_value2 is not None:
196+
self.assertEqual(propagation_value2, leaf_span.attributes.get(_TEST_KEY_2))
197+
else:
198+
self.assertIsNone(leaf_span.attributes.get(_TEST_KEY_2))

0 commit comments

Comments
 (0)