示例#1
0
    def test_init(self, mock_generate_kind):
        # Test that all arguments are correctly saved
        zipkin_attrs = ZipkinAttrs(None, None, None, None, None)
        transport = MockTransportHandler()
        firehose = MockTransportHandler()
        stack = Stack([])
        span_storage = SpanStorage()
        tracer = MockTracer()

        context = tracer.zipkin_span(
            service_name="test_service",
            span_name="test_span",
            zipkin_attrs=zipkin_attrs,
            transport_handler=transport,
            max_span_batch_size=10,
            annotations={"test_annotation": 1},
            binary_annotations={"status": "200"},
            port=80,
            sample_rate=100.0,
            include=("cs", "cr"),
            add_logging_annotation=True,
            report_root_timestamp=True,
            use_128bit_trace_id=True,
            host="127.0.0.255",
            context_stack=stack,
            span_storage=span_storage,
            firehose_handler=firehose,
            kind=Kind.CLIENT,
            timestamp=1234,
            duration=10,
            encoding=Encoding.V2_JSON,
        )

        assert context.service_name == "test_service"
        assert context.span_name == "test_span"
        assert context.zipkin_attrs_override == zipkin_attrs
        assert context.transport_handler == transport
        assert context.max_span_batch_size == 10
        assert context.annotations == {"test_annotation": 1}
        assert context.binary_annotations == {"status": "200"}
        assert context.port == 80
        assert context.sample_rate == 100.0
        assert context.add_logging_annotation is True
        assert context.report_root_timestamp_override is True
        assert context.use_128bit_trace_id is True
        assert context.host == "127.0.0.255"
        assert context._context_stack == stack
        assert context._span_storage == span_storage
        assert context.firehose_handler == firehose
        assert mock_generate_kind.call_count == 1
        assert mock_generate_kind.call_args == mock.call(
            context, Kind.CLIENT, ("cs", "cr"),
        )
        assert context.timestamp == 1234
        assert context.duration == 10
        assert context.encoding == Encoding.V2_JSON
        assert context._tracer == tracer
        # Check for backward compatibility
        assert tracer.get_spans() == span_storage
        assert tracer.get_context() == stack
示例#2
0
def test_encoding(encoding, validate_fn):
    zipkin_attrs = ZipkinAttrs(
        trace_id=generate_random_64bit_string(),
        span_id=generate_random_64bit_string(),
        parent_span_id=generate_random_64bit_string(),
        is_sampled=True,
        flags=None,
    )
    inner_span_id = generate_random_64bit_string()
    mock_transport_handler = MockTransportHandler(10000)
    # Let's hardcode the timestamp rather than call time.time() every time.
    # The issue with time.time() is that the convertion to int of the
    # returned float value * 1000000 is not precise and in the same test
    # sometimes returns N and sometimes N+1. This ts value doesn't have that
    # issue afaict, probably since it ends in zeros.
    ts = 1538544126.115900
    with mock.patch("time.time", autospec=True) as mock_time:
        # zipkin.py start, logging_helper.start, 3 x logging_helper.stop
        # I don't understand why logging_helper.stop would run 3 times, but
        # that's what I'm seeing in the test
        mock_time.side_effect = iter(
            [ts, ts, ts + 10, ts + 10, ts + 10, ts + 10, ts + 10]
        )
        with zipkin.zipkin_span(
            service_name="test_service_name",
            span_name="test_span_name",
            transport_handler=mock_transport_handler,
            binary_annotations={"some_key": "some_value"},
            encoding=encoding,
            zipkin_attrs=zipkin_attrs,
            host="10.0.0.0",
            port=8080,
            kind=Kind.CLIENT,
        ) as span:
            with mock.patch.object(
                zipkin, "generate_random_64bit_string", return_value=inner_span_id,
            ):
                with zipkin.zipkin_span(
                    service_name="test_service_name",
                    span_name="inner_span",
                    timestamp=ts,
                    duration=5,
                    annotations={"ws": ts},
                ):
                    span.add_sa_binary_annotation(
                        8888, "sa_service", "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
                    )
                with zipkin.zipkin_span(
                    service_name="test_service_name",
                    span_name="producer_span",
                    timestamp=ts,
                    duration=10,
                    kind=Kind.PRODUCER,
                ):
                    pass

    output = mock_transport_handler.get_payloads()[0]
    validate_fn(output, zipkin_attrs, inner_span_id, ts)
