def extract(self, carrier):
        count = 0
        span_id, trace_id, sampled = (0, 0, False)
        baggage = {}
        for k in carrier:
            v = carrier[k]
            k = k.lower()
            if k == field_name_span_id:
                span_id = int(v, 16)
                count += 1
            elif k == field_name_trace_id:
                trace_id = int(v, 16)
                count += 1
            elif k == field_name_sampled:
                if v == "1":
                    sampled = True
                elif v == "0":
                    sampled = False
                else:
                    raise SpanContextCorruptedException()
                count += 1

        if count != field_count:
            raise SpanContextCorruptedException()

        return SpanContext(span_id=span_id,
                           trace_id=trace_id,
                           baggage=baggage,
                           sampled=sampled)
Exemple #2
0
    def extract(self, carrier: Dict[str, str]) -> SpanContext:  # noqa
        fields = carrier[HeaderNames.traceparent].split(self.field_separator)
        try:
            version_str, trace_id_str, parent_id_str, flags_str = fields
        except ValueError:
            raise SpanContextCorruptedException()
        else:
            try:
                trace_id = UUID(hex=trace_id_str)
            except ValueError:
                raise SpanContextCorruptedException()
            if trace_id.variant != uuid.RFC_4122 or trace_id.version != 1:
                raise SpanContextCorruptedException()
            try:
                parent_id = bytes.fromhex(parent_id_str)
                version = ord(bytes.fromhex(version_str))
            except ValueError:
                raise SpanContextCorruptedException()
            if version == 255:
                raise SpanContextCorruptedException()

            return SpanContext(
                event_reference=EventReference(trace_id=trace_id,
                                               event_id=parent_id,
                                               eu_id=None),
                baggage=SpanContext.EMPTY_BAGGAGE,
            )
Exemple #3
0
    def extract(self, carrier: memoryview):

        if type(carrier) is not memoryview:
            raise InvalidCarrierException()

        if len(carrier) < _BINARY_FORMAT_LENGTH:
            raise SpanContextCorruptedException()

        try:
            version = carrier[0]
            trace_id_bytes = bytes(carrier[1:17])
            parent_id = bytes(carrier[17:25])
            #  flags_bytes = carrier[25]
        except (IndexError, ):
            raise SpanContextCorruptedException()
        else:
            if version == 255:
                raise SpanContextCorruptedException()
            try:
                trace_id = UUID(bytes=trace_id_bytes)
            except ValueError:
                raise SpanContextCorruptedException()
            if trace_id.variant != uuid.RFC_4122 or trace_id.version != 1:
                raise SpanContextCorruptedException()

            return SpanContext(
                event_reference=EventReference(trace_id=trace_id,
                                               event_id=parent_id,
                                               eu_id=None),
                baggage=SpanContext.EMPTY_BAGGAGE,
            )
Exemple #4
0
    def extract(self, carrier):  # noqa
        count = 0
        span_id, trace_id, sampled = (0, 0, False)
        baggage = {}
        for k in carrier:
            v = carrier[k]
            k = k.lower()
            if k == field_name_span_id:
                span_id = int(v, 16)
                count += 1
            elif k == field_name_trace_id:
                trace_id = int(v, 16)
                count += 1
            elif k == field_name_sampled:
                if v == str(True).lower():
                    sampled = True
                elif v == str(False).lower():
                    sampled = False
                else:
                    raise SpanContextCorruptedException()
                count += 1
            elif k.startswith(prefix_baggage):
                baggage[k[len(prefix_baggage):]] = v

        if count != field_count:
            raise SpanContextCorruptedException()

        return SpanContext(span_id=span_id,
                           trace_id=trace_id,
                           baggage=baggage,
                           sampled=sampled)
def span_context_from_string(value):
    """
    Decode span ID from a string into a TraceContext.
    Returns None if the string value is malformed.

    :param value: formatted {trace_id}:{span_id}:{parent_id}:{flags}
    """
    if type(value) is list and len(value) > 0:
        # sometimes headers are presented as arrays of values
        if len(value) > 1:
            raise SpanContextCorruptedException(
                'trace context must be a string or array of 1: "%s"' % value)
        value = value[0]
    if not isinstance(value, basestring):
        raise SpanContextCorruptedException('trace context not a string "%s"' %
                                            value)
    parts = value.split(':')
    if len(parts) != 4:
        raise SpanContextCorruptedException('malformed trace context "%s"' %
                                            value)
    try:
        trace_id = long(parts[0], 16)
        span_id = long(parts[1], 16)
        parent_id = long(parts[2], 16)
        flags = int(parts[3], 16)
        if trace_id < 1 or span_id < 1 or parent_id < 0 or flags < 0:
            raise SpanContextCorruptedException(
                'malformed trace context "%s"' % value)
        if parent_id == 0:
            parent_id = None
        return trace_id, span_id, parent_id, flags
    except ValueError as e:
        raise SpanContextCorruptedException(
            'malformed trace context "%s": %s' % (value, e))
