def test_non_sampled_span_thrift(recorder): mock_connection = MockConnection() mock_connection.open() non_sampled_span = BasicSpan( lightstep.tracer._LightstepTracer(False, recorder, None), operation_name="non_sampled", context=SpanContext(trace_id=1, span_id=1, sampled=False), start_time=time.time(), ) non_sampled_span.finish() sampled_span = BasicSpan( lightstep.tracer._LightstepTracer(False, recorder, None), operation_name="sampled", context=SpanContext(trace_id=1, span_id=2, sampled=True), start_time=time.time(), ) sampled_span.finish() recorder.record_span(non_sampled_span) recorder.record_span(sampled_span) recorder.flush(mock_connection) if recorder.use_thrift: for span_record in mock_connection.reports[0].span_records: assert span_record.span_name == "sampled" else: for span in mock_connection.reports[0].spans: assert span.operation_name == "sampled"
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): # noqa try: if type(carrier) is dict or hasattr(carrier, "__dict__"): dc = carrier elif type(carrier) is list: dc = dict(carrier) else: raise ot.SpanContextCorruptedException() # Look for standard X-Instana-T/S format if self.HEADER_KEY_T in dc and self.header_key_s in dc: trace_id = util.header_to_id(dc[self.HEADER_KEY_T]) span_id = util.header_to_id(dc[self.HEADER_KEY_S]) # Alternatively check for alternate HTTP_X_INSTANA_T/S style elif self.ALT_HEADER_KEY_T in dc and self.ALT_HEADER_KEY_S in dc: trace_id = util.header_to_id(dc[self.ALT_HEADER_KEY_T]) span_id = util.header_to_id(dc[self.ALT_HEADER_KEY_S]) return SpanContext(span_id=span_id, trace_id=trace_id, baggage={}, sampled=True) except Exception as e: log.debug("extract error: ", str(e))
async def read_loop(self, *args, **kwargs): try: while not self.stopped: msg = await self.inner_read_message(*args, **kwargs) assert type(msg) == dict, 'Msg is: {!r}'.format(msg) ctx_headers = msg['context_headers'] if self.remote: parent_ctx = SpanContext(**ctx_headers) method = msg['method'] ctx = Context(self.discovery, self.endpoint.service, parent_ctx, method) self.log.debug('CTX: {}'.format(ctx.span.context.trace_id)) call_args = msg.get('args', ()) call_kwargs = msg.get('kwargs', {}) hc = self.handle_call(method, ctx, *call_args, **call_kwargs) if isinstance(hc, CoroutineType): resp = await hc else: resp = hc self.log.debug('Remote send resp ==> {}'.format(resp)) response = { 'context_headers': msg['context_headers'], 'method': msg['method'], 'response': resp } await self.remote_send_response(msg, response) else: self.log.debug('Proxy return resp: {}'.format(msg)) self.proxy_send_response(msg) except asyncio.CancelledError: pass except Exception as e: self.log.exception('In read loop') self.terminate(e)
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 = v count += 1 elif k == field_name_trace_id: trace_id = v 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 dummy_basic_span(self, recorder, i): return BasicSpan( lightstep.tracer._LightstepTracer(False, recorder), operation_name=str(i), context=SpanContext( trace_id=1000+i, span_id=2000+i), start_time=time.time())
def dummy_basic_span(recorder, i): span = BasicSpan( lightstep.tracer._LightstepTracer(False, recorder, None), operation_name=str(i), context=SpanContext(trace_id=1000 + i, span_id=2000 + i), start_time=time.time() - 100, ) span.finish() return span
def extract(self, carrier): # TODO: support ot-debug => override sampled to true # generate custom label try: kwargs = {'baggage': {}} for k, v in self.mapping.items(): name, _, extract = v kwargs[k] = extract(carrier[name]) return SpanContext(**kwargs) except Exception: pass
def finish(self, finish_time=None): if self.parent_id is None: self.tracer.cur_ctx = None else: # Set tracer context to the parent span pctx = SpanContext(span_id=self.parent_id, trace_id=self.context.trace_id, baggage={}, sampled=True) self.tracer.cur_ctx = pctx super(InstanaSpan, self).finish(finish_time)
def extract(self, carrier): self.log.debug('Run extract: {}'.format(carrier)) try: kwargs = {'baggage': {}} for k, v in self.mapping.items(): name, _, extract = v key = name if name in carrier else 'HTTP_{}'.format(name) # crash when no HTTP_OT_SPAN_ID => return None kwargs[k] = extract(carrier[key]) return SpanContext(**kwargs) except Exception: pass
def extract(self, carrier): # noqa try: if type(carrier) is dict or hasattr(carrier, "__dict__"): dc = carrier elif type(carrier) is list: dc = dict(carrier) else: raise ot.SpanContextCorruptedException() if field_name_trace_id in dc and field_name_span_id in dc: trace_id = util.header_to_id(dc[field_name_trace_id]) span_id = util.header_to_id(dc[field_name_span_id]) return SpanContext(span_id=span_id, trace_id=trace_id, baggage={}, sampled=True) except Exception as e: log.debug("extract error: ", str(e)) return SpanContext()
def start_span(self, operation_name=None, child_of=None, references=None, tags=None, start_time=None, ignore_active_span=False): "Taken from BasicTracer so we can override generate_id calls to ours" start_time = time.time() if start_time is None else start_time # See if we have a parent_ctx in `references` parent_ctx = None if child_of is not None: parent_ctx = ( child_of if isinstance(child_of, ot.SpanContext) else child_of.context) elif references is not None and len(references) > 0: # TODO only the first reference is currently used parent_ctx = references[0].referenced_context # retrieve the active SpanContext if not ignore_active_span and parent_ctx is None: scope = self.scope_manager.active if scope is not None: parent_ctx = scope.span.context # Assemble the child ctx gid = generate_id() ctx = SpanContext(span_id=gid) if parent_ctx is not None: if parent_ctx._baggage is not None: ctx._baggage = parent_ctx._baggage.copy() ctx.trace_id = parent_ctx.trace_id ctx.sampled = parent_ctx.sampled else: ctx.trace_id = gid ctx.sampled = self.sampler.sampled(ctx.trace_id) # Tie it all together span = InstanaSpan(self, operation_name=operation_name, context=ctx, parent_id=(None if parent_ctx is None else parent_ctx.span_id), tags=tags, start_time=start_time) if operation_name in self.recorder.entry_spans: # For entry spans, add only a backtrace fingerprint self.__add_stack(span, limit=2) if operation_name in self.recorder.exit_spans: self.__add_stack(span) return span
def extract(self, carrier): if type(carrier) is not bytearray: raise InvalidCarrierException() serializedProto = standard_b64decode(carrier) state = BinaryCarrier() state.ParseFromString(bytes(serializedProto)) baggage = {} for k in state.basic_ctx.baggage_items: baggage[k] = state.basic_ctx.baggage_items[k] return SpanContext(span_id=state.basic_ctx.span_id, trace_id=state.basic_ctx.trace_id, baggage=baggage, sampled=state.basic_ctx.sampled)
def extract(self, carrier): if type(carrier) is not bytearray: raise InvalidCarrierException() m = memoryview(carrier) m, trace_id = _rstr(m) m, span_id = _rstr(m) sampled = struct.unpack('?', m[:1])[0] m = m[1:] bl = struct.unpack('>I', m[:4])[0] m = m[4:] baggage = {} for _ in range(bl): m, k = _rstr(m) m, v = _rstr(m) baggage[k] = v return SpanContext(span_id=span_id, trace_id=trace_id, baggage=baggage, sampled=sampled)
def start_span(self, operation_name=None, child_of=None, references=None, tags=None, start_time=None): "Taken from BasicTracer so we can override generate_id calls to ours" start_time = time.time() if start_time is None else start_time # See if we have a parent_ctx in `references` parent_ctx = None if child_of is not None: parent_ctx = (child_of if isinstance(child_of, ot.SpanContext) else child_of.context) elif references is not None and len(references) > 0: # TODO only the first reference is currently used parent_ctx = references[0].referenced_context # Assemble the child ctx instana_id = generate_id() ctx = SpanContext(span_id=instana_id) if parent_ctx is not None: if parent_ctx._baggage is not None: ctx._baggage = parent_ctx._baggage.copy() ctx.trace_id = parent_ctx.trace_id ctx.sampled = parent_ctx.sampled else: ctx.trace_id = instana_id ctx.sampled = self.sampler.sampled(ctx.trace_id) # Tie it all together span = InstanaSpan( self, operation_name=operation_name, context=ctx, parent_id=(None if parent_ctx is None else parent_ctx.span_id), tags=tags, start_time=start_time) self.cur_ctx = span.context return span
def extract(self, carrier): # noqa trace_id = None span_id = None try: if type(carrier) is dict or hasattr(carrier, "__getitem__"): dc = carrier elif hasattr(carrier, "__dict__"): dc = carrier.__dict__ elif type(carrier) is list: dc = dict(carrier) else: raise ot.SpanContextCorruptedException() # Headers can exist in the standard X-Instana-T/S format or the alternate HTTP_X_INSTANA_T/S style # We do a case insensitive search to cover all possible variations of incoming headers. for key in dc.keys(): lc_key = key.lower() if self.LC_HEADER_KEY_T == lc_key: trace_id = header_to_id(dc[key]) elif self.LC_HEADER_KEY_S == lc_key: span_id = header_to_id(dc[key]) elif self.ALT_LC_HEADER_KEY_T == lc_key: trace_id = header_to_id(dc[key]) elif self.ALT_LC_HEADER_KEY_S == lc_key: span_id = header_to_id(dc[key]) ctx = None if trace_id is not None and span_id is not None: ctx = SpanContext(span_id=span_id, trace_id=trace_id, baggage={}, sampled=True) return ctx except Exception as e: logger.debug("extract error:", exc_info=True)
def extract(self, carrier): return SpanContext()
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)
def test_exception_formatting(recorder): mock_connection = MockConnection() mock_connection.open() assert len(recorder._span_records) == 0 span = BasicSpan( lightstep.tracer._LightstepTracer(False, recorder, None), operation_name="exception span", context=SpanContext(trace_id=1000, span_id=2000), start_time=time.time() - 100, ) span.log_kv({ERROR_KIND: AttributeError}) span.finish() assert len(recorder._span_records) == 1 assert recorder.flush(mock_connection) spans = recorder.converter.get_span_records(mock_connection.reports[0]) if hasattr(spans[0], "log_records"): assert len(spans[0].log_records) == 1 assert len(spans[0].log_records[0].fields) == 1 assert spans[0].log_records[0].fields[0] == ttypes.KeyValue( Key="error.kind", Value="AttributeError") else: assert len(spans[0].logs) == 1 assert len(spans[0].logs[0].fields) == 1 assert spans[0].logs[0].fields[0].key == "error.kind" assert spans[0].logs[0].fields[0].string_value == "AttributeError" span = BasicSpan( lightstep.tracer._LightstepTracer(False, recorder, None), operation_name="exception span", context=SpanContext(trace_id=1000, span_id=2000), start_time=time.time() - 100, ) try: raise Exception except Exception: # pylint: disable=broad-except exc_type, exc_value, exc_tb = sys.exc_info() span.log_kv({ STACK: exc_tb, ERROR_KIND: exc_type, ERROR_OBJECT: exc_value }) span.finish() assert len(recorder._span_records) == 1 assert recorder.flush(mock_connection) spans = recorder.converter.get_span_records(mock_connection.reports[1]) if hasattr(spans[0], "log_records"): assert len(spans[0].log_records) == 1 assert len(spans[0].log_records[0].fields) == 3 for field in spans[0].log_records[0].fields: if field.Key == "stack": assert "Traceback (most recent call last):" in field.Value elif field.Key == "error.kind": assert field.Value == "Exception" elif field.Key == "error.object": assert field.Value == "" else: raise AttributeError("unexpected field: %s".format(field.Key)) else: assert len(spans[0].logs) == 1 assert len(spans[0].logs[0].fields) == 3 for field in spans[0].logs[0].fields: if field.key == "stack": assert "Traceback (most recent call last):" in field.string_value elif field.key == "error.kind": assert field.string_value == "Exception" elif field.key == "error.object": assert field.string_value == "" else: raise AttributeError("unexpected field: %s".format(field.key))
def dummy_basic_span(self, recorder, i): return BasicSpan(zipkin_ot.tracer._OpenZipkinTracer(recorder), operation_name=str(i), context=SpanContext(trace_id=1000 + i, span_id=2000 + i), start_time=time.time())
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)