示例#1
0
    def test_should_skip_request(self):
        """
        When calling should_skip_request
            with an enabled Pin and non-internal request
                returns False
            with a disabled Pin and non-internal request
                returns True
            with an enabled Pin and internal request
                returns True
            with a disabled Pin and internal request
                returns True
        """
        # Enabled Pin and non-internal request
        self.tracer.enabled = True
        request = self.get_http_connection(SOCKET)
        pin = Pin.get_from(request)
        self.assertFalse(should_skip_request(pin, request))

        # Disabled Pin and non-internal request
        self.tracer.enabled = False
        request = self.get_http_connection(SOCKET)
        pin = Pin.get_from(request)
        self.assertTrue(should_skip_request(pin, request))

        # Enabled Pin and internal request
        self.tracer.enabled = True
        request = self.get_http_connection(self.tracer.writer.api.hostname, self.tracer.writer.api.port)
        pin = Pin.get_from(request)
        self.assertTrue(should_skip_request(pin, request))

        # Disabled Pin and internal request
        self.tracer.enabled = False
        request = self.get_http_connection(self.tracer.writer.api.hostname, self.tracer.writer.api.port)
        pin = Pin.get_from(request)
        self.assertTrue(should_skip_request(pin, request))
示例#2
0
    def test_should_skip_request(self):
        """
        When calling should_skip_request
            with an enabled Pin and non-internal request
                returns False
            with a disabled Pin and non-internal request
                returns True
            with an enabled Pin and internal request
                returns True
            with a disabled Pin and internal request
                returns True
        """
        # Enabled Pin and non-internal request
        self.tracer.enabled = True
        request = self.get_http_connection(SOCKET)
        pin = Pin.get_from(request)
        self.assertFalse(should_skip_request(pin, request))

        # Disabled Pin and non-internal request
        self.tracer.enabled = False
        request = self.get_http_connection(SOCKET)
        pin = Pin.get_from(request)
        self.assertTrue(should_skip_request(pin, request))

        # Enabled Pin and internal request
        self.tracer.enabled = True
        request = self.get_http_connection(self.tracer.writer.api.hostname, self.tracer.writer.api.port)
        pin = Pin.get_from(request)
        self.assertTrue(should_skip_request(pin, request))

        # Disabled Pin and internal request
        self.tracer.enabled = False
        request = self.get_http_connection(self.tracer.writer.api.hostname, self.tracer.writer.api.port)
        pin = Pin.get_from(request)
        self.assertTrue(should_skip_request(pin, request))
示例#3
0
    def _trace_method(self, method, resource, extra_tags, *args, **kwargs):
        pin = Pin.get_from(self)
        if not pin or not pin.enabled():
            result = yield from method(*args, **kwargs)
            return result
        service = pin.service

        with pin.tracer.trace(self._datadog_name,
                              service=service,
                              resource=resource,
                              span_type=SpanTypes.SQL) as s:
            s.set_tag(SPAN_MEASURED_KEY)
            s.set_tag(sql.QUERY, resource)
            s.set_tags(pin.tags)
            s.set_tags(extra_tags)

            # set analytics sample rate
            s.set_tag(ANALYTICS_SAMPLE_RATE_KEY,
                      config.aiopg.get_analytics_sample_rate())

            try:
                result = yield from method(*args, **kwargs)
                return result
            finally:
                s.set_metric("db.rowcount", self.rowcount)
