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 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.DefaultSpan(
            build_test_span_context(
                trace_id, span_id, is_remote, trace_flags, trace_state
            )
        )
    )
    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.DefaultSpan(
            trace.SpanContext(self.TRACE_ID, self.SPAN_ID, is_remote=False))
        ctx = trace.set_span_in_context(span)
        FORMAT.inject(dict.__setitem__, output, ctx)
        self.assertTrue("traceparent" in output)
        self.assertFalse("tracestate" in output)
    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_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,
         ),
     )
Exemple #6
0
    def extract(
        self,
        get_from_carrier: Getter[HTTPTextFormatT],
        carrier: HTTPTextFormatT,
        context: typing.Optional[Context] = None,
    ) -> Context:
        trace_id_list = get_from_carrier(carrier, self.TRACE_ID_KEY)
        span_id_list = get_from_carrier(carrier, self.SPAN_ID_KEY)

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

        return set_span_in_context(
            trace.DefaultSpan(
                trace.SpanContext(
                    trace_id=int(trace_id_list[0]),
                    span_id=int(span_id_list[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.DefaultSpan(
                trace.SpanContext(
                    trace_id=int(trace_id_list[0]),
                    span_id=int(span_id_list[0]),
                    is_remote=True,
                )))
Exemple #8
0
 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)
Exemple #10
0
 def test_default_span(self):
     span = trace.DefaultSpan(trace.INVALID_SPAN_CONTEXT)
     self.assertEqual(span.get_context(), trace.INVALID_SPAN_CONTEXT)
     self.assertIs(span.is_recording_events(), False)
Exemple #11
0
    def create_span(
        self,
        name: str,
        parent: trace_api.ParentSpan = trace_api.Tracer.CURRENT_SPAN,
        kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL,
    ) -> "trace_api.Span":
        """See `opentelemetry.trace.Tracer.create_span`.

        If `parent` is null the new span will be created as a root span, i.e. a
        span with no parent context. By default, the new span will be created
        as a child of the current span in this tracer's context, or as a root
        span if no current span exists.
        """
        span_id = generate_span_id()

        if parent is Tracer.CURRENT_SPAN:
            parent = self.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

        if parent_context is None or not parent_context.is_valid():
            parent = parent_context = None
            trace_id = generate_trace_id()
            trace_options = None
            trace_state = None
        else:
            trace_id = parent_context.trace_id
            trace_options = parent_context.trace_options
            trace_state = parent_context.trace_state

        context = trace_api.SpanContext(trace_id, span_id, trace_options,
                                        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.sampler.should_sample(
            parent_context,
            context.trace_id,
            context.span_id,
            name,
            {},  # TODO: links
        )

        if sampling_decision.sampled:
            return Span(
                name=name,
                context=context,
                parent=parent,
                sampler=self.sampler,
                attributes=sampling_decision.attributes,
                span_processor=self._active_span_processor,
                kind=kind,
            )

        return trace_api.DefaultSpan(context=context)
    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,
        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.source.ids_generator.generate_trace_id()
            trace_flags = None
            trace_state = None
        else:
            trace_id = parent_span_context.trace_id
            trace_flags = parent_span_context.trace_flags
            trace_state = parent_span_context.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 result.
        sampling_result = self.source.sampler.should_sample(
            context, trace_id, name, 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.source.ids_generator.generate_span_id(),
            is_remote=False,
            trace_flags=trace_flags,
            trace_state=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.source.sampler,
                resource=self.source.resource,
                attributes=sampling_result.attributes.copy(),
                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, parent_context=context)
        else:
            span = trace_api.DefaultSpan(context=span_context)
        return span
Exemple #13
0
    def extract(
        self,
        getter: Getter[TextMapPropagatorT],
        carrier: TextMapPropagatorT,
        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(
            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:
                return trace.set_span_in_context(trace.INVALID_SPAN)
        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 (
            self._trace_id_regex.fullmatch(trace_id) is None
            or self._span_id_regex.fullmatch(span_id) is None
        ):
            ids_generator = trace.get_tracer_provider().ids_generator
            trace_id = ids_generator.generate_trace_id()
            span_id = ids_generator.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 start_span(
        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,
    ) -> "Span":
        """See `opentelemetry.trace.Tracer.start_span`."""

        if parent is Tracer.CURRENT_SPAN:
            parent = self.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

        if parent_context is None or not parent_context.is_valid():
            parent = parent_context = None
            trace_id = generate_trace_id()
            trace_options = None
            trace_state = None
        else:
            trace_id = parent_context.trace_id
            trace_options = parent_context.trace_options
            trace_state = parent_context.trace_state

        context = trace_api.SpanContext(
            trace_id, generate_span_id(), trace_options, 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.sampler.should_sample(
            parent_context,
            context.trace_id,
            context.span_id,
            name,
            attributes,
            links,
        )

        if sampling_decision.sampled:
            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)
            span = Span(
                name=name,
                context=context,
                parent=parent,
                sampler=self.sampler,
                attributes=span_attributes,
                span_processor=self._active_span_processor,
                kind=kind,
                links=links,
            )
            span.start(start_time=start_time)
        else:
            span = trace_api.DefaultSpan(context=context)
        return span
Exemple #15
0
    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 create_default_span() -> trace_api.Span:
     span_context = trace_api.SpanContext(37, 73, is_remote=False)
     return trace_api.DefaultSpan(span_context)
 def test_use_span(self):
     span = trace.DefaultSpan(trace.INVALID_SPAN_CONTEXT)
     with self.tracer.use_span(span):
         pass
    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 test_ctor(self):
     context = trace.SpanContext(
         1, 1, trace.DEFAULT_TRACE_OPTIONS, trace.DEFAULT_TRACE_STATE
     )
     span = trace.DefaultSpan(context)
     self.assertEqual(context, span.get_context())