def test_configuration_get_from_twice(self):
     # ensure the configuration is the same if `get_from` is used
     # in the same instance
     instance = self.Klass()
     cfg1 = config.get_from(instance)
     cfg2 = config.get_from(instance)
     ok_(cfg1 is cfg2)
 def test_configuration_get_from_twice(self):
     # ensure the configuration is the same if `get_from` is used
     # in the same instance
     instance = self.Klass()
     cfg1 = config.get_from(instance)
     cfg2 = config.get_from(instance)
     ok_(cfg1 is cfg2)
 def test_configuration_override_instance(self):
     # ensure instance configuration doesn't override global settings
     global_cfg = config.get_from(self.Klass)
     global_cfg['distributed_tracing'] = True
     instance = self.Klass()
     cfg = config.get_from(instance)
     cfg['distributed_tracing'] = False
     ok_(config.get_from(self.Klass)['distributed_tracing'] is True)
     ok_(config.get_from(instance)['distributed_tracing'] is False)
 def test_configuration_override_instance(self):
     # ensure instance configuration doesn't override global settings
     global_cfg = config.get_from(self.Klass)
     global_cfg['distributed_tracing'] = True
     instance = self.Klass()
     cfg = config.get_from(instance)
     cfg['distributed_tracing'] = False
     ok_(config.get_from(self.Klass)['distributed_tracing'] is True)
     ok_(config.get_from(instance)['distributed_tracing'] is False)
    def test_propagation_true(self):
        # ensure distributed tracing can be enabled manually
        cfg = config.get_from(self.session)
        cfg['distributed_tracing'] = True
        adapter = Adapter()
        self.session.mount('mock', adapter)

        with self.tracer.trace('root') as root:

            def matcher(request):
                return self.headers_here(self.tracer, request, root)

            adapter.register_uri('GET',
                                 'mock://datadog/foo',
                                 additional_matcher=matcher,
                                 text='bar')
            resp = self.session.get('mock://datadog/foo')
            eq_(200, resp.status_code)
            eq_('bar', resp.text)

        spans = self.tracer.writer.spans
        root, req = spans
        eq_('root', root.name)
        eq_('requests.request', req.name)
        eq_(root.trace_id, req.trace_id)
        eq_(root.span_id, req.parent_id)
Beispiel #6
0
def _wrap_request(func, instance, args, kwargs):
    # Use any attached tracer if available, otherwise use the global tracer
    pin = Pin.get_from(instance)
    if should_skip_request(pin, instance):
        return func(*args, **kwargs)

    cfg = config.get_from(instance)

    try:
        # Create a new span and attach to this instance (so we can retrieve/update/close later on the response)
        span = pin.tracer.trace(span_name, span_type=SpanTypes.HTTP)
        setattr(instance, "_datadog_span", span)

        # propagate distributed tracing headers
        if cfg.get("distributed_tracing"):
            if len(args) > 3:
                headers = args[3]
            else:
                headers = kwargs.setdefault("headers", {})
            HTTPPropagator.inject(span.context, headers)
    except Exception:
        log.debug("error configuring request", exc_info=True)
        span = getattr(instance, "_datadog_span", None)
        if span:
            span.finish()

    try:
        return func(*args, **kwargs)
    except Exception:
        span = getattr(instance, "_datadog_span", None)
        exc_info = sys.exc_info()
        if span:
            span.set_exc_info(*exc_info)
            span.finish()
        six.reraise(*exc_info)
 def test_service_name_for_pin(self):
     # ensure for backward compatibility that changing the service
     # name via the Pin object also updates integration config
     Pin(service='intake').onto(self.Klass)
     instance = self.Klass()
     cfg = config.get_from(instance)
     eq_(cfg['service_name'], 'intake')