Exemple #6
0
def header_to_hex(header):
    if not isinstance(header, (str, six.text_type)):
        raise SpanContextCorruptedException(
            'malformed trace context "%s", expected hex string' % header)

    try:
        return int(header, 16)
    except ValueError:
        raise SpanContextCorruptedException(
            'malformed trace context "%s", expected hex string' % header)
Exemple #7
0
    def extract(self, carrier):
        count = 0
        baggage = {}
        parent_id = None

        for key in carrier:
            lc_key = key.lower()
            value = carrier[key]
            if lc_key == self._propagator_opts.span_id_key.lower():
                span_id = value
                count += 1
            elif lc_key == self._propagator_opts.trace_id_key.lower():
                trace_id = value
                count += 1
            elif lc_key.startswith(
                    self._propagator_opts.baggage_key_prefix.lower()):
                baggage[
                    key[len(self._propagator_opts.baggage_key_prefix):]] = \
                    value
            elif lc_key == self._propagator_opts.parent_id_key.lower():
                parent_id = value

        if count == 0:
            return None
        elif count != 2:
            raise SpanContextCorruptedException(
                "Both SpanID and TraceID are required")

        return SpanContext(span_id=span_id,
                           trace_id=trace_id,
                           parent_id=parent_id,
                           baggage=baggage)
 def extract(self, carrier):
     if not hasattr(carrier, 'iteritems'):
         raise InvalidCarrierException('carrier not a collection')
     trace_id, span_id, parent_id, flags = None, None, None, None
     baggage = None
     debug_id = None
     for key, value in six.iteritems(carrier):
         uc_key = key.lower()
         if uc_key == self.trace_id_header:
             if self.url_encoding:
                 value = urllib.parse.unquote(value)
             trace_id, span_id, parent_id, flags = \
                 span_context_from_string(value)
         elif uc_key.startswith(self.baggage_prefix):
             if self.url_encoding:
                 value = urllib.parse.unquote(value)
             attr_key = key[self.prefix_length:]
             if baggage is None:
                 baggage = {attr_key.lower(): value}
             else:
                 baggage[attr_key.lower()] = value
         elif uc_key == self.debug_id_header:
             if self.url_encoding:
                 value = urllib.parse.unquote(value)
             debug_id = value
     if not trace_id and baggage:
         raise SpanContextCorruptedException('baggage without trace ctx')
     if not trace_id:
         if debug_id is not None:
             return SpanContext.with_debug_id(debug_id=debug_id)
         return None
     return SpanContext(trace_id=trace_id, span_id=span_id,
                        parent_id=parent_id, flags=flags,
                        baggage=baggage)
Exemple #9
0
 def extract(self, format, carrier):
     if format in (Format.HTTP_HEADERS, Format.TEXT_MAP):
         trace_parent = disttracing.TraceParent.from_headers(carrier)
         if not trace_parent:
             raise SpanContextCorruptedException("could not extract span context from carrier")
         return OTSpanContext(trace_parent=trace_parent)
     raise UnsupportedFormatException