示例#4
0
def _patched_search(func, instance, wrapt_args, wrapt_kwargs):
    """
        wrapt_args is called the way it is to distinguish it from the 'args'
        argument to the algoliasearch.index.Index.search() method.
    """

    if algoliasearch_version < (2, 0) and algoliasearch_version >= (1, 0):
        function_query_arg_name = 'args'
    elif algoliasearch_version >= (2, 0) and algoliasearch_version < (3, 0):
        function_query_arg_name = 'request_options'
    else:
        return func(*wrapt_args, **wrapt_kwargs)

    pin = Pin.get_from(instance)
    if not pin or not pin.enabled():
        return func(*wrapt_args, **wrapt_kwargs)

    with pin.tracer.trace('algoliasearch.search', service=pin.service) as span:
        span.set_tag(SPAN_MEASURED_KEY)

        if not span.sampled:
            return func(*wrapt_args, **wrapt_kwargs)

        if config.algoliasearch.collect_query_text:
            span.set_tag('query.text', wrapt_kwargs.get('query', wrapt_args[0]))

        query_args = wrapt_kwargs.get(function_query_arg_name, wrapt_args[1] if len(wrapt_args) > 1 else None)

        if query_args and isinstance(query_args, dict):
            for query_arg, tag_name in QUERY_ARGS_DD_TAG_MAP.items():
                value = query_args.get(query_arg)
                if value is not None:
                    span.set_tag('query.args.{}'.format(tag_name), value)

        # Result would look like this
        # {
        #   'hits': [
        #     {
        #       .... your search results ...
        #     }
        #   ],
        #   'processingTimeMS': 1,
        #   'nbHits': 1,
        #   'hitsPerPage': 20,
        #   'exhaustiveNbHits': true,
        #   'params': 'query=xxx',
        #   'nbPages': 1,
        #   'query': 'xxx',
        #   'page': 0
        # }
        result = func(*wrapt_args, **wrapt_kwargs)

        if isinstance(result, dict):
            if result.get('processingTimeMS', None) is not None:
                span.set_metric('processing_time_ms', int(result['processingTimeMS']))

            if result.get('nbHits', None) is not None:
                span.set_metric('number_of_hits', int(result['nbHits']))

        return result
        def pytest_configure(config):
            """Make sure the tracer on pin is the same as global one."""
            from ddtrace.pin import Pin

            pin = Pin.get_from(config)
            if pin:
                pin.tracer.writer = tracer.writer
示例#6
0
async def traced_13_execute_pipeline(func, instance, args, kwargs):
    pin = Pin.get_from(instance)
    if not pin or not pin.enabled():
        return await func(*args, **kwargs)

    cmds = []
    for _, cmd, cmd_args, _ in instance._pipeline:
        parts = [cmd]
        parts.extend(cmd_args)
        cmds.append(format_command_args(parts))
    resource = "\n".join(cmds)
    with pin.tracer.trace(
            redisx.CMD,
            resource=resource,
            service=trace_utils.ext_service(pin, config.aioredis),
            span_type=SpanTypes.REDIS,
    ) as span:

        span.set_tags({
            net.TARGET_HOST: instance._pool_or_conn.address[0],
            net.TARGET_PORT: instance._pool_or_conn.address[1],
            redisx.DB: instance._pool_or_conn.db or 0,
        })

        span.set_tag(SPAN_MEASURED_KEY)
        span.set_tag(redisx.RAWCMD, resource)
        span.set_metric(redisx.PIPELINE_LEN, len(instance._pipeline))
        # set analytics sample rate if enabled
        span.set_tag(ANALYTICS_SAMPLE_RATE_KEY,
                     config.aioredis.get_analytics_sample_rate())

        return await func(*args, **kwargs)
示例#7
0
async def traced_execute_command(func, instance, args, kwargs):
    pin = Pin.get_from(instance)
    if not pin or not pin.enabled():
        return await func(*args, **kwargs)

    with _trace_redis_cmd(pin, config.aioredis, instance, args):
        return await func(*args, **kwargs)
示例#8
0
def pytest_sessionstart(session):
    pin = Pin.get_from(session.config)
    if pin is not None:
        tracer_filters = pin.tracer._filters
        if not any(
                isinstance(tracer_filter, TraceCiVisibilityFilter)
                for tracer_filter in tracer_filters):
            tracer_filters += [TraceCiVisibilityFilter()]
            pin.tracer.configure(settings={"FILTERS": tracer_filters})
