|
1 | 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2 | 2 | # SPDX-License-Identifier: Apache-2.0
|
| 3 | +from typing import Callable, Optional |
3 | 4 | from unittest import TestCase
|
4 | 5 |
|
| 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 |
5 | 8 | 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" |
6 | 22 |
|
7 | 23 |
|
8 | 24 | 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