def extract(
        self,
        carrier: textmap.CarrierT,
        context: typing.Optional[Context] = None,
        getter: textmap.Getter = textmap.default_getter,
    ) -> Context:
        if context is None:
            context = Context()

        header = self._get_header_value(getter, carrier)

        if not header:
            return context

        match = re.fullmatch(_TRACE_CONTEXT_HEADER_RE, header)
        if match is None:
            return context

        trace_id = match.group("trace_id")
        span_id = match.group("span_id")
        trace_options = match.group("trace_flags")

        if trace_id == "0" * 32 or int(span_id) == 0:
            return context

        span_context = SpanContext(
            trace_id=int(trace_id, 16),
            span_id=int(span_id),
            is_remote=True,
            trace_flags=TraceFlags(trace_options),
        )
        return trace.set_span_in_context(trace.NonRecordingSpan(span_context),
                                         context)
示例#2
0
    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.NonRecordingSpan(
            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)
示例#3
0
    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)
示例#4
0
    def test_propagation(self):
        traceparent_value = "00-{trace_id}-{span_id}-00".format(
            trace_id=format(self.TRACE_ID, "032x"),
            span_id=format(self.SPAN_ID, "016x"),
        )
        tracestate_value = "foo=1,bar=2,baz=3"
        headers = {
            "baggage": ["key1=val1,key2=val2"],
            "traceparent": [traceparent_value],
            "tracestate": [tracestate_value],
        }
        ctx = extract(carrier_getter, headers)
        baggage_entries = baggage.get_all(context=ctx)
        expected = {"key1": "val1", "key2": "val2"}
        self.assertEqual(baggage_entries, expected)
        span_context = get_current_span(context=ctx).get_span_context()

        self.assertEqual(span_context.trace_id, self.TRACE_ID)
        self.assertEqual(span_context.span_id, self.SPAN_ID)

        span = trace.NonRecordingSpan(span_context)
        ctx = baggage.set_baggage("key3", "val3")
        ctx = baggage.set_baggage("key4", "val4", context=ctx)
        ctx = set_span_in_context(span, context=ctx)
        output = {}
        inject(dict.__setitem__, output, context=ctx)
        self.assertEqual(traceparent_value, output["traceparent"])
        self.assertIn("key3=val3", output["baggage"])
        self.assertIn("key4=val4", output["baggage"])
        self.assertIn("foo=1", output["tracestate"])
        self.assertIn("bar=2", output["tracestate"])
        self.assertIn("baz=3", output["tracestate"])
示例#5
0
    def test_headers_with_tracestate(self):
        """When there is a traceparent and tracestate header, data from
        both should be addded to the SpanContext.
        """
        traceparent_value = "00-{trace_id}-{span_id}-00".format(
            trace_id=format(self.TRACE_ID, "032x"),
            span_id=format(self.SPAN_ID, "016x"),
        )
        tracestate_value = "foo=1,bar=2,baz=3"
        span_context = trace.get_current_span(
            FORMAT.extract(
                {
                    "traceparent": [traceparent_value],
                    "tracestate": [tracestate_value],
                },
            )
        ).get_span_context()
        self.assertEqual(span_context.trace_id, self.TRACE_ID)
        self.assertEqual(span_context.span_id, self.SPAN_ID)
        self.assertEqual(
            span_context.trace_state, {"foo": "1", "bar": "2", "baz": "3"}
        )
        self.assertTrue(span_context.is_remote)
        output = {}  # type:typing.Dict[str, str]
        span = trace.NonRecordingSpan(span_context)

        ctx = trace.set_span_in_context(span)
        FORMAT.inject(output, context=ctx)
        self.assertEqual(output["traceparent"], traceparent_value)
        for pair in ["foo=1", "bar=2", "baz=3"]:
            self.assertIn(pair, output["tracestate"])
        self.assertEqual(output["tracestate"].count(","), 2)
