Example #1
0
def test_tracer_tags_on_root_span(span_type, expected_tags):
    reporter = mock.MagicMock()
    sampler = ConstSampler(True)
    with mock.patch('socket.gethostname', return_value='dream-host.com'):
        tracer = Tracer(service_name='x',
                        reporter=reporter,
                        sampler=sampler,
                        tags={'global-tag': 'global-tag'})
        span = tracer.start_span(operation_name='root')
        if span_type == 'child':
            span = tracer.start_span('child', child_of=span)
        if span_type == 'rpc-server':
            span = tracer.start_span(
                'child',
                child_of=span.context,
                tags={ext_tags.SPAN_KIND: ext_tags.SPAN_KIND_RPC_SERVER})
        for key, value in six.iteritems(expected_tags):
            found_tag = None
            for tag in span.tags:
                if tag.key == key:
                    found_tag = tag
            if value is None:
                assert found_tag is None, 'test (%s)' % span_type
                continue

            assert found_tag is not None, 'test (%s): expecting tag %s' % (
                span_type, key)
            assert found_tag.value == value, \
                'test (%s): expecting tag %s=%s' % (span_type, key, value)
Example #2
0
def test_non_ascii_baggage_with_httplib(httpserver):
    # httpserver is provided by pytest-localserver
    httpserver.serve_content(content='Hello', code=200, headers=None)

    tracer = Tracer(
        service_name='test',
        reporter=InMemoryReporter(),
        # don't sample to avoid logging baggage to the span
        sampler=ConstSampler(False),
    )
    tracer.codecs[Format.TEXT_MAP] = TextCodec(url_encoding=True)

    baggage = [
        (b'key', b'value'),
        (u'key', b'value'),
        (b'key', bytes(chr(255))),
        (u'caf\xe9', 'caf\xc3\xa9'),
        ('caf\xc3\xa9', 'value'),
    ]
    for b in baggage:
        span = tracer.start_span('test')
        span.set_baggage_item(b[0], b[1])

        headers = {}
        tracer.inject(span_context=span.context,
                      format=Format.TEXT_MAP,
                      carrier=headers)
        # make sure httplib doesn't blow up
        request = urllib2.Request(httpserver.url, None, headers)
        response = urllib2.urlopen(request)
        assert response.read() == 'Hello'
        response.close()
Example #3
0
def test_baggage_as_unicode_strings_with_httplib(httpserver):
    if six.PY2:
        import urllib2
        urllib_under_test = urllib2
    else:
        import urllib.request
        urllib_under_test = urllib.request

    # httpserver is provided by pytest-localserver
    httpserver.serve_content(content='Hello', code=200, headers=None)

    tracer = Tracer(
        service_name='test',
        reporter=InMemoryReporter(),
        # don't sample to avoid logging baggage to the span
        sampler=ConstSampler(False),
    )
    tracer.codecs[Format.TEXT_MAP] = TextCodec(url_encoding=True)

    baggage = [(b'key1', b'value'), (u'key2', b'value'), ('key3', u'value'),
               (b'key4', bytes(chr(255)) if six.PY2 else bytes([255])),
               (u'key5', u'\U0001F47E')]
    for b in baggage:
        span = tracer.start_span('test')
        span.set_baggage_item(b[0], b[1])

        headers = {}
        tracer.inject(span_context=span.context,
                      format=Format.TEXT_MAP,
                      carrier=headers)
        # make sure httplib doesn't blow up
        request = urllib_under_test.Request(httpserver.url, None, headers)
        response = urllib_under_test.urlopen(request)
        assert response.read() == b'Hello'
        response.close()
def test_tracer_throttler():
    tracer = Tracer(service_name='x',
                    reporter=mock.MagicMock(),
                    sampler=mock.MagicMock(),
                    throttler=mock.MagicMock())
    tracer.throttler.is_allowed.return_value = True
    assert tracer.is_debug_allowed()
    tracer.throttler.is_allowed.return_value = False
    assert not tracer.is_debug_allowed()
    debug_span_context = SpanContext.with_debug_id('debug-id')
    span = tracer.start_span('test-operation', child_of=debug_span_context)
    assert not span.is_debug()