示例#3
0
def _do_test_concurrent_subrequests_in_threads(thread_class):
    """What if a thread has a span context, then fires off N threads to do N
    subrequests in parallel?

    Surely the spans of the subrequests should be siblings whose parentID is
    equal to the first thread's span context's spanID.

    Furthermore, a final sub-span created in the first thread should be a child
    of that thread (no leaking of span stack from the child threads).
    """
    transport = MockTransportHandler()
    with zipkin_span(
        service_name="main_thread",
        span_name="handle_one_big_request",
        transport_handler=transport,
        sample_rate=100.0,
        encoding=Encoding.V2_JSON,
        use_128bit_trace_id=True,
    ) as span_ctx:
        assert True is span_ctx._is_local_root_span
        assert span_ctx.logging_context
        expected_trace_id = span_ctx.zipkin_attrs.trace_id
        expected_parent_id = span_ctx.zipkin_attrs.span_id

        # Now do three subrequests
        req_count = 3
        threads = []
        output_q = queue.Queue()
        for thread_idx in range(req_count):
            this_thread = thread_class(
                target=_do_one_little_request, args=(output_q, "input-%d" % thread_idx)
            )
            threads.append(this_thread)
            this_thread.start()
        outputs = set()
        for thread in threads:
            thread.join()
            outputs.add(output_q.get())
        assert {"input-0-output", "input-1-output", "input-2-output"} == outputs

    output = transport.get_payloads()
    assert len(output) == 1

    spans = sorted(json.loads(output[0]), key=itemgetter("timestamp"))
    assert len(spans) == 4
    parent_span = spans[0]
    subrequest_spans = spans[1:]
    assert len(subrequest_spans) == 3
    assert parent_span["name"] == "handle_one_big_request"
    for span in subrequest_spans:
        assert "do-the-thing" == span["name"]
        assert span["tags"]["input.was"] in ("input-0", "input-1", "input-2")
        assert expected_trace_id == span["traceId"]
        # Perhaps most importantly, all the subrequest spans should share the same
        # parentId, which is the main thread's span's 'id'
        assert expected_parent_id == span["parentId"]
示例#4
0
def _create_root_span(is_sampled, firehose_enabled):
    return (), {
        'root_span': zipkin.zipkin_span(
            service_name='my_service',
            span_name='my_span_name',
            transport_handler=MockTransportHandler(),
            firehose_handler=MockTransportHandler() if firehose_enabled else None,
            port=42,
            sample_rate=0 if not is_sampled else 100,
        )
    }
示例#5
0
    def test__get_path_content_type(self, encoding, path, content_type):
        transport = MockTransportHandler()
        with zipkin_span(
                service_name="my_service",
                span_name="home",
                sample_rate=100,
                transport_handler=transport,
                encoding=encoding,
        ):
            pass

        spans = transport.get_payloads()[0]
        http_transport = SimpleHTTPTransport("localhost", 9411)
        assert http_transport._get_path_content_type(spans) == (path,
                                                                content_type)
示例#6
0
def test_adding_error_annotation_on_exception(
    mock_update_binary_annotations,
    exception_message,
    expected_error_string,
):
    zipkin_attrs = ZipkinAttrs(
        trace_id='0',
        span_id='1',
        parent_span_id=None,
        flags='0',
        is_sampled=True,
    )
    context = zipkin.zipkin_span(
        service_name='my_service',
        span_name='span_name',
        zipkin_attrs=zipkin_attrs,
        transport_handler=MockTransportHandler(),
        port=5,
    )
    with pytest.raises(ValueError):
        with context:
            raise ValueError(exception_message)
    assert mock_update_binary_annotations.call_count == 1
    call_args, _ = mock_update_binary_annotations.call_args
    assert 'error' in call_args[1]
    assert expected_error_string == call_args[1]['error']