Beispiel #8
0
    def test_propagation_true(self):
        # ensure distributed tracing can be enabled manually
        cfg = config.get_from(self.session)
        cfg["distributed_tracing"] = True
        adapter = Adapter()
        self.session.mount("mock", adapter)

        with self.tracer.trace("root") as root:

            def matcher(request):
                return self.headers_here(self.tracer, request, root)

            adapter.register_uri("GET",
                                 "mock://datadog/foo",
                                 additional_matcher=matcher,
                                 text="bar")
            resp = self.session.get("mock://datadog/foo")
            assert 200 == resp.status_code
            assert "bar" == resp.text

        spans = self.tracer.writer.spans
        root, req = spans
        assert "root" == root.name
        assert "requests.request" == req.name
        assert root.trace_id == req.trace_id
        assert root.span_id == req.parent_id
 def test_service_name_for_pin(self):
     # ensure for backward compatibility that changing the service
     # name via the Pin object also updates integration config
     Pin(service='intake').onto(self.Klass)
     instance = self.Klass()
     cfg = config.get_from(instance)
     eq_(cfg['service_name'], 'intake')
Beispiel #10
0
 def test_service_attribute_priority(self):
     # ensure the `service` arg has highest priority over configuration
     # for backward compatibility
     global_config = {
         "service_name": "primary_service",
     }
     Pin(service="service", _config=global_config).onto(self.Klass)
     instance = self.Klass()
     cfg = config.get_from(instance)
     assert cfg["service_name"] == "service"
 def test_service_attribute_priority(self):
     # ensure the `service` arg has highest priority over configuration
     # for backward compatibility
     global_config = {
         'service_name': 'primary_service',
     }
     Pin(service='service', _config=global_config).onto(self.Klass)
     instance = self.Klass()
     cfg = config.get_from(instance)
     eq_(cfg['service_name'], 'service')
 def test_configuration_copy(self):
     # ensure when a Pin is used, the given configuration is copied
     global_config = {
         'service_name': 'service',
     }
     Pin(service='service', _config=global_config).onto(self.Klass)
     instance = self.Klass()
     cfg = config.get_from(instance)
     cfg['service_name'] = 'metrics'
     eq_(global_config['service_name'], 'service')
 def test_user_set_service(self):
     # ensure a service name set by the user has precedence
     cfg = config.get_from(self.session)
     cfg["service"] = "clients"
     out = self.session.get(URL_200)
     assert out.status_code == 200
     spans = self.pop_spans()
     assert len(spans) == 1
     s = spans[0]
     assert s.service == "clients"
 def test_service_attribute_priority(self):
     # ensure the `service` arg has highest priority over configuration
     # for backward compatibility
     global_config = {
         'service_name': 'primary_service',
     }
     Pin(service='service', _config=global_config).onto(self.Klass)
     instance = self.Klass()
     cfg = config.get_from(instance)
     eq_(cfg['service_name'], 'service')
 def test_configuration_copy(self):
     # ensure when a Pin is used, the given configuration is copied
     global_config = {
         'service_name': 'service',
     }
     Pin(service='service', _config=global_config).onto(self.Klass)
     instance = self.Klass()
     cfg = config.get_from(instance)
     cfg['service_name'] = 'metrics'
     eq_(global_config['service_name'], 'service')
Beispiel #16
0
 def test_configuration_copy(self):
     # ensure when a Pin is used, the given configuration is copied
     global_config = {
         "service_name": "service",
     }
     Pin(service="service", _config=global_config).onto(self.Klass)
     instance = self.Klass()
     cfg = config.get_from(instance)
     cfg["service_name"] = "metrics"
     assert global_config["service_name"] == "service"
Beispiel #17
0
def _distributed_tracing(self):
    """Deprecated: this method has been deprecated in favor of
    the configuration system. It will be removed in newer versions
    of the Tracer.
    """
    deprecation(
        name="client.distributed_tracing",
        message="Use the configuration object instead `config.get_from(client)['distributed_tracing'`",
        version="1.0.0",
    )
    return config.get_from(self)["distributed_tracing"]
Beispiel #18
0
def _distributed_tracing(self):
    """Deprecated: this method has been deprecated in favor of
    the configuration system. It will be removed in newer versions
    of the Tracer.
    """
    deprecation(
        name='client.distributed_tracing',
        message='Use the configuration object instead `config.get_from(client)[\'distributed_tracing\'`',
        version='1.0.0',
    )
    return config.get_from(self)['distributed_tracing']
