def test_valid_header(self): header = "{}/{};o=1".format( get_hexadecimal_trace_id(self.valid_trace_id), self.valid_span_id) new_span_context = self._extract(header) self.assertEqual(new_span_context.trace_id, self.valid_trace_id) self.assertEqual(new_span_context.span_id, self.valid_span_id) self.assertEqual(new_span_context.trace_flags, TraceFlags(1)) self.assertTrue(new_span_context.is_remote) header = "{}/{};o=10".format( get_hexadecimal_trace_id(self.valid_trace_id), self.valid_span_id) new_span_context = self._extract(header) self.assertEqual(new_span_context.trace_id, self.valid_trace_id) self.assertEqual(new_span_context.span_id, self.valid_span_id) self.assertEqual(new_span_context.trace_flags, TraceFlags(10)) self.assertTrue(new_span_context.is_remote) header = "{}/{};o=0".format( get_hexadecimal_trace_id(self.valid_trace_id), self.valid_span_id) new_span_context = self._extract(header) self.assertEqual(new_span_context.trace_id, self.valid_trace_id) self.assertEqual(new_span_context.span_id, self.valid_span_id) self.assertEqual(new_span_context.trace_flags, TraceFlags(0)) self.assertTrue(new_span_context.is_remote) header = "{}/{};o=0".format( get_hexadecimal_trace_id(self.valid_trace_id), 345) new_span_context = self._extract(header) self.assertEqual(new_span_context.trace_id, self.valid_trace_id) self.assertEqual(new_span_context.span_id, 345) self.assertEqual(new_span_context.trace_flags, TraceFlags(0)) self.assertTrue(new_span_context.is_remote)
def _extract_links(links: Sequence[trace_api.Link]) -> ProtoSpan.Links: """Convert span.links""" if not links: return None extracted_links = [] dropped_links = 0 if len(links) > MAX_NUM_LINKS: logger.warning( "Exporting more then %s links, some will be truncated", MAX_NUM_LINKS, ) dropped_links = len(links) - MAX_NUM_LINKS links = links[:MAX_NUM_LINKS] for link in links: link_attributes = link.attributes or {} if len(link_attributes) > MAX_LINK_ATTRS: logger.warning( "Link has more then %s attributes, some will be truncated", MAX_LINK_ATTRS, ) trace_id = get_hexadecimal_trace_id(link.context.trace_id) span_id = get_hexadecimal_span_id(link.context.span_id) extracted_links.append({ "trace_id": trace_id, "span_id": span_id, "type": "TYPE_UNSPECIFIED", "attributes": _extract_attributes(link_attributes, MAX_LINK_ATTRS), }) return ProtoSpan.Links(link=extracted_links, dropped_links_count=dropped_links)
def _translate_to_cloud_trace( self, spans: Sequence[Span]) -> List[Dict[str, Any]]: """Translate the spans to Cloud Trace format. Args: spans: Tuple of spans to convert """ cloud_trace_spans = [] for span in spans: ctx = span.get_context() trace_id = get_hexadecimal_trace_id(ctx.trace_id) span_id = get_hexadecimal_span_id(ctx.span_id) span_name = "projects/{}/traces/{}/spans/{}".format( self.project_id, trace_id, span_id) parent_id = None if span.parent: parent_id = get_hexadecimal_span_id(span.parent.span_id) start_time = _get_time_from_ns(span.start_time) end_time = _get_time_from_ns(span.end_time) if len(span.attributes) > MAX_SPAN_ATTRS: logger.warning( "Span has more then %s attributes, some will be truncated", MAX_SPAN_ATTRS, ) cloud_trace_spans.append({ "name": span_name, "span_id": span_id, "display_name": _get_truncatable_str_object(span.name, 128), "start_time": start_time, "end_time": end_time, "parent_span_id": parent_id, "attributes": _extract_attributes(span.attributes, MAX_SPAN_ATTRS), "links": _extract_links(span.links), "status": _extract_status(span.status), "time_events": _extract_events(span.events), }) # TODO: Leverage more of the Cloud Trace API, e.g. # same_process_as_parent_span and child_span_count return cloud_trace_spans
def test_invalid_span_id(self): header = "{}/{};o={}".format( get_hexadecimal_trace_id(self.valid_trace_id), INVALID_SPAN_ID, 1) self.assertEqual(self._extract(header), trace.INVALID_SPAN.get_span_context()) header = "{}/{};o={}".format( get_hexadecimal_trace_id(self.valid_trace_id), "0" * 16, 1) self.assertEqual(self._extract(header), trace.INVALID_SPAN.get_span_context()) header = "{}/{};o={}".format( get_hexadecimal_trace_id(self.valid_trace_id), "0", 1) self.assertEqual(self._extract(header), trace.INVALID_SPAN.get_span_context()) header = "{}/{};o={}".format( get_hexadecimal_trace_id(self.valid_trace_id), self.too_long_id, 1) self.assertEqual(self._extract(header), trace.INVALID_SPAN.get_span_context())
def test_invalid_header_format(self): header = "invalid_header" self.assertEqual(self._extract(header), trace.INVALID_SPAN.get_span_context()) header = "{}/{};o=".format( get_hexadecimal_trace_id(self.valid_trace_id), self.valid_span_id) self.assertEqual(self._extract(header), trace.INVALID_SPAN.get_span_context()) header = "extra_chars/{}/{};o=1".format( get_hexadecimal_trace_id(self.valid_trace_id), self.valid_span_id) self.assertEqual(self._extract(header), trace.INVALID_SPAN.get_span_context()) header = "{}/{}extra_chars;o=1".format( get_hexadecimal_trace_id(self.valid_trace_id), self.valid_span_id) self.assertEqual(self._extract(header), trace.INVALID_SPAN.get_span_context()) header = "{}/{};o=1extra_chars".format( get_hexadecimal_trace_id(self.valid_trace_id), self.valid_span_id) self.assertEqual(self._extract(header), trace.INVALID_SPAN.get_span_context()) header = "{}/;o=1".format(get_hexadecimal_trace_id( self.valid_trace_id)) self.assertEqual(self._extract(header), trace.INVALID_SPAN.get_span_context()) header = "/{};o=1".format(self.valid_span_id) self.assertEqual(self._extract(header), trace.INVALID_SPAN.get_span_context()) header = "{}/{};o={}".format("123", "34", "4") self.assertEqual(self._extract(header), trace.INVALID_SPAN.get_span_context())
def test_inject_with_valid_context(self): span_context = SpanContext( trace_id=self.valid_trace_id, span_id=self.valid_span_id, is_remote=True, trace_flags=TraceFlags(1), ) output = self._inject(trace.DefaultSpan(span_context)) self.assertEqual( output, "{}/{};o={}".format( get_hexadecimal_trace_id(self.valid_trace_id), self.valid_span_id, 1, ), )
def inject( self, set_in_carrier: httptextformat.Setter[httptextformat.HTTPTextFormatT], carrier: httptextformat.HTTPTextFormatT, context: typing.Optional[Context] = None, ) -> None: span = trace.get_current_span(context) span_context = span.get_context() if span_context == trace.INVALID_SPAN_CONTEXT: return header = "{}/{};o={}".format( get_hexadecimal_trace_id(span_context.trace_id), span_context.span_id, int(span_context.trace_flags.sampled), ) set_in_carrier(carrier, _TRACE_CONTEXT_HEADER_NAME, header)
def test_mixed_case_header_key(self): header_value = "{}/{};o=1".format( get_hexadecimal_trace_id(self.valid_trace_id), self.valid_span_id) for header_key in ( "X-Cloud-Trace-Context", "X-ClOuD-tRace-ConTeXt", "X-CLOUD-TRACE-CONTEXT", ): header_map = {header_key: [header_value]} new_context = self.propagator.extract(dict_getter, header_map) new_span_context = trace.get_current_span( new_context).get_span_context() self.assertEqual(new_span_context.trace_id, self.valid_trace_id) self.assertEqual(new_span_context.span_id, self.valid_span_id) self.assertEqual(new_span_context.trace_flags, TraceFlags(1)) self.assertTrue(new_span_context.is_remote)
def _translate_to_cloud_trace( self, spans: Sequence[Span]) -> List[Dict[str, Any]]: """Translate the spans to Cloud Trace format. Args: spans: Sequence of spans to convert """ cloud_trace_spans = [] for span in spans: ctx = span.get_span_context() trace_id = get_hexadecimal_trace_id(ctx.trace_id) span_id = get_hexadecimal_span_id(ctx.span_id) span_name = "projects/{}/traces/{}/spans/{}".format( self.project_id, trace_id, span_id) parent_id = None if span.parent: parent_id = get_hexadecimal_span_id(span.parent.span_id) start_time = _get_time_from_ns(span.start_time) end_time = _get_time_from_ns(span.end_time) if len(span.attributes) > MAX_SPAN_ATTRS: logger.warning( "Span has more then %s attributes, some will be truncated", MAX_SPAN_ATTRS, ) # Span does not support a MonitoredResource object. We put the # information into attributes instead. resources_and_attrs = _extract_resources(span.resource) resources_and_attrs.update(span.attributes) cloud_trace_spans.append({ "name": span_name, "span_id": span_id, "display_name": _get_truncatable_str_object(span.name, 128), "start_time": start_time, "end_time": end_time, "parent_span_id": parent_id, "attributes": _extract_attributes( resources_and_attrs, MAX_SPAN_ATTRS, add_agent_attr=True, ), "links": _extract_links(span.links), # type: ignore[has-type] "status": _extract_status(span.status), # type: ignore[arg-type] "time_events": _extract_events(span.events), "span_kind": _extract_span_kind(span.kind), }) # TODO: Leverage more of the Cloud Trace API, e.g. # same_process_as_parent_span and child_span_count return cloud_trace_spans