def test_to_json(self): context = trace_api.SpanContext( trace_id=0x000000000000000000000000DEADBEEF, span_id=0x00000000DEADBEF0, is_remote=False, trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED), ) span = trace._Span("span-name", context) span.resource = Resource({}) self.assertEqual( span.to_json(), """{ "name": "span-name", "context": { "trace_id": "0x000000000000000000000000deadbeef", "span_id": "0x00000000deadbef0", "trace_state": "{}" }, "kind": "SpanKind.INTERNAL", "parent_id": null, "start_time": null, "end_time": null, "attributes": {}, "events": [], "links": [], "resource": {} }""", ) self.assertEqual( span.to_json(indent=None), '{"name": "span-name", "context": {"trace_id": "0x000000000000000000000000deadbeef", "span_id": "0x00000000deadbef0", "trace_state": "{}"}, "kind": "SpanKind.INTERNAL", "parent_id": null, "start_time": null, "end_time": null, "attributes": {}, "events": [], "links": [], "resource": {}}', )
def test_service_name_fallback(self): context = trace_api.SpanContext( trace_id=0x000000000000000000000000DEADBEEF, span_id=0x34BF92DEEFC58C92, is_remote=False, trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED), ) trace_api.get_tracer_provider().sampler = sampling.TraceIdRatioBased( 0.5) resource_with_default_name = Resource( attributes={ "key_resource": "some_resource", "service.name": "unknown_service", }) span = trace._Span( name="sampled", context=context, parent=None, resource=resource_with_default_name, ) span.start() span.end() # pylint: disable=protected-access exporter = datadog.DatadogSpanExporter(service="fallback_service_name") datadog_spans = [ span.to_dict() for span in exporter._translate_to_datadog([span]) ] self.assertEqual(len(datadog_spans), 1) span = datadog_spans[0] self.assertEqual(span["service"], "fallback_service_name")
def test_sampling_rate(self): context = trace_api.SpanContext( trace_id=0x000000000000000000000000DEADBEEF, span_id=0x34BF92DEEFC58C92, is_remote=False, trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED), ) sampler = sampling.TraceIdRatioBased(0.5) span = trace.Span(name="sampled", context=context, parent=None, sampler=sampler) span.start() span.end() # pylint: disable=protected-access exporter = datadog.DatadogSpanExporter() datadog_spans = [ span.to_dict() for span in exporter._translate_to_datadog([span]) ] self.assertEqual(len(datadog_spans), 1) actual = [ span["metrics"].get(datadog.constants.SAMPLE_RATE_METRIC_KEY) if "metrics" in span else None for span in datadog_spans ] expected = [0.5] self.assertListEqual(actual, expected)
def extract( self, getter: Getter[TextMapPropagatorT], carrier: TextMapPropagatorT, context: typing.Optional[Context] = None, ) -> Context: if context is None: context = get_current() header = getter.get(carrier, self.TRACE_ID_KEY) if not header: return trace.set_span_in_context(trace.INVALID_SPAN, context) fields = _extract_first_element(header).split(":") context = self._extract_baggage(getter, carrier, context) if len(fields) != 4: return trace.set_span_in_context(trace.INVALID_SPAN, context) trace_id, span_id, _parent_id, flags = fields if (trace_id == trace.INVALID_TRACE_ID or span_id == trace.INVALID_SPAN_ID): return trace.set_span_in_context(trace.INVALID_SPAN, context) span = trace.DefaultSpan( trace.SpanContext( trace_id=int(trace_id, 16), span_id=int(span_id, 16), is_remote=True, trace_flags=trace.TraceFlags( int(flags, 16) & trace.TraceFlags.SAMPLED), )) return trace.set_span_in_context(span, context)
def test_start_span_explicit(self): tracer = new_tracer() other_parent = trace._Span( "name", trace_api.SpanContext( trace_id=0x000000000000000000000000DEADBEEF, span_id=0x00000000DEADBEF0, is_remote=False, trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED), ), ) other_parent_context = trace_api.set_span_in_context(other_parent) self.assertEqual(trace_api.get_current_span(), trace_api.INVALID_SPAN) root = tracer.start_span("root") self.assertIsNotNone(root.start_time) self.assertIsNone(root.end_time) # Test with the implicit root span with tracer.use_span(root, True): self.assertIs(trace_api.get_current_span(), root) with tracer.start_span("stepchild", other_parent_context) as child: # The child's parent should be the one passed in, # not the current span. self.assertNotEqual(child.parent, root) self.assertIs(child.parent, other_parent.get_span_context()) self.assertIsNotNone(child.start_time) self.assertIsNone(child.end_time) # The child should inherit its context from the explicit # parent, not the current span. child_context = child.get_span_context() self.assertEqual( other_parent.get_span_context().trace_id, child_context.trace_id, ) self.assertNotEqual( other_parent.get_span_context().span_id, child_context.span_id, ) self.assertEqual( other_parent.get_span_context().trace_state, child_context.trace_state, ) self.assertEqual( other_parent.get_span_context().trace_flags, child_context.trace_flags, ) # Verify start_span() did not set the current span. self.assertIs(trace_api.get_current_span(), root) # Verify ending the child did not set the current span. self.assertIs(trace_api.get_current_span(), root) self.assertIsNotNone(child.end_time)
def test_start_as_current_span_explicit(self): tracer = new_tracer() other_parent = trace_api.SpanContext( trace_id=0x000000000000000000000000DEADBEEF, span_id=0x00000000DEADBEF0, is_remote=False, trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED), ) self.assertEqual(trace_api.get_current_span(), trace_api.INVALID_SPAN) # Test with the implicit root span with tracer.start_as_current_span("root") as root: self.assertIs(trace_api.get_current_span(), root) self.assertIsNotNone(root.start_time) self.assertIsNone(root.end_time) with tracer.start_as_current_span( "stepchild", other_parent ) as child: # The child should become the current span as usual, but its # parent should be the one passed in, not the # previously-current span. self.assertIs(trace_api.get_current_span(), child) self.assertNotEqual(child.parent, root) self.assertIs(child.parent, other_parent) # After exiting the child's scope the last span on the stack should # become current, not the child's parent. self.assertNotEqual(trace_api.get_current_span(), other_parent) self.assertIs(trace_api.get_current_span(), root) self.assertIsNotNone(child.end_time)
def extract( self, carrier: CarrierT, context: typing.Optional[Context] = None, getter: Getter = default_getter, ) -> Context: if context is None: context = Context() header = getter.get(carrier, self.TRACE_ID_KEY) if not header: return context context = self._extract_baggage(getter, carrier, context) trace_id, span_id, flags = _parse_trace_id_header(header) if (trace_id == trace.INVALID_TRACE_ID or span_id == trace.INVALID_SPAN_ID): return context span = trace.NonRecordingSpan( trace.SpanContext( trace_id=trace_id, span_id=span_id, is_remote=True, trace_flags=trace.TraceFlags(flags & trace.TraceFlags.SAMPLED), )) return trace.set_span_in_context(span, context)
def extract( self, get_from_carrier: Getter[HTTPTextFormatT], carrier: HTTPTextFormatT, context: typing.Optional[Context] = None, ) -> Context: trace_id = format_trace_id(trace.INVALID_TRACE_ID) span_id = format_span_id(trace.INVALID_SPAN_ID) sampled = "0" flags = None single_header = _extract_first_element( get_from_carrier(carrier, self.SINGLE_HEADER_KEY)) if single_header: # The b3 spec calls for the sampling state to be # "deferred", which is unspecified. This concept does not # translate to SpanContext, so we set it as recorded. sampled = "1" fields = single_header.split("-", 4) if len(fields) == 1: sampled = fields[0] elif len(fields) == 2: trace_id, span_id = fields elif len(fields) == 3: trace_id, span_id, sampled = fields elif len(fields) == 4: trace_id, span_id, sampled, _ = fields else: return set_span_in_context(trace.INVALID_SPAN) else: trace_id = (_extract_first_element( get_from_carrier(carrier, self.TRACE_ID_KEY)) or trace_id) span_id = (_extract_first_element( get_from_carrier(carrier, self.SPAN_ID_KEY)) or span_id) sampled = (_extract_first_element( get_from_carrier(carrier, self.SAMPLED_KEY)) or sampled) flags = (_extract_first_element( get_from_carrier(carrier, self.FLAGS_KEY)) or flags) options = 0 # The b3 spec provides no defined behavior for both sample and # flag values set. Since the setting of at least one implies # the desire for some form of sampling, propagate if either # header is set to allow. if sampled in self._SAMPLE_PROPAGATE_VALUES or flags == "1": options |= trace.TraceFlags.SAMPLED return set_span_in_context( trace.DefaultSpan( trace.SpanContext( # trace an span ids are encoded in hex, so must be converted trace_id=int(trace_id, 16), span_id=int(span_id, 16), is_remote=True, trace_flags=trace.TraceFlags(options), trace_state=trace.TraceState(), )))
def extract( self, carrier: CarrierT, context: typing.Optional[Context] = None, getter: Getter = default_getter, ) -> Context: if context is None: context = Context() trace_id = extract_first_element(getter.get(carrier, self.TRACE_ID_KEY)) span_id = extract_first_element(getter.get(carrier, self.PARENT_ID_KEY)) sampled = extract_first_element( getter.get(carrier, self.SAMPLING_PRIORITY_KEY)) origin = extract_first_element(getter.get(carrier, self.ORIGIN_KEY)) trace_flags = trace.TraceFlags() if sampled and int(sampled) in ( constants.AUTO_KEEP, constants.USER_KEEP, ): trace_flags = trace.TraceFlags(trace.TraceFlags.SAMPLED) if trace_id is None or span_id is None: return context trace_state = [] if origin is not None: trace_state.append((constants.DD_ORIGIN, origin)) span_context = trace.SpanContext( trace_id=int(trace_id), span_id=int(span_id), is_remote=True, trace_flags=trace_flags, trace_state=trace.TraceState(trace_state), ) return set_span_in_context(trace.NonRecordingSpan(span_context), context)
def extract( self, getter: Getter[TextMapPropagatorT], carrier: TextMapPropagatorT, context: typing.Optional[Context] = None, ) -> Context: trace_header_list = getter.get(carrier, TRACE_HEADER_KEY) if not trace_header_list or len(trace_header_list) != 1: return trace.set_span_in_context( trace.INVALID_SPAN, context=context ) trace_header = trace_header_list[0] if not trace_header: return trace.set_span_in_context( trace.INVALID_SPAN, context=context ) try: ( trace_id, span_id, sampled, ) = AwsXRayFormat._extract_span_properties(trace_header) except AwsParseTraceHeaderError as err: _logger.debug(err.message) return trace.set_span_in_context( trace.INVALID_SPAN, context=context ) options = 0 if sampled: options |= trace.TraceFlags.SAMPLED span_context = trace.SpanContext( trace_id=trace_id, span_id=span_id, is_remote=True, trace_flags=trace.TraceFlags(options), trace_state=trace.TraceState(), ) if not span_context.is_valid: _logger.debug( "Invalid Span Extracted. Insertting INVALID span into provided context." ) return trace.set_span_in_context( trace.INVALID_SPAN, context=context ) return trace.set_span_in_context( trace.DefaultSpan(span_context), context=context )
def _create_start_and_end_span(name, span_processor): span = trace._Span( name, trace_api.SpanContext( 0xDEADBEEF, 0xDEADBEEF, is_remote=False, trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED), ), span_processor=span_processor, ) span.start() span.end()
def extract( self, carrier: textmap.CarrierT, context: typing.Optional[Context] = None, getter: textmap.Getter = textmap.default_getter, ) -> Context: """Extracts SpanContext from the carrier. See `opentelemetry.propagators.textmap.TextMapPropagator.extract` """ if context is None: context = Context() header = getter.get(carrier, self._TRACEPARENT_HEADER_NAME) if not header: return context match = re.search(self._TRACEPARENT_HEADER_FORMAT_RE, header[0]) if not match: return context version = match.group(1) trace_id = match.group(2) span_id = match.group(3) trace_flags = match.group(4) if trace_id == "0" * 32 or span_id == "0" * 16: return context if version == "00": if match.group(5): return context if version == "ff": return context tracestate_headers = getter.get(carrier, self._TRACESTATE_HEADER_NAME) if tracestate_headers is None: tracestate = None else: tracestate = TraceState.from_header(tracestate_headers) span_context = trace.SpanContext( trace_id=int(trace_id, 16), span_id=int(span_id, 16), is_remote=True, trace_flags=trace.TraceFlags(trace_flags), trace_state=tracestate, ) return trace.set_span_in_context(trace.NonRecordingSpan(span_context), context)
def extract( self, getter: Getter[TextMapPropagatorT], carrier: TextMapPropagatorT, context: typing.Optional[Context] = None, ) -> Context: trace_id = extract_first_element(getter.get(carrier, self.TRACE_ID_KEY)) span_id = extract_first_element(getter.get(carrier, self.PARENT_ID_KEY)) sampled = extract_first_element( getter.get(carrier, self.SAMPLING_PRIORITY_KEY)) origin = extract_first_element(getter.get(carrier, self.ORIGIN_KEY)) trace_flags = trace.TraceFlags() if sampled and int(sampled) in ( constants.AUTO_KEEP, constants.USER_KEEP, ): trace_flags = trace.TraceFlags(trace.TraceFlags.SAMPLED) if trace_id is None or span_id is None: return set_span_in_context(trace.INVALID_SPAN, context) span_context = trace.SpanContext( trace_id=int(trace_id), span_id=int(span_id), is_remote=True, trace_flags=trace_flags, trace_state=trace.TraceState([(constants.DD_ORIGIN, origin)]), ) return set_span_in_context(trace.DefaultSpan(span_context), context)
def extract( self, getter: textmap.Getter[textmap.TextMapPropagatorT], carrier: textmap.TextMapPropagatorT, context: typing.Optional[Context] = None, ) -> Context: """Extracts SpanContext from the carrier. See `opentelemetry.trace.propagation.textmap.TextMapPropagator.extract` """ header = getter.get(carrier, self._TRACEPARENT_HEADER_NAME) if not header: return trace.set_span_in_context(trace.INVALID_SPAN, context) match = re.search(self._TRACEPARENT_HEADER_FORMAT_RE, header[0]) if not match: return trace.set_span_in_context(trace.INVALID_SPAN, context) version = match.group(1) trace_id = match.group(2) span_id = match.group(3) trace_flags = match.group(4) if trace_id == "0" * 32 or span_id == "0" * 16: return trace.set_span_in_context(trace.INVALID_SPAN, context) if version == "00": if match.group(5): return trace.set_span_in_context(trace.INVALID_SPAN, context) if version == "ff": return trace.set_span_in_context(trace.INVALID_SPAN, context) tracestate_headers = getter.get(carrier, self._TRACESTATE_HEADER_NAME) if tracestate_headers is None: tracestate = None else: tracestate = _parse_tracestate(tracestate_headers) span_context = trace.SpanContext( trace_id=int(trace_id, 16), span_id=int(span_id, 16), is_remote=True, trace_flags=trace.TraceFlags(trace_flags), trace_state=tracestate, ) return trace.set_span_in_context(trace.DefaultSpan(span_context), context)
def test_attributes_to_json(self): context = trace_api.SpanContext( trace_id=0x000000000000000000000000DEADBEEF, span_id=0x00000000DEADBEF0, is_remote=False, trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED), ) span = trace._Span("span-name", context, resource=Resource({})) span.set_attribute("key", "value") span.add_event("event", {"key2": "value2"}, 123) date_str = ns_to_iso_str(123) self.assertEqual( span.to_json(indent=None), '{"name": "span-name", "context": {"trace_id": "0x000000000000000000000000deadbeef", "span_id": "0x00000000deadbef0", "trace_state": "[]"}, "kind": "SpanKind.INTERNAL", "parent_id": null, "start_time": null, "end_time": null, "status": {"status_code": "UNSET"}, "attributes": {"key": "value"}, "events": [{"name": "event", "timestamp": "' + date_str + '", "attributes": {"key2": "value2"}}], "links": [], "resource": {}}', )
def test_inject_not_sampled(self): span = trace.NonRecordingSpan( trace.SpanContext( trace_id=1, span_id=2, is_remote=False, trace_flags=trace.TraceFlags(0), trace_state=trace.DEFAULT_TRACE_STATE, ), ) ctx = trace.set_span_in_context(span) prop = _ServerTimingResponsePropagator() carrier = {} prop.inject(carrier, ctx) self.assertEqual(carrier["Access-Control-Expose-Headers"], "Server-Timing") self.assertEqual( carrier["Server-Timing"], 'traceparent;desc="00-00000000000000000000000000000001-0000000000000002-00"', )
def extract( cls, get_from_carrier: httptextformat.Getter[_T], carrier: _T ) -> trace.SpanContext: """Extracts a valid SpanContext from the carrier. """ header = get_from_carrier(carrier, cls._TRACEPARENT_HEADER_NAME) if not header: return trace.INVALID_SPAN_CONTEXT match = re.search(cls._TRACEPARENT_HEADER_FORMAT_RE, header[0]) if not match: return trace.INVALID_SPAN_CONTEXT version = match.group(1) trace_id = match.group(2) span_id = match.group(3) trace_flags = match.group(4) if trace_id == "0" * 32 or span_id == "0" * 16: return trace.INVALID_SPAN_CONTEXT if version == "00": if match.group(5): return trace.INVALID_SPAN_CONTEXT if version == "ff": return trace.INVALID_SPAN_CONTEXT tracestate_headers = get_from_carrier( carrier, cls._TRACESTATE_HEADER_NAME ) tracestate = _parse_tracestate(tracestate_headers) span_context = trace.SpanContext( trace_id=int(trace_id, 16), span_id=int(span_id, 16), trace_flags=trace.TraceFlags(trace_flags), trace_state=tracestate, ) return span_context
# # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import sys import unittest from opentelemetry import trace from opentelemetry.sdk.trace import sampling TO_DEFAULT = trace.TraceFlags(trace.TraceFlags.DEFAULT) TO_SAMPLED = trace.TraceFlags(trace.TraceFlags.SAMPLED) class TestDecision(unittest.TestCase): def test_is_recording(self): self.assertTrue( sampling.Decision.is_recording(sampling.Decision.RECORD_ONLY)) self.assertTrue( sampling.Decision.is_recording( sampling.Decision.RECORD_AND_SAMPLE)) self.assertFalse(sampling.Decision.is_recording( sampling.Decision.DROP)) def test_is_sampled(self): self.assertFalse(
def start_span( # pylint: disable=too-many-locals self, name: str, parent: trace_api.ParentSpan = trace_api.Tracer.CURRENT_SPAN, kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL, attributes: Optional[types.Attributes] = None, links: Sequence[trace_api.Link] = (), start_time: Optional[int] = None, set_status_on_exception: bool = True, ) -> trace_api.Span: if parent is Tracer.CURRENT_SPAN: parent = trace_api.get_current_span() parent_context = parent if isinstance(parent_context, trace_api.Span): parent_context = parent.get_context() if parent_context is not None and not isinstance( parent_context, trace_api.SpanContext): raise TypeError("parent must be a Span, SpanContext or None.") if parent_context is None or not parent_context.is_valid: parent = parent_context = None trace_id = generate_trace_id() trace_flags = None trace_state = None else: trace_id = parent_context.trace_id trace_flags = parent_context.trace_flags trace_state = parent_context.trace_state context = trace_api.SpanContext( trace_id, generate_span_id(), is_remote=False, trace_flags=trace_flags, trace_state=trace_state, ) # The sampler decides whether to create a real or no-op span at the # time of span creation. No-op spans do not record events, and are not # exported. # The sampler may also add attributes to the newly-created span, e.g. # to include information about the sampling decision. sampling_decision = self.source.sampler.should_sample( parent_context, context.trace_id, context.span_id, name, attributes, links, ) if sampling_decision.sampled: options = context.trace_flags | trace_api.TraceFlags.SAMPLED context.trace_flags = trace_api.TraceFlags(options) if attributes is None: span_attributes = sampling_decision.attributes else: # apply sampling decision attributes after initial attributes span_attributes = attributes.copy() span_attributes.update(sampling_decision.attributes) # pylint:disable=protected-access span = Span( name=name, context=context, parent=parent_context, sampler=self.source.sampler, resource=self.source.resource, attributes=span_attributes, span_processor=self.source._active_span_processor, kind=kind, links=links, instrumentation_info=self.instrumentation_info, set_status_on_exception=set_status_on_exception, ) span.start(start_time=start_time) else: span = trace_api.DefaultSpan(context=context) return span
def start_span( # pylint: disable=too-many-locals self, name: str, context: Optional[context_api.Context] = None, kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL, attributes: types.Attributes = None, links: Sequence[trace_api.Link] = (), start_time: Optional[int] = None, record_exception: bool = True, set_status_on_exception: bool = True, ) -> trace_api.Span: parent_span_context = trace_api.get_current_span( context ).get_span_context() if parent_span_context is not None and not isinstance( parent_span_context, trace_api.SpanContext ): raise TypeError( "parent_span_context must be a SpanContext or None." ) # is_valid determines root span if parent_span_context is None or not parent_span_context.is_valid: parent_span_context = None trace_id = self.id_generator.generate_trace_id() else: trace_id = parent_span_context.trace_id # The sampler decides whether to create a real or no-op span at the # time of span creation. No-op spans do not record events, and are not # exported. # The sampler may also add attributes to the newly-created span, e.g. # to include information about the sampling result. # The sampler may also modify the parent span context's tracestate sampling_result = self.sampler.should_sample( context, trace_id, name, kind, attributes, links ) trace_flags = ( trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED) if sampling_result.decision.is_sampled() else trace_api.TraceFlags(trace_api.TraceFlags.DEFAULT) ) span_context = trace_api.SpanContext( trace_id, self.id_generator.generate_span_id(), is_remote=False, trace_flags=trace_flags, trace_state=sampling_result.trace_state, ) # Only record if is_recording() is true if sampling_result.decision.is_recording(): # pylint:disable=protected-access span = _Span( name=name, context=span_context, parent=parent_span_context, sampler=self.sampler, resource=self.resource, attributes=sampling_result.attributes.copy(), span_processor=self.span_processor, kind=kind, links=links, instrumentation_info=self.instrumentation_info, record_exception=record_exception, set_status_on_exception=set_status_on_exception, limits=self._span_limits, ) span.start(start_time=start_time, parent_context=context) else: span = trace_api.NonRecordingSpan(context=span_context) return span
def extract( self, get_from_carrier: Getter[TextMapPropagatorT], carrier: TextMapPropagatorT, context: typing.Optional[Context] = None, ) -> Context: if not carrier: raise ValueError(("Could not extract from carrier: %s", carrier)) trace_header = get_from_carrier(carrier, self.TRACE_HEADER_KEY) if not trace_header or trace_header == "": return trace.set_span_in_context(trace.INVALID_SPAN) trace_id = trace.INVALID_TRACE_ID span_id = trace.INVALID_SPAN_ID sampled = False next_kv_pair_start = 0 while next_kv_pair_start < len(trace_header): try: kv_pair_delimiter_index = trace_header.index( self.KV_PAIR_DELIMITER, next_kv_pair_start ) kv_pair_subset = trace_header[ next_kv_pair_start:kv_pair_delimiter_index ] next_kv_pair_start = kv_pair_delimiter_index + 1 except ValueError as _: kv_pair_subset = trace_header[next_kv_pair_start:] next_kv_pair_start = len(trace_header) stripped_kv_pair = kv_pair_subset.strip() try: key_and_value_delimiter_index = stripped_kv_pair.index( self.KEY_AND_VALUE_DELIMITER ) except ValueError as _: _logger.error( ( "Error parsing X-Ray trace header. Invalid key value pair: %s. Returning INVALID span context.", kv_pair_subset, ) ) return trace.set_span_in_context(trace.INVALID_SPAN) value = stripped_kv_pair[key_and_value_delimiter_index + 1 :] if stripped_kv_pair.startswith(self.TRACE_ID_KEY): if ( len(value) != self.TRACE_ID_LENGTH or not value.startswith(self.TRACE_ID_VERSION) or value[self.TRACE_ID_DELIMITER_INDEX_1] != self.TRACE_ID_DELIMITER or value[self.TRACE_ID_DELIMITER_INDEX_2] != self.TRACE_ID_DELIMITER ): _logger.error( ( "Invalid TraceId in X-Ray trace header: '%s' with value '%s'. Returning INVALID span context.", self.TRACE_HEADER_KEY, trace_header, ) ) return trace.INVALID_SPAN_CONTEXT timestamp_subset = value[ self.TRACE_ID_DELIMITER_INDEX_1 + 1 : self.TRACE_ID_DELIMITER_INDEX_2 ] unique_id_subset = value[ self.TRACE_ID_DELIMITER_INDEX_2 + 1 : self.TRACE_ID_LENGTH ] trace_id = int(timestamp_subset + unique_id_subset, 16) elif stripped_kv_pair.startswith(self.PARENT_ID_KEY): if len(value) != self.PARENT_ID_LENGTH: _logger.error( ( "Invalid ParentId in X-Ray trace header: '%s' with value '%s'. Returning INVALID span context.", self.TRACE_HEADER_KEY, trace_header, ) ) return trace.INVALID_SPAN_CONTEXT span_id = int(value, 16) elif stripped_kv_pair.startswith(self.SAMPLED_FLAG_KEY): is_sampled_flag_valid = True if len(value) != self.SAMPLED_FLAG_LENGTH: is_sampled_flag_valid = False if is_sampled_flag_valid: sampled_flag = value[0] if sampled_flag == self.IS_SAMPLED: sampled = True elif sampled_flag == self.NOT_SAMPLED: sampled = False else: is_sampled_flag_valid = False if not is_sampled_flag_valid: _logger.error( ( "Invalid Sampling flag in X-Ray trace header: '%s' with value '%s'. Returning INVALID span context.", self.TRACE_HEADER_KEY, trace_header, ) ) return trace.INVALID_SPAN_CONTEXT options = 0 if sampled: options |= trace.TraceFlags.SAMPLED span_context = trace.SpanContext( trace_id=trace_id, span_id=span_id, is_remote=True, trace_flags=trace.TraceFlags(options), trace_state=trace.TraceState(), ) if not span_context.is_valid: return context return trace.set_span_in_context( trace.DefaultSpan(span_context), context=context )