示例#9
0
def traced_13_execute_command(func, instance, args, kwargs):
    # If we have a _RedisBuffer then we are in a pipeline
    if isinstance(instance.connection, _RedisBuffer):
        return func(*args, **kwargs)

    pin = Pin.get_from(instance)
    if not pin or not pin.enabled():
        return func(*args, **kwargs)

    # Don't activate the span since this operation is performed as a future which concludes sometime later on in
    # execution so subsequent operations in the stack are not necessarily semantically related
    # (we don't want this span to be the parent of all other spans created before the future is resolved)
    parent = pin.tracer.current_span()
    span = pin.tracer.start_span(
        redisx.CMD,
        service=trace_utils.ext_service(pin, config.aioredis),
        span_type=SpanTypes.REDIS,
        activate=False,
        child_of=parent,
    )

    span.set_tag(SPAN_MEASURED_KEY)
    query = format_command_args(args)
    span.resource = query
    span.set_tag(redisx.RAWCMD, query)
    if pin.tags:
        span.set_tags(pin.tags)

    span.set_tags({
        net.TARGET_HOST: instance.address[0],
        net.TARGET_PORT: instance.address[1],
        redisx.DB: instance.db or 0,
    })
    span.set_metric(redisx.ARGS_LEN, len(args))
    # set analytics sample rate if enabled
    span.set_tag(ANALYTICS_SAMPLE_RATE_KEY,
                 config.aioredis.get_analytics_sample_rate())

    def _finish_span(future):
        try:
            # Accessing the result will raise an exception if:
            #   - The future was cancelled
            #   - There was an error executing the future (`future.exception()`)
            #   - The future is in an invalid state
            future.result()
        except Exception:
            span.set_exc_info(*sys.exc_info())
        finally:
            span.finish()

    task = func(*args, **kwargs)
    # Execute command returns a coroutine when no free connections are available
    # https://github.com/aio-libs/aioredis-py/blob/v1.3.1/aioredis/pool.py#L191
    task = asyncio.ensure_future(task)
    task.add_done_callback(_finish_span)
    return task
示例#10
0
async def traced_execute_pipeline(func, instance, args, kwargs):
    pin = Pin.get_from(instance)
    if not pin or not pin.enabled():
        return await func(*args, **kwargs)

    cmds = [format_command_args(c) for c, _ in instance.command_stack]
    resource = "\n".join(cmds)
    with _trace_redis_execute_pipeline(pin, config.aioredis, resource,
                                       instance):
        return await func(*args, **kwargs)
示例#11
0
def pytest_runtest_protocol(item, nextitem):
    pin = Pin.get_from(item.config)
    if pin is None:
        yield
        return
    with pin.tracer.trace(
            ddtrace.config.pytest.operation_name,
            service=int_service(pin, ddtrace.config.pytest),
            resource=item.nodeid,
            span_type=SpanTypes.TEST.value,
    ) as span:
        span.context.dd_origin = ci.CI_APP_TEST_ORIGIN
        span.context.sampling_priority = AUTO_KEEP
        span.set_tags(pin.tags)
        span.set_tag(SPAN_KIND, KIND)
        span.set_tag(test.FRAMEWORK, FRAMEWORK)
        span.set_tag(test.NAME, item.name)
        if hasattr(item, "module"):
            span.set_tag(test.SUITE, item.module.__name__)
        elif hasattr(item, "dtest") and isinstance(item.dtest, DocTest):
            span.set_tag(test.SUITE, item.dtest.globs["__name__"])
        span.set_tag(test.TYPE, SpanTypes.TEST.value)

        span.set_tag(test.FRAMEWORK_VERSION, pytest.__version__)

        # We preemptively set FAIL as a status, because if pytest_runtest_makereport is not called
        # (where the actual test status is set), it means there was a pytest error
        span.set_tag(test.STATUS, test.Status.FAIL.value)

        # Parameterized test cases will have a `callspec` attribute attached to the pytest Item object.
        # Pytest docs: https://docs.pytest.org/en/6.2.x/reference.html#pytest.Function
        if getattr(item, "callspec", None):
            parameters = {
                "arguments": {},
                "metadata": {}
            }  # type: Dict[str, Dict[str, str]]
            for param_name, param_val in item.callspec.params.items():
                try:
                    parameters["arguments"][param_name] = repr(param_val)
                except Exception:
                    parameters["arguments"][param_name] = "Could not encode"
                    log.warning("Failed to encode %r",
                                param_name,
                                exc_info=True)
            span.set_tag(test.PARAMETERS, json.dumps(parameters))

        markers = [
            marker.kwargs for marker in item.iter_markers(name="dd_tags")
        ]
        for tags in markers:
            span.set_tags(tags)
        _store_span(item, span)

        yield
