def test_b3_extract(self): tracer = get_dummy_tracer() tracer.configure(http_propagator=B3HTTPPropagator) headers = { "x-b3-traceid": "4d2", "x-b3-spanid": "162e", "x-b3-sampled": "1", } 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 == AUTO_KEEP headers = { "x-b3-traceid": "4d2", "x-b3-spanid": "162e", "x-b3-sampled": "0", } propagator = HTTPPropagator() context = propagator.extract(headers) tracer.context_provider.activate(context) with tracer.trace("local_root_span") as span: assert span.context.sampling_priority == AUTO_REJECT
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 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 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, ) # 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) 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 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 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)) context = HTTPPropagator.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, query=req.query_string, request_headers=req.headers)
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)
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 activate_distributed_headers(tracer, int_config, request_headers=None): """ Helper for activating a distributed trace headers' context if enabled in integration config. """ int_config = int_config or {} if int_config.get("distributed_tracing_enabled", False): context = HTTPPropagator.extract(request_headers) # Only need to activate the new context if something was propagated if context.trace_id: tracer.context_provider.activate(context)
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 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 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_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_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_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_extract(self): tracer = DummyTracer() headers = { "x-datadog-trace-id": "1234", "x-datadog-parent-id": "5678", "x-datadog-sampling-priority": "1", "x-datadog-origin": "synthetics", } 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"
def activate_distributed_headers(tracer, int_config=None, request_headers=None, override=None): # type: (Tracer, Optional[IntegrationConfig], Optional[Dict[str, str]], Optional[bool]) -> None """ Helper for activating a distributed trace headers' context if enabled in integration config. int_config will be used to check if distributed trace headers context will be activated, but override will override whatever value is set in int_config if passed any value other than None. """ if override is False: return None if override or (int_config and distributed_tracing_enabled(int_config)): context = HTTPPropagator.extract(request_headers) # Only need to activate the new context if something was propagated if context.trace_id: tracer.context_provider.activate(context)
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 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'
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', 'HTTP_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'
def test_extract_invalid_tags(self): # Malformed tags do not fail to extract the rest of the context tracer = DummyTracer() headers = { "x-datadog-trace-id": "1234", "x-datadog-parent-id": "5678", "x-datadog-sampling-priority": "1", "x-datadog-origin": "synthetics", "x-datadog-tags": "malformed=,=tags,", } 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"}
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: context = HTTPPropagator.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.WEB, ) 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) method = scope.get("method") server = scope.get("server") if server and len(server) == 2: port = server[1] server_host = server[0] + (":" + str(port) if port is not None and port != 80 else "") full_path = scope.get("root_path", "") + scope.get("path", "") url = scope.get("scheme", "http") + "://" + server_host + full_path else: url = None if self.integration_config.trace_query_string: query_string = scope.get("query_string") if len(query_string) > 0: query_string = bytes_to_str(query_string) else: query_string = None trace_utils.set_http_meta(span, self.integration_config, method=method, url=url, query=query_string, request_headers=headers) tags = _extract_versions_from_scope(scope, self.integration_config) span.set_tags(tags) async def wrapped_send(message): if span and message.get( "type") == "http.response.start" and "status" in message: status_code = message["status"] else: status_code = None if "headers" in message: response_headers = message["headers"] else: response_headers = None trace_utils.set_http_meta(span, self.integration_config, status_code=status_code, response_headers=response_headers) 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()
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)
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: scope["ddtrace_asgi.tracer"] = self.tracer if scope["type"] != "http": await self.app(scope, receive, send) return request = Request(scope=scope, receive=receive) try: request_headers = request.headers method = request.method url = request.url except KeyError: # ASGI message is invalid - most likely missing the 'headers' or 'method' # fields. await self.app(scope, receive, send) return # Make sure we don't use potentially unsafe request attributes after this point. del request if self._distributed_tracing: propagator = HTTPPropagator() context = propagator.extract(request_headers) if context.trace_id: self.tracer.context_provider.activate(context) resource = "%s %s" % (method, 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, method) span.set_tag(http_tags.URL, str(url)) if config.asgi.get("trace_query_string"): span.set_tag(http_tags.QUERY_STRING, url.query) span.set_tags(self.tags) # 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 span and message.get("type") == "http.response.start": if "status" in message: status_code: int = message["status"] span.set_tag(http_tags.STATUS_CODE, str(status_code)) if "headers" in message: response_headers = Headers(raw=message["headers"]) store_response_headers(response_headers, span, config.asgi) 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()