def test_origin(self): context = trace_api.SpanContext( trace_id=0x000000000000000000000000DEADBEEF, span_id=trace_api.INVALID_SPAN, is_remote=True, trace_state=trace_api.TraceState( {datadog.constants.DD_ORIGIN: "origin-service"}), ) root_span = trace.Span(name="root", context=context, parent=None) child_span = trace.Span(name="child", context=context, parent=root_span) root_span.start() child_span.start() child_span.end() root_span.end() # pylint: disable=protected-access exporter = datadog.DatadogSpanExporter() datadog_spans = [ span.to_dict() for span in exporter._translate_to_datadog([root_span, child_span]) ] self.assertEqual(len(datadog_spans), 2) actual = [ span["meta"].get(datadog.constants.DD_ORIGIN) if "meta" in span else None for span in datadog_spans ] expected = ["origin-service", None] self.assertListEqual(actual, expected)
def test_probability_sampler(self): trace_state = trace.TraceState({"key": "value"}) sampler = sampling.TraceIdRatioBased(0.5) # Check that we sample based on the trace ID if the parent context is # null sampled_result = sampler.should_sample( None, 0x7FFFFFFFFFFFFFFF, "sampled true", attributes={"sampled.expect": "true"}, trace_state=trace_state, ) self.assertTrue(sampled_result.decision.is_sampled()) self.assertEqual(sampled_result.attributes, {"sampled.expect": "true"}) self.assertEqual(sampled_result.trace_state, trace_state) not_sampled_result = sampler.should_sample( None, 0x8000000000000000, "sampled false", attributes={"sampled.expect": "false"}, trace_state=trace_state, ) self.assertFalse(not_sampled_result.decision.is_sampled()) self.assertEqual(not_sampled_result.attributes, {}) self.assertEqual(not_sampled_result.trace_state, trace_state)
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 exec_parent_based(self, parent_sampling_context): trace_state = trace.TraceState({"key": "value"}) sampler = sampling.ParentBased(sampling.ALWAYS_ON) with parent_sampling_context( self._create_parent_span(trace_flags=TO_DEFAULT)) as context: # Check that the sampling decision matches the parent context if given not_sampled_result = sampler.should_sample( context, 0x7FFFFFFFFFFFFFFF, "unsampled parent, sampling on", attributes={"sampled": "false"}, trace_state=trace_state, ) self.assertFalse(not_sampled_result.decision.is_sampled()) self.assertEqual(not_sampled_result.attributes, {}) self.assertEqual(not_sampled_result.trace_state, trace_state) with parent_sampling_context( self._create_parent_span(trace_flags=TO_SAMPLED)) as context: sampler2 = sampling.ParentBased(sampling.ALWAYS_OFF) sampled_result = sampler2.should_sample( context, 0x8000000000000000, "sampled parent, sampling off", attributes={"sampled": "true"}, trace_state=trace_state, ) self.assertTrue(sampled_result.decision.is_sampled()) self.assertEqual(sampled_result.attributes, {"sampled": "true"}) self.assertEqual(sampled_result.trace_state, trace_state) # for root span follow decision of delegate sampler with parent_sampling_context(trace.INVALID_SPAN) as context: sampler3 = sampling.ParentBased(sampling.ALWAYS_OFF) not_sampled_result = sampler3.should_sample( context, 0x8000000000000000, "parent, sampling off", attributes={"sampled": "false"}, trace_state=trace_state, ) self.assertFalse(not_sampled_result.decision.is_sampled()) self.assertEqual(not_sampled_result.attributes, {}) self.assertEqual(not_sampled_result.trace_state, trace_state) with parent_sampling_context(trace.INVALID_SPAN) as context: sampler4 = sampling.ParentBased(sampling.ALWAYS_ON) sampled_result = sampler4.should_sample( context, 0x8000000000000000, "no parent, sampling on", attributes={"sampled": "true"}, trace_state=trace_state, ) self.assertTrue(sampled_result.decision.is_sampled()) self.assertEqual(sampled_result.attributes, {"sampled": "true"}) self.assertEqual(sampled_result.trace_state, trace_state)
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 _parse_tracestate(header_list: typing.List[str]) -> trace.TraceState: """Parse one or more w3c tracestate header into a TraceState. Args: string: the value of the tracestate header. Returns: A valid TraceState that contains values extracted from the tracestate header. If the format of one headers is illegal, all values will be discarded and an empty tracestate will be returned. If the number of keys is beyond the maximum, all values will be discarded and an empty tracestate will be returned. """ tracestate = trace.TraceState() value_count = 0 for header in header_list: for member in re.split(_DELIMITER_FORMAT_RE, header): # empty members are valid, but no need to process further. if not member: continue match = _MEMBER_FORMAT_RE.fullmatch(member) if not match: # TODO: log this? return trace.TraceState() key, _eq, value = match.groups() if key in tracestate: # pylint:disable=E1135 # duplicate keys are not legal in # the header, so we will remove return trace.TraceState() # typing.Dict's update is not recognized by pylint: # https://github.com/PyCQA/pylint/issues/2420 tracestate[key] = value # pylint:disable=E1137 value_count += 1 if value_count > _TRACECONTEXT_MAXIMUM_TRACESTATE_KEYS: return trace.TraceState() return tracestate
def test_always_off(self): trace_state = trace.TraceState({"key": "value"}) test_data = (TO_DEFAULT, TO_SAMPLED, None) for trace_flags in test_data: with self.subTest(trace_flags=trace_flags): context = self._create_parent(trace_flags) sample_result = sampling.ALWAYS_OFF.should_sample( context, 0xDEADBEF1, "sampling off", attributes={"sampled.expect": "false"}, trace_state=trace_state, ) self.assertFalse(sample_result.decision.is_sampled()) self.assertEqual(sample_result.attributes, {}) self.assertEqual(sample_result.trace_state, trace_state)
def test_always_off(self): trace_state = trace.TraceState([("key", "value")]) test_data = (TO_DEFAULT, TO_SAMPLED, None) for trace_flags in test_data: with self.subTest(trace_flags=trace_flags): context = self._create_parent(trace_flags, False, trace_state) sample_result = sampling.ALWAYS_OFF.should_sample( context, 0xDEADBEF1, "sampling off", trace.SpanKind.INTERNAL, attributes={"sampled.expect": "false"}, ) self.assertFalse(sample_result.decision.is_sampled()) self.assertEqual(sample_result.attributes, {}) if context is not None: self.assertEqual(sample_result.trace_state, trace_state) else: self.assertIsNone(sample_result.trace_state)
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 mock_opentelemetry_context(trace_id=0xDEADBEEF, span_id=0xBEEF, trace_flags=None, trace_state=None): span_context = trace.SpanContext( trace_id=trace_id, span_id=span_id, is_remote=True, trace_flags=trace_flags, trace_state=trace.TraceState( **(trace_state if trace_state is not None else { "some_key": "some_value" })), ) ctx = trace.set_span_in_context(trace.DefaultSpan(span_context)) token = context.attach(ctx) try: yield finally: context.detach(token)
def extract( self, get_from_carrier: Getter[TextMapPropagatorT], carrier: TextMapPropagatorT, context: typing.Optional[Context] = None, ) -> Context: trace_id = extract_first_element( get_from_carrier(carrier, self.TRACE_ID_KEY) ) span_id = extract_first_element( get_from_carrier(carrier, self.PARENT_ID_KEY) ) sampled = extract_first_element( get_from_carrier(carrier, self.SAMPLING_PRIORITY_KEY) ) origin = extract_first_element( get_from_carrier(carrier, self.ORIGIN_KEY) ) trace_flags = trace.TraceFlags() if sampled and int(sampled) in ( constants.AUTO_KEEP, constants.USER_KEEP, ): trace_flags |= 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 mock_opentelemetry_context(trace_id=0xDEADBEEF, span_id=0xBEEF, trace_flags=None, trace_state=None): if trace_state is None: trace_state = [("some_key", "some_value")] span_context = trace.SpanContext( trace_id=trace_id, span_id=span_id, is_remote=True, trace_flags=trace_flags, trace_state=trace.TraceState(trace_state), ) ctx = trace.set_span_in_context( trace.span.NonRecordingSpan(span_context)) token = context.attach(ctx) try: yield finally: context.detach(token)
def test_default_on(self): trace_state = trace.TraceState({"key": "value"}) context = self._create_parent(trace_flags=TO_DEFAULT) sample_result = sampling.DEFAULT_ON.should_sample( context, 0xDEADBEF1, "unsampled parent, sampling on", attributes={"sampled.expect": "false"}, trace_state=trace_state, ) self.assertFalse(sample_result.decision.is_sampled()) self.assertEqual(sample_result.attributes, {}) self.assertEqual(sample_result.trace_state, trace_state) context = self._create_parent(trace_flags=TO_SAMPLED) sample_result = sampling.DEFAULT_ON.should_sample( context, 0xDEADBEF1, "sampled parent, sampling on", attributes={"sampled.expect": "true"}, trace_state=trace_state, ) self.assertTrue(sample_result.decision.is_sampled()) self.assertEqual(sample_result.attributes, {"sampled.expect": "true"}) self.assertEqual(sample_result.trace_state, trace_state) sample_result = sampling.DEFAULT_ON.should_sample( None, 0xDEADBEF1, "no parent, sampling on", attributes={"sampled.expect": "true"}, trace_state=trace_state, ) self.assertTrue(sample_result.decision.is_sampled()) self.assertEqual(sample_result.attributes, {"sampled.expect": "true"}) self.assertEqual(sample_result.trace_state, trace_state)
def test_default_off(self): trace_state = trace.TraceState([("key", "value")]) context = self._create_parent(TO_DEFAULT, False, trace_state) sample_result = sampling.DEFAULT_OFF.should_sample( context, 0xDEADBEF1, "unsampled parent, sampling off", trace.SpanKind.INTERNAL, attributes={"sampled.expect", "false"}, ) self.assertFalse(sample_result.decision.is_sampled()) self.assertEqual(sample_result.attributes, {}) self.assertEqual(sample_result.trace_state, trace_state) context = self._create_parent(TO_SAMPLED, False, trace_state) sample_result = sampling.DEFAULT_OFF.should_sample( context, 0xDEADBEF1, "sampled parent, sampling on", trace.SpanKind.INTERNAL, attributes={"sampled.expect": "true"}, ) self.assertTrue(sample_result.decision.is_sampled()) self.assertEqual(sample_result.attributes, {"sampled.expect": "true"}) self.assertEqual(sample_result.trace_state, trace_state) default_off = sampling.DEFAULT_OFF.should_sample( None, 0xDEADBEF1, "unsampled parent, sampling off", trace.SpanKind.INTERNAL, attributes={"sampled.expect": "false"}, ) self.assertFalse(default_off.decision.is_sampled()) self.assertEqual(default_off.attributes, {}) self.assertIsNone(default_off.trace_state)
def exec_parent_based(self, parent_sampling_context): trace_state = trace.TraceState([("key", "value")]) sampler = sampling.ParentBased(sampling.ALWAYS_ON) # Check that the sampling decision matches the parent context if given with parent_sampling_context( self._create_parent_span( trace_flags=TO_DEFAULT, trace_state=trace_state, )) as context: # local, not sampled not_sampled_result = sampler.should_sample( context, 0x7FFFFFFFFFFFFFFF, "unsampled parent, sampling on", trace.SpanKind.INTERNAL, attributes={"sampled": "false"}, ) self.assertFalse(not_sampled_result.decision.is_sampled()) self.assertEqual(not_sampled_result.attributes, {}) self.assertEqual(not_sampled_result.trace_state, trace_state) with parent_sampling_context( self._create_parent_span( trace_flags=TO_DEFAULT, trace_state=trace_state, )) as context: sampler = sampling.ParentBased( root=sampling.ALWAYS_OFF, local_parent_not_sampled=sampling.ALWAYS_ON, ) # local, not sampled -> opposite sampler sampled_result = sampler.should_sample( context, 0x7FFFFFFFFFFFFFFF, "unsampled parent, sampling on", trace.SpanKind.INTERNAL, attributes={"sampled": "false"}, ) self.assertTrue(sampled_result.decision.is_sampled()) self.assertEqual(sampled_result.attributes, {"sampled": "false"}) self.assertEqual(sampled_result.trace_state, trace_state) with parent_sampling_context( self._create_parent_span( trace_flags=TO_SAMPLED, trace_state=trace_state, )) as context: sampler = sampling.ParentBased(sampling.ALWAYS_OFF) # local, sampled sampled_result = sampler.should_sample( context, 0x8000000000000000, "sampled parent, sampling off", trace.SpanKind.INTERNAL, attributes={"sampled": "true"}, trace_state=trace_state, ) self.assertTrue(sampled_result.decision.is_sampled()) self.assertEqual(sampled_result.attributes, {"sampled": "true"}) self.assertEqual(sampled_result.trace_state, trace_state) with parent_sampling_context( self._create_parent_span( trace_flags=TO_SAMPLED, trace_state=trace_state, )) as context: sampler = sampling.ParentBased( root=sampling.ALWAYS_ON, local_parent_sampled=sampling.ALWAYS_OFF, ) # local, sampled -> opposite sampler not_sampled_result = sampler.should_sample( context, 0x7FFFFFFFFFFFFFFF, "unsampled parent, sampling on", trace.SpanKind.INTERNAL, attributes={"sampled": "false"}, trace_state=trace_state, ) self.assertFalse(not_sampled_result.decision.is_sampled()) self.assertEqual(not_sampled_result.attributes, {}) self.assertEqual(not_sampled_result.trace_state, trace_state) with parent_sampling_context( self._create_parent_span( trace_flags=TO_DEFAULT, is_remote=True, trace_state=trace_state, )) as context: sampler = sampling.ParentBased(sampling.ALWAYS_ON) # remote, not sampled not_sampled_result = sampler.should_sample( context, 0x7FFFFFFFFFFFFFFF, "unsampled parent, sampling on", trace.SpanKind.INTERNAL, attributes={"sampled": "false"}, trace_state=trace_state, ) self.assertFalse(not_sampled_result.decision.is_sampled()) self.assertEqual(not_sampled_result.attributes, {}) self.assertEqual(not_sampled_result.trace_state, trace_state) with parent_sampling_context( self._create_parent_span( trace_flags=TO_DEFAULT, is_remote=True, trace_state=trace_state, )) as context: sampler = sampling.ParentBased( root=sampling.ALWAYS_OFF, remote_parent_not_sampled=sampling.ALWAYS_ON, ) # remote, not sampled -> opposite sampler sampled_result = sampler.should_sample( context, 0x7FFFFFFFFFFFFFFF, "unsampled parent, sampling on", trace.SpanKind.INTERNAL, attributes={"sampled": "false"}, ) self.assertTrue(sampled_result.decision.is_sampled()) self.assertEqual(sampled_result.attributes, {"sampled": "false"}) self.assertEqual(sampled_result.trace_state, trace_state) with parent_sampling_context( self._create_parent_span( trace_flags=TO_SAMPLED, is_remote=True, trace_state=trace_state, )) as context: sampler = sampling.ParentBased(sampling.ALWAYS_OFF) # remote, sampled sampled_result = sampler.should_sample( context, 0x8000000000000000, "sampled parent, sampling off", trace.SpanKind.INTERNAL, attributes={"sampled": "true"}, ) self.assertTrue(sampled_result.decision.is_sampled()) self.assertEqual(sampled_result.attributes, {"sampled": "true"}) self.assertEqual(sampled_result.trace_state, trace_state) with parent_sampling_context( self._create_parent_span( trace_flags=TO_SAMPLED, is_remote=True, trace_state=trace_state, )) as context: sampler = sampling.ParentBased( root=sampling.ALWAYS_ON, remote_parent_sampled=sampling.ALWAYS_OFF, ) # remote, sampled -> opposite sampler not_sampled_result = sampler.should_sample( context, 0x7FFFFFFFFFFFFFFF, "unsampled parent, sampling on", trace.SpanKind.INTERNAL, attributes={"sampled": "false"}, ) self.assertFalse(not_sampled_result.decision.is_sampled()) self.assertEqual(not_sampled_result.attributes, {}) self.assertEqual(not_sampled_result.trace_state, trace_state) # for root span follow decision of root sampler with parent_sampling_context(trace.INVALID_SPAN) as context: sampler = sampling.ParentBased(sampling.ALWAYS_OFF) not_sampled_result = sampler.should_sample( context, 0x8000000000000000, "parent, sampling off", trace.SpanKind.INTERNAL, attributes={"sampled": "false"}, ) self.assertFalse(not_sampled_result.decision.is_sampled()) self.assertEqual(not_sampled_result.attributes, {}) self.assertIsNone(not_sampled_result.trace_state) with parent_sampling_context(trace.INVALID_SPAN) as context: sampler = sampling.ParentBased(sampling.ALWAYS_ON) sampled_result = sampler.should_sample( context, 0x8000000000000000, "no parent, sampling on", trace.SpanKind.INTERNAL, attributes={"sampled": "true"}, trace_state=trace_state, ) self.assertTrue(sampled_result.decision.is_sampled()) self.assertEqual(sampled_result.attributes, {"sampled": "true"}) self.assertIsNone(sampled_result.trace_state)
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 )
def test_translate_to_collector(self): trace_id = 0x6E0C63257DE34C926F9EFCD03927272E span_id = 0x34BF92DEEFC58C92 parent_id = 0x1111111111111111 base_time = 683647322 * 10**9 # in ns start_times = ( base_time, base_time + 150 * 10**6, base_time + 300 * 10**6, ) durations = (50 * 10**6, 100 * 10**6, 200 * 10**6) end_times = ( start_times[0] + durations[0], start_times[1] + durations[1], start_times[2] + durations[2], ) span_context = trace_api.SpanContext( trace_id, span_id, is_remote=False, trace_flags=TraceFlags(TraceFlags.SAMPLED), trace_state=trace_api.TraceState({"testKey": "testValue"}), ) parent_context = trace_api.SpanContext(trace_id, parent_id, is_remote=False) other_context = trace_api.SpanContext(trace_id, span_id, is_remote=False) event_attributes = { "annotation_bool": True, "annotation_string": "annotation_test", "key_float": 0.3, } event_timestamp = base_time + 50 * 10**6 event = trace.Event( name="event0", timestamp=event_timestamp, attributes=event_attributes, ) link_attributes = {"key_bool": True} link_1 = trace_api.Link(context=other_context, attributes=link_attributes) link_2 = trace_api.Link(context=parent_context, attributes=link_attributes) span_1 = trace.Span( name="test1", context=span_context, parent=parent_context, events=(event, ), links=(link_1, ), kind=trace_api.SpanKind.CLIENT, ) span_2 = trace.Span( name="test2", context=parent_context, parent=None, kind=trace_api.SpanKind.SERVER, ) span_3 = trace.Span( name="test3", context=other_context, links=(link_2, ), parent=span_2.get_context(), ) otel_spans = [span_1, span_2, span_3] otel_spans[0].start(start_time=start_times[0]) otel_spans[0].set_attribute("key_bool", False) otel_spans[0].set_attribute("key_string", "hello_world") otel_spans[0].set_attribute("key_float", 111.22) otel_spans[0].set_attribute("key_int", 333) otel_spans[0].set_status( trace_api.Status( trace_api.status.StatusCanonicalCode.INTERNAL, "test description", )) otel_spans[0].end(end_time=end_times[0]) otel_spans[1].start(start_time=start_times[1]) otel_spans[1].set_status( trace_api.Status( trace_api.status.StatusCanonicalCode.INTERNAL, {"test", "val"}, )) otel_spans[1].end(end_time=end_times[1]) otel_spans[2].start(start_time=start_times[2]) otel_spans[2].end(end_time=end_times[2]) output_spans = translate_to_collector(otel_spans) self.assertEqual(len(output_spans), 3) self.assertEqual(output_spans[0].trace_id, b"n\x0cc%}\xe3L\x92o\x9e\xfc\xd09''.") self.assertEqual(output_spans[0].span_id, b"4\xbf\x92\xde\xef\xc5\x8c\x92") self.assertEqual(output_spans[0].name, trace_pb2.TruncatableString(value="test1")) self.assertEqual(output_spans[1].name, trace_pb2.TruncatableString(value="test2")) self.assertEqual(output_spans[2].name, trace_pb2.TruncatableString(value="test3")) self.assertEqual( output_spans[0].start_time.seconds, int(start_times[0] / 1000000000), ) self.assertEqual(output_spans[0].end_time.seconds, int(end_times[0] / 1000000000)) self.assertEqual(output_spans[0].kind, trace_api.SpanKind.CLIENT.value) self.assertEqual(output_spans[1].kind, trace_api.SpanKind.SERVER.value) self.assertEqual(output_spans[0].parent_span_id, b"\x11\x11\x11\x11\x11\x11\x11\x11") self.assertEqual(output_spans[2].parent_span_id, b"\x11\x11\x11\x11\x11\x11\x11\x11") self.assertEqual( output_spans[0].status.code, trace_api.status.StatusCanonicalCode.INTERNAL.value, ) self.assertEqual(output_spans[0].status.message, "test description") self.assertEqual(len(output_spans[0].tracestate.entries), 1) self.assertEqual(output_spans[0].tracestate.entries[0].key, "testKey") self.assertEqual(output_spans[0].tracestate.entries[0].value, "testValue") self.assertEqual( output_spans[0].attributes.attribute_map["key_bool"].bool_value, False, ) self.assertEqual( output_spans[0].attributes.attribute_map["key_string"]. string_value.value, "hello_world", ) self.assertEqual( output_spans[0].attributes.attribute_map["key_float"].double_value, 111.22, ) self.assertEqual( output_spans[0].attributes.attribute_map["key_int"].int_value, 333) self.assertEqual( output_spans[0].time_events.time_event[0].time.seconds, 683647322) self.assertEqual( output_spans[0].time_events.time_event[0].annotation.description. value, "event0", ) self.assertEqual( output_spans[0].time_events.time_event[0].annotation.attributes. attribute_map["annotation_bool"].bool_value, True, ) self.assertEqual( output_spans[0].time_events.time_event[0].annotation.attributes. attribute_map["annotation_string"].string_value.value, "annotation_test", ) self.assertEqual( output_spans[0].time_events.time_event[0].annotation.attributes. attribute_map["key_float"].double_value, 0.3, ) self.assertEqual( output_spans[0].links.link[0].trace_id, b"n\x0cc%}\xe3L\x92o\x9e\xfc\xd09''.", ) self.assertEqual( output_spans[0].links.link[0].span_id, b"4\xbf\x92\xde\xef\xc5\x8c\x92", ) self.assertEqual( output_spans[0].links.link[0].type, trace_pb2.Span.Link.Type.TYPE_UNSPECIFIED, ) self.assertEqual( output_spans[1].status.code, trace_api.status.StatusCanonicalCode.INTERNAL.value, ) self.assertEqual( output_spans[2].links.link[0].type, trace_pb2.Span.Link.Type.PARENT_LINKED_SPAN, ) self.assertEqual( output_spans[0].links.link[0].attributes.attribute_map["key_bool"]. bool_value, True, )