Exemple #10
0
    def extract(self, carrier):
        if not isinstance(carrier, bytearray):
            raise InvalidCarrierException('carrier not a bytearray')

        ctx_len = struct.unpack('I', carrier[0:4])[0]
        carrier = json.loads(str(carrier[4:4 + ctx_len]))
        trace_id, span_id, parent_id, flags = None, None, None, None
        baggage = None
        for key, value in six.iteritems(carrier):
            uc_key = key.lower()
            if uc_key == self.trace_id_header:
                trace_id, span_id, parent_id, flags = \
                    span_context_from_string(value)
            else:
                if baggage is None:
                    baggage = {uc_key: value}
                else:
                    baggage[uc_key] = value

        if baggage == None or (not isinstance(baggage, dict)):
            raise SpanContextCorruptedException()

        return SpanContext(trace_id=trace_id,
                           span_id=span_id,
                           parent_id=parent_id,
                           flags=flags,
                           baggage=baggage)
    def extract(self, carrier):
        """Extract a span context from a carrier.

        :class:`ddtrace.propagation.http.HTTPPropagator` is used to extract
        ddtracer supported fields into a `ddtrace.Context` context which is
        combined with new logic to extract the baggage which is returned in an
        OpenTracing compatible span context.

        :param carrier: carrier to extract from.

        :return: extracted span context.
        """
        if not isinstance(carrier, dict):
            raise InvalidCarrierException(
                'propagator expects carrier to be a dict')

        ddspan_ctx = self._dd_propagator.extract(carrier)

        # if the dd propagator fails then it will return a new empty span
        # context (with trace_id=None), we however want to raise an exception
        # if this occurs.
        if not ddspan_ctx.trace_id:
            raise SpanContextCorruptedException(
                'failed to extract span context')

        baggage = {}
        for key in carrier:
            if key.startswith(HTTP_BAGGAGE_PREFIX):
                baggage[key[HTTP_BAGGAGE_PREFIX_LEN:]] = carrier[key]

        return SpanContext(ddcontext=ddspan_ctx, baggage=baggage)
    def extract(self, carrier):  # noqa
        count = 0
        span_id, trace_id, sampled = (0, 0, False)
        baggage = {}
        for k in carrier:
            v = carrier[k]
            k = k.lower()
            if k == field_name_span_id:
                span_id = parse_hex_for_field(field_name_span_id, v)
                count += 1
            elif k == field_name_trace_id:
                trace_id = parse_hex_for_field(field_name_trace_id, v)
                count += 1
            elif k == field_name_sampled:
                sampled = parse_boolean_for_field(field_name_sampled, v)
                count += 1
            elif k.startswith(prefix_baggage):
                baggage[k[len(prefix_baggage):]] = v

        if count != field_count:
            msg = (
                'expected to parse {field_count} fields'
                ', but parsed {count} instead'
            )
            raise SpanContextCorruptedException(msg.format(
                field_count=field_count,
                count=count,
            ))

        return SpanContext(
            span_id=span_id,
            trace_id=trace_id,
            baggage=baggage,
            sampled=sampled)
Exemple #13
0
 def extract(self, format, carrier):
     if format in (Format.HTTP_HEADERS, Format.TEXT_MAP):
         if constants.TRACEPARENT_HEADER_NAME not in carrier:
             raise SpanContextCorruptedException(
                 "could not extract span context from carrier")
         trace_parent = disttracing.TraceParent.from_string(
             carrier[constants.TRACEPARENT_HEADER_NAME])
         return OTSpanContext(trace_parent=trace_parent)
     raise UnsupportedFormatException
def parse_hex_for_field(field_name, value):
    """parses the hexadecimal value of a field into an integer.
    Raises SpanContextCorruptedException in case of failure
    """
    try:
        return int(value, 16)
    except ValueError:
        msg = '{field_name} got an invalid hexadecimal value {value!r}'
        msg = msg.format(field_name=field_name, value=value)
        raise SpanContextCorruptedException(msg)
Exemple #15
0
    def extract(self, carrier):
        if type(carrier) is not bytearray:
            raise InvalidCarrierException()

        try:
            span_context = pickle.loads(carrier)
        except (EOFError, pickle.PickleError):
            raise SpanContextCorruptedException()

        return span_context
Exemple #16
0
    def extract(self, carrier: Dict[str, str]) -> SpanContext:  # noqa
        count = 0
        span_id, trace_id, sampled = (None, None, False)
        baggage = {}
        for k in carrier:
            v = carrier[k]
            k = k.lower()
            if k == field_name_span_id:
                try:
                    span_id = bytes.fromhex(v)
                except ValueError:
                    raise SpanContextCorruptedException()
                count += 1
            elif k == field_name_trace_id:
                try:
                    trace_id = UUID(hex=v)
                except ValueError:
                    raise SpanContextCorruptedException()
                if trace_id.variant != uuid.RFC_4122 or trace_id.version != 1:
                    raise SpanContextCorruptedException()
                count += 1
            elif k == field_name_sampled:
                if v in ('true', '1'):
                    sampled = True
                elif v in ('false', '0'):
                    sampled = False
                else:
                    raise SpanContextCorruptedException()
                count += 1
            elif k.startswith(prefix_baggage):
                baggage[k[len(prefix_baggage):]] = v

        if count != field_count:
            raise SpanContextCorruptedException()

        return SpanContext(
            event_reference=EventReference(trace_id=trace_id,
                                           event_id=span_id,
                                           eu_id=None),
            baggage=baggage,
        )