示例#12
0
def patched_query_request(original_func, instance, args, kwargs):

    pin = Pin.get_from(instance)
    if not pin or not pin.enabled():
        return original_func(*args, **kwargs)

    endpoint_name = getattr(instance, "host").split(".")[0]

    with pin.tracer.trace(
            "{}.command".format(endpoint_name),
            service="{}.{}".format(pin.service, endpoint_name),
            span_type=SpanTypes.HTTP,
    ) as span:
        span.set_tag(SPAN_MEASURED_KEY)

        operation_name = None
        if args:
            operation_name = get_argument_value(args, kwargs, 0, "action")
            span.resource = "%s.%s" % (endpoint_name, operation_name.lower())
        else:
            span.resource = endpoint_name

        aws.add_span_arg_tags(span, endpoint_name, args, AWS_QUERY_ARGS_NAME,
                              AWS_QUERY_TRACED_ARGS)

        # Obtaining region name
        region_name = _get_instance_region_name(instance)

        meta = {
            aws.AGENT: "boto",
            aws.OPERATION: operation_name,
        }
        if region_name:
            meta[aws.REGION] = region_name

        span.set_tags(meta)

        # Original func returns a boto.connection.HTTPResponse object
        result = original_func(*args, **kwargs)
        span.set_tag(http.STATUS_CODE, getattr(result, "status"))
        span.set_tag(http.METHOD, getattr(result, "_method"))

        # set analytics sample rate
        span.set_tag(ANALYTICS_SAMPLE_RATE_KEY,
                     config.boto.get_analytics_sample_rate())

        return result
示例#13
0
async def _wrapped_async_send(
    wrapped,  # type: BoundFunctionWrapper
    instance,  # type: httpx.AsyncClient
    args,  # type: typing.Tuple[httpx.Request],
    kwargs,  # type: typing.Dict[typing.Str, typing.Any]
):
    # type: (...) -> typing.Coroutine[None, None, httpx.Response]
    req = get_argument_value(args, kwargs, 0, "request")

    pin = Pin.get_from(instance)
    if not pin or not pin.enabled():
        return await wrapped(*args, **kwargs)

    with pin.tracer.trace("http.request", service=_get_service_name(pin, req), span_type=SpanTypes.HTTP) as span:
        _init_span(span, req)
        resp = None
        try:
            resp = await wrapped(*args, **kwargs)
            return resp
        finally:
            _set_span_meta(span, req, resp)
