def test_configure_dogstatsd_url_host_port(self):
        tracer = Tracer()
        tracer.configure(dogstatsd_url="foo:1234")
        assert tracer.writer.dogstatsd.host == "foo"
        assert tracer.writer.dogstatsd.port == 1234

        tracer = Tracer()
        writer = AgentWriter("http://localhost:8126")
        tracer.configure(writer=writer, dogstatsd_url="foo:1234")
        assert tracer.writer.dogstatsd.host == "foo"
        assert tracer.writer.dogstatsd.port == 1234
    def test_configure_dogstatsd_url_socket(self):
        tracer = Tracer()
        tracer.configure(dogstatsd_url="unix:///foo.sock")
        assert tracer.writer.dogstatsd.host is None
        assert tracer.writer.dogstatsd.port is None
        assert tracer.writer.dogstatsd.socket_path == "/foo.sock"

        tracer = Tracer()
        writer = AgentWriter("http://localhost:8126")
        tracer.configure(writer=writer, dogstatsd_url="unix:///foo.sock")
        assert tracer.writer.dogstatsd.host is None
        assert tracer.writer.dogstatsd.port is None
        assert tracer.writer.dogstatsd.socket_path == "/foo.sock"
Example #3
0
def test_tracer_trace_across_fork():
    """
    When a trace is started in a parent process and a child process is spawned
        The trace should be continued in the child process
    """
    tracer = Tracer()
    tracer.writer = DummyWriter()

    def task(tracer, q):
        tracer.writer = DummyWriter()
        with tracer.trace("child"):
            pass
        spans = tracer.writer.pop()
        q.put(
            [dict(trace_id=s.trace_id, parent_id=s.parent_id) for s in spans])

    # Assert tracer in a new process correctly recreates the writer
    q = multiprocessing.Queue()
    with tracer.trace("parent") as parent:
        p = multiprocessing.Process(target=task, args=(tracer, q))
        p.start()
        p.join()

    children = q.get()
    assert len(children) == 1
    (child, ) = children
    assert parent.trace_id == child["trace_id"]
    assert child["parent_id"] == parent.span_id
Example #4
0
def test_tracer_wrap_class():
    writer = DummyWriter()
    tracer = Tracer()
    tracer.writer = writer

    class Foo(object):
        @staticmethod
        @tracer.wrap()
        def s():
            return 1

        @classmethod
        @tracer.wrap()
        def c(cls):
            return 2

        @tracer.wrap()
        def i(cls):
            return 3

    f = Foo()
    eq_(f.s(), 1)
    eq_(f.c(), 2)
    eq_(f.i(), 3)

    spans = writer.pop()
    eq_(len(spans), 3)
    names = [s.name for s in spans]
    # FIXME[matt] include the class name here.
    eq_(sorted(names),
        sorted(["tests.test_tracer.%s" % n for n in ["s", "c", "i"]]))
    def test_memcached_cache_tracing_with_a_wrong_connection(self):
        # initialize the dummy writer
        writer = DummyWriter()
        tracer = Tracer()
        tracer.writer = writer

        # create the TracedCache instance for a Flask app
        Cache = get_traced_cache(tracer, service=self.SERVICE)
        app = Flask(__name__)
        config = {
            'CACHE_TYPE': 'memcached',
            'CACHE_MEMCACHED_SERVERS': ['localhost:2230'],
        }
        cache = Cache(app, config=config)

        # use a wrong memcached connection
        try:
            cache.get(u'á_complex_operation')
        except Exception:
            pass

        # ensure that the error is not caused by our tracer
        spans = writer.pop()
        assert len(spans) == 1
        span = spans[0]
        assert span.service == self.SERVICE
        assert span.resource == 'get'
        assert span.name == 'flask_cache.cmd'
        assert span.span_type == 'cache'
        assert span.meta[CACHE_BACKEND] == 'memcached'
        assert span.meta[net.TARGET_HOST] == 'localhost'
        assert span.metrics[net.TARGET_PORT] == 2230
    def test_cache_add_without_arguments(self):
        # initialize the dummy writer
        writer = DummyWriter()
        tracer = Tracer()
        tracer.writer = writer

        # create the TracedCache instance for a Flask app
        Cache = get_traced_cache(tracer, service=self.SERVICE)
        app = Flask(__name__)
        cache = Cache(app, config={'CACHE_TYPE': 'simple'})

        # make a wrong call
        with pytest.raises(TypeError) as ex:
            cache.add()

        # ensure that the error is not caused by our tracer
        assert 'add()' in ex.value.args[0]
        assert 'argument' in ex.value.args[0]
        spans = writer.pop()
        # an error trace must be sent
        assert len(spans) == 1
        span = spans[0]
        assert span.service == self.SERVICE
        assert span.resource == 'add'
        assert span.name == 'flask_cache.cmd'
        assert span.span_type == 'cache'
        assert span.error == 1