示例#7
0
def test_update_binary_annotations():
    zipkin_attrs = ZipkinAttrs(
        trace_id='0',
        span_id='1',
        parent_span_id=None,
        flags='0',
        is_sampled=True,
    )
    context = zipkin.zipkin_span(
        service_name='my_service',
        span_name='span_name',
        zipkin_attrs=zipkin_attrs,
        transport_handler=MockTransportHandler(),
        port=5,
    )

    with context:
        assert 'test' not in context.logging_context.binary_annotations_dict
        context.update_binary_annotations({'test': 'hi'})
        assert context.logging_context.binary_annotations_dict['test'] == 'hi'

        nested_context = zipkin.zipkin_span(
            service_name='my_service',
            span_name='nested_span',
            binary_annotations={'one': 'one'},
        )
        with nested_context:
            assert 'one' not in context.logging_context.binary_annotations_dict
            nested_context.update_binary_annotations({'two': 'two'})
            assert 'two' in nested_context.binary_annotations
            assert 'two' not in context.logging_context.binary_annotations_dict
示例#8
0
def test_span_context_sampled_no_handlers(
    generate_string_mock,
    thread_local_mock,
):
    zipkin_attrs = ZipkinAttrs(
        trace_id='1111111111111111',
        span_id='2222222222222222',
        parent_span_id='3333333333333333',
        flags='flags',
        is_sampled=True,
    )
    thread_local_mock.zipkin_attrs = [zipkin_attrs]

    generate_string_mock.return_value = '1'

    context = zipkin.zipkin_span(
        service_name='my_service',
        port=5,
        transport_handler=MockTransportHandler(),
        sample_rate=None,
    )
    with context:
        # Assert that the new ZipkinAttrs were saved
        new_zipkin_attrs = ThreadLocalStack().get()
        assert new_zipkin_attrs.span_id == '1'

    # Outside of the context, things should be returned to normal
    assert ThreadLocalStack().get() == zipkin_attrs
示例#9
0
def test_zipkin_span_trace_with_no_sampling(
    logging_context_cls_mock,
    create_endpoint_mock,
    create_attrs_for_span_mock,
    mock_context_stack,
):
    zipkin_attrs = ZipkinAttrs(
        trace_id='0',
        span_id='1',
        parent_span_id=None,
        flags='0',
        is_sampled=False,
    )

    with zipkin.zipkin_span(
            service_name='my_service',
            span_name='span_name',
            zipkin_attrs=zipkin_attrs,
            transport_handler=MockTransportHandler(),
            port=5,
            context_stack=mock_context_stack,
            span_storage=SpanStorage(),
    ):
        pass

    assert create_attrs_for_span_mock.call_count == 0
    mock_context_stack.push.assert_called_once_with(zipkin_attrs, )
    assert create_endpoint_mock.call_count == 0
    assert logging_context_cls_mock.call_count == 0
    mock_context_stack.pop.assert_called_once_with()
示例#10
0
def test_batch_sender_add_span_many_times(fake_endpoint):
    # We create MAX_PORTION_SIZE * 2 + 1 spans, so we should trigger flush 3
    # times, once every MAX_PORTION_SIZE spans.
    encoder = MockEncoder()
    sender = logging_helper.ZipkinBatchSender(
        transport_handler=MockTransportHandler(),
        max_portion_size=None,
        encoder=encoder,
    )
    max_portion_size = logging_helper.ZipkinBatchSender.MAX_PORTION_SIZE
    with sender:
        for _ in range(max_portion_size * 2 + 1):
            sender.add_span(
                Span(
                    trace_id="000000000000000f",
                    name="span",
                    parent_id="0000000000000001",
                    span_id="0000000000000002",
                    kind=Kind.CLIENT,
                    timestamp=26.0,
                    duration=4.0,
                    local_endpoint=fake_endpoint,
                    annotations={},
                    tags={},
                )
            )

    assert encoder.encode_queue.call_count == 3
    assert len(encoder.encode_queue.call_args_list[0][0][0]) == max_portion_size
    assert len(encoder.encode_queue.call_args_list[1][0][0]) == max_portion_size
    assert len(encoder.encode_queue.call_args_list[2][0][0]) == 1