示例#14
0
def traced_execute_pipeline(func, instance, args, kwargs):
    pin = Pin.get_from(instance)
    if not pin or not pin.enabled():
        return func(*args, **kwargs)

    cmds = [format_command_args(c.args) for c in instance.command_stack]
    resource = "\n".join(cmds)
    tracer = pin.tracer
    with tracer.trace(redisx.CMD,
                      resource=resource,
                      service=pin.service,
                      span_type=SpanTypes.REDIS) as s:
        s.set_tag(SPAN_MEASURED_KEY)
        s.set_tag(redisx.RAWCMD, resource)
        s.set_metric(redisx.PIPELINE_LEN, len(instance.command_stack))

        # set analytics sample rate if enabled
        s.set_tag(ANALYTICS_SAMPLE_RATE_KEY,
                  config.rediscluster.get_analytics_sample_rate())

        return func(*args, **kwargs)
示例#15
0
def pytest_runtest_protocol(item, nextitem):
    pin = Pin.get_from(item.config)
    if pin is None:
        yield
        return
    with pin.tracer.trace(
        ddtrace.config.pytest.operation_name,
        service=int_service(pin, ddtrace.config.pytest),
        resource=item.nodeid,
        span_type=SpanTypes.TEST.value,
    ) as span:
        span.context.dd_origin = ci.CI_APP_TEST_ORIGIN
        span.set_tags(pin.tags)
        span.set_tag(SPAN_KIND, KIND)
        span.set_tag(test.FRAMEWORK, FRAMEWORK)
        span.set_tag(test.NAME, item.name)
        span.set_tag(test.SUITE, item.module.__name__)
        span.set_tag(test.TYPE, SpanTypes.TEST.value)

        # Parameterized test cases will have a `callspec` attribute attached to the pytest Item object.
        # Pytest docs: https://docs.pytest.org/en/6.2.x/reference.html#pytest.Function
        if getattr(item, "callspec", None):
            parameters = {"arguments": {}, "metadata": {}}  # type: Dict[str, Dict[str, str]]
            for param_name, param_val in item.callspec.params.items():
                try:
                    parameters["arguments"][param_name] = repr(param_val)
                except Exception:
                    parameters["arguments"][param_name] = "Could not encode"
                    log.warning("Failed to encode %r", param_name, exc_info=True)
            span.set_tag(test.PARAMETERS, json.dumps(parameters))

        markers = [marker.kwargs for marker in item.iter_markers(name="dd_tags")]
        for tags in markers:
            span.set_tags(tags)
        _store_span(item, span)

        yield
示例#16
0
def ddspan(request):
    pin = Pin.get_from(request.config)
    if pin:
        return _extract_span(request.node)
示例#17
0
def pytest_sessionfinish(session, exitstatus):
    """Flush open tracer."""
    pin = Pin.get_from(session.config)
    if pin is not None:
        pin.tracer.shutdown()
示例#18
0
 def _cursor(self, *args, **kwargs):
     cursor = yield from self.__wrapped__._cursor(*args, **kwargs)
     pin = Pin.get_from(self)
     if not pin:
         return cursor
     return self._self_cursor_cls(cursor, pin)
示例#19
0
def traced_13_pipeline(func, instance, args, kwargs):
    pipeline = func(*args, **kwargs)
    pin = Pin.get_from(instance)
    if pin:
        pin.onto(pipeline)
    return pipeline