示例#6
0
    def test_use_span_exception(self):
        class TestUseSpanException(Exception):
            pass

        default_span = trace_api.NonRecordingSpan(
            trace_api.INVALID_SPAN_CONTEXT)
        tracer = new_tracer()
        with self.assertRaises(TestUseSpanException):
            with tracer.use_span(default_span):
                raise TestUseSpanException()
 def test_ctor(self):
     context = trace.SpanContext(
         1,
         1,
         is_remote=False,
         trace_flags=trace.DEFAULT_TRACE_OPTIONS,
         trace_state=trace.DEFAULT_TRACE_STATE,
     )
     span = trace.NonRecordingSpan(context)
     self.assertEqual(context, span.get_span_context())
示例#8
0
 def _create_parent_span(trace_flags: trace.TraceFlags,
                         is_remote=False,
                         trace_state=None) -> trace.NonRecordingSpan:
     return trace.NonRecordingSpan(
         trace.SpanContext(
             0xDEADBEEF,
             0xDEADBEF0,
             is_remote=is_remote,
             trace_flags=trace_flags,
             trace_state=trace_state,
         ))
def build_test_current_context(
    trace_id=int(TRACE_ID_BASE16, 16),
    span_id=int(SPAN_ID_BASE16, 16),
    is_remote=True,
    trace_flags=DEFAULT_TRACE_OPTIONS,
    trace_state=DEFAULT_TRACE_STATE,
):
    return set_span_in_context(
        trace_api.NonRecordingSpan(
            build_test_span_context(trace_id, span_id, is_remote, trace_flags,
                                    trace_state)))
示例#10
0
 def test_get_current_span(self):
     """_DefaultTracer's start_span will also
     be retrievable via get_current_span
     """
     self.assertEqual(trace.get_current_span(), trace.INVALID_SPAN)
     span = trace.NonRecordingSpan(trace.INVALID_SPAN_CONTEXT)
     ctx = trace.set_span_in_context(span)
     token = context.attach(ctx)
     try:
         self.assertIs(trace.get_current_span(), span)
     finally:
         context.detach(token)
     self.assertEqual(trace.get_current_span(), trace.INVALID_SPAN)
示例#11
0
    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,
        carrier: CarrierT,
        context: typing.Optional[Context] = None,
        getter: Getter = default_getter,
    ) -> Context:
        if context is None:
            context = Context()

        trace_header_list = getter.get(carrier, TRACE_HEADER_KEY)

        if not trace_header_list or len(trace_header_list) != 1:
            return context

        trace_header = trace_header_list[0]

        if not trace_header:
            return context

        try:
            (
                trace_id,
                span_id,
                sampled,
            ) = AwsXRayPropagator._extract_span_properties(trace_header)
        except AwsParseTraceHeaderError as err:
            _logger.debug(err.message)
            return 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. Inserting INVALID span into provided context."
            )
            return context

        return trace.set_span_in_context(
            trace.NonRecordingSpan(span_context), context=context
        )
示例#13
0
    def test_no_send_empty_tracestate(self):
        """If the tracestate is empty, do not set the header.

        RFC 3.3.1.1

        Empty and whitespace-only list members are allowed. Vendors MUST accept
        empty tracestate headers but SHOULD avoid sending them.
        """
        output = {}  # type:typing.Dict[str, str]
        span = trace.NonRecordingSpan(
            trace.SpanContext(self.TRACE_ID, self.SPAN_ID, is_remote=False))
        ctx = trace.set_span_in_context(span)
        FORMAT.inject(output, context=ctx)
        self.assertTrue("traceparent" in output)
        self.assertFalse("tracestate" in output)
 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.NonRecordingSpan(span_context))
     self.assertEqual(
         output,
         "{}/{};o={}".format(
             format_trace_id(self.valid_trace_id),
             self.valid_span_id,
             1,
         ),
     )