Example #7
0
    def test_simple_cache_delete_many(self):
        # initialize the dummy writer
        writer = DummyWriter()
        tracer = Tracer()
        tracer.writer = writer

        # create the TracedCache instance for a Flask app
        Cache = get_traced_cache(tracer, service=self.SERVICE)
        app = Flask(__name__)
        cache = Cache(app, config={"CACHE_TYPE": "simple"})

        cache.delete_many("complex_operation", "another_complex_op")
        spans = writer.pop()
        eq_(len(spans), 1)
        span = spans[0]
        eq_(span.service, self.SERVICE)
        eq_(span.resource, "delete_many")
        eq_(span.name, "flask_cache.cmd")
        eq_(span.span_type, "cache")
        eq_(span.error, 0)

        expected_meta = {
            "flask_cache.key": "['complex_operation', 'another_complex_op']",
            "flask_cache.backend": "simple",
        }

        eq_(span.meta, expected_meta)
    def test_redis_cache_tracing_with_a_wrong_connection(self):
        # initialize the dummy writer
        writer = DummyWriter()
        tracer = Tracer()
        tracer.writer = writer

        # create the TracedCache instance for a Flask app
        Cache = get_traced_cache(tracer, service=self.SERVICE)
        app = Flask(__name__)
        config = {
            'CACHE_TYPE': 'redis',
            'CACHE_REDIS_PORT': 2230,
            'CACHE_REDIS_HOST': '127.0.0.1'
        }
        cache = Cache(app, config=config)

        # use a wrong redis connection
        with pytest.raises(ConnectionError) as ex:
            cache.get(u'á_complex_operation')

        # ensure that the error is not caused by our tracer
        assert '127.0.0.1:2230. Connection refused.' in ex.value.args[0]
        spans = writer.pop()
        # an error trace must be sent
        assert len(spans) == 1
        span = spans[0]
        assert span.service == self.SERVICE
        assert span.resource == 'get'
        assert span.name == 'flask_cache.cmd'
        assert span.span_type == 'cache'
        assert span.meta[CACHE_BACKEND] == 'redis'
        assert span.meta[net.TARGET_HOST] == '127.0.0.1'
        assert span.metrics[net.TARGET_PORT] == 2230
        assert span.error == 1
    def test_memcached_cache_tracing_with_a_wrong_connection(self):
        # initialize the dummy writer
        writer = DummyWriter()
        tracer = Tracer()
        tracer.writer = writer

        # create the TracedCache instance for a Flask app
        Cache = get_traced_cache(tracer, service=self.SERVICE)
        app = Flask(__name__)
        config = {
            "CACHE_TYPE": "memcached",
            "CACHE_MEMCACHED_SERVERS": ['localhost:22230'],
        }
        cache = Cache(app, config=config)

        # use a wrong memcached connection
        try:
            cache.get(u"á_complex_operation")
        except Exception:
            pass

        # ensure that the error is not caused by our tracer
        spans = writer.pop()
        eq_(len(spans), 1)
        span = spans[0]
        eq_(span.service, self.SERVICE)
        eq_(span.resource, "get")
        eq_(span.name, "flask_cache.cmd")
        eq_(span.span_type, "cache")
        eq_(span.meta[CACHE_BACKEND], "memcached")
        eq_(span.meta[net.TARGET_HOST], 'localhost')
        eq_(span.meta[net.TARGET_PORT], '22230')
