def process_request(self, req, resp): if self._distributed_tracing: # Falcon uppercases all header names. headers = dict((k.lower(), v) for k, v in iteritems(req.headers)) propagator = HTTPPropagator() context = propagator.extract(headers) # Only activate the new context if there was a trace id extracted if context.trace_id: self.tracer.context_provider.activate(context) span = self.tracer.trace( "falcon.request", service=self.service, span_type=httpx.TYPE, ) # Configure trace search sample rate if config.falcon.event_sample_rate is not None: span.set_tag(EVENT_SAMPLE_RATE_KEY, config.falcon.event_sample_rate) span.set_tag(httpx.METHOD, req.method) span.set_tag(httpx.URL, req.url) # Note: any request header set after this line will not be stored in the span store_request_headers(req.headers, span, config.falcon)
def process_request(self, req, resp): if self._distributed_tracing: # Falcon uppercases all header names. headers = dict((k.lower(), v) for k, v in iteritems(req.headers)) propagator = HTTPPropagator() context = propagator.extract(headers) # Only activate the new context if there was a trace id extracted if context.trace_id: self.tracer.context_provider.activate(context) span = self.tracer.trace( 'falcon.request', service=self.service, span_type=SpanTypes.WEB, ) span.set_tag(SPAN_MEASURED_KEY) # set analytics sample rate with global config enabled span.set_tag( ANALYTICS_SAMPLE_RATE_KEY, config.falcon.get_analytics_sample_rate(use_global_config=True)) trace_utils.set_http_meta(span, config.falcon, method=req.method, url=req.url) if config.falcon.trace_query_string: span.set_tag(httpx.QUERY_STRING, req.query_string) # Note: any request header set after this line will not be stored in the span store_request_headers(req.headers, span, config.falcon)
def patch_handle_request(wrapped, instance, args, kwargs): """Wrapper for Sanic.handle_request""" request = kwargs.get("request", args[0]) write_callback = kwargs.get("write_callback", args[1]) stream_callback = kwargs.get("stream_callback", args[2]) resource = "{} {}".format(request.method, request.path) headers = request.headers.copy() if config.sanic.distributed_tracing: propagator = HTTPPropagator() context = propagator.extract(headers) if context.trace_id: ddtrace.tracer.context_provider.activate(context) span = ddtrace.tracer.trace( "sanic.request", service=config.sanic.service, resource=resource, span_type=SpanTypes.WEB ) sample_rate = config.sanic.get_analytics_sample_rate(use_global_config=True) if sample_rate is not None: span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, sample_rate) tags = _extract_tags_from_request(request=request) span.set_tags(tags) store_request_headers(headers, span, config.sanic) if write_callback is not None: write_callback = _wrap_response_callback(span, write_callback) if stream_callback is not None: stream_callback = _wrap_response_callback(span, stream_callback) return wrapped(request, write_callback, stream_callback, **kwargs)
def process_request(self, req, resp): if self._distributed_tracing: # Falcon uppercases all header names. headers = dict((k.lower(), v) for k, v in iteritems(req.headers)) propagator = HTTPPropagator() context = propagator.extract(headers) # Only activate the new context if there was a trace id extracted if context.trace_id: self.tracer.context_provider.activate(context) span = self.tracer.trace( 'falcon.request', service=self.service, span_type=httpx.TYPE, ) # set analytics sample rate with global config enabled span.set_tag( ANALYTICS_SAMPLE_RATE_KEY, config.falcon.get_analytics_sample_rate(use_global_config=True)) span.set_tag(httpx.METHOD, req.method) span.set_tag(httpx.URL, req.url) # Note: any request header set after this line will not be stored in the span store_request_headers(req.headers, span, config.falcon)
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 test_extract_bad_values(trace_id, parent_span_id, sampling_priority, dd_origin): headers = dict() wsgi_headers = dict() if trace_id is not NOT_SET: headers[HTTP_HEADER_TRACE_ID] = trace_id wsgi_headers[get_wsgi_header(HTTP_HEADER_TRACE_ID)] = trace_id if parent_span_id is not NOT_SET: headers[HTTP_HEADER_PARENT_ID] = parent_span_id wsgi_headers[get_wsgi_header(HTTP_HEADER_PARENT_ID)] = parent_span_id if sampling_priority is not NOT_SET: headers[HTTP_HEADER_SAMPLING_PRIORITY] = sampling_priority wsgi_headers[get_wsgi_header( HTTP_HEADER_SAMPLING_PRIORITY)] = sampling_priority if dd_origin is not NOT_SET: headers[HTTP_HEADER_ORIGIN] = dd_origin wsgi_headers[get_wsgi_header(HTTP_HEADER_ORIGIN)] = dd_origin # x-datadog-*headers context = HTTPPropagator.extract(headers) assert context.trace_id is None assert context.span_id is None assert context.sampling_priority is None assert context.dd_origin is None # HTTP_X_DATADOG_* headers context = HTTPPropagator.extract(wsgi_headers) assert context.trace_id is None assert context.span_id is None assert context.sampling_priority is None assert context.dd_origin is None
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
async def __call__(self, scope, receive, send): if scope["type"] != "http": return await self.app(scope, receive, send) headers = _extract_headers(scope) if self.integration_config.distributed_tracing: propagator = HTTPPropagator() context = propagator.extract(headers) if context.trace_id: self.tracer.context_provider.activate(context) resource = "{} {}".format(scope["method"], scope["path"]) span = self.tracer.trace( name=self.integration_config.get("request_span_name", "asgi.request"), service=trace_utils.int_service(None, self.integration_config), resource=resource, span_type=SpanTypes.HTTP, ) if self.span_modifier: self.span_modifier(span, scope) sample_rate = self.integration_config.get_analytics_sample_rate( use_global_config=True) if sample_rate is not None: span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, sample_rate) tags = _extract_tags_from_scope(scope, self.integration_config) span.set_tags(tags) store_request_headers(headers, span, self.integration_config) async def wrapped_send(message): if span and message.get("type") == "http.response.start": if "status" in message: status_code = message["status"] span.set_tag(http.STATUS_CODE, status_code) if 500 <= int(status_code) < 600: span.error = 1 if "headers" in message: store_response_headers(message["headers"], span, self.integration_config) return await send(message) try: return await self.app(scope, receive, wrapped_send) except Exception as exc: (exc_type, exc_val, exc_tb) = sys.exc_info() span.set_exc_info(exc_type, exc_val, exc_tb) self.handle_exception_span(exc, span) reraise(exc_type, exc_val, exc_tb) finally: span.finish()
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: if scope["type"] != "http": await self.app(scope, receive, send) return request = Request(scope=scope, receive=receive) if self._distributed_tracing: propagator = HTTPPropagator() context = propagator.extract(request.headers) if context.trace_id: self.tracer.context_provider.activate(context) resource = "%s %s" % (request.method, request.url.path) span = self.tracer.trace( name="asgi.request", service=self.service, resource=resource, span_type=http_tags.TYPE, ) span.set_tag( ANALYTICS_SAMPLE_RATE_KEY, config.asgi.get_analytics_sample_rate(use_global_config=True), ) span.set_tag(http_tags.METHOD, request.method) span.set_tag(http_tags.URL, str(request.url)) if config.asgi.trace_query_string: span.set_tag(http_tags.QUERY_STRING, request.url.query) # NOTE: any request header set in the future will not be stored in the span. store_request_headers(request.headers, span, config.asgi) async def send_with_tracing(message: Message) -> None: span = self.tracer.current_span() if not span: # Unexpected. await send(message) return if message["type"] == "http.response.start": status_code: int = message["status"] response_headers = Headers(raw=message["headers"]) store_response_headers(response_headers, span, config.asgi) span.set_tag(http_tags.STATUS_CODE, str(status_code)) await send(message) try: await self.app(scope, receive, send_with_tracing) except BaseException as exc: span.set_traceback() raise exc from None finally: span.finish()
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 = 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
async def patch_handle_request(wrapped, instance, args, kwargs): """Wrapper for Sanic.handle_request""" request = kwargs.get("request", args[0]) write_callback = kwargs.get("write_callback", args[1]) stream_callback = kwargs.get("stream_callback", args[2]) if request.scheme not in ("http", "https"): return await wrapped(request, write_callback, stream_callback, **kwargs) resource = "{} {}".format(request.method, _get_path(request)) headers = request.headers.copy() if config.sanic.distributed_tracing: propagator = HTTPPropagator() context = propagator.extract(headers) if context.trace_id: ddtrace.tracer.context_provider.activate(context) span = ddtrace.tracer.trace( "sanic.request", service=trace_utils.int_service(None, config.sanic), resource=resource, span_type=SpanTypes.WEB, ) sample_rate = config.sanic.get_analytics_sample_rate( use_global_config=True) if sample_rate is not None: span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, sample_rate) method = request.method url = "{scheme}://{host}{path}".format(scheme=request.scheme, host=request.host, path=request.path) query_string = request.query_string if isinstance(query_string, bytes): query_string = query_string.decode() trace_utils.set_http_meta(span, config.sanic, method=method, url=url, query=query_string, request_headers=headers) if write_callback is not None: write_callback = _wrap_response_callback(span, write_callback) if stream_callback is not None: stream_callback = _wrap_response_callback(span, stream_callback) return await wrapped(request, write_callback, stream_callback, **kwargs)
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_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(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_extract(self): tracer = get_dummy_tracer() headers = { HTTP_HEADER_TRACE_ID: '1234', HTTP_HEADER_PARENT_ID: '5678', HTTP_HEADER_SAMPLING_PRIORITY: '1', } propagator = HTTPPropagator() context = propagator.extract(headers) tracer.context_provider.activate(context) with tracer.trace("local_root_span") as span: eq_(span.trace_id, 1234) eq_(span.parent_id, 5678)
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 process_request(self, req, resp): if self._distributed_tracing: # Falcon uppercases all header names. headers = dict((k.lower(), v) for k, v in iteritems(req.headers)) propagator = HTTPPropagator() context = propagator.extract(headers) self.tracer.context_provider.activate(context) span = self.tracer.trace( "falcon.request", service=self.service, span_type=httpx.TYPE, ) span.set_tag(httpx.METHOD, req.method) span.set_tag(httpx.URL, req.url)
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_WSGI_extract(self): """Ensure we support the WSGI formatted headers as well.""" tracer = DummyTracer() headers = { "HTTP_X_DATADOG_TRACE_ID": "1234", "HTTP_X_DATADOG_PARENT_ID": "5678", "HTTP_X_DATADOG_SAMPLING_PRIORITY": "1", "HTTP_X_DATADOG_ORIGIN": "synthetics", "HTTP_X_DATADOG_TAGS": "_dd.p.test=value,any=tag", } context = HTTPPropagator.extract(headers) tracer.context_provider.activate(context) with tracer.trace("local_root_span") as span: assert span.trace_id == 1234 assert span.parent_id == 5678 assert span.context.sampling_priority == 1 assert span.context.dd_origin == "synthetics" assert span.context._meta == { "_dd.origin": "synthetics", "_dd.p.test": "value", "any": "tag", }
def extract(carrier): # type: (Dict[str, str]) -> SpanContext """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 = DDHTTPPropagator.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)
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_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_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_extract(self): tracer = get_dummy_tracer() headers = { "x-datadog-trace-id": "1234", "x-datadog-parent-id": "5678", "x-datadog-sampling-priority": "1", } propagator = HTTPPropagator() context = propagator.extract(headers) tracer.context_provider.activate(context) with tracer.trace("local_root_span") as span: eq_(span.trace_id, 1234) eq_(span.parent_id, 5678) eq_(span.context.sampling_priority, 1)
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_WSGI_extract(self): """Ensure we support the WSGI formatted headers as well.""" tracer = get_dummy_tracer() headers = { "HTTP_X_DATADOG_TRACE_ID": "1234", "HTTP_X_DATADOG_PARENT_ID": "5678", "HTTP_X_DATADOG_SAMPLING_PRIORITY": "1", } propagator = HTTPPropagator() context = propagator.extract(headers) tracer.context_provider.activate(context) with tracer.trace("local_root_span") as span: eq_(span.trace_id, 1234) eq_(span.parent_id, 5678) eq_(span.context.sampling_priority, 1)
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_extract(self): tracer = get_dummy_tracer() headers = { "x-datadog-trace-id": "1234", "x-datadog-parent-id": "5678", "x-datadog-sampling-priority": "1", "x-datadog-origin": "synthetics", } propagator = HTTPPropagator() context = propagator.extract(headers) tracer.context_provider.activate(context) with tracer.trace("local_root_span") as span: assert span.trace_id == 1234 assert span.parent_id == 5678 assert span.context.sampling_priority == 1 assert span.context._dd_origin == "synthetics"