def parse_boolean_for_field(field_name, value):
    """parses the string value of a field into a boolean.
    Raises SpanContextCorruptedException in case of failure
    """
    if value in ('true', '1'):
        return True
    elif value in ('false', '0'):
        return False

    msg = ('{field} got an invalid value {value!r}, '
           "should be one of \'true\', \'false\', \'0\', \'1\'")
    raise SpanContextCorruptedException(
        msg.format(value=value, field=field_name_sampled))
    def extract(self, carrier):

        case_insensitive_carrier = {}
        for key, value in carrier.items():
            for b3_key in [
                    _SINGLE_HEADER,
                    _TRACEID,
                    _SPANID,
                    _PARENTSPANID,
                    _SAMPLED,
                    _FLAGS,
            ]:
                if key.lower() == b3_key:
                    case_insensitive_carrier[b3_key] = value
                else:
                    case_insensitive_carrier[key] = value

        carrier = case_insensitive_carrier
        baggage = {}

        if _SINGLE_HEADER in carrier.keys():
            fields = carrier.pop(_SINGLE_HEADER).split("-", 4)
            baggage.update(carrier)
            len_fields = len(fields)
            if len_fields == 1:
                sampled = fields[0]
            elif len_fields == 2:
                traceid, spanid = fields
            elif len_fields == 3:
                traceid, spanid, sampled = fields
            else:
                traceid, spanid, sampled, parent_spanid = fields
                baggage[_PARENTSPANID] = int(parent_spanid, 16)
            if sampled == "d":
                baggage[_FLAGS] = 1
            else:
                baggage[_SAMPLED] = int(sampled, 16)
        else:
            traceid = carrier.pop(_TRACEID, None)
            spanid = carrier.pop(_SPANID, None)
            parentspanid = carrier.pop(_PARENTSPANID, None)
            sampled = carrier.pop(_SAMPLED, None)
            flags = carrier.pop(_FLAGS, None)

            if sampled is flags is (traceid and spanid) is None:

                raise SpanContextCorruptedException()

            if parentspanid is not None:
                baggage[_PARENTSPANID] = int(parentspanid, 16)

            if flags == 1:
                baggage[_FLAGS] = flags
                if sampled is not None:
                    warn("x-b3-flags: 1 implies x-b3-sampled: 1, ignoring "
                         "the received value of x-b3-sampled")
            elif sampled is not None:
                baggage[_SAMPLED] = sampled

            baggage.update(carrier)

            if baggage == OTSpanContext.EMPTY_BAGGAGE:
                baggage = None

        return SpanContext(trace_id=int(traceid, 16),
                           span_id=int(spanid, 16),
                           baggage=baggage)
    def extract(self, carrier):
        traceparent = None
        tracestate = None

        multiple_header_template = "Found more than one header value for {}"

        # https://www.w3.org/TR/2019/CR-trace-context-20190813/#header-name

        trace_context_free_carrier = {}

        for key, value in carrier.items():

            lower_key = key.lower()

            # The document requires that the headers be accepted regardless of
            # the case of their characters. This means that the keys of carrier
            # may contain 2 or more strings that match traceparent or
            # tracestate when the case of the characters of such strings is
            # ignored. The document does not specify what is to be done in such
            # a situation, this implementation will raise an exception.
            if lower_key == _TRACEPARENT:

                if traceparent is None:
                    traceparent = value

                else:
                    raise SpanContextCorruptedException(
                        multiple_header_template.format(_TRACEPARENT))

            elif lower_key == _TRACESTATE:

                if tracestate is None:
                    tracestate = value

                else:
                    raise SpanContextCorruptedException(
                        multiple_header_template.format(_TRACEPARENT))

            else:
                trace_context_free_carrier[key] = value

        if traceparent is None:
            # https://www.w3.org/TR/trace-context/#no-traceparent-received
            _LOG.warning("No traceparent was received")
            return SpanContext(trace_id=getrandbits(128),
                               span_id=getrandbits(64))

        else:

            version_match = _VERSION.match(traceparent)

            if version_match is None:
                # https://www.w3.org/TR/2019/CR-trace-context-20190813/#versioning-of-traceparent
                _LOG.warning("Unable to parse version from traceparent")
                return SpanContext(trace_id=getrandbits(128),
                                   span_id=getrandbits(64))

            # https://www.w3.org/TR/2019/CR-trace-context-20190813/#version
            version = version_match.group("version")
            if version == "ff":
                _LOG.warning("Forbidden value of 255 found in version")
                return SpanContext(trace_id=getrandbits(128),
                                   span_id=getrandbits(64))

            # https://www.w3.org/TR/2019/CR-trace-context-20190813/#versioning-of-traceparent
            if int(version, 16) > 0:

                if len(traceparent) < 55:
                    _LOG.warning(
                        "traceparent shorter than 55 characters found")
                    return SpanContext(trace_id=getrandbits(128),
                                       span_id=getrandbits(64))

                remainder_match = _FUTURE_VERSION_REMAINDER.match(traceparent)

            else:
                remainder_match = _00_VERSION_REMAINDER.match(traceparent)

            if remainder_match is None:
                # This will happen if any of the trace-id, parent-id or
                # trace-flags fields contains non-allowed characters.
                # The document specifies that traceparent must be ignored if
                # these kind of characters appear in trace-id and parent-id,
                # but it does not specify what to do if they appear in
                # trace-flags.
                # Here it is assumed that traceparent is to be ignored also if
                # non-allowed characters are present in trace-flags too.
                _LOG.warning(
                    "Received an invalid traceparent: {}".format(traceparent))
                return SpanContext(trace_id=getrandbits(128),
                                   span_id=getrandbits(64))

            # https://www.w3.org/TR/2019/CR-trace-context-20190813/#trace-id
            trace_id = remainder_match.group("trace_id")
            if trace_id == 32 * "0":
                _LOG.warning(
                    "Forbidden value of {} found in trace-id".format(trace_id))
                return SpanContext(trace_id=getrandbits(128),
                                   span_id=getrandbits(64))

            # https://www.w3.org/TR/2019/CR-trace-context-20190813/#parent-id
            parent_id = remainder_match.group("parent_id")
            if parent_id == 16 * "0":
                _LOG.warning("Forbidden value of {}"
                             " found in parent-id".format(parent_id))
                return SpanContext(trace_id=getrandbits(128),
                                   span_id=getrandbits(64))

            # https://www.w3.org/TR/2019/CR-trace-context-20190813/#trace-flags
            raw_trace_flags = remainder_match.group("trace_flags")

            trace_flags = []

            for index, bit_flag in enumerate(
                    zip(
                        bin(int(raw_trace_flags, 16))[2:].zfill(8),
                        # https://www.w3.org/TR/2019/CR-trace-context-20190813/#other-flags
                        # Flags can be added in the next list as the document
                        # progresses and they get defined. This list represents the
                        # 8 bits that are available in trace-flags and their
                        # respective meaning.
                        [None, None, None, None, None, None, None, "sampled"
                         ])):
                bit, flag = bit_flag

                if int(bit):
                    if flag is None:
                        warn("Invalid flag set at bit {}".format(index))
                        trace_flags.append("0")

                    else:
                        trace_flags.append("1")

                else:
                    trace_flags.append("0")

            trace_context_free_carrier["trace-flags"] = int(
                "".join(trace_flags), 2)

            if tracestate is not None:

                tracestate_dictionary = OrderedDict()

                for counter, list_member in enumerate(tracestate.split(",")):
                    # https://www.w3.org/TR/trace-context/#tracestate-header-field-values
                    if counter > 31:
                        _LOG.warning(
                            "More than 32 list-members "
                            "found in tracestate {}".format(tracestate))
                        break

                    # https://www.w3.org/TR/trace-context/#tracestate-header-field-values
                    if _BLANK.match(list_member):
                        _LOG.debug("Ignoring empty tracestate list-member")
                        continue

                    key_value = _KEY_VALUE.match(list_member)

                    if key_value is None:
                        _LOG.warning("Invalid key/value pair found: {}".format(
                            key_value))
                        break

                    key, value = key_value.groups()

                    if key in tracestate_dictionary.keys():
                        _LOG.warning(
                            "Duplicate tracestate key found: {}".format(key))
                        break

                    tracestate_dictionary[key] = value

                else:
                    trace_context_free_carrier[_TRACESTATE] = ",".join([
                        "{0}={1}".format(key, value)
                        for key, value in tracestate_dictionary.items()
                    ])

        return SpanContext(trace_id=int(trace_id, 16),
                           span_id=int(parent_id, 16),
                           baggage=trace_context_free_carrier)