Example #10
0
    def test_simple_cache_add(self):
        # initialize the dummy writer
        writer = DummyWriter()
        tracer = Tracer()
        tracer.writer = writer

        # create the TracedCache instance for a Flask app
        Cache = get_traced_cache(tracer, service=self.SERVICE)
        app = Flask(__name__)
        cache = Cache(app, config={"CACHE_TYPE": "simple"})

        cache.add(u"á_complex_number", 50)
        spans = writer.pop()
        eq_(len(spans), 1)
        span = spans[0]
        eq_(span.service, self.SERVICE)
        eq_(span.resource, "add")
        eq_(span.name, "flask_cache.cmd")
        eq_(span.span_type, "cache")
        eq_(span.error, 0)

        expected_meta = {
            "flask_cache.key": u"á_complex_number",
            "flask_cache.backend": "simple",
        }

        eq_(span.meta, expected_meta)
Example #11
0
    def test_simple_cache_set_many(self):
        # initialize the dummy writer
        writer = DummyWriter()
        tracer = Tracer()
        tracer.writer = writer

        # create the TracedCache instance for a Flask app
        Cache = get_traced_cache(tracer, service=self.SERVICE)
        app = Flask(__name__)
        cache = Cache(app, config={"CACHE_TYPE": "simple"})

        cache.set_many({
            'first_complex_op': 10,
            'second_complex_op': 20,
        })
        spans = writer.pop()
        eq_(len(spans), 1)
        span = spans[0]
        eq_(span.service, self.SERVICE)
        eq_(span.resource, "set_many")
        eq_(span.name, "flask_cache.cmd")
        eq_(span.span_type, "cache")
        eq_(span.error, 0)

        expected_meta = {
            "flask_cache.key": "['first_complex_op', 'second_complex_op']",
            "flask_cache.backend": "simple",
        }

        eq_(span.meta["flask_cache.backend"], "simple")
        ok_("first_complex_op" in span.meta["flask_cache.key"])
        ok_("second_complex_op" in span.meta["flask_cache.key"])
Example #12
0
    def test_long_run(self):
        writer = DummyWriter()
        tracer = Tracer()
        tracer.writer = writer

        # Test a big matrix of combinaisons
        # Ensure to have total_time >> BUFFER_DURATION to reduce edge effects
        for tps in [10, 23, 15, 31]:
            for (traces_per_s, total_time) in [(80, 23), (75, 66), (1000, 77)]:

                with patch_time() as fake_time:
                    # We do tons of operations in this test, do not let the time slowly shift
                    fake_time.set_delta(0)

                    tracer.sampler = ThroughputSampler(tps)

                    for _ in range(total_time):
                        for _ in range(traces_per_s):
                            s = tracer.trace("whatever")
                            s.finish()
                        fake_time.sleep(1)

                traces = writer.pop()
                # The current sampler implementation can introduce an error of up to
                # `tps * BUFFER_DURATION` traces at initialization (since the sampler starts empty)
                got = len(traces)
                expected = tps * total_time
                error_delta = tps * tracer.sampler.BUFFER_DURATION

                assert abs(got - expected) <= error_delta, \
                    "Wrong number of traces sampled, %s instead of %s (error_delta > %s)" % (got, expected, error_delta)