示例#20
0
def _wrap_urlopen(func, instance, args, kwargs):
    """
    Wrapper function for the lower-level urlopen in urllib3

    :param func: The original target function "urlopen"
    :param instance: The patched instance of ``HTTPConnectionPool``
    :param args: Positional arguments from the target function
    :param kwargs: Keyword arguments from the target function
    :return: The ``HTTPResponse`` from the target function
    """
    request_method = get_argument_value(args, kwargs, 0, "method")
    request_url = get_argument_value(args, kwargs, 1, "url")
    try:
        request_headers = get_argument_value(args, kwargs, 3, "headers")
    except ArgumentError:
        request_headers = None
    try:
        request_retries = get_argument_value(args, kwargs, 4, "retries")
    except ArgumentError:
        request_retries = None

    # HTTPConnectionPool allows relative path requests; convert the request_url to an absolute url
    if request_url.startswith("/"):
        request_url = parse.urlunparse((
            instance.scheme,
            "{}:{}".format(instance.host, instance.port) if instance.port
            and instance.port not in DROP_PORTS else str(instance.host),
            request_url,
            None,
            None,
            None,
        ))

    parsed_uri = parse.urlparse(request_url)
    hostname = parsed_uri.netloc

    pin = Pin.get_from(instance)
    if not pin or not pin.enabled():
        return func(*args, **kwargs)

    with pin.tracer.trace("urllib3.request",
                          service=trace_utils.ext_service(pin, config.urllib3),
                          span_type=SpanTypes.HTTP) as span:
        if config.urllib3.split_by_domain:
            span.service = hostname

        # If distributed tracing is enabled, propagate the tracing headers to downstream services
        if config.urllib3.distributed_tracing:
            if request_headers is None:
                request_headers = {}
                kwargs["headers"] = request_headers
            HTTPPropagator.inject(span.context, request_headers)

        if config.urllib3.analytics_enabled:
            span.set_tag(ANALYTICS_SAMPLE_RATE_KEY,
                         config.urllib3.get_analytics_sample_rate())

        retries = request_retries.total if isinstance(
            request_retries, urllib3.util.retry.Retry) else None

        # Call the target function
        response = None
        try:
            response = func(*args, **kwargs)
        finally:
            trace_utils.set_http_meta(
                span,
                integration_config=config.urllib3,
                method=request_method,
                url=request_url,
                status_code=None if response is None else response.status,
                query=parsed_uri.query,
                request_headers=request_headers,
                response_headers={}
                if response is None else dict(response.headers),
                retries_remain=retries,
            )

        return response
示例#21
0
def patched_auth_request(original_func, instance, args, kwargs):

    # Catching the name of the operation that called make_request()
    operation_name = None

    # Go up the stack until we get the first non-ddtrace module
    # DEV: For `lambda.list_functions()` this should be:
    #        - ddtrace.contrib.boto.patch
    #        - ddtrace.vendor.wrapt.wrappers
    #        - boto.awslambda.layer1 (make_request)
    #        - boto.awslambda.layer1 (list_functions)
    # But can vary depending on Python versions; that's why we use an heuristic
    frame = inspect.currentframe().f_back
    operation_name = None
    while frame:
        if frame.f_code.co_name == "make_request":
            operation_name = frame.f_back.f_code.co_name
            break
        frame = frame.f_back

    pin = Pin.get_from(instance)
    if not pin or not pin.enabled():
        return original_func(*args, **kwargs)

    endpoint_name = getattr(instance, "host").split(".")[0]

    with pin.tracer.trace(
            "{}.command".format(endpoint_name),
            service="{}.{}".format(pin.service, endpoint_name),
            span_type=SpanTypes.HTTP,
    ) as span:
        span.set_tag(SPAN_MEASURED_KEY)
        if args:
            http_method = get_argument_value(args, kwargs, 0, "method")
            span.resource = "%s.%s" % (endpoint_name, http_method.lower())
        else:
            span.resource = endpoint_name

        aws.add_span_arg_tags(span, endpoint_name, args, AWS_AUTH_ARGS_NAME,
                              AWS_AUTH_TRACED_ARGS)

        # Obtaining region name
        region_name = _get_instance_region_name(instance)

        meta = {
            aws.AGENT: "boto",
            aws.OPERATION: operation_name,
        }
        if region_name:
            meta[aws.REGION] = region_name

        span.set_tags(meta)

        # Original func returns a boto.connection.HTTPResponse object
        result = original_func(*args, **kwargs)
        span.set_tag(http.STATUS_CODE, getattr(result, "status"))
        span.set_tag(http.METHOD, getattr(result, "_method"))

        # set analytics sample rate
        span.set_tag(ANALYTICS_SAMPLE_RATE_KEY,
                     config.boto.get_analytics_sample_rate())

        return result