示例#15
0
    def extract(
        self,
        getter: Getter[TextMapPropagatorT],
        carrier: TextMapPropagatorT,
        context: typing.Optional[Context] = None,
    ) -> Context:
        trace_id_list = getter.get(carrier, self.TRACE_ID_KEY)
        span_id_list = getter.get(carrier, self.SPAN_ID_KEY)

        if not trace_id_list or not span_id_list:
            return trace.set_span_in_context(trace.INVALID_SPAN)

        return trace.set_span_in_context(
            trace.NonRecordingSpan(
                trace.SpanContext(
                    trace_id=int(trace_id_list[0]),
                    span_id=int(span_id_list[0]),
                    is_remote=True,
                )))
    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)
示例#17
0
    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 test_inject(self):
        span = trace.NonRecordingSpan(
            trace.SpanContext(
                trace_id=1,
                span_id=2,
                is_remote=False,
                trace_flags=trace.DEFAULT_TRACE_OPTIONS,
                trace_state=trace.DEFAULT_TRACE_STATE,
            ), )

        ctx = trace.set_span_in_context(span)
        prop = TraceResponsePropagator()
        carrier = {}
        prop.inject(carrier, ctx)
        self.assertEqual(carrier["Access-Control-Expose-Headers"],
                         "traceresponse")
        self.assertEqual(
            carrier["traceresponse"],
            "00-00000000000000000000000000000001-0000000000000002-00",
        )
示例#19
0
    def extract(
        self,
        carrier: CarrierT,
        context: typing.Optional[Context] = None,
        getter: Getter = default_getter,
    ) -> Context:
        if context is None:
            context = Context()
        trace_id_list = getter.get(carrier, self.TRACE_ID_KEY)
        span_id_list = getter.get(carrier, self.SPAN_ID_KEY)

        if not trace_id_list or not span_id_list:
            return context

        return trace.set_span_in_context(
            trace.NonRecordingSpan(
                trace.SpanContext(
                    trace_id=int(trace_id_list[0]),
                    span_id=int(span_id_list[0]),
                    is_remote=True,
                )),
            context,
        )
示例#20
0
 def test_use_span(self):
     span = trace.NonRecordingSpan(trace.INVALID_SPAN_CONTEXT)
     with self.tracer.use_span(span):
         pass
示例#21
0
    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
示例#22
0
    def extract(
        self,
        carrier: CarrierT,
        context: typing.Optional[Context] = None,
        getter: Getter = default_getter,
    ) -> Context:
        if context is None:
            context = Context()
        trace_id = trace.INVALID_TRACE_ID
        span_id = trace.INVALID_SPAN_ID
        sampled = "0"
        flags = None

        single_header = _extract_first_element(
            getter.get(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:
            trace_id = (_extract_first_element(
                getter.get(carrier, self.TRACE_ID_KEY)) or trace_id)
            span_id = (_extract_first_element(
                getter.get(carrier, self.SPAN_ID_KEY)) or span_id)
            sampled = (_extract_first_element(
                getter.get(carrier, self.SAMPLED_KEY)) or sampled)
            flags = (_extract_first_element(getter.get(
                carrier, self.FLAGS_KEY)) or flags)

        if (trace_id == trace.INVALID_TRACE_ID
                or span_id == trace.INVALID_SPAN_ID
                or self._trace_id_regex.fullmatch(trace_id) is None
                or self._span_id_regex.fullmatch(span_id) is None):
            return context

        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.NonRecordingSpan(
                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(),
                )),
            context,
        )
示例#23
0
 def create_default_span() -> trace_api.Span:
     span_context = trace_api.SpanContext(37, 73, is_remote=False)
     return trace_api.NonRecordingSpan(span_context)
示例#24
0
 def test_use_span(self):
     self.assertEqual(trace.get_current_span(), trace.INVALID_SPAN)
     span = trace.NonRecordingSpan(trace.INVALID_SPAN_CONTEXT)
     with trace.use_span(span):
         self.assertIs(trace.get_current_span(), span)
     self.assertEqual(trace.get_current_span(), trace.INVALID_SPAN)
示例#25
0
 def test_default_span(self):
     span = trace.NonRecordingSpan(trace.INVALID_SPAN_CONTEXT)
     self.assertEqual(span.get_span_context(), trace.INVALID_SPAN_CONTEXT)
     self.assertIs(span.is_recording(), False)