示例#11
0
    def test_get_current_context_root_sample_rate_override_not_sampled(
        self,
        mock_create_attr,
    ):
        # Root span, with custom zipkin_attrs, not sampled and sample_rate
        zipkin_attrs = ZipkinAttrs(
            trace_id=generate_random_64bit_string(),
            span_id=generate_random_64bit_string(),
            parent_span_id=generate_random_64bit_string(),
            flags=None,
            is_sampled=False,
        )
        context = zipkin.zipkin_span(
            service_name='test_service',
            span_name='test_span',
            transport_handler=MockTransportHandler(),
            zipkin_attrs=zipkin_attrs,
            sample_rate=100.0,
        )

        report_root, _ = context._get_current_context()

        assert mock_create_attr.call_args == mock.call(
            sample_rate=100.0,
            trace_id=zipkin_attrs.trace_id,
        )
        # It wasn't sampled before and now it is, so this is the trace root
        assert report_root is True
示例#12
0
    def test_get_current_context_root_sample_rate_override_sampled(
            self,
            mock_create_attr,
    ):
        # Root span, with custom zipkin_attrs, sampled
        # Just return the custom zipkin_attrs.
        zipkin_attrs = ZipkinAttrs(
            trace_id=generate_random_64bit_string(),
            span_id=generate_random_64bit_string(),
            parent_span_id=generate_random_64bit_string(),
            flags=None,
            is_sampled=True,
        )
        context = zipkin.zipkin_span(
            service_name='test_service',
            span_name='test_span',
            transport_handler=MockTransportHandler(),
            zipkin_attrs=zipkin_attrs,
            sample_rate=100.0,
        )

        report_root, current_attrs = context._get_current_context()

        assert mock_create_attr.call_count == 0
        assert current_attrs == zipkin_attrs
        # The override was set and was already sampled, so this is probably
        # not the trace root.
        assert report_root is False
示例#13
0
def test_batch_sender_add_span(
    empty_annotations_dict,
    empty_binary_annotations_dict,
    fake_endpoint,
):
    # This test verifies it's possible to add 1 span without throwing errors.
    # It also checks that exiting the ZipkinBatchSender context manager
    # triggers a flush of all the already added spans.
    encoder = MockEncoder(encoded_queue='foobar')
    sender = logging_helper.ZipkinBatchSender(
        transport_handler=MockTransportHandler(),
        max_portion_size=None,
        encoder=encoder,
    )
    with sender:
        sender.add_span(
            SpanBuilder(
                trace_id='000000000000000f',
                name='span',
                parent_id='0000000000000001',
                span_id='0000000000000002',
                timestamp=26.0,
                duration=4.0,
                annotations=empty_annotations_dict,
                tags=empty_binary_annotations_dict,
                kind=Kind.CLIENT,
                local_endpoint=fake_endpoint,
                service_name='test_service',
            ))
    assert encoder.encode_queue.call_count == 1
示例#14
0
def test_batch_sender_with_error_on_exit():
    sender = logging_helper.ZipkinBatchSender(
        MockTransportHandler(), None, MockEncoder(),
    )
    with pytest.raises(ZipkinError):
        with sender:
            raise Exception("Error!")
