def setUpClass(cls): cls.serialized_trace_id = b3_format.format_trace_id( trace.generate_trace_id()) cls.serialized_span_id = b3_format.format_span_id( trace.generate_span_id()) cls.serialized_parent_id = b3_format.format_span_id( trace.generate_span_id())
def test_links(self): other_context1 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), ) other_context2 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), ) other_context3 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), ) links = [ trace_api.Link(other_context1), trace_api.Link(other_context2, {"name": "neighbor"}), trace_api.Link(other_context3, {"component": "http"}), ] with self.tracer.start_as_current_span("root", links=links) as root: self.assertEqual(len(root.links), 3) self.assertEqual(root.links[0].context.trace_id, other_context1.trace_id) self.assertEqual(root.links[0].context.span_id, other_context1.span_id) self.assertEqual(root.links[0].attributes, {}) self.assertEqual(root.links[1].context.trace_id, other_context2.trace_id) self.assertEqual(root.links[1].context.span_id, other_context2.span_id) self.assertEqual(root.links[1].attributes, {"name": "neighbor"}) self.assertEqual(root.links[2].context.span_id, other_context3.span_id) self.assertEqual(root.links[2].attributes, {"component": "http"})
def test_links(self): other_context1 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), is_remote=False, ) other_context2 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), is_remote=False, ) links = ( trace_api.Link(other_context1), trace_api.Link(other_context2, {"name": "neighbor"}), ) with self.tracer.start_as_current_span("root", links=links) as root: self.assertEqual(len(root.links), 2) self.assertEqual( root.links[0].context.trace_id, other_context1.trace_id ) self.assertEqual( root.links[0].context.span_id, other_context1.span_id ) self.assertEqual(root.links[0].attributes, None) self.assertEqual( root.links[1].context.trace_id, other_context2.trace_id ) self.assertEqual( root.links[1].context.span_id, other_context2.span_id ) self.assertEqual(root.links[1].attributes, {"name": "neighbor"})
def test_sampling_priority_auto_reject(self): """Test sampling priority rejected.""" parent_context = get_current_span( FORMAT.extract( get_as_list, { FORMAT.TRACE_ID_KEY: self.serialized_trace_id, FORMAT.PARENT_ID_KEY: self.serialized_parent_id, FORMAT.SAMPLING_PRIORITY_KEY: str(constants.AUTO_REJECT), }, )).get_context() self.assertEqual(parent_context.trace_flags, constants.AUTO_REJECT) child = trace.Span( "child", trace_api.SpanContext( parent_context.trace_id, trace.generate_span_id(), is_remote=False, trace_flags=parent_context.trace_flags, trace_state=parent_context.trace_state, ), parent=parent_context, ) child_carrier = {} child_context = set_span_in_context(child) FORMAT.inject(dict.__setitem__, child_carrier, context=child_context) self.assertEqual( child_carrier[FORMAT.SAMPLING_PRIORITY_KEY], str(constants.AUTO_REJECT), )
def test_full_path(self): trace_id = trace_sdk.generate_trace_id() # We need to use the Werkzeug test app because # The headers are injected at the wsgi layer. # The flask test app will not include these, and # result in the values not propagated. client = Client(self.app.wsgi_app, BaseResponse) # emulate b3 headers client.get( "/", headers={ "traceparent": "00-{:032x}-{:016x}-{:02x}".format( trace_id, trace_sdk.generate_span_id(), trace.TraceFlags.SAMPLED, ) }, ) # assert the http request header was propagated through. prepared_request = self.send.call_args[0][1] headers = prepared_request.headers self.assertRegex( headers["traceparent"], r"00-{:032x}-[0-9a-f]{{16}}-01".format(trace_id), )
def test_ended_span(self): """"Events, attributes are not allowed after span is ended""" tracer = trace.Tracer("test_ended_span") other_context1 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), ) with tracer.start_span("root") as root: # everything should be empty at the beginning self.assertEqual(len(root.attributes), 0) self.assertEqual(len(root.events), 0) self.assertEqual(len(root.links), 0) # call end first time root.end() end_time0 = root.end_time # call it a second time root.end() # end time shouldn't be changed self.assertEqual(end_time0, root.end_time) root.set_attribute("component", "http") self.assertEqual(len(root.attributes), 0) root.add_event("event1") self.assertEqual(len(root.events), 0) root.add_link(other_context1) self.assertEqual(len(root.links), 0) root.update_name("xxx") self.assertEqual(root.name, "root")
def test_links(self): other_context1 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), is_remote=False, ) other_context2 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), is_remote=False, ) other_context3 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), is_remote=False, ) def get_link_attributes(): return {"component": "http"} links = ( trace_api.Link(other_context1), trace_api.Link(other_context2, {"name": "neighbor"}), trace_api.LazyLink(other_context3, get_link_attributes), ) with self.tracer.start_as_current_span("root", links=links) as root: self.assertEqual(len(root.links), 3) self.assertEqual( root.links[0].context.trace_id, other_context1.trace_id ) self.assertEqual( root.links[0].context.span_id, other_context1.span_id ) self.assertEqual(root.links[0].attributes, None) self.assertEqual( root.links[1].context.trace_id, other_context2.trace_id ) self.assertEqual( root.links[1].context.span_id, other_context2.span_id ) self.assertEqual(root.links[1].attributes, {"name": "neighbor"}) self.assertEqual( root.links[2].context.span_id, other_context3.span_id ) self.assertEqual(root.links[2].attributes, {"component": "http"})
def setUpClass(cls): cls.serialized_trace_id = propagator.format_trace_id( trace.generate_trace_id() ) cls.serialized_parent_id = propagator.format_span_id( trace.generate_span_id() ) cls.serialized_origin = "origin-service"
def test_context_propagation(self): """Test the propagation of Datadog headers.""" parent_context = get_span_from_context( FORMAT.extract( get_as_list, { FORMAT.TRACE_ID_KEY: self.serialized_trace_id, FORMAT.PARENT_ID_KEY: self.serialized_parent_id, FORMAT.SAMPLING_PRIORITY_KEY: str(constants.AUTO_KEEP), FORMAT.ORIGIN_KEY: self.serialized_origin, }, ) ).get_context() self.assertEqual( parent_context.trace_id, int(self.serialized_trace_id) ) self.assertEqual( parent_context.span_id, int(self.serialized_parent_id) ) self.assertEqual(parent_context.trace_flags, constants.AUTO_KEEP) self.assertEqual( parent_context.trace_state.get(constants.DD_ORIGIN), self.serialized_origin, ) self.assertTrue(parent_context.is_remote) child = trace.Span( "child", trace_api.SpanContext( parent_context.trace_id, trace.generate_span_id(), is_remote=False, trace_flags=parent_context.trace_flags, trace_state=parent_context.trace_state, ), parent=parent_context, ) child_carrier = {} child_context = set_span_in_context(child) FORMAT.inject(dict.__setitem__, child_carrier, context=child_context) self.assertEqual( child_carrier[FORMAT.TRACE_ID_KEY], self.serialized_trace_id ) self.assertEqual( child_carrier[FORMAT.PARENT_ID_KEY], str(child.context.span_id) ) self.assertEqual( child_carrier[FORMAT.SAMPLING_PRIORITY_KEY], str(constants.AUTO_KEEP), ) self.assertEqual( child_carrier.get(FORMAT.ORIGIN_KEY), self.serialized_origin )
def test_ended_span(self): """"Events, attributes are not allowed after span is ended""" tracer = trace.Tracer("test_ended_span") other_context1 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), ) with tracer.start_as_current_span("root") as root: # everything should be empty at the beginning self.assertEqual(len(root.attributes), 0) self.assertEqual(len(root.events), 0) self.assertEqual(len(root.links), 0) # call end first time root.end() end_time0 = root.end_time # call it a second time root.end() # end time shouldn't be changed self.assertEqual(end_time0, root.end_time) root.set_attribute("component", "http") self.assertEqual(len(root.attributes), 0) root.add_event("event1") self.assertEqual(len(root.events), 0) root.add_link(other_context1) self.assertEqual(len(root.links), 0) root.update_name("xxx") self.assertEqual(root.name, "root") new_status = trace_api.status.Status( trace_api.status.StatusCanonicalCode.CANCELLED, "Test description", ) root.set_status(new_status) # default status self.assertTrue(root.status.is_ok) self.assertEqual( root.status.canonical_code, trace_api.status.StatusCanonicalCode.OK, ) self.assertIs(root.status.description, None)
def get_child_parent_new_carrier(old_carrier): parent_context = FORMAT.extract(get_as_list, old_carrier) parent = trace.Span("parent", parent_context) child = trace.Span( "child", trace_api.SpanContext( parent_context.trace_id, trace.generate_span_id(), trace_flags=parent_context.trace_flags, trace_state=parent_context.trace_state, ), parent=parent, ) new_carrier = {} FORMAT.inject(child, dict.__setitem__, new_carrier) return child, parent, new_carrier
def get_child_parent_new_carrier(old_carrier): ctx = FORMAT.extract(get_as_list, old_carrier) parent_context = get_span_from_context(ctx).get_context() parent = trace.Span("parent", parent_context) child = trace.Span( "child", trace_api.SpanContext( parent_context.trace_id, trace.generate_span_id(), is_remote=False, trace_flags=parent_context.trace_flags, trace_state=parent_context.trace_state, ), parent=parent, ) new_carrier = {} ctx = set_span_in_context(child) FORMAT.inject(dict.__setitem__, new_carrier, context=ctx) return child, parent, new_carrier
def test_full_path(self): trace_id = trace.generate_trace_id() # We need to use the Werkzeug test app because # The headers are injected at the wsgi layer. # The flask test app will not include these, and # result in the values not propagated. client = Client(self.app.wsgi_app, BaseResponse) # emulate b3 headers client.get( "/", headers={ "x-b3-traceid": b3_format.format_trace_id(trace_id), "x-b3-spanid": b3_format.format_span_id(trace.generate_span_id()), "x-b3-sampled": "1", }, ) # assert the http request header was propagated through. prepared_request = self.send.call_args[0][1] headers = prepared_request.headers for required_header in {"x-b3-traceid", "x-b3-spanid", "x-b3-sampled"}: self.assertIn(required_header, headers) self.assertEqual(headers["x-b3-traceid"], b3_format.format_trace_id(trace_id))
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 trace.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) if (self._trace_id_regex.fullmatch(trace_id) is None or self._span_id_regex.fullmatch(span_id) is None): trace_id = generate_trace_id() span_id = generate_span_id() sampled = "0" else: trace_id = int(trace_id, 16) span_id = int(span_id, 16) 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 trace.set_span_in_context( trace.DefaultSpan( trace.SpanContext( # trace an span ids are encoded in hex, so must be converted trace_id=trace_id, span_id=span_id, is_remote=True, trace_flags=trace.TraceFlags(options), trace_state=trace.TraceState(), )))
def test_span_members(self): tracer = trace.Tracer("test_span_members") other_context1 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), ) other_context2 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), ) self.assertIsNone(tracer.get_current_span()) with tracer.start_span("root") as root: root.set_attribute("component", "http") root.set_attribute("http.method", "GET") root.set_attribute("http.url", "https://example.com:779/path/12/?q=d#123") root.set_attribute("http.status_code", 200) root.set_attribute("http.status_text", "OK") root.set_attribute("misc.pi", 3.14) # Setting an attribute with the same key as an existing attribute # SHOULD overwrite the existing attribute's value. root.set_attribute("attr-key", "attr-value1") root.set_attribute("attr-key", "attr-value2") root.add_event("event0") root.add_event("event1", {"name": "birthday"}) root.add_link(other_context1) root.add_link(other_context2, {"name": "neighbor"}) root.update_name("toor") self.assertEqual(root.name, "toor") # The public API does not expose getters. # Checks by accessing the span members directly self.assertEqual(len(root.attributes), 7) self.assertEqual(root.attributes["component"], "http") self.assertEqual(root.attributes["http.method"], "GET") self.assertEqual( root.attributes["http.url"], "https://example.com:779/path/12/?q=d#123", ) self.assertEqual(root.attributes["http.status_code"], 200) self.assertEqual(root.attributes["http.status_text"], "OK") self.assertEqual(root.attributes["misc.pi"], 3.14) self.assertEqual(root.attributes["attr-key"], "attr-value2") self.assertEqual(len(root.events), 2) self.assertEqual(root.events[0], trace.Event(name="event0", attributes={})) self.assertEqual( root.events[1], trace.Event(name="event1", attributes={"name": "birthday"}), ) self.assertEqual(len(root.links), 2) self.assertEqual(root.links[0].context.trace_id, other_context1.trace_id) self.assertEqual(root.links[0].context.span_id, other_context1.span_id) self.assertEqual(root.links[0].attributes, {}) self.assertEqual(root.links[1].context.trace_id, other_context2.trace_id) self.assertEqual(root.links[1].context.span_id, other_context2.span_id) self.assertEqual(root.links[1].attributes, {"name": "neighbor"})
def test_span_members(self): tracer = trace.Tracer("test_span_members") other_context1 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), ) other_context2 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), ) other_context3 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), ) self.assertIsNone(tracer.get_current_span()) with tracer.start_span("root") as root: # attributes root.set_attribute("component", "http") root.set_attribute("http.method", "GET") root.set_attribute("http.url", "https://example.com:779/path/12/?q=d#123") root.set_attribute("http.status_code", 200) root.set_attribute("http.status_text", "OK") root.set_attribute("misc.pi", 3.14) # Setting an attribute with the same key as an existing attribute # SHOULD overwrite the existing attribute's value. root.set_attribute("attr-key", "attr-value1") root.set_attribute("attr-key", "attr-value2") self.assertEqual(len(root.attributes), 7) self.assertEqual(root.attributes["component"], "http") self.assertEqual(root.attributes["http.method"], "GET") self.assertEqual( root.attributes["http.url"], "https://example.com:779/path/12/?q=d#123", ) self.assertEqual(root.attributes["http.status_code"], 200) self.assertEqual(root.attributes["http.status_text"], "OK") self.assertEqual(root.attributes["misc.pi"], 3.14) self.assertEqual(root.attributes["attr-key"], "attr-value2") # events root.add_event("event0") root.add_event("event1", {"name": "birthday"}) now = util.time_ns() root.add_lazy_event( trace_api.Event("event2", now, {"name": "hello"})) self.assertEqual(len(root.events), 3) self.assertEqual(root.events[0].name, "event0") self.assertEqual(root.events[0].attributes, {}) self.assertEqual(root.events[1].name, "event1") self.assertEqual(root.events[1].attributes, {"name": "birthday"}) self.assertEqual(root.events[2].name, "event2") self.assertEqual(root.events[2].attributes, {"name": "hello"}) self.assertEqual(root.events[2].timestamp, now) # links root.add_link(other_context1) root.add_link(other_context2, {"name": "neighbor"}) root.add_lazy_link( trace_api.Link(other_context3, {"component": "http"})) self.assertEqual(len(root.links), 3) self.assertEqual(root.links[0].context.trace_id, other_context1.trace_id) self.assertEqual(root.links[0].context.span_id, other_context1.span_id) self.assertEqual(root.links[0].attributes, {}) self.assertEqual(root.links[1].context.trace_id, other_context2.trace_id) self.assertEqual(root.links[1].context.span_id, other_context2.span_id) self.assertEqual(root.links[1].attributes, {"name": "neighbor"}) self.assertEqual(root.links[2].context.span_id, other_context3.span_id) self.assertEqual(root.links[2].attributes, {"component": "http"}) # name root.update_name("toor") self.assertEqual(root.name, "toor")