Example #13
0
    def test_simple_limit(self):
        writer = DummyWriter()
        tracer = Tracer()
        tracer.writer = writer

        with patch_time() as fake_time:
            tps = 5
            tracer.sampler = ThroughputSampler(tps)

            for _ in range(10):
                s = tracer.trace("whatever")
                s.finish()
            traces = writer.pop()

            got = len(traces)
            expected = 10

            assert got == expected, \
                "Wrong number of traces sampled, %s instead of %s" % (got, expected)

            # Wait enough to reset
            fake_time.sleep(tracer.sampler.BUFFER_DURATION + 1)

            for _ in range(100):
                s = tracer.trace("whatever")
                s.finish()
            traces = writer.pop()

            got = len(traces)
            expected = tps * tracer.sampler.BUFFER_DURATION

            assert got == expected, \
                "Wrong number of traces sampled, %s instead of %s" % (got, expected)
Example #14
0
    def test_sample_rate_deviation(self):
        writer = DummyWriter()

        for sample_rate in [0.1, 0.25, 0.5, 1]:
            tracer = Tracer()
            tracer.writer = writer

            sample_rate = 0.5
            tracer.sampler = RateSampler(sample_rate)

            random.seed(1234)

            iterations = int(2e4)

            for i in range(iterations):
                span = tracer.trace(i)
                span.finish()

            samples = writer.pop()

            # We must have at least 1 sample, check that it has its sample rate properly assigned
            assert samples[0].get_metric(SAMPLE_RATE_METRIC_KEY) == 0.5

            # Less than 1% deviation when "enough" iterations (arbitrary, just check if it converges)
            deviation = abs(len(samples) -
                            (iterations * sample_rate)) / (iterations *
                                                           sample_rate)
            assert deviation < 0.01, "Deviation too high %f with sample_rate %f" % (
                deviation, sample_rate)
Example #15
0
def gen_trace(nspans=1000, ntags=50, key_size=15, value_size=20, nmetrics=10):
    t = Tracer()

    root = None
    trace = []
    for i in range(0, nspans):
        parent_id = root.span_id if root else None
        with Span(
            t,
            "span_name",
            resource="/fsdlajfdlaj/afdasd%s" % i,
            service="myservice",
            parent_id=parent_id,
        ) as span:
            span._parent = root
            span.set_tags({rands(key_size): rands(value_size) for _ in range(0, ntags)})

            # only apply a span type to the root span
            if not root:
                span.span_type = "web"

            for _ in range(0, nmetrics):
                span.set_tag(rands(key_size), random.randint(0, 2 ** 16))

            trace.append(span)

            if not root:
                root = span

    return trace
Example #16
0
 def test_detect_agentless_env_with_lambda(self):
     assert _in_aws_lambda()
     assert not _has_aws_lambda_agent_extension()
     tracer = Tracer()
     assert isinstance(tracer.writer, LogWriter)
     tracer.configure(enabled=True)
     assert isinstance(tracer.writer, LogWriter)
Example #17
0
    def test_redis_cache_tracing_with_a_wrong_connection(self):
        # initialize the dummy writer
        writer = DummyWriter()
        tracer = Tracer()
        tracer.writer = writer

        # create the TracedCache instance for a Flask app
        Cache = get_traced_cache(tracer, service=self.SERVICE)
        app = Flask(__name__)
        config = {
            "CACHE_TYPE": "redis",
            "CACHE_REDIS_PORT": 22230,
        }
        cache = Cache(app, config=config)

        # use a wrong redis connection
        with assert_raises(ConnectionError) as ex:
            cache.get(u"á_complex_operation")

        # ensure that the error is not caused by our tracer
        ok_("localhost:22230. Connection refused." in ex.exception.args[0])
        spans = writer.pop()
        # an error trace must be sent
        eq_(len(spans), 1)
        span = spans[0]
        eq_(span.service, self.SERVICE)
        eq_(span.resource, "get")
        eq_(span.name, "flask_cache.cmd")
        eq_(span.span_type, "cache")
        eq_(span.meta[CACHE_BACKEND], "redis")
        eq_(span.meta[net.TARGET_HOST], 'localhost')
        eq_(span.meta[net.TARGET_PORT], '22230')
        eq_(span.error, 1)