示例#15
0
def test_batch_sender_add_span(fake_endpoint):
    # This test verifies it's possible to add 1 span without throwing errors.
    # It also checks that exiting the ZipkinBatchSender context manager
    # triggers a flush of all the already added spans.
    encoder = MockEncoder(encoded_queue="foobar")
    sender = logging_helper.ZipkinBatchSender(
        transport_handler=MockTransportHandler(),
        max_portion_size=None,
        encoder=encoder,
    )
    with sender:
        sender.add_span(
            Span(
                trace_id="000000000000000f",
                name="span",
                parent_id="0000000000000001",
                span_id="0000000000000002",
                kind=Kind.CLIENT,
                timestamp=26.0,
                duration=4.0,
                local_endpoint=fake_endpoint,
                annotations={},
                tags={},
            )
        )
    assert encoder.encode_queue.call_count == 1
示例#16
0
    def test_stop_with_bad_error_that_cannot_be_stringified(self):
        class BadExceptionThatCannotBeStringified(Exception):
            def __str__(self):
                # Returning a non-str will cause a TypeError.
                return 42

            __unicode__ = __str__

        # Transport is not setup, exit immediately
        transport = MockTransportHandler()
        span_storage = SpanStorage()
        context = zipkin.zipkin_span(
            service_name="test_service",
            span_name="test_span",
            transport_handler=transport,
            sample_rate=100.0,
            span_storage=span_storage,
        )

        with mock.patch.object(context,
                               "update_binary_annotations") as mock_upd:
            context.start()
            context.stop(
                BadExceptionThatCannotBeStringified,
                BadExceptionThatCannotBeStringified(),
            )
            assert mock_upd.call_args == mock.call({
                zipkin.ERROR_KEY:
                "BadExceptionThatCannotBeStringified: BadExceptionThatCannotBeStringified()"  # noqa
            })
            assert len(span_storage) == 0
示例#17
0
    def test_call_calls_send(self):
        with mock.patch.object(MockTransportHandler, "send", autospec=True):
            transport = MockTransportHandler()
            transport("foobar")

            assert transport.send.call_count == 1
            assert transport.send.call_args == mock.call(transport, "foobar")
示例#18
0
def test_decorator_works_in_a_new_thread():
    """The zipkin_span decorator is instantiated in a thread and then run in
    another. Let's verify that it works and that it stores the span in the
    right thread's thread-storage.
    """
    transport = MockTransportHandler()
    thread = threading.Thread(target=run_inside_another_thread, args=(transport,))
    thread.start()
    thread.join()

    output = transport.get_payloads()
    assert len(output) == 1

    spans = json.loads(output[0])
    assert len(spans) == 2
    assert spans[0]["name"] == "service1_do_stuff"
    assert spans[1]["name"] == "index"
示例#19
0
    def test_initinvalid_sample_rate(self):
        with pytest.raises(ZipkinError):
            with zipkin.zipkin_span(
                    service_name='some_service_name',
                    span_name='span_name',
                    transport_handler=MockTransportHandler(),
                    sample_rate=101.0,
            ):
                pass

        with pytest.raises(ZipkinError):
            with zipkin.zipkin_span(
                    service_name='some_service_name',
                    span_name='span_name',
                    transport_handler=MockTransportHandler(),
                    sample_rate=-0.1,
            ):
                pass
示例#20
0
    def test_init_local_root_transport_zipkin_attrs(self):
        # If transport_handler and zipkin_attrs are set, then this is a local root
        context = zipkin.zipkin_span(
            service_name='test_service',
            span_name='test_span',
            transport_handler=MockTransportHandler(),
            zipkin_attrs=zipkin.create_attrs_for_span(),
        )

        assert context._is_local_root_span is True
示例#21
0
    def test_init_local_root_transport_sample_rate(self):
        # If transport_handler and sample_rate are set, then this is a local root
        context = zipkin.zipkin_span(
            service_name='test_service',
            span_name='test_span',
            transport_handler=MockTransportHandler(),
            sample_rate=100.0,
        )

        assert context._is_local_root_span is True
