def test_distributed_tracing_propagation_async(self): @self.app.task def fn_task(): return 42 # This header manipulation is copying the work that should be done # by the before_publish signal. Rip it out if Celery ever fixes their bug. current_context = Pin.get_from( self.app).tracer.context_provider.active() headers = {} HTTPPropagator.inject(current_context, headers) with self.override_config("celery", dict(distributed_tracing=True)): result = fn_task.apply_async(headers=headers) assert result.get(timeout=self.ASYNC_GET_TIMEOUT) == 42 traces = self.pop_traces() if self.ASYNC_USE_CELERY_FIXTURES: assert 2 == len(traces) assert 1 == len(traces[0]) assert 1 == len(traces[1]) async_span = traces[0][0] run_span = traces[1][0] assert async_span.trace_id == 12345 else: assert 1 == len(traces) assert 1 == len(traces[0]) run_span = traces[0][0] assert run_span.trace_id == 12345
def __init__(self, base_url): # type: (str) -> None self._base_url = base_url self._session = requests.Session() # Propagate traces with trace_id = 1 for the ping trace so we can filter them out. c, d = Context(trace_id=1, span_id=1), {} HTTPPropagator.inject(c, d) self._ignore_headers = d
def _init_span(span, request): # type: (Span, httpx.Request) -> None span.set_tag(SPAN_MEASURED_KEY) if distributed_tracing_enabled(config.httpx): HTTPPropagator.inject(span.context, request.headers) sample_rate = config.httpx.get_analytics_sample_rate(use_global_config=True) if sample_rate is not None: span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, sample_rate)
def test_inject(self): tracer = get_dummy_tracer() with tracer.trace("global_root_span") as span: headers = {} propagator = HTTPPropagator() propagator.inject(span.context, headers) eq_(int(headers[HTTP_HEADER_TRACE_ID]), span.trace_id) eq_(int(headers[HTTP_HEADER_PARENT_ID]), span.span_id)
def test_inject(self): tracer = get_dummy_tracer() with tracer.trace("global_root_span") as span: headers = {} propagator = HTTPPropagator() propagator.inject(span.context, headers) eq_(int(headers[HTTP_HEADER_TRACE_ID]), span.trace_id) eq_(int(headers[HTTP_HEADER_PARENT_ID]), span.span_id)
def test_inject(self): tracer = DummyTracer() ctx = Context(trace_id=1234, sampling_priority=2, dd_origin="synthetics") tracer.context_provider.activate(ctx) with tracer.trace("global_root_span") as span: headers = {} HTTPPropagator.inject(span.context, headers) assert int(headers[HTTP_HEADER_TRACE_ID]) == span.trace_id assert int(headers[HTTP_HEADER_PARENT_ID]) == span.span_id assert int(headers[HTTP_HEADER_SAMPLING_PRIORITY]) == span.context.sampling_priority assert headers[HTTP_HEADER_ORIGIN] == span.context.dd_origin
def test_inject(self): tracer = get_dummy_tracer() with tracer.trace("global_root_span") as span: span.context.sampling_priority = 2 span.context._dd_origin = "synthetics" headers = {} propagator = HTTPPropagator() propagator.inject(span.context, headers) assert int(headers[HTTP_HEADER_TRACE_ID]) == span.trace_id assert int(headers[HTTP_HEADER_PARENT_ID]) == span.span_id assert int(headers[HTTP_HEADER_SAMPLING_PRIORITY]) == span.context.sampling_priority assert headers[HTTP_HEADER_ORIGIN] == span.context._dd_origin
def test_inject(self): tracer = get_dummy_tracer() with tracer.trace("global_root_span") as span: span.context.sampling_priority = 2 headers = {} propagator = HTTPPropagator() propagator.inject(span.context, headers) eq_(int(headers[HTTP_HEADER_TRACE_ID]), span.trace_id) eq_(int(headers[HTTP_HEADER_PARENT_ID]), span.span_id) eq_( int(headers[HTTP_HEADER_SAMPLING_PRIORITY]), span.context.sampling_priority, )
def test_b3_inject(self): tracer = get_dummy_tracer() tracer.configure(http_propagator=B3HTTPPropagator) with tracer.trace("global_root_span") as span: headers = {} set_http_propagator_factory(B3HTTPPropagator) propagator = HTTPPropagator() propagator.inject(span.context, headers) assert int(headers[B3HTTPPropagator.TRACE_ID_KEY], 16) == span.trace_id assert int(headers[B3HTTPPropagator.SPAN_ID_KEY], 16) == span.span_id assert int(headers[B3HTTPPropagator.SAMPLED_KEY]) == 1
def test_inject_tags_unicode(self): """Unicode characters are not allowed""" tracer = DummyTracer() meta = {u"_dd.p.unicode_☺️": u"unicode value ☺️"} ctx = Context(trace_id=1234, sampling_priority=2, dd_origin="synthetics", meta=meta) tracer.context_provider.activate(ctx) with tracer.trace("global_root_span") as span: headers = {} HTTPPropagator.inject(span.context, headers) assert HTTP_HEADER_TAGS not in headers assert ctx._meta["_dd.propagation_error"] == "encoding_error"
def test_inject_tags_invalid(self): tracer = DummyTracer() # DEV: "=" and "," are not allowed in keys or values meta = {"_dd.p.test=": ",value="} ctx = Context(trace_id=1234, sampling_priority=2, dd_origin="synthetics", meta=meta) tracer.context_provider.activate(ctx) with tracer.trace("global_root_span") as span: headers = {} HTTPPropagator.inject(span.context, headers) assert HTTP_HEADER_TAGS not in headers assert ctx._meta["_dd.propagation_error"] == "encoding_error"
def test_inject_tags_many_large(self): """When we have too many tags that cause us to reach the max size limit""" tracer = DummyTracer() meta = {"_dd.p.test_{}".format(i): "test" * 10 for i in range(100)} ctx = Context(trace_id=1234, sampling_priority=2, dd_origin="synthetics", meta=meta) tracer.context_provider.activate(ctx) with tracer.trace("global_root_span") as span: headers = {} HTTPPropagator.inject(span.context, headers) assert HTTP_HEADER_TAGS not in headers assert ctx._meta["_dd.propagation_error"] == "max_size"
def test_inject_tags_large(self): """When we have a single large tag that won't fit""" tracer = DummyTracer() # DEV: Limit is 512 meta = {"_dd.p.test": "long" * 200} ctx = Context(trace_id=1234, sampling_priority=2, dd_origin="synthetics", meta=meta) tracer.context_provider.activate(ctx) with tracer.trace("global_root_span") as span: headers = {} HTTPPropagator.inject(span.context, headers) assert HTTP_HEADER_TAGS not in headers assert ctx._meta["_dd.propagation_error"] == "max_size"
def test_distributed_tracing_propagation(self): @self.app.task def fn_task(): return 42 # This header manipulation is copying the work that should be done # by the before_publish signal. Rip it out if Celery ever fixes their bug. current_context = Pin.get_from(self.app).tracer.context_provider.active() headers = {} HTTPPropagator.inject(current_context, headers) with self.override_config("celery", dict(distributed_tracing=True)): fn_task.apply(headers=headers) traces = self.pop_traces() span = traces[0][0] assert span.trace_id == 12345
def test_inject_tags_bytes(self): """We properly encode when the meta key as long as it is just ascii characters""" tracer = DummyTracer() # Context._meta allows str and bytes for keys meta = {u"_dd.p.unicode": u"unicode", b"_dd.p.bytes": b"bytes"} ctx = Context(trace_id=1234, sampling_priority=2, dd_origin="synthetics", meta=meta) tracer.context_provider.activate(ctx) with tracer.trace("global_root_span") as span: headers = {} HTTPPropagator.inject(span.context, headers) # The ordering is non-deterministic, so compare as a list of tags tags = set(headers[HTTP_HEADER_TAGS].split(",")) assert tags == set(["_dd.p.unicode=unicode", "_dd.p.bytes=bytes"])
def test_inject_tags_previous_error(self): """When we have previously gotten an error, do not try to propagate tags""" tracer = DummyTracer() # This value is valid meta = { "_dd.p.key": "value", "_dd.propagation_error": "some fake test value" } ctx = Context(trace_id=1234, sampling_priority=2, dd_origin="synthetics", meta=meta) tracer.context_provider.activate(ctx) with tracer.trace("global_root_span") as span: headers = {} HTTPPropagator.inject(span.context, headers) assert HTTP_HEADER_TAGS not in headers
def _init_span(span, request): # type: (Span, httpx.Request) -> None if config.httpx.split_by_domain: if hasattr(request.url, "netloc"): span.service = request.url.netloc else: service = ensure_binary(request.url.host) if request.url.port: service += b":" + ensure_binary(str(request.url.port)) span.service = service span.set_tag(SPAN_MEASURED_KEY) if distributed_tracing_enabled(config.httpx): HTTPPropagator.inject(span.context, request.headers) sample_rate = config.httpx.get_analytics_sample_rate(use_global_config=True) if sample_rate is not None: span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, sample_rate)
def test_inject(self): tracer = DummyTracer() # We will only pass along `_dd.p.*` tags meta = {"_dd.p.test": "value", "non._dd.p_tag": "value"} ctx = Context(trace_id=1234, sampling_priority=2, dd_origin="synthetics", meta=meta) tracer.context_provider.activate(ctx) with tracer.trace("global_root_span") as span: headers = {} HTTPPropagator.inject(span.context, headers) assert int(headers[HTTP_HEADER_TRACE_ID]) == span.trace_id assert int(headers[HTTP_HEADER_PARENT_ID]) == span.span_id assert int(headers[HTTP_HEADER_SAMPLING_PRIORITY] ) == span.context.sampling_priority assert headers[HTTP_HEADER_ORIGIN] == span.context.dd_origin
def inject(span_context, carrier): """Inject a span context into a carrier. *span_context* is injected into the carrier by first using an :class:`ddtrace.propagation.http.HTTPPropagator` to inject the ddtracer specific fields. Then the baggage is injected into *carrier*. :param span_context: span context to inject. :param carrier: carrier to inject into. """ if not isinstance(carrier, dict): raise InvalidCarrierException("propagator expects carrier to be a dict") DDHTTPPropagator.inject(span_context._dd_context, carrier) # Add the baggage if span_context.baggage is not None: for key in span_context.baggage: carrier[HTTP_BAGGAGE_PREFIX + key] = span_context.baggage[key]
class HTTPPropagator(Propagator): """OpenTracing compatible HTTP_HEADER and TEXT_MAP format propagator. `HTTPPropagator` provides compatibility by using existing OpenTracing compatible methods from the ddtracer along with new logic supporting the outstanding OpenTracing-defined functionality. """ __slots__ = ['_dd_propagator'] def __init__(self): self._dd_propagator = DDHTTPPropagator() def inject(self, span_context, carrier): """Inject a span context into a carrier. *span_context* is injected into the carrier by first using an :class:`ddtrace.propagation.http.HTTPPropagator` to inject the ddtracer specific fields. Then the baggage is injected into *carrier*. :param span_context: span context to inject. :param carrier: carrier to inject into. """ if not isinstance(carrier, dict): raise InvalidCarrierException( 'propagator expects carrier to be a dict') self._dd_propagator.inject(span_context._dd_context, carrier) # Add the baggage if span_context.baggage is not None: for key in span_context.baggage: carrier[HTTP_BAGGAGE_PREFIX + key] = span_context.baggage[key] def extract(self, carrier): """Extract a span context from a carrier. :class:`ddtrace.propagation.http.HTTPPropagator` is used to extract ddtracer supported fields into a `ddtrace.Context` context which is combined with new logic to extract the baggage which is returned in an OpenTracing compatible span context. :param carrier: carrier to extract from. :return: extracted span context. """ if not isinstance(carrier, dict): raise InvalidCarrierException( 'propagator expects carrier to be a dict') ddspan_ctx = self._dd_propagator.extract(carrier) # if the dd propagator fails then it will return a new empty span # context (with trace_id=None), we however want to raise an exception # if this occurs. if not ddspan_ctx.trace_id: raise SpanContextCorruptedException( 'failed to extract span context') baggage = {} for key in carrier: if key.startswith(HTTP_BAGGAGE_PREFIX): baggage[key[HTTP_BAGGAGE_PREFIX_LEN:]] = carrier[key] return SpanContext(ddcontext=ddspan_ctx, baggage=baggage)