def test_non_ascii_baggage_with_httplib(httpserver):
    # TODO this test requires `futurize`. Unfortunately, that also changes
    # how the test works under Py2.
    # Some observation:
    # - In Py2, the httplib does not like unicode strings, maybe we need to convert everything to bytes.
    # - Not sure yet what's the story with httplib in Py3, it seems not to like raw bytes.
    if six.PY3:
        raise ValueError('this test does not work with Py3')
    # httpserver is provided by pytest-localserver
    httpserver.serve_content(content='Hello', code=200, headers=None)

    tracer = Tracer(
        service_name='test',
        reporter=InMemoryReporter(),
        # don't sample to avoid logging baggage to the span
        sampler=ConstSampler(False),
    )
    tracer.codecs[Format.TEXT_MAP] = TextCodec(url_encoding=True)

    baggage = [
        (b'key', b'value'),
        (u'key', b'value'),
        (b'key', byte255),
        (u'caf\xe9', 'caf\xc3\xa9'),
        ('caf\xc3\xa9', 'value'),
    ]
    for b in baggage:
        span = tracer.start_span('test')
        span.set_baggage_item(b[0], b[1])

        headers = {}
        tracer.inject(span_context=span.context,
                      format=Format.TEXT_MAP,
                      carrier=headers)
        # make sure httplib doesn't blow up
        import urllib2
        request = urllib2.Request(httpserver.url, None, headers)
        response = urllib2.urlopen(request)
        assert response.read() == b'Hello'
        response.close()
Example #6
0
def test_debug_id():
    debug_header = 'correlation-id'
    tracer = Tracer(
        service_name='test',
        reporter=InMemoryReporter(),
        sampler=ConstSampler(True),
        debug_id_header=debug_header,
    )
    tracer.codecs[Format.TEXT_MAP] = TextCodec(
        url_encoding=False,
        debug_id_header=debug_header,
    )
    carrier = {debug_header: 'Coraline'}
    context = tracer.extract(Format.TEXT_MAP, carrier)
    assert context.is_debug_id_container_only
    assert context.debug_id == 'Coraline'
    span = tracer.start_span('test', child_of=context)
    assert span.is_debug()
    assert span.is_sampled()
    tags = [t for t in span.tags if t.key == debug_header]
    assert len(tags) == 1
    assert tags[0].vStr == 'Coraline'
Example #7
0
def test_baggage_as_unicode_strings_with_httplib(httpserver):
    if six.PY2:
        import urllib2
        urllib_under_test = urllib2
    else:
        import urllib.request
        urllib_under_test = urllib.request

    # httpserver is provided by pytest-localserver
    httpserver.serve_content(content='Hello', code=200, headers=None)

    tracer = Tracer(
        service_name='test',
        reporter=InMemoryReporter(),
        # don't sample to avoid logging baggage to the span
        sampler=ConstSampler(False),
    )
    tracer.codecs[Format.TEXT_MAP] = TextCodec(url_encoding=True)

    baggage = [
        (b'key1', b'value'),
        (u'key2', b'value'),
        ('key3', u'value'),
        (b'key4', bytes(chr(255)) if six.PY2 else bytes([255])),
        (u'key5', u'\U0001F47E')
    ]
    for b in baggage:
        span = tracer.start_span('test')
        span.set_baggage_item(b[0], b[1])

        headers = {}
        tracer.inject(span_context=span.context,
                      format=Format.TEXT_MAP,
                      carrier=headers)
        # make sure httplib doesn't blow up
        request = urllib_under_test.Request(httpserver.url, None, headers)
        response = urllib_under_test.urlopen(request)
        assert response.read() == b'Hello'
        response.close()
Example #8
0
def test_debug_id():
    debug_header = 'correlation-id'
    tracer = Tracer(
        service_name='test',
        reporter=InMemoryReporter(),
        sampler=ConstSampler(True),
        debug_id_header=debug_header,
    )
    tracer.codecs[Format.TEXT_MAP] = TextCodec(
        url_encoding=False,
        debug_id_header=debug_header,
    )
    carrier = {debug_header: 'Coraline'}
    context = tracer.extract(Format.TEXT_MAP, carrier)
    assert context.is_debug_id_container_only
    assert context.debug_id == 'Coraline'
    span = tracer.start_span('test', child_of=context)
    assert span.is_debug()
    assert span.is_sampled()
    tags = [t for t in span.tags if t.key == debug_header]
    assert len(tags) == 1
    assert tags[0].vStr == 'Coraline'