Beispiel #19
0
    def test_split_by_domain_wrong(self):
        # ensure the split by domain doesn't crash in case of a wrong URL;
        # in that case, no spans are created
        cfg = config.get_from(self.session)
        cfg['split_by_domain'] = True
        with pytest.raises(MissingSchema):
            self.session.get('http:/some>thing')

        # We are wrapping `requests.Session.send` and this error gets thrown before that function
        spans = self.tracer.writer.pop()
        assert len(spans) == 0
Beispiel #20
0
    def test_split_by_domain_remove_auth_in_url(self):
        # ensure that auth details are stripped from URL
        cfg = config.get_from(self.session)
        cfg['split_by_domain'] = True
        out = self.session.get('http://*****:*****@httpbin.org')
        eq_(out.status_code, 200)

        spans = self.tracer.writer.pop()
        eq_(len(spans), 1)
        s = spans[0]

        eq_(s.service, 'httpbin.org')
Beispiel #21
0
    def test_split_by_domain_includes_port(self):
        # ensure that port is included if present in URL
        cfg = config.get_from(self.session)
        cfg['split_by_domain'] = True
        out = self.session.get('http://httpbin.org:80')
        eq_(out.status_code, 200)

        spans = self.tracer.writer.pop()
        eq_(len(spans), 1)
        s = spans[0]

        eq_(s.service, 'httpbin.org:80')
Beispiel #22
0
    def test_user_set_service_name(self):
        # ensure a service name set by the user has precedence
        cfg = config.get_from(self.session)
        cfg['service_name'] = 'clients'
        out = self.session.get(URL_200)
        eq_(out.status_code, 200)

        spans = self.tracer.writer.pop()
        eq_(len(spans), 1)
        s = spans[0]

        eq_(s.service, 'clients')
    def test_global_config_service_env_precedence(self):
        out = self.session.get(URL_200)
        assert out.status_code == 200
        spans = self.pop_spans()
        assert spans[0].service == "override"

        cfg = config.get_from(self.session)
        cfg["service"] = "override2"
        out = self.session.get(URL_200)
        assert out.status_code == 200
        spans = self.pop_spans()
        assert spans[0].service == "override2"
Beispiel #24
0
def _distributed_tracing_setter(self, value):
    """Deprecated: this method has been deprecated in favor of
    the configuration system. It will be removed in newer versions
    of the Tracer.
    """
    deprecation(
        name='client.distributed_tracing',
        message=
        'Use the configuration object instead `config.get_from(client)[\'distributed_tracing\'] = value`',
        version='1.0.0',
    )
    config.get_from(self)['distributed_tracing'] = value
Beispiel #25
0
    def test_user_set_service_name(self):
        # ensure a service name set by the user has precedence
        cfg = config.get_from(self.session)
        cfg['service_name'] = 'clients'
        out = self.session.get(URL_200)
        eq_(out.status_code, 200)

        spans = self.tracer.writer.pop()
        eq_(len(spans), 1)
        s = spans[0]

        eq_(s.service, 'clients')
Beispiel #26
0
    def test_split_by_domain_remove_auth_in_url(self):
        # ensure that auth details are stripped from URL
        cfg = config.get_from(self.session)
        cfg['split_by_domain'] = True
        out = self.session.get('http://*****:*****@httpbin.org')
        assert out.status_code == 200

        spans = self.tracer.writer.pop()
        assert len(spans) == 1
        s = spans[0]

        assert s.service == 'httpbin.org'
Beispiel #27
0
    def test_split_by_domain_includes_port_path(self):
        # ensure that port is included if present in URL but not path
        cfg = config.get_from(self.session)
        cfg['split_by_domain'] = True
        out = self.session.get('http://httpbin.org:80/anything/v1/foo')
        assert out.status_code == 200

        spans = self.tracer.writer.pop()
        assert len(spans) == 1
        s = spans[0]

        assert s.service == 'httpbin.org:80'