Example #18
0
    def test_simple_cache_set(self):
        # initialize the dummy writer
        writer = DummyWriter()
        tracer = Tracer()
        tracer.writer = writer

        # create the TracedCache instance for a Flask app
        Cache = get_traced_cache(tracer, service=self.SERVICE)
        app = Flask(__name__)
        cache = Cache(app, config={"CACHE_TYPE": "simple"})

        cache.set(u"á_complex_operation", u"with_á_value\nin two lines")
        spans = writer.pop()
        eq_(len(spans), 1)
        span = spans[0]
        eq_(span.service, self.SERVICE)
        eq_(span.resource, "set")
        eq_(span.name, "flask_cache.cmd")
        eq_(span.span_type, "cache")
        eq_(span.error, 0)

        expected_meta = {
            "flask_cache.key": u"á_complex_operation",
            "flask_cache.backend": "simple",
        }

        assert_dict_issuperset(span.meta, expected_meta)
Example #19
0
    def test_cache_add_without_arguments(self):
        # initialize the dummy writer
        writer = DummyWriter()
        tracer = Tracer()
        tracer.writer = writer

        # create the TracedCache instance for a Flask app
        Cache = get_traced_cache(tracer, service=self.SERVICE)
        app = Flask(__name__)
        cache = Cache(app, config={"CACHE_TYPE": "simple"})

        # make a wrong call
        with assert_raises(TypeError) as ex:
            cache.add()

        # ensure that the error is not caused by our tracer
        ok_("add()" in ex.exception.args[0])
        ok_("argument" in ex.exception.args[0])
        spans = writer.pop()
        # an error trace must be sent
        eq_(len(spans), 1)
        span = spans[0]
        eq_(span.service, self.SERVICE)
        eq_(span.resource, "add")
        eq_(span.name, "flask_cache.cmd")
        eq_(span.span_type, "cache")
        eq_(span.error, 1)
Example #20
0
def test_tracer_wrap_factory():
    # it should use a wrap_factory if defined
    writer = DummyWriter()
    tracer = Tracer()
    tracer.writer = writer

    def wrap_executor(tracer,
                      fn,
                      args,
                      kwargs,
                      span_name=None,
                      service=None,
                      resource=None,
                      span_type=None):
        with tracer.trace('wrap.overwrite') as span:
            span.set_tag('args', args)
            span.set_tag('kwargs', kwargs)
            return fn(*args, **kwargs)

    @tracer.wrap()
    def wrapped_function(param, kw_param=None):
        eq_(42, param)
        eq_(42, kw_param)

    # set the custom wrap factory after the wrapper has been called
    tracer.configure(wrap_executor=wrap_executor)

    # call the function expecting that the custom tracing wrapper is used
    wrapped_function(42, kw_param=42)
    eq_(writer.spans[0].name, 'wrap.overwrite')
    eq_(writer.spans[0].get_tag('args'), '(42,)')
    eq_(writer.spans[0].get_tag('kwargs'), '{\'kw_param\': 42}')
Example #21
0
def test_tracer_pid():
    writer = DummyWriter()
    tracer = Tracer()
    tracer.writer = writer
    with tracer.trace("root") as root_span:
        with tracer.trace("child") as child_span:
            time.sleep(0.05)
    eq_(root_span.get_tag(system.PID), str(getpid())) # Root span should contain the pid of the current process
    eq_(child_span.get_tag(system.PID), None) # Child span should not contain a pid tag
Example #22
0
 def test_configure_keeps_api_hostname_and_port(self):
     tracer = Tracer()  # use real tracer with real api
     assert 'localhost' == tracer.writer.api.hostname
     assert 8126 == tracer.writer.api.port
     tracer.configure(hostname='127.0.0.1', port=8127)
     assert '127.0.0.1' == tracer.writer.api.hostname
     assert 8127 == tracer.writer.api.port
     tracer.configure(priority_sampling=True)
     assert '127.0.0.1' == tracer.writer.api.hostname
     assert 8127 == tracer.writer.api.port
