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)
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) 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"])
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_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)
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')
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", } assert_dict_issuperset(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_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}')
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)
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 assert_raises(ConnectionError) as ex: cache.get(u"á_complex_operation") print(ex.exception) # ensure that the error is not caused by our tracer ok_("127.0.0.1:2230. 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], '127.0.0.1') eq_(span.meta[net.TARGET_PORT], '2230') eq_(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: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() 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], '2230')
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"])
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_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)
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)
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)
def test_tracer_wrap_factory_nested(): # it should use a wrap_factory if defined even in nested tracing 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 with tracer.trace('wrap.parent', service='webserver'): wrapped_function(42, kw_param=42) eq_(writer.spans[0].name, 'wrap.parent') eq_(writer.spans[0].service, 'webserver') eq_(writer.spans[1].name, 'wrap.overwrite') eq_(writer.spans[1].service, 'webserver') eq_(writer.spans[1].get_tag('args'), '(42,)') eq_(writer.spans[1].get_tag('kwargs'), '{\'kw_param\': 42}')
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)
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)
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
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_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
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
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
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')
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
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')
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)
def setUp(self): tracer = Tracer() tracer.writer = DummyWriter() self.tracer = tracer # Backup the old conf self.backupTracer = settings.TRACER self.backupEnabled = settings.ENABLED # Disable tracing settings.ENABLED = False settings.TRACER = tracer # Restart the app app = apps.get_app_config('datadog_django') app.ready()
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)
def test_tracer(): # add some dummy tracing code. writer = DummyWriter() tracer = Tracer() tracer.writer = writer sleep = 0.05 def _mix(): with tracer.trace("cake.mix"): time.sleep(sleep) def _bake(): with tracer.trace("cake.bake"): time.sleep(sleep) def _make_cake(): with tracer.trace("cake.make") as span: span.service = "baker" span.resource = "cake" _mix() _bake() # let's run it and make sure all is well. assert not writer.spans _make_cake() spans = writer.pop() assert spans, "%s" % spans eq_(len(spans), 3) spans_by_name = {s.name:s for s in spans} eq_(len(spans_by_name), 3) make = spans_by_name["cake.make"] assert make.span_id assert make.parent_id is None assert make.trace_id for other in ["cake.mix", "cake.bake"]: s = spans_by_name[other] eq_(s.parent_id, make.span_id) eq_(s.trace_id, make.trace_id) eq_(s.service, make.service) # ensure it inherits the service eq_(s.resource, s.name) # ensure when we don't set a resource, it's there. # do it again and make sure it has new trace ids _make_cake() spans = writer.pop() for s in spans: assert s.trace_id != make.trace_id
def test_tracer(): # add some dummy tracing code. writer = DummyWriter() tracer = Tracer() tracer.writer = writer sleep = 0.05 def _mix(): with tracer.trace("cake.mix"): time.sleep(sleep) def _bake(): with tracer.trace("cake.bake"): time.sleep(sleep) def _make_cake(): with tracer.trace("cake.make") as span: span.service = "baker" span.resource = "cake" _mix() _bake() # let's run it and make sure all is well. assert not writer.spans _make_cake() spans = writer.pop() assert spans, "%s" % spans eq_(len(spans), 3) spans_by_name = {s.name: s for s in spans} eq_(len(spans_by_name), 3) make = spans_by_name["cake.make"] assert make.span_id assert make.parent_id is None assert make.trace_id for other in ["cake.mix", "cake.bake"]: s = spans_by_name[other] eq_(s.parent_id, make.span_id) eq_(s.trace_id, make.trace_id) eq_(s.service, make.service) # ensure it inherits the service eq_(s.resource, s.name) # ensure when we don't set a resource, it's there. # do it again and make sure it has new trace ids _make_cake() spans = writer.pop() for s in spans: assert s.trace_id != make.trace_id
def test_tracer_disabled_mem_leak(): # ensure that if the tracer is disabled, we still remove things from the # span buffer upon finishing. writer = DummyWriter() tracer = Tracer() tracer.writer = writer tracer.enabled = False s1 = tracer.trace("foo") s1.finish() p1 = tracer.current_span() s2 = tracer.trace("bar") assert not s2._parent, s2._parent s2.finish() assert not p1, p1
def test_get_report_hostname_default(get_hostname): get_hostname.return_value = "test-hostname" tracer = Tracer() tracer.writer = DummyWriter() with override_global_config(dict(report_hostname=False)): with tracer.trace("span"): with tracer.trace("child"): pass spans = tracer.writer.pop() root = spans[0] child = spans[1] assert root.get_tag(HOSTNAME_KEY) is None assert child.get_tag(HOSTNAME_KEY) is None
def test_tracer_wrap_multiple_calls(): # Make sure that we create a new span each time the function is called writer = DummyWriter() tracer = Tracer() tracer.writer = writer @tracer.wrap() def f(): pass f() f() spans = writer.pop() eq_(len(spans), 2) assert spans[0].span_id != spans[1].span_id
def test_unserializable_span_with_finish(): try: import numpy as np except ImportError: raise SkipTest("numpy not installed") # a weird case where manually calling finish with an unserializable # span was causing an loop of serialization. writer = DummyWriter() tracer = Tracer() tracer.writer = writer with tracer.trace("parent") as span: span.metrics['as'] = np.int64(1) # circumvent the data checks span.finish()
def test_tracer_disabled(): # add some dummy tracing code. writer = DummyWriter() tracer = Tracer() tracer.writer = writer tracer.enabled = True with tracer.trace("foo") as s: s.set_tag("a", "b") assert writer.pop() tracer.enabled = False with tracer.trace("foo") as s: s.set_tag("a", "b") assert not writer.pop()
def test_tracer_wrap_multiple_calls(): # Make sure that we create a new span each time the function is called writer = DummyWriter() tracer = Tracer() tracer.writer = writer @tracer.wrap() def f(): pass f() f() spans = writer.pop() eq_(len(spans), 2) assert spans[0].span_id != spans[1].span_id
def test_tracer_disabled(): # add some dummy tracing code. writer = DummyWriter() tracer = Tracer() tracer.writer = writer tracer.enabled = True with tracer.trace("foo") as s: s.set_tag("a", "b") assert writer.pop() tracer.enabled = False with tracer.trace("foo") as s: s.set_tag("a", "b") assert not writer.pop()
def test_tracer_disabled_mem_leak(): # ensure that if the tracer is disabled, we still remove things from the # span buffer upon finishing. writer = DummyWriter() tracer = Tracer() tracer.writer = writer tracer.enabled = False s1 = tracer.trace("foo") s1.finish() p1 = tracer.current_span() s2 = tracer.trace("bar") assert not s2._parent, s2._parent s2.finish() assert not p1, p1
def test_unserializable_span_with_finish(): try: import numpy as np except ImportError: raise SkipTest("numpy not installed") # a weird case where manually calling finish with an unserializable # span was causing an loop of serialization. writer = DummyWriter() tracer = Tracer() tracer.writer = writer with tracer.trace("parent") as span: span.metrics['as'] = np.int64(1) # circumvent the data checks span.finish()
def test_manual_drop(): tracer = Tracer() tracer.writer = DummyWriter() # On a root span with tracer.trace("asdf") as s: s.set_tag(MANUAL_DROP_KEY) spans = tracer.writer.pop() assert spans[0].metrics[SAMPLING_PRIORITY_KEY] is priority.USER_REJECT # On a child span with tracer.trace("asdf"): with tracer.trace("child") as s: s.set_tag(MANUAL_DROP_KEY) spans = tracer.writer.pop() assert spans[0].metrics[SAMPLING_PRIORITY_KEY] is priority.USER_REJECT
def test_tracer_vars(): writer = DummyWriter() tracer = Tracer() tracer.writer = writer # explicit vars s = tracer.trace("a", service="s", resource="r", span_type="t") eq_(s.service, "s") eq_(s.resource, "r") eq_(s.span_type, "t") s.finish() # defaults s = tracer.trace("a") eq_(s.service, None) eq_(s.resource, "a") # inherits eq_(s.span_type, None)
def test_tracer_vars(): writer = DummyWriter() tracer = Tracer() tracer.writer = writer # explicit vars s = tracer.trace("a", service="s", resource="r", span_type="t") eq_(s.service, "s") eq_(s.resource, "r") eq_(s.span_type, "t") s.finish() # defaults s = tracer.trace("a") eq_(s.service, None) eq_(s.resource, "a") # inherits eq_(s.span_type, None)
def test_tracer_global_tags(): writer = DummyWriter() tracer = Tracer() tracer.writer = writer s1 = tracer.trace('brie') s1.finish() assert not s1.meta tracer.set_tags({'env': 'prod'}) s2 = tracer.trace('camembert') s2.finish() assert s2.meta == {'env': 'prod'} tracer.set_tags({'env': 'staging', 'other': 'tag'}) s3 = tracer.trace('gruyere') s3.finish() assert s3.meta == {'env': 'staging', 'other': 'tag'}
def test_tracer_global_tags(): writer = DummyWriter() tracer = Tracer() tracer.writer = writer s1 = tracer.trace('brie') s1.finish() assert not s1.get_tag('env') assert not s1.get_tag('other') tracer.set_tags({'env': 'prod'}) s2 = tracer.trace('camembert') s2.finish() assert s2.get_tag('env') == 'prod' assert not s2.get_tag('other') tracer.set_tags({'env': 'staging', 'other': 'tag'}) s3 = tracer.trace('gruyere') s3.finish() assert s3.get_tag('env') == 'staging' assert s3.get_tag('other') == 'tag'
def test_tracer_wrap(): writer = DummyWriter() tracer = Tracer() tracer.writer = writer @tracer.wrap('decorated_function', service='s', resource='r', span_type='t') def f(tag_name, tag_value): # make sure we can still set tags span = tracer.current_span() span.set_tag(tag_name, tag_value) f('a', 'b') spans = writer.pop() eq_(len(spans), 1) s = spans[0] eq_(s.name, 'decorated_function') eq_(s.service, 's') eq_(s.resource, 'r') eq_(s.span_type, 't') eq_(s.to_dict()['meta']['a'], 'b')
def test_tracer_wrap_span_nesting(): # Make sure that nested spans have the correct parents writer = DummyWriter() tracer = Tracer() tracer.writer = writer @tracer.wrap('inner') def inner(): pass @tracer.wrap('outer') def outer(): with tracer.trace('mid'): inner() outer() spans = writer.pop() eq_(len(spans), 3) # sift through the list so we're not dependent on span ordering within the # writer for span in spans: if span.name == 'outer': outer_span = span elif span.name == 'mid': mid_span = span elif span.name == 'inner': inner_span = span else: assert False, 'unknown span found' # should never get here assert outer_span assert mid_span assert inner_span eq_(outer_span.parent_id, None) eq_(mid_span.parent_id, outer_span.span_id) eq_(inner_span.parent_id, mid_span.span_id)
def get_dummy_tracer(): tracer = Tracer() tracer.writer = DummyWriter() return tracer