Beispiel #28
0
    def test_user_set_service_name(self):
        # ensure a service name set by the user has precedence
        cfg = config.get_from(self.session)
        cfg['service_name'] = 'clients'
        out = self.session.get(URL_200)
        assert out.status_code == 200

        spans = self.tracer.writer.pop()
        assert len(spans) == 1
        s = spans[0]

        assert s.service == 'clients'
Beispiel #29
0
    def test_split_by_domain_includes_port(self):
        # ensure that port is included if present in URL
        cfg = config.get_from(self.session)
        cfg["split_by_domain"] = True
        out = self.session.get("http://httpbin.org:80")
        assert out.status_code == 200

        spans = self.pop_spans()
        assert len(spans) == 1
        s = spans[0]

        assert s.service == "httpbin.org:80"
Beispiel #30
0
def _wrap_request(func, instance, args, kwargs):
    """Trace the `Session.request` instance method"""
    # TODO[manu]: we already offer a way to provide the Global Tracer
    # and is ddtrace.tracer; it's used only inside our tests and can
    # be easily changed by providing a TracingTestCase that sets common
    # tracing functionalities.
    tracer = getattr(instance, 'datadog_tracer', ddtrace.tracer)

    # skip if tracing is not enabled
    if not tracer.enabled:
        return func(*args, **kwargs)

    method = kwargs.get('method') or args[0]
    url = kwargs.get('url') or args[1]
    headers = kwargs.get('headers', {})
    parsed_uri = parse.urlparse(url)
    # sanitize url of query
    sanitized_url = parse.urlunparse((
        parsed_uri.scheme,
        parsed_uri.netloc,
        parsed_uri.path,
        parsed_uri.params,
        None,  # drop parsed_uri.query
        parsed_uri.fragment))

    with tracer.trace("requests.request", span_type=http.TYPE) as span:
        # update the span service name before doing any action
        hostname = parsed_uri.hostname
        if parsed_uri.port:
            hostname += ':{}'.format(parsed_uri.port)
        span.service = _extract_service_name(instance, span, hostname=hostname)

        # propagate distributed tracing headers
        if config.get_from(instance).get('distributed_tracing'):
            propagator = HTTPPropagator()
            propagator.inject(span.context, headers)
            kwargs['headers'] = headers

        response = None
        try:
            response = func(*args, **kwargs)
            return response
        finally:
            try:
                span.set_tag(http.METHOD, method.upper())
                span.set_tag(http.URL, sanitized_url)
                if response is not None:
                    span.set_tag(http.STATUS_CODE, response.status_code)
                    # `span.error` must be an integer
                    span.error = int(500 <= response.status_code)
            except Exception:
                log.debug("requests: error adding tags", exc_info=True)
Beispiel #31
0
    def test_split_by_domain_precedence(self):
        # ensure the split by domain has precedence all the time
        cfg = config.get_from(self.session)
        cfg['split_by_domain'] = True
        cfg['service_name'] = 'intake'
        out = self.session.get(URL_200)
        eq_(out.status_code, 200)

        spans = self.tracer.writer.pop()
        eq_(len(spans), 1)
        s = spans[0]

        eq_(s.service, 'httpbin.org')
Beispiel #32
0
    def test_split_by_domain_precedence(self):
        # ensure the split by domain has precedence all the time
        cfg = config.get_from(self.session)
        cfg['split_by_domain'] = True
        cfg['service_name'] = 'intake'
        out = self.session.get(URL_200)
        assert out.status_code == 200

        spans = self.tracer.writer.pop()
        assert len(spans) == 1
        s = spans[0]

        assert s.service == 'httpbin.org'
Beispiel #33
0
    def test_split_by_domain(self):
        # ensure a service name is generated by the domain name
        # of the ongoing call
        cfg = config.get_from(self.session)
        cfg['split_by_domain'] = True
        out = self.session.get(URL_200)
        assert out.status_code == 200

        spans = self.tracer.writer.pop()
        assert len(spans) == 1
        s = spans[0]

        assert s.service == 'httpbin.org'
