def zipkin_span_params(self, span): span_params = self.log_span_params(span) timing = {'ss': span.start_time, 'sr': span.start_time + span.duration} annotations = annotation_list_builder(timing, self.endpoint) zipkin_attrs = ZipkinAttrs( trace_id=span_params['trace_id'], span_id=span_params['span_id'], parent_span_id=span_params['parent_span_id'], flags=span_params['flags'], is_sampled=span_params['is_sampled']) return { **span_params, 'zipkin_attrs': zipkin_attrs, 'annotations': annotations, 'binary_annotations': binary_annotation_list_builder( { **span.context.baggage, **span.tags }, self.endpoint), }
def encode_span(self, span_builder): """Encodes the current span to thrift.""" span = span_builder.build_v1_span() thrift_endpoint = thrift.create_endpoint( span.endpoint.port, span.endpoint.service_name, span.endpoint.ipv4, span.endpoint.ipv6, ) thrift_annotations = thrift.annotation_list_builder( span.annotations, thrift_endpoint, ) thrift_binary_annotations = thrift.binary_annotation_list_builder( span.binary_annotations, thrift_endpoint, ) # Add sa binary annotation if span.sa_endpoint is not None: thrift_sa_endpoint = thrift.create_endpoint( span.sa_endpoint.port, span.sa_endpoint.service_name, span.sa_endpoint.ipv4, span.sa_endpoint.ipv6, ) thrift_binary_annotations.append(thrift.create_binary_annotation( key=thrift.zipkin_core.SERVER_ADDR, value=thrift.SERVER_ADDR_VAL, annotation_type=thrift.zipkin_core.AnnotationType.BOOL, host=thrift_sa_endpoint, )) thrift_span = thrift.create_span( span.id, span.parent_id, span.trace_id, span.name, thrift_annotations, thrift_binary_annotations, span.timestamp, span.duration, ) encoded_span = thrift.span_to_bytes(thrift_span) return encoded_span
def test__decode_thrift_annotations_server_span(self, thrift_endpoint): timestamp = 1.0 decoder = _V1ThriftDecoder() thrift_annotations = thrift.annotation_list_builder( {"sr": timestamp, "ss": timestamp + 10}, thrift_endpoint, ) annotations, end, kind, ts, dur = decoder._decode_thrift_annotations( thrift_annotations, ) assert annotations == {} assert end == Endpoint("test_service", "10.0.0.1", None, 8888) assert kind == Kind.SERVER assert ts == timestamp * USEC assert dur == 10 * USEC
def test__decode_thrift_annotations(self, thrift_endpoint): timestamp = 1.0 decoder = _V1ThriftDecoder() thrift_annotations = thrift.annotation_list_builder( {"cs": timestamp, "cr": timestamp + 10, "my_annotation": timestamp + 15}, thrift_endpoint, ) annotations, end, kind, ts, dur = decoder._decode_thrift_annotations( thrift_annotations, ) assert annotations == {"my_annotation": 16.0} assert end == Endpoint("test_service", "10.0.0.1", None, 8888) assert kind == Kind.CLIENT assert ts == timestamp * USEC assert dur == 10 * USEC
def test__decode_thrift_annotations(self, thrift_endpoint): timestamp = 1.0 decoder = _V1ThriftDecoder() thrift_annotations = thrift.annotation_list_builder( { 'cs': timestamp, 'cr': timestamp + 10, 'my_annotation': timestamp + 15, }, thrift_endpoint, ) annotations, end, kind, ts, dur = decoder._decode_thrift_annotations( thrift_annotations, ) assert annotations == {'my_annotation': 16.0} assert end == Endpoint('test_service', '10.0.0.1', None, 8888) assert kind == Kind.CLIENT assert ts == timestamp * USEC assert dur == 10 * USEC
def encode_span(self, v2_span): """Encodes the current span to thrift.""" span = v2_span.build_v1_span() thrift_endpoint = thrift.create_endpoint( span.endpoint.port, span.endpoint.service_name, span.endpoint.ipv4, span.endpoint.ipv6, ) thrift_annotations = thrift.annotation_list_builder( span.annotations, thrift_endpoint, ) thrift_binary_annotations = thrift.binary_annotation_list_builder( span.binary_annotations, thrift_endpoint, ) # Add sa/ca binary annotations if v2_span.remote_endpoint: self.encode_remote_endpoint( v2_span.remote_endpoint, v2_span.kind, thrift_binary_annotations, ) thrift_span = thrift.create_span( span.id, span.parent_id, span.trace_id, span.name, thrift_annotations, thrift_binary_annotations, span.timestamp, span.duration, ) encoded_span = thrift.span_to_bytes(thrift_span) return encoded_span
def test__decode_thrift_annotations_local_span(self, thrift_endpoint): timestamp = 1.0 decoder = _V1ThriftDecoder() thrift_annotations = thrift.annotation_list_builder( { 'cs': timestamp, 'sr': timestamp, 'ss': timestamp + 10, 'cr': timestamp + 10, }, thrift_endpoint, ) annotations, end, kind, ts, dur = decoder._decode_thrift_annotations( thrift_annotations, ) assert annotations == {} assert end == Endpoint('test_service', '10.0.0.1', None, 8888) assert kind == Kind.LOCAL # ts and dur are not computed for a local span since those always have # timestamp and duration set as span arguments. assert ts is None assert dur is None
def log_spans(self): """Main function to log all the annotations stored during the entire request. This is done if the request is sampled and the response was a success. It also logs the service `ss` and `sr` annotations. """ if self.zipkin_attrs.is_sampled: # Collect additional annotations from the logging handler annotations_by_span_id = defaultdict(dict) binary_annotations_by_span_id = defaultdict(dict) for msg in self.log_handler.extra_annotations: span_id = msg['parent_span_id'] or self.zipkin_attrs.span_id # This should check if these are non-None annotations_by_span_id[span_id].update(msg['annotations']) binary_annotations_by_span_id[span_id].update( msg['binary_annotations']) # Collect, annotate, and log client spans from the logging handler for span in self.log_handler.client_spans: # The parent_span_id is either the parent ID set in the # logging handler or the current Zipkin context's span ID. parent_span_id = (span['parent_span_id'] or self.zipkin_attrs.span_id) # A new client span's span ID can be overridden span_id = span['span_id'] or generate_random_64bit_string() endpoint = copy_endpoint_with_new_service_name( self.thrift_endpoint, span['service_name']) # Collect annotations both logged with the new spans and # logged in separate log messages. annotations = span['annotations'] annotations.update(annotations_by_span_id[span_id]) binary_annotations = span['binary_annotations'] binary_annotations.update( binary_annotations_by_span_id[span_id]) # Create serializable thrift objects of annotations thrift_annotations = annotation_list_builder( annotations, endpoint) thrift_binary_annotations = binary_annotation_list_builder( binary_annotations, endpoint) print("logging_helper:1") log_span( span_id=span_id, parent_span_id=parent_span_id, trace_id=self.zipkin_attrs.trace_id, span_name=span['span_name'], annotations=thrift_annotations, binary_annotations=thrift_binary_annotations, transport_handler=self.transport_handler, ) # Collect extra annotations for server span, then log it. extra_annotations = annotations_by_span_id[ self.zipkin_attrs.span_id] extra_binary_annotations = binary_annotations_by_span_id[ self.zipkin_attrs.span_id] annotations = dict(sr=self.start_timestamp, ss=self.end_timestamp, **extra_annotations) """ FIXME sunyan: disable it! if self.add_logging_annotation: annotations[LOGGING_START_KEY] = logging_start """ thrift_annotations = annotation_list_builder( annotations, self.thrift_endpoint, ) # Binary annotations can be set through debug messages or the # set_extra_binary_annotations registry setting. self.binary_annotations_dict.update(extra_binary_annotations) thrift_binary_annotations = binary_annotation_list_builder( self.binary_annotations_dict, self.thrift_endpoint, ) print("logging_helper:2") log_span( span_id=self.zipkin_attrs.span_id, parent_span_id=self.zipkin_attrs.parent_span_id, trace_id=self.zipkin_attrs.trace_id, span_name=self.span_name, annotations=thrift_annotations, binary_annotations=thrift_binary_annotations, transport_handler=self.transport_handler, )
def check_v1_thrift(obj, zipkin_attrs, inner_span_id, ts): inner_span, root_span = _decode_binary_thrift_objs(obj) endpoint = thrift.create_endpoint( port=8080, service_name='test_service_name', ipv4='10.0.0.0', ) binary_annotations = thrift.binary_annotation_list_builder( {'some_key': 'some_value'}, endpoint, ) binary_annotations.append(thrift.create_binary_annotation( 'sa', '\x01', zipkin_core.AnnotationType.BOOL, thrift.create_endpoint( port=8888, service_name='sa_service', ipv6='2001:0db8:85a3:0000:0000:8a2e:0370:7334', ), )) expected_root = thrift.create_span( span_id=zipkin_attrs.span_id, parent_span_id=zipkin_attrs.parent_span_id, trace_id=zipkin_attrs.trace_id, span_name='test_span_name', annotations=thrift.annotation_list_builder( OrderedDict([('cs', ts), ('cr', ts + 10)]), endpoint, ), binary_annotations=binary_annotations, timestamp_s=None, duration_s=None, ) # py.test diffs of thrift Spans are pretty useless and hide many things # These prints would only appear on stdout if the test fails and help comparing # the 2 spans. print(root_span) print(expected_root) assert root_span == expected_root expected_inner = thrift.create_span( span_id=inner_span_id, parent_span_id=zipkin_attrs.span_id, trace_id=zipkin_attrs.trace_id, span_name='inner_span', annotations=thrift.annotation_list_builder( OrderedDict([('cs', ts), ('sr', ts), ('ss', ts + 5), ('cr', ts + 5), ('ws', ts)]), endpoint, ), binary_annotations=[], timestamp_s=ts, duration_s=5, ) # py.test diffs of thrift Spans are pretty useless and hide many things # These prints would only appear on stdout if the test fails and help comparing # the 2 spans. print(inner_span) print(expected_inner) assert inner_span == expected_inner
def log_spans(self): """Main function to log all the annotations stored during the entire request. This is done if the request is sampled and the response was a success. It also logs the service (`ss` and `sr`) or the client ('cs' and 'cr') annotations. """ if self.zipkin_attrs.is_sampled: end_timestamp = time.time() # Collect additional annotations from the logging handler annotations_by_span_id = defaultdict(dict) binary_annotations_by_span_id = defaultdict(dict) for msg in self.log_handler.extra_annotations: span_id = msg['parent_span_id'] or self.zipkin_attrs.span_id # This should check if these are non-None annotations_by_span_id[span_id].update(msg['annotations']) binary_annotations_by_span_id[span_id].update( msg['binary_annotations']) # Collect, annotate, and log client spans from the logging handler for span in self.log_handler.client_spans: # The parent_span_id is either the parent ID set in the # logging handler or the current Zipkin context's span ID. parent_span_id = (span['parent_span_id'] or self.zipkin_attrs.span_id) # A new client span's span ID can be overridden span_id = span['span_id'] or generate_random_64bit_string() endpoint = copy_endpoint_with_new_service_name( self.thrift_endpoint, span['service_name']) # Collect annotations both logged with the new spans and # logged in separate log messages. annotations = span['annotations'] annotations.update(annotations_by_span_id[span_id]) binary_annotations = span['binary_annotations'] binary_annotations.update( binary_annotations_by_span_id[span_id]) timestamp, duration = get_local_span_timestamp_and_duration( annotations) # Create serializable thrift objects of annotations thrift_annotations = annotation_list_builder( annotations, endpoint) thrift_binary_annotations = binary_annotation_list_builder( binary_annotations, endpoint) if span.get('sa_binary_annotations'): thrift_binary_annotations += span['sa_binary_annotations'] log_span( span_id=span_id, parent_span_id=parent_span_id, trace_id=self.zipkin_attrs.trace_id, span_name=span['span_name'], annotations=thrift_annotations, binary_annotations=thrift_binary_annotations, transport_handler=self.transport_handler, timestamp_s=timestamp, duration_s=duration, ) extra_annotations = annotations_by_span_id[ self.zipkin_attrs.span_id] extra_binary_annotations = binary_annotations_by_span_id[ self.zipkin_attrs.span_id] k1, k2 = ('sr', 'ss') if self.client_context: k1, k2 = ('cs', 'cr') annotations = {k1: self.start_timestamp, k2: end_timestamp} annotations.update(extra_annotations) if self.add_logging_annotation: annotations[LOGGING_END_KEY] = time.time() thrift_annotations = annotation_list_builder( annotations, self.thrift_endpoint, ) # Binary annotations can be set through debug messages or the # set_extra_binary_annotations registry setting. self.binary_annotations_dict.update(extra_binary_annotations) thrift_binary_annotations = binary_annotation_list_builder( self.binary_annotations_dict, self.thrift_endpoint, ) if self.sa_binary_annotations: thrift_binary_annotations += self.sa_binary_annotations if self.report_root_timestamp: timestamp = self.start_timestamp duration = end_timestamp - self.start_timestamp else: timestamp = duration = None log_span( span_id=self.zipkin_attrs.span_id, parent_span_id=self.zipkin_attrs.parent_span_id, trace_id=self.zipkin_attrs.trace_id, span_name=self.span_name, annotations=thrift_annotations, binary_annotations=thrift_binary_annotations, timestamp_s=timestamp, duration_s=duration, transport_handler=self.transport_handler, )
def test_annotation_list_builder(ann_mock): ann_list = {'foo': 1, 'bar': 2} thrift.annotation_list_builder(ann_list, 'host') ann_mock.assert_any_call(1000000, 'foo', 'host') ann_mock.assert_any_call(2000000, 'bar', 'host') assert ann_mock.call_count == 2
def check_v1_thrift(obj, zipkin_attrs, inner_span_id, ts): inner_span, producer_span, root_span = _decode_binary_thrift_objs(obj) endpoint = thrift.create_endpoint( port=8080, service_name="test_service_name", ipv4="10.0.0.0", ) binary_annotations = thrift.binary_annotation_list_builder( {"some_key": "some_value"}, endpoint, ) binary_annotations.append( thrift.create_binary_annotation( "sa", "\x01", zipkin_core.AnnotationType.BOOL, thrift.create_endpoint( port=8888, service_name="sa_service", ipv6="2001:0db8:85a3:0000:0000:8a2e:0370:7334", ), ) ) expected_root = thrift.create_span( span_id=zipkin_attrs.span_id, parent_span_id=zipkin_attrs.parent_span_id, trace_id=zipkin_attrs.trace_id, span_name="test_span_name", annotations=thrift.annotation_list_builder( OrderedDict([("cs", ts), ("cr", ts + 10)]), endpoint, ), binary_annotations=binary_annotations, timestamp_s=None, duration_s=None, ) # py.test diffs of thrift Spans are pretty useless and hide many things # These prints would only appear on stdout if the test fails and help comparing # the 2 spans. print(root_span) print(expected_root) assert root_span == expected_root expected_inner = thrift.create_span( span_id=inner_span_id, parent_span_id=zipkin_attrs.span_id, trace_id=zipkin_attrs.trace_id, span_name="inner_span", annotations=thrift.annotation_list_builder( OrderedDict([("ws", ts)]), endpoint, ), binary_annotations=[], timestamp_s=ts, duration_s=5, ) # py.test diffs of thrift Spans are pretty useless and hide many things # These prints would only appear on stdout if the test fails and help comparing # the 2 spans. print(inner_span) print(expected_inner) assert inner_span == expected_inner expected_producer = thrift.create_span( span_id=inner_span_id, parent_span_id=zipkin_attrs.span_id, trace_id=zipkin_attrs.trace_id, span_name="producer_span", annotations=thrift.annotation_list_builder( OrderedDict([("ms", ts)]), endpoint, ), binary_annotations=[], timestamp_s=ts, duration_s=10, ) # py.test diffs of thrift Spans are pretty useless and hide many things # These prints would only appear on stdout if the test fails and help comparing # the 2 spans. print(producer_span) print(expected_producer) assert producer_span == expected_producer
def test_annotation_list_builder(ann_mock): ann_list = {"foo": 1, "bar": 2} thrift.annotation_list_builder(ann_list, "host") ann_mock.assert_any_call(1000000, "foo", "host") ann_mock.assert_any_call(2000000, "bar", "host") assert ann_mock.call_count == 2
def add_span( self, span_id, parent_span_id, trace_id, span_name, annotations, binary_annotations, timestamp_s, duration_s, endpoint, sa_endpoint, ): thrift_endpoint = thrift.create_endpoint( endpoint.port, endpoint.service_name, endpoint.ipv4, endpoint.ipv6, ) thrift_annotations = thrift.annotation_list_builder( annotations, thrift_endpoint, ) # Binary annotations can be set through debug messages or the # set_extra_binary_annotations registry setting. thrift_binary_annotations = thrift.binary_annotation_list_builder( binary_annotations, thrift_endpoint, ) # Add sa binary annotation if sa_endpoint is not None: thrift_sa_endpoint = thrift.create_endpoint( sa_endpoint.port, sa_endpoint.service_name, sa_endpoint.ipv4, sa_endpoint.ipv6, ) thrift_binary_annotations.append(thrift.create_binary_annotation( key=thrift.zipkin_core.SERVER_ADDR, value=thrift.SERVER_ADDR_VAL, annotation_type=thrift.zipkin_core.AnnotationType.BOOL, host=thrift_sa_endpoint, )) thrift_span = thrift.create_span( span_id, parent_span_id, trace_id, span_name, thrift_annotations, thrift_binary_annotations, timestamp_s, duration_s, ) encoded_span = thrift.span_to_bytes(thrift_span) # If we've already reached the max batch size or the new span doesn't # fit in max_payload_bytes, send what we've collected until now and # start a new batch. is_over_size_limit = ( self.max_payload_bytes is not None and self.current_size + len(encoded_span) > self.max_payload_bytes ) is_over_portion_limit = len(self.queue) >= self.max_portion_size if is_over_size_limit or is_over_portion_limit: self.flush() self.queue.append(encoded_span) self.current_size += len(encoded_span)