示例#22
0
def test_zipkin_span_add_logging_annotation(mock_context):
    with zipkin.zipkin_span(
            service_name='my_service',
            transport_handler=MockTransportHandler(),
            sample_rate=100.0,
            add_logging_annotation=True,
    ):
        pass
    _, kwargs = mock_context.call_args
    assert kwargs['add_logging_annotation']
示例#23
0
    def test_init_local_root_firehose(self):
        # If firehose_handler is set, then this is a local root even if there's
        # no sample_rate or zipkin_attrs
        context = zipkin.zipkin_span(
            service_name='test_service',
            span_name='test_span',
            firehose_handler=MockTransportHandler(),
        )

        assert context._is_local_root_span is True
示例#24
0
def test_zipkin_extraneous_kind_raises(mock_zipkin_span, span_func):
    with pytest.raises(ValueError):
        with span_func(
                service_name='some_service_name',
                span_name='span_name',
                transport_handler=MockTransportHandler(),
                sample_rate=100.0,
                kind=Kind.LOCAL,
        ):
            pass
示例#25
0
    def test_override_span_name(self):
        with zipkin.zipkin_client_span(
                service_name='test_service',
                span_name='test_span',
                transport_handler=MockTransportHandler(),
                sample_rate=100.0,
        ) as span:
            span.override_span_name('new_name')

            assert span.span_name == 'new_name'
            assert span.logging_context.span_name == 'new_name'
示例#26
0
    def test_start_root_span_not_sampled_firehose(self, mock_log_ctx):
        # This request is not sampled, but firehose is setup. So we need to
        # setup the transport anyway.
        transport = MockTransportHandler()
        firehose = MockTransportHandler()
        tracer = MockTracer()
        context = tracer.zipkin_span(
            service_name='test_service',
            span_name='test_span',
            transport_handler=transport,
            firehose_handler=firehose,
            sample_rate=0.0,
        )

        context.start()

        assert context.zipkin_attrs is not None
        assert mock_log_ctx.call_count == 1
        assert mock_log_ctx.return_value.start.call_count == 1
        assert tracer.is_transport_configured() is True
示例#27
0
    def test_override_span_name(self):
        with zipkin.zipkin_client_span(
            service_name="test_service",
            span_name="test_span",
            transport_handler=MockTransportHandler(),
            sample_rate=100.0,
        ) as span:
            span.override_span_name("new_name")

            assert span.span_name == "new_name"
            assert span.logging_context.span_name == "new_name"
示例#28
0
def context():
    attr = ZipkinAttrs(None, None, None, None, False)
    return logging_helper.ZipkinLoggingContext(
        zipkin_attrs=attr,
        endpoint=create_endpoint(80, 'test_server', '127.0.0.1'),
        span_name='span_name',
        transport_handler=MockTransportHandler(),
        report_root_timestamp=False,
        span_storage=SpanStorage(),
        service_name='test_server',
        encoding=Encoding.V1_JSON,
    )
示例#29
0
 def test_init_span_storage_wrong_type(self):
     # Missing transport_handler
     with pytest.raises(ZipkinError):
         with zipkin.zipkin_span(
                 service_name='some_service_name',
                 span_name='span_name',
                 transport_handler=MockTransportHandler(),
                 port=5,
                 sample_rate=100.0,
                 span_storage=[],
         ):
             pass
示例#30
0
def test_override_span_name():
    transport = MockTransportHandler()
    with zipkin.zipkin_span(
            service_name='my_service',
            span_name='span_name',
            transport_handler=transport,
            kind=Kind.CLIENT,
            sample_rate=100.0,
            encoding=Encoding.V1_JSON,
    ) as context:
        context.override_span_name('new_span_name')

        with zipkin.zipkin_span(
                service_name='my_service',
                span_name='nested_span',
        ) as nested_context:
            nested_context.override_span_name('new_nested_span')

    spans = json.loads(transport.get_payloads()[0])
    assert len(spans) == 2
    assert spans[0]['name'] == 'new_nested_span'
    assert spans[1]['name'] == 'new_span_name'