Beispiel #34
0
    def test_split_by_domain_precedence(self):
        # ensure the split by domain has precedence all the time
        cfg = config.get_from(self.session)
        cfg["split_by_domain"] = True
        cfg["service_name"] = "intake"
        out = self.session.get(URL_200)
        assert out.status_code == 200

        spans = self.pop_spans()
        assert len(spans) == 1
        s = spans[0]

        assert s.service == "httpbin.org"
Beispiel #35
0
    def test_split_by_domain_precedence(self):
        # ensure the split by domain has precedence all the time
        cfg = config.get_from(self.session)
        cfg['split_by_domain'] = True
        cfg['service_name'] = 'intake'
        out = self.session.get(URL_200)
        eq_(out.status_code, 200)

        spans = self.tracer.writer.pop()
        eq_(len(spans), 1)
        s = spans[0]

        eq_(s.service, 'httpbin.org')
Beispiel #36
0
    def test_split_by_domain_wrong(self):
        # ensure the split by domain doesn't crash in case of a wrong URL;
        # in that case, the default service name must be used
        cfg = config.get_from(self.session)
        cfg['split_by_domain'] = True
        with assert_raises(MissingSchema):
            self.session.get('http:/some>thing')

        spans = self.tracer.writer.pop()
        eq_(len(spans), 1)
        s = spans[0]

        eq_(s.service, 'requests')
Beispiel #37
0
    def test_split_by_domain_wrong(self):
        # ensure the split by domain doesn't crash in case of a wrong URL;
        # in that case, the default service name must be used
        cfg = config.get_from(self.session)
        cfg['split_by_domain'] = True
        with assert_raises(MissingSchema):
            self.session.get('http:/some>thing')

        spans = self.tracer.writer.pop()
        eq_(len(spans), 1)
        s = spans[0]

        eq_(s.service, 'requests')
Beispiel #38
0
    def test_split_by_domain(self):
        # ensure a service name is generated by the domain name
        # of the ongoing call
        cfg = config.get_from(self.session)
        cfg['split_by_domain'] = True
        out = self.session.get(URL_200)
        eq_(out.status_code, 200)

        spans = self.tracer.writer.pop()
        eq_(len(spans), 1)
        s = spans[0]

        eq_(s.service, 'httpbin.org')
Beispiel #39
0
    def test_split_by_domain(self):
        # ensure a service name is generated by the domain name
        # of the ongoing call
        cfg = config.get_from(self.session)
        cfg['split_by_domain'] = True
        out = self.session.get(URL_200)
        eq_(out.status_code, 200)

        spans = self.tracer.writer.pop()
        eq_(len(spans), 1)
        s = spans[0]

        eq_(s.service, 'httpbin.org')
    def test_propagation_false(self):
        # ensure distributed tracing can be disabled
        cfg = config.get_from(self.session)
        cfg['distributed_tracing'] = False
        adapter = Adapter()
        self.session.mount('mock', adapter)

        with self.tracer.trace('root'):
            def matcher(request):
                return self.headers_not_here(self.tracer, request)
            adapter.register_uri('GET', 'mock://datadog/foo', additional_matcher=matcher, text='bar')
            resp = self.session.get('mock://datadog/foo')
            eq_(200, resp.status_code)
            eq_('bar', resp.text)
 def test_configuration_copy_upside_down(self):
     # ensure when a Pin is created, it does not copy the given configuration
     # until it's used for at least once
     global_config = {
         'service_name': 'service',
     }
     Pin(service='service', _config=global_config).onto(self.Klass)
     # override the global config: users do that before using the integration
     global_config['service_name'] = 'metrics'
     # use the Pin via `get_from`
     instance = self.Klass()
     cfg = config.get_from(instance)
     # it should have users updated value
     eq_(cfg['service_name'], 'metrics')
Beispiel #42
0
    def test_user_service_name_precedence(self):
        # ensure the user service name takes precedence over
        # the parent Span
        cfg = config.get_from(self.session)
        cfg['service_name'] = 'clients'
        with self.tracer.trace('parent.span', service='web'):
            out = self.session.get(URL_200)
            eq_(out.status_code, 200)

        spans = self.tracer.writer.pop()
        eq_(len(spans), 2)
        s = spans[1]

        eq_(s.name, 'requests.request')
        eq_(s.service, 'clients')
