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)
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()
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()
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'
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()
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