Example #9
0
class SimpleTracer:
    '''A very basic OpenTracing tracer, with null reporter'''
    def __init__(self, sname):
        self.tracer = Tracer(one_span_per_rpc=True,
                             service_name=sname,
                             reporter=NullReporter(),
                             sampler=ConstSampler(decision=True),
                             extra_codecs={Format.HTTP_HEADERS: B3Codec()})

    def trace(self):
        '''
        Decorator that creates opentracing span from incoming b3 headers
        '''
        def decorator(f):
            def wrapper(*args, **kwargs):
                global stack
                request = stack.top.request
                try:
                    # Create a new span context, reading in values (traceid,
                    # spanid, etc) from the incoming x-b3-*** headers.
                    span_ctx = self.tracer.extract(Format.HTTP_HEADERS,
                                                   dict(request.headers))
                    # Note: this tag means that the span will *not* be
                    # a child span. It will use the incoming traceid and
                    # spanid. We do this to propagate the headers verbatim.
                    rpc_tag = {tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER}
                    span = self.tracer.start_span(operation_name='op',
                                                  child_of=span_ctx,
                                                  tags=rpc_tag)
                except Exception as e:
                    # We failed to create a context, possibly due to no
                    # incoming x-b3-*** headers. Start a fresh span.
                    # Note: This is a fallback only, and will create fresh
                    # headers, not propagate headers.
                    span = self.tracer.start_span('op')
                with span_in_context(span):
                    r = f(*args, **kwargs)
                    return r

            wrapper.__name__ = f.__name__
            return wrapper

        return decorator

    def getForwardHeaders(self, request):
        '''
        Return the headers that should be forwarded to subservices.

        This function currently forwards instances of tracing headers
        for a much wider range of tracing tools than are used in
        this course. If those headers are not present, nothing
        happens other than a bit of wasted computation here.

        At some point, we should pare down the list to only those
        headers needed by the tools used in class.

        The final block, "Application-specific", includes headers
        not needed for tracing but that our application requires to
        be forwarded.
        '''
        headers = {}

        # x-b3-*** headers can be populated using the opentracing span
        span = get_current_span()
        carrier = {}
        self.tracer.inject(span_context=span.context,
                           format=Format.HTTP_HEADERS,
                           carrier=carrier)

        headers.update(carrier)

        # CMPT 756 --- COMMENTED OUT FROM bookinfo
        # We handle other (non x-b3-***) headers manually
        #if 'user' in session:
        #    headers['end-user'] = session['user']

        # Keep this in sync with the headers in details and reviews.
        incoming_headers = [
            # All applications should propagate x-request-id. This header is
            # included in access log statements and is used for consistent trace
            # sampling and log sampling decisions in Istio.
            'x-request-id',

            # Lightstep tracing header. Propagate this if you use lightstep tracing
            # in Istio (see
            # https://istio.io/latest/docs/tasks/observability/distributed-tracing/lightstep/)
            # Note: this should probably be changed to use B3 or W3C TRACE_CONTEXT.
            # Lightstep recommends using B3 or TRACE_CONTEXT and most application
            # libraries from lightstep do not support x-ot-span-context.
            'x-ot-span-context',

            # Datadog tracing header. Propagate these headers if you use Datadog
            # tracing.
            'x-datadog-trace-id',
            'x-datadog-parent-id',
            'x-datadog-sampling-priority',

            # W3C Trace Context. Compatible with OpenCensusAgent and Stackdriver Istio
            # configurations.
            'traceparent',
            'tracestate',

            # Cloud trace context. Compatible with OpenCensusAgent and Stackdriver Istio
            # configurations.
            'x-cloud-trace-context',

            # Grpc binary trace context. Compatible with OpenCensusAgent nad
            # Stackdriver Istio configurations.
            'grpc-trace-bin',

            # b3 trace headers. Compatible with Zipkin, OpenCensusAgent, and
            # Stackdriver Istio configurations. Commented out since they are
            # propagated by the OpenTracing tracer above.
            # 'x-b3-traceid',
            # 'x-b3-spanid',
            # 'x-b3-parentspanid',
            # 'x-b3-sampled',
            # 'x-b3-flags',

            # Application-specific headers to forward.
            'user-agent',
            'Authorization',  # CMPT 756
        ]
        # For Zipkin, always propagate b3 headers.
        # For Lightstep, always propagate the x-ot-span-context header.
        # For Datadog, propagate the corresponding datadog headers.
        # For OpenCensusAgent and Stackdriver configurations, you can choose any
        # set of compatible headers to propagate within your application. For
        # example, you can propagate b3 headers or W3C trace context headers with
        # the same result. This can also allow you to translate between context
        # propagation mechanisms between different applications.

        for ihdr in incoming_headers:
            val = request.headers.get(ihdr)
            if val is not None:
                headers[ihdr] = val

        return headers