Example #23
0
 def setUp(self):
     """
     Create a tracer with running workers, while spying the ``_put()`` method to
     keep trace of triggered API calls.
     """
     # create a new tracer
     self.tracer = Tracer()
     # spy the send() method
     self.api = self.tracer.writer.api
     self.api._put = mock.Mock(self.api._put, wraps=self.api._put)
Example #24
0
 def test_configure_keeps_api_hostname_and_port(self):
     tracer = Tracer()  # use real tracer with real api
     eq_('localhost', tracer.writer.api.hostname)
     eq_(8126, tracer.writer.api.port)
     tracer.configure(hostname='127.0.0.1', port=8127)
     eq_('127.0.0.1', tracer.writer.api.hostname)
     eq_(8127, tracer.writer.api.port)
     tracer.configure(priority_sampling=True)
     eq_('127.0.0.1', tracer.writer.api.hostname)
     eq_(8127, tracer.writer.api.port)
Example #25
0
 def test_resource_from_cache_without_prefix(self):
     # create the TracedCache instance for a Flask app
     tracer = Tracer()
     Cache = get_traced_cache(tracer, service=self.SERVICE)
     app = Flask(__name__)
     traced_cache = Cache(app, config={"CACHE_TYPE": "redis"})
     # expect only the resource name
     expected_resource = "get"
     resource = _resource_from_cache_prefix("GET", traced_cache.config)
     assert resource == expected_resource
Example #26
0
def test_tracer_wrap_default_name():
    writer = DummyWriter()
    tracer = Tracer()
    tracer.writer = writer

    @tracer.wrap()
    def f():
        pass
    f()

    eq_(writer.spans[0].name, 'tests.test_tracer.f')
Example #27
0
def test_manual_keep_then_drop():
    tracer = Tracer()
    tracer.writer = DummyWriter()

    # Test changing the value before finish.
    with tracer.trace("asdf") as root:
        with tracer.trace("child") as child:
            child.set_tag(MANUAL_KEEP_KEY)
        root.set_tag(MANUAL_DROP_KEY)
    spans = tracer.writer.pop()
    assert spans[0].metrics[SAMPLING_PRIORITY_KEY] is priority.USER_REJECT
Example #28
0
def test_bad_agent_url(monkeypatch):
    with pytest.raises(ValueError):
        Tracer(url="bad://localhost:8126")

    monkeypatch.setenv("DD_TRACE_AGENT_URL", "bad://localhost:1234")
    with pytest.raises(ValueError) as e:
        Tracer()
    assert (
        str(e.value) ==
        "Unsupported protocol 'bad' in Agent URL 'bad://localhost:1234'. Must be one of: http, https, unix"
    )

    monkeypatch.setenv("DD_TRACE_AGENT_URL", "unix://")
    with pytest.raises(ValueError) as e:
        Tracer()
    assert str(e.value) == "Invalid file path in Agent URL 'unix://'"

    monkeypatch.setenv("DD_TRACE_AGENT_URL", "http://")
    with pytest.raises(ValueError) as e:
        Tracer()
    assert str(e.value) == "Invalid hostname in Agent URL 'http://'"
Example #29
0
 def test_default_span_tags(self):
     # create the TracedCache instance for a Flask app
     tracer = Tracer()
     Cache = get_traced_cache(tracer, service=self.SERVICE)
     app = Flask(__name__)
     cache = Cache(app, config={"CACHE_TYPE": "simple"})
     # test tags and attributes
     with cache._TracedCache__trace("flask_cache.cmd") as span:
         eq_(span.service, cache._datadog_service)
         eq_(span.span_type, TYPE)
         eq_(span.meta[CACHE_BACKEND], "simple")
         ok_(net.TARGET_HOST not in span.meta)
         ok_(net.TARGET_PORT not in span.meta)
Example #30
0
def test_tracer_wrap_exception():
    writer = DummyWriter()
    tracer = Tracer()
    tracer.writer = writer

    @tracer.wrap()
    def f():
        raise Exception('bim')

    assert_raises(Exception, f)

    eq_(len(writer.spans), 1)
    eq_(writer.spans[0].error, 1)