Beispiel #43
0
def _wrap_request(func, instance, args, kwargs):
    """Trace the `Session.request` instance method"""
    # TODO[manu]: we already offer a way to provide the Global Tracer
    # and is ddtrace.tracer; it's used only inside our tests and can
    # be easily changed by providing a TracingTestCase that sets common
    # tracing functionalities.
    tracer = getattr(instance, 'datadog_tracer', ddtrace.tracer)

    # skip if tracing is not enabled
    if not tracer.enabled:
        return func(*args, **kwargs)

    method = kwargs.get('method') or args[0]
    url = kwargs.get('url') or args[1]
    headers = kwargs.get('headers', {})
    parsed_uri = parse.urlparse(url)

    with tracer.trace("requests.request", span_type=http.TYPE) as span:
        # update the span service name before doing any action
        span.service = _extract_service_name(instance, span, netloc=parsed_uri.netloc)

        # propagate distributed tracing headers
        if config.get_from(instance).get('distributed_tracing'):
            propagator = HTTPPropagator()
            propagator.inject(span.context, headers)
            kwargs['headers'] = headers

        response = None
        try:
            response = func(*args, **kwargs)
            return response
        finally:
            try:
                span.set_tag(http.METHOD, method.upper())
                span.set_tag(http.URL, url)
                if response is not None:
                    span.set_tag(http.STATUS_CODE, response.status_code)
                    # `span.error` must be an integer
                    span.error = int(500 <= response.status_code)
            except Exception:
                log.debug("requests: error adding tags", exc_info=True)
    def test_propagation_true(self):
        # ensure distributed tracing can be enabled
        cfg = config.get_from(self.session)
        cfg['distributed_tracing'] = True
        adapter = Adapter()
        self.session.mount('mock', adapter)

        with self.tracer.trace('root') as root:
            def matcher(request):
                return self.headers_here(self.tracer, request, root)
            adapter.register_uri('GET', 'mock://datadog/foo', additional_matcher=matcher, text='bar')
            resp = self.session.get('mock://datadog/foo')
            eq_(200, resp.status_code)
            eq_('bar', resp.text)

        spans = self.tracer.writer.spans
        root, req = spans
        eq_('root', root.name)
        eq_('requests.request', req.name)
        eq_(root.trace_id, req.trace_id)
        eq_(root.span_id, req.parent_id)
Beispiel #45
0
def _extract_service_name(session, span, netloc=None):
    """Extracts the right service name based on the following logic:
    - `requests` is the default service name
    - users can change it via `session.service_name = 'clients'`
    - if the Span doesn't have a parent, use the set service name
      or fallback to the default
    - if the Span has a parent, use the set service name or the
      parent service value if the set service name is the default
    - if `split_by_domain` is used, always override users settings
      and use the network location as a service name

    The priority can be represented as:
    Updated service name > parent service name > default to `requests`.
    """
    cfg = config.get_from(session)
    if cfg['split_by_domain'] and netloc:
        return netloc

    service_name = cfg['service_name']
    if (service_name == DEFAULT_SERVICE and
            span._parent is not None and
            span._parent.service is not None):
        service_name = span._parent.service
    return service_name
 def test_configuration_get_from(self):
     # ensure a dictionary is returned
     cfg = config.get_from(self.Klass)
     ok_(isinstance(cfg, dict))
 def test_configuration_set(self):
     # ensure the configuration can be updated in the Pin
     instance = self.Klass()
     cfg = config.get_from(instance)
     cfg['distributed_tracing'] = True
     ok_(config.get_from(instance)['distributed_tracing'] is True)
 def test_global_configuration_inheritance(self):
     # ensure global configuration is inherited when it's set
     cfg = config.get_from(self.Klass)
     cfg['distributed_tracing'] = True
     instance = self.Klass()
     ok_(config.get_from(instance)['distributed_tracing'] is True)