def tracer_and_collector(): t = ddtrace.Tracer() r = recorder.Recorder() c = stack.StackCollector(r, tracer=t) c.start() try: yield t, c finally: c.stop()
def test_tracer_shutdown_timeout(): t = ddtrace.Tracer() t.writer = mock.Mock(wraps=t.writer) with t.trace("something"): pass t.shutdown(timeout=2) t.writer.stop.assert_called_once_with(timeout=2)
def test_tracer_info_level_log(self): logging.basicConfig(level=logging.INFO) tracer = ddtrace.Tracer() tracer.log = mock.MagicMock() tracer.configure() assert tracer.log.log.mock_calls == [ mock.call(logging.INFO, re_matcher("- DATADOG TRACER CONFIGURATION - ")) ]
def test_tags_from_DD_TAGS_override(self): t = ddtrace.Tracer() ddtrace.config.env = "env" ddtrace.config.service = "service" ddtrace.config.version = "0.123" with t.trace("test") as s: assert s.service == "service" assert s.get_tag("env") == "env" assert s.get_tag("version") == "0.123"
def test_ctx(): tracer = ddtrace.Tracer() tracer.writer = DummyWriter() with tracer.trace("test") as s1: assert tracer.current_span() == s1 assert tracer.current_root_span() == s1 assert tracer.get_call_context().trace_id == s1.trace_id assert tracer.get_call_context().span_id == s1.span_id with tracer.trace("test2") as s2: assert tracer.current_span() == s2 assert tracer.current_root_span() == s1 assert tracer.get_call_context().trace_id == s1.trace_id assert tracer.get_call_context().span_id == s2.span_id with tracer.trace("test3") as s3: assert tracer.current_span() == s3 assert tracer.current_root_span() == s1 assert tracer.get_call_context().trace_id == s1.trace_id assert tracer.get_call_context().span_id == s3.span_id assert tracer.get_call_context().trace_id == s1.trace_id assert tracer.get_call_context().span_id == s2.span_id with tracer.trace("test4") as s4: assert tracer.current_span() == s4 assert tracer.current_root_span() == s1 assert tracer.get_call_context().trace_id == s1.trace_id assert tracer.get_call_context().span_id == s4.span_id assert tracer.current_span() == s1 assert tracer.current_root_span() == s1 assert tracer.current_span() is None assert tracer.current_root_span() is None assert s1.parent_id is None assert s2.parent_id == s1.span_id assert s3.parent_id == s2.span_id assert s4.parent_id == s1.span_id assert s1.trace_id == s2.trace_id == s3.trace_id == s4.trace_id assert s1.metrics[SAMPLING_PRIORITY_KEY] == 1 assert SAMPLING_PRIORITY_KEY not in s2.metrics assert ORIGIN_KEY not in s1.meta t = tracer.writer.pop_traces() assert len(t) == 1 assert len(t[0]) == 4 _s1, _s2, _s3, _s4 = t[0] assert s1 == _s1 assert s2 == _s2 assert s3 == _s3 assert s4 == _s4 with tracer.trace("s") as s: assert s.parent_id is None assert s.trace_id != s1.trace_id
def tracer_and_collector(monkeypatch): monkeypatch.setenv("DD_TRACE_STARTUP_LOGS", "0") t = ddtrace.Tracer() r = recorder.Recorder() c = stack.StackCollector(r, tracer=t) c.start() try: yield t, c finally: c.stop()
def test_tracer_loglevel_info_no_connection(self): tracer = ddtrace.Tracer() tracer.log = mock.MagicMock() tracer.configure() # Python 2 logs will go to stderr directly since there's no log handler if ddtrace.compat.PY3: assert tracer.log.log.mock_calls == [ mock.call(logging.INFO, re_matcher("- DATADOG TRACER CONFIGURATION - ")), mock.call(logging.WARNING, re_matcher("- DATADOG TRACER DIAGNOSTIC - ")), ]
def test_tracer_configure_writer_stop_started(): t = ddtrace.Tracer() t.writer = mock.Mock(wraps=t.writer) orig_writer = t.writer # Do a write to start the writer with t.trace("something"): pass t.configure(hostname="localhost", port=8126) orig_writer.stop.assert_called_once_with()
def test_start_span_hooks(): t = ddtrace.Tracer() result = {} @t.on_start_span def store_span(span): result["span"] = span span = t.start_span("hello") assert span == result["span"]
def test_tracer_loglevel_info_no_connection_py2_handler(self): tracer = ddtrace.Tracer() tracer.log = mock.MagicMock() logging.basicConfig() tracer.configure() if ddtrace.compat.PY2: assert tracer.log.log.mock_calls == [ mock.call(logging.INFO, re_matcher("- DATADOG TRACER CONFIGURATION - ")), mock.call(logging.WARNING, re_matcher("- DATADOG TRACER DIAGNOSTIC - ")), ]
def test_tracer_url(): t = ddtrace.Tracer() assert t.writer.api.hostname == 'localhost' assert t.writer.api.port == 8126 t = ddtrace.Tracer(url='http://foobar:12') assert t.writer.api.hostname == 'foobar' assert t.writer.api.port == 12 t = ddtrace.Tracer(url='unix:///foobar') assert t.writer.api.uds_path == '/foobar' t = ddtrace.Tracer(url='http://localhost') assert t.writer.api.hostname == 'localhost' assert t.writer.api.port == 80 assert not t.writer.api.https t = ddtrace.Tracer(url='https://localhost') assert t.writer.api.hostname == 'localhost' assert t.writer.api.port == 443 assert t.writer.api.https with pytest.raises(ValueError) as e: t = ddtrace.Tracer(url='foo://foobar:12') assert str(e) == 'Unknown scheme `https` for agent URL'
def test_tracer_url(): t = ddtrace.Tracer() assert t.writer._hostname == "localhost" assert t.writer._port == 8126 t = ddtrace.Tracer(url="http://foobar:12") assert t.writer._hostname == "foobar" assert t.writer._port == 12 t = ddtrace.Tracer(url="unix:///foobar") assert t.writer._uds_path == "/foobar" t = ddtrace.Tracer(url="http://localhost") assert t.writer._hostname == "localhost" assert t.writer._port == 80 assert not t.writer._https t = ddtrace.Tracer(url="https://localhost") assert t.writer._hostname == "localhost" assert t.writer._port == 443 assert t.writer._https with pytest.raises(ValueError) as e: t = ddtrace.Tracer(url="foo://foobar:12") assert str(e) == "Unknown scheme `https` for agent URL"
def test_tracer_loglevel_info_no_connection(self): tracer = ddtrace.Tracer() logging.basicConfig(level=logging.INFO) with mock.patch.object(logging.Logger, "log") as mock_logger: tracer.configure() # Python 2 logs will go to stderr directly since there's no log handler if PY3: assert mock.call(logging.INFO, re_matcher("- DATADOG TRACER CONFIGURATION - ") ) in mock_logger.mock_calls assert mock.call(logging.WARNING, re_matcher("- DATADOG TRACER DIAGNOSTIC - ") ) in mock_logger.mock_calls
def test_tracer_trace_across_multiple_forks(): """ When a trace is started and crosses multiple process boundaries The trace should be continued in the child processes """ tracer = ddtrace.Tracer() tracer.writer = DummyWriter() # Start a span in this process then start a child process which itself # starts a span and spawns another child process which starts a span. def task(tracer, q): tracer.writer = DummyWriter() def task2(tracer, q): tracer.writer = DummyWriter() with tracer.trace("child2"): pass spans = tracer.writer.pop() q.put([ dict(trace_id=s.trace_id, parent_id=s.parent_id) for s in spans ]) with tracer.trace("child1"): q2 = multiprocessing.Queue() p = multiprocessing.Process(target=task2, args=(tracer, q2)) p.start() p.join() task2_spans = q2.get() spans = tracer.writer.pop() q.put([ dict(trace_id=s.trace_id, parent_id=s.parent_id, span_id=s.span_id) for s in spans ] + task2_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) == 2 child1, child2 = children assert parent.trace_id == child1["trace_id"] == child2["trace_id"] assert child1["parent_id"] == parent.span_id assert child2["parent_id"] == child1["span_id"]
def test_deregister_start_span_hooks(): t = ddtrace.Tracer() result = {} @t.on_start_span def store_span(span): result["span"] = span t.deregister_on_start_span(store_span) t.start_span("hello") assert result == {}
def test_tracer_shutdown_no_timeout(): t = ddtrace.Tracer() t.writer = mock.Mock(wraps=t.writer) # The writer thread does not start until the first write. t.shutdown() assert not t.writer.stop.called assert not t.writer.join.called # Do a write to start the writer. with t.trace("something"): pass t.shutdown() t.writer.stop.assert_called_once_with() t.writer.join.assert_called_once_with(timeout=None)
def test_startup_logs_sampling_rules(): tracer = ddtrace.Tracer() sampler = ddtrace.sampler.DatadogSampler(rules=[ddtrace.sampler.SamplingRule(sample_rate=1.0)]) tracer.configure(sampler=sampler) f = debug.collect(tracer) assert f.get("sampler_rules") == ["SamplingRule(sample_rate=1.0, service='NO_RULE', name='NO_RULE')"] sampler = ddtrace.sampler.DatadogSampler( rules=[ddtrace.sampler.SamplingRule(sample_rate=1.0, service="xyz", name="abc")] ) tracer.configure(sampler=sampler) f = debug.collect(tracer) assert f.get("sampler_rules") == ["SamplingRule(sample_rate=1.0, service='xyz', name='abc')"]
def test_partial_flush_log(): tracer = ddtrace.Tracer() tracer.configure( partial_flush_enabled=True, partial_flush_min_spans=300, ) f = debug.collect(tracer) partial_flush_enabled = f.get("partial_flush_enabled") partial_flush_min_spans = f.get("partial_flush_min_spans") assert partial_flush_enabled is True assert partial_flush_min_spans == 300
def test_tracer_fork(): t = ddtrace.Tracer() original_pid = t._pid original_writer = t.writer @contextlib.contextmanager def capture_failures(errors): try: yield except AssertionError as e: errors.put(e) def task(t, errors): # Start a new span to trigger process checking with t.trace('test', service='test') as span: # Assert we recreated the writer and have a new queue with capture_failures(errors): assert t._pid != original_pid assert t.writer != original_writer assert t.writer._trace_queue != original_writer._trace_queue # Assert the trace got written into the correct queue assert original_writer._trace_queue.qsize() == 0 assert t.writer._trace_queue.qsize() == 1 assert [[span]] == list(t.writer._trace_queue.get()) # Assert tracer in a new process correctly recreates the writer errors = multiprocessing.Queue() p = multiprocessing.Process(target=task, args=(t, errors)) try: p.start() finally: p.join(timeout=2) while errors.qsize() > 0: raise errors.get() # Ensure writing into the tracer in this process still works as expected with t.trace('test', service='test') as span: assert t._pid == original_pid assert t.writer == original_writer assert t.writer._trace_queue == original_writer._trace_queue # Assert the trace got written into the correct queue assert original_writer._trace_queue.qsize() == 1 assert t.writer._trace_queue.qsize() == 1 assert [[span]] == list(t.writer._trace_queue.get())
def test_early_exit(): t = ddtrace.Tracer() s1 = t.trace("1") s2 = t.trace("2") s1.finish() s2.finish() assert s1.parent_id is None assert s2.parent_id is s1.span_id s1 = t.trace("1-1") s1.finish() assert s1.parent_id is None s1 = t.trace("1-2") s1.finish() assert s1.parent_id is None
def test_tracer_runtime_tags_fork(): tracer = ddtrace.Tracer() def task(tracer, q): span = tracer.start_span("foobaz") q.put(span.get_tag("runtime-id")) span = tracer.start_span("foobar") q = multiprocessing.Queue() p = multiprocessing.Process(target=task, args=(tracer, q)) p.start() p.join() children_tag = q.get() assert children_tag != span.get_tag("runtime-id")
def test_tracer_fork(): t = ddtrace.Tracer() original_pid = t._pid original_writer = t.writer @contextlib.contextmanager def capture_failures(errors): try: yield except AssertionError as e: errors.put(e) def task(t, errors): # Start a new span to trigger process checking with t.trace("test", service="test"): # Assert we recreated the writer and have a new queue with capture_failures(errors): assert t._pid != original_pid assert t.writer != original_writer assert t.writer._buffer != original_writer._buffer # Assert the trace got written into the correct queue assert len(original_writer._buffer) == 0 assert len(t.writer._buffer) == 1 # Assert tracer in a new process correctly recreates the writer errors = multiprocessing.Queue() p = multiprocessing.Process(target=task, args=(t, errors)) try: p.start() finally: p.join(timeout=2) assert errors.empty(), errors.get() # Ensure writing into the tracer in this process still works as expected with t.trace("test", service="test"): assert t._pid == original_pid assert t.writer == original_writer assert t.writer._buffer == original_writer._buffer # Assert the trace got written into the correct queue assert len(original_writer._buffer) == 1 assert len(t.writer._buffer) == 1
def test_runtime_id_parent_only(): tracer = ddtrace.Tracer() # Parent spans should have runtime-id s = tracer.trace("test") rtid = s.get_tag("runtime-id") assert isinstance(rtid, six.string_types) # Child spans should not s2 = tracer.trace("test2") assert s2.get_tag("runtime-id") is None s2.finish() s.finish() # Parent spans should have runtime-id s = tracer.trace("test") rtid = s.get_tag("runtime-id") assert isinstance(rtid, six.string_types)
def test_custom_writer(): tracer = ddtrace.Tracer() class CustomWriter(TraceWriter): def recreate(self): # type: () -> TraceWriter return self def stop(self, timeout=None): # type: (Optional[float]) -> None pass def write(self, spans=None): # type: (Optional[List[Span]]) -> None pass tracer.writer = CustomWriter() info = debug.collect(tracer) assert info.get("agent_url") == "CUSTOM"
def test_early_exit(): t = ddtrace.Tracer() t.writer = DummyWriter() s1 = t.trace("1") s2 = t.trace("2") s1.finish() s2.finish() assert s1.parent_id is None assert s2.parent_id is s1.span_id traces = t.writer.pop_traces() assert len(traces) == 1 assert len(traces[0]) == 2 s1 = t.trace("1-1") s1.finish() assert s1.parent_id is None s1 = t.trace("1-2") s1.finish() assert s1.parent_id is None
def test_ctx_distributed(): tracer = ddtrace.Tracer() tracer.writer = DummyWriter() # Test activating an invalid context. ctx = Context(span_id=None, trace_id=None) tracer.context_provider.activate(ctx) assert tracer.current_span() is None with tracer.trace("test") as s1: assert tracer.current_span() == s1 assert tracer.current_root_span() == s1 assert tracer.get_call_context().trace_id == s1.trace_id assert tracer.get_call_context().span_id == s1.span_id assert s1.parent_id is None trace = tracer.writer.pop_traces() assert len(trace) == 1 # Test activating a valid context. ctx = Context(span_id=1234, trace_id=4321, sampling_priority=2, _dd_origin="somewhere") tracer.context_provider.activate(ctx) assert tracer.current_span() is None with tracer.trace("test2") as s2: assert tracer.current_span() == s2 assert tracer.current_root_span() == s2 assert tracer.get_call_context().trace_id == s2.trace_id == 4321 assert tracer.get_call_context().span_id == s2.span_id assert s2.parent_id == 1234 trace = tracer.writer.pop_traces() assert len(trace) == 1 assert s2.metrics[SAMPLING_PRIORITY_KEY] == 2 assert s2.meta[ORIGIN_KEY] == "somewhere"
def test_excepthook(): class Foobar(Exception): pass called = {} def original(type, value, traceback): called['yes'] = True sys.excepthook = original ddtrace.install_excepthook() e = Foobar() tracer = ddtrace.Tracer() tracer._dogstatsd_client = mock.Mock() with override_global_tracer(tracer): sys.excepthook(e.__class__, e, None) tracer._dogstatsd_client.increment.assert_has_calls(( mock.call('datadog.tracer.uncaught_exceptions', 1, tags=['class:Foobar']), )) assert called
def test_runtime_id_fork(): tracer = ddtrace.Tracer() s = tracer.trace("test") s.finish() rtid = s.get_tag("runtime-id") assert isinstance(rtid, six.string_types) pid = os.fork() if pid == 0: # child s = tracer.trace("test") s.finish() rtid_child = s.get_tag("runtime-id") assert isinstance(rtid_child, six.string_types) assert rtid != rtid_child os._exit(12) _, status = os.waitpid(pid, 0) exit_code = os.WEXITSTATUS(status) assert exit_code == 12
def test_multithreaded(): tracer = ddtrace.Tracer() tracer.writer = DummyWriter() def target(): with tracer.trace("s1"): with tracer.trace("s2"): pass with tracer.trace("s3"): pass for i in range(1000): ts = [threading.Thread(target=target) for _ in range(10)] for t in ts: t.start() for t in ts: t.join() traces = tracer.writer.pop_traces() assert len(traces) == 10 for trace in traces: assert len(trace) == 3
def test_tracer_url(): t = ddtrace.Tracer() assert t.writer.agent_url == "http://localhost:8126" t = ddtrace.Tracer(url="http://foobar:12") assert t.writer.agent_url == "http://foobar:12" t = ddtrace.Tracer(url="unix:///foobar") assert t.writer.agent_url == "unix:///foobar" t = ddtrace.Tracer(url="http://localhost") assert t.writer.agent_url == "http://localhost:80" t = ddtrace.Tracer(url="https://localhost") assert t.writer.agent_url == "https://localhost:443" with pytest.raises(ValueError) as e: ddtrace.Tracer(url="foo://foobar:12") assert str(e) == "Unknown scheme `https` for agent URL"