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)
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, )
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, )
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))
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)
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)
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
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)
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)
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
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)