def test_context_sampled(self): # a context is sampled if the spans are sampled ctx = Context() span = Span(tracer=None, name='fake_span') ctx.add_span(span) assert ctx._sampled is True assert ctx.sampling_priority is None
def test_log_unfinished_spans_disabled(self, log): # the trace finished status logging is disabled tracer = get_dummy_tracer() tracer.debug_logging = False ctx = Context() # manually create a root-child trace root = Span(tracer=tracer, name='root') child_1 = Span(tracer=tracer, name='child_1', trace_id=root.trace_id, parent_id=root.span_id) child_2 = Span(tracer=tracer, name='child_2', trace_id=root.trace_id, parent_id=root.span_id) child_1._parent = root child_2._parent = root ctx.add_span(root) ctx.add_span(child_1) ctx.add_span(child_2) # close only the parent root.finish() assert ctx.is_finished() is False # the logger has never been invoked to print unfinished spans for call, _ in log.call_args_list: msg = call[0] assert 'the trace has %d unfinished spans' not in msg
def test_log_unfinished_spans(self, log): # when the root parent is finished, notify if there are spans still pending tracer = get_dummy_tracer() tracer.debug_logging = True ctx = Context() # manually create a root-child trace root = Span(tracer=tracer, name='root') child_1 = Span(tracer=tracer, name='child_1', trace_id=root.trace_id, parent_id=root.span_id) child_2 = Span(tracer=tracer, name='child_2', trace_id=root.trace_id, parent_id=root.span_id) child_1._parent = root child_2._parent = root ctx.add_span(root) ctx.add_span(child_1) ctx.add_span(child_2) # close only the parent root.finish() ok_(ctx.is_finished() is False) unfinished_spans_log = log.call_args_list[-3][0][2] child_1_log = log.call_args_list[-2][0][1] child_2_log = log.call_args_list[-1][0][1] eq_(2, unfinished_spans_log) ok_('name child_1' in child_1_log) ok_('name child_2' in child_2_log) ok_('duration 0.000000s' in child_1_log) ok_('duration 0.000000s' in child_2_log)
def test_finished(self): # a Context is finished if all spans inside are finished ctx = Context() span = Span(tracer=None, name='fake_span') ctx.add_span(span) ctx.close_span(span) assert ctx.is_finished()
def test_partial_flush_too_few(self): """ When calling `Context.get` When partial flushing is enabled When we do not have enough finished spans to flush We return no spans """ tracer = get_dummy_tracer() ctx = Context() # Create a root span with 5 children, all of the children are finished, the root is not root = Span(tracer=tracer, name='root') ctx.add_span(root) for i in range(5): child = Span(tracer=tracer, name='child_{}'.format(i), trace_id=root.trace_id, parent_id=root.span_id) child._parent = root child.finished = True ctx.add_span(child) ctx.close_span(child) # Test with having 1 too few spans for partial flush with self.override_partial_flush(ctx, enabled=True, min_spans=6): trace, sampled = ctx.get() self.assertIsNone(trace) self.assertIsNone(sampled) self.assertEqual(len(ctx._trace), 6) self.assertEqual( set(['root', 'child_0', 'child_1', 'child_2', 'child_3', 'child_4']), set([span.name for span in ctx._trace]) )
def test_log_unfinished_spans(log, tracer_with_debug_logging): # when the root parent is finished, notify if there are spans still pending tracer = tracer_with_debug_logging ctx = Context() # manually create a root-child trace root = Span(tracer=tracer, name="root") child_1 = Span(tracer=tracer, name="child_1", trace_id=root.trace_id, parent_id=root.span_id) child_2 = Span(tracer=tracer, name="child_2", trace_id=root.trace_id, parent_id=root.span_id) child_1._parent = root child_2._parent = root ctx.add_span(root) ctx.add_span(child_1) ctx.add_span(child_2) # close only the parent root.finish() unfinished_spans_log = log.call_args_list[-3][0][2] child_1_log = log.call_args_list[-2][0][1] child_2_log = log.call_args_list[-1][0][1] assert 2 == unfinished_spans_log assert "name child_1" in child_1_log assert "name child_2" in child_2_log assert "duration 0.000000s" in child_1_log assert "duration 0.000000s" in child_2_log
def test_log_unfinished_spans_disabled(self, log): # the trace finished status logging is disabled tracer = DummyTracer() ctx = Context() # manually create a root-child trace root = Span(tracer=tracer, name="root") child_1 = Span(tracer=tracer, name="child_1", trace_id=root.trace_id, parent_id=root.span_id) child_2 = Span(tracer=tracer, name="child_2", trace_id=root.trace_id, parent_id=root.span_id) child_1._parent = root child_2._parent = root ctx.add_span(root) ctx.add_span(child_1) ctx.add_span(child_2) # close only the parent root.finish() # the logger has never been invoked to print unfinished spans for call, _ in log.call_args_list: msg = call[0] assert "the trace has %d unfinished spans" not in msg
def test_log_unfinished_spans(self, log): # when the root parent is finished, notify if there are spans still pending tracer = get_dummy_tracer() tracer.debug_logging = True ctx = Context() # manually create a root-child trace root = Span(tracer=tracer, name='root') child_1 = Span(tracer=tracer, name='child_1', trace_id=root.trace_id, parent_id=root.span_id) child_2 = Span(tracer=tracer, name='child_2', trace_id=root.trace_id, parent_id=root.span_id) child_1._parent = root child_2._parent = root ctx.add_span(root) ctx.add_span(child_1) ctx.add_span(child_2) # close only the parent root.finish() assert ctx.is_finished() is False unfinished_spans_log = log.call_args_list[-3][0][2] child_1_log = log.call_args_list[-2][0][1] child_2_log = log.call_args_list[-1][0][1] assert 2 == unfinished_spans_log assert 'name child_1' in child_1_log assert 'name child_2' in child_2_log assert 'duration 0.000000s' in child_1_log assert 'duration 0.000000s' in child_2_log
def test_finished(self): # a Context is finished if all spans inside are finished ctx = Context() span = Span(tracer=None, name='fake_span') ctx.add_span(span) ctx.close_span(span) ok_(ctx.is_finished())
def test_context_sampled(self): # a context is sampled if the spans are sampled ctx = Context() span = Span(tracer=None, name='fake_span') ctx.add_span(span) ok_(ctx._sampled is True) ok_(ctx.sampling_priority is None)
def test_add_span(self): # it should add multiple spans ctx = Context() span = Span(tracer=None, name='fake_span') ctx.add_span(span) eq_(1, len(ctx._trace)) eq_('fake_span', ctx._trace[0].name) eq_(ctx, span.context)
def test_close_span(self): # it should keep track of closed spans, moving # the current active to its parent ctx = Context() span = Span(tracer=None, name="fake_span") ctx.add_span(span) ctx.close_span(span) assert ctx.get_current_span() is None
def test_get_trace_empty(self): # it should return None if the Context is not finished ctx = Context() span = Span(tracer=None, name='fake_span') ctx.add_span(span) trace, sampled = ctx.get() ok_(trace is None) ok_(sampled is None)
def test_get_trace_empty(self): # it should return None if the Context is not finished ctx = Context() span = Span(tracer=None, name='fake_span') ctx.add_span(span) trace, sampled = ctx.get() assert trace is None assert sampled is None
def test_finish_called_multiple_times(self): # we should only record a span the first time finish is called on it ctx = Context() s = Span(self.tracer, 'bar', context=ctx) ctx.add_span(s) s.finish() s.finish() self.assert_span_count(1)
def test_add_span(self): # it should add multiple spans ctx = Context() span = Span(tracer=None, name='fake_span') ctx.add_span(span) assert 1 == len(ctx._trace) assert 'fake_span' == ctx._trace[0].name assert ctx == span.context
def test_finish_called_multiple_times(): # we should only record a span the first time finish is called on it dt = DummyTracer() ctx = Context() s = Span(dt, 'bar', context=ctx) ctx.add_span(s) s.finish() s.finish() assert dt.spans_recorded == 1
def test_close_span(self): # it should keep track of closed spans, moving # the current active to it's parent ctx = Context() span = Span(tracer=None, name='fake_span') ctx.add_span(span) ctx.close_span(span) eq_(1, ctx._finished_spans) ok_(ctx.get_current_span() is None)
def test_context_sampled(self): # a context is sampled if the spans are sampled ctx = Context() span = Span(tracer=None, name="fake_span") ctx.add_span(span) span.finished = True trace, sampled = ctx.close_span(span) assert sampled is True assert ctx.sampling_priority is None
def test_get_report_hostname_enabled(self, get_hostname): get_hostname.return_value = "test-hostname" with self.override_global_config(dict(report_hostname=True)): # Create a context and add a span and finish it ctx = Context() span = Span(tracer=None, name="fake_span") ctx.add_span(span) span.finish() assert span.get_tag(HOSTNAME_KEY) == "test-hostname"
def test_get_trace(self): # it should return the internal trace structure # if the context is finished ctx = Context() span = Span(tracer=None, name="fake_span") ctx.add_span(span) span.finished = True trace, sampled = ctx.close_span(span) assert [span] == trace assert sampled is True # the context should be empty assert 0 == len(ctx._trace) assert ctx._current_span is None
def test_context_priority(self): # a context is sampled if the spans are sampled ctx = Context() for priority in [USER_REJECT, AUTO_REJECT, AUTO_KEEP, USER_KEEP, None, 999]: ctx.sampling_priority = priority span = Span(tracer=None, name=('fake_span_%s' % repr(priority))) ctx.add_span(span) # It's "normal" to have sampled be true even when priority sampling is # set to 0 or -1. It would stay false even even with priority set to 2. # The only criteria to send (or not) the spans to the agent should be # this "sampled" attribute, as it's tightly related to the trace weight. ok_(ctx._sampled is True, 'priority has no impact on sampled status') eq_(priority, ctx.sampling_priority)
def test_finish(self): # ensure finish will record a span ctx = Context() s = Span(self.tracer, 'test.span', context=ctx) ctx.add_span(s) assert s.duration is None sleep = 0.05 with s as s1: assert s is s1 time.sleep(sleep) assert s.duration >= sleep, '%s < %s' % (s.duration, sleep) self.assert_span_count(1)
def test_finish(): # ensure finish will record a span dt = DummyTracer() ctx = Context() s = Span(dt, "test.span", context=ctx) ctx.add_span(s) assert s.duration is None sleep = 0.05 with s as s1: assert s is s1 time.sleep(sleep) assert s.duration >= sleep, "%s < %s" % (s.duration, sleep) eq_(1, dt.spans_recorded)
def test_get_report_hostname_default(self, get_hostname): get_hostname.return_value = "test-hostname" # Create a context and add a span and finish it ctx = Context() span = Span(tracer=None, name="fake_span") ctx.add_span(span) span.finished = True # Assert that we have not added the tag to the span yet assert span.get_tag(HOSTNAME_KEY) is None # Assert that retrieving the trace does not set the tag trace, _ = ctx.close_span(span) assert trace[0].get_tag(HOSTNAME_KEY) is None assert span.get_tag(HOSTNAME_KEY) is None
def test_get_trace(self): # it should return the internal trace structure # if the context is finished ctx = Context() span = Span(tracer=None, name='fake_span') ctx.add_span(span) ctx.close_span(span) trace, sampled = ctx.get() eq_(1, len(trace)) eq_(span, trace[0]) ok_(sampled is True) # the context should be empty eq_(0, len(ctx._trace)) eq_(0, ctx._finished_spans) ok_(ctx._current_span is None) ok_(ctx._sampled is False)
def test_clone(self): ctx = Context() ctx.sampling_priority = 2 # manually create a root-child trace root = Span(tracer=None, name='root') child = Span(tracer=None, name='child_1', trace_id=root.trace_id, parent_id=root.span_id) child._parent = root ctx.add_span(root) ctx.add_span(child) cloned_ctx = ctx.clone() assert cloned_ctx._parent_trace_id == ctx._parent_trace_id assert cloned_ctx._parent_span_id == ctx._parent_span_id assert cloned_ctx._sampling_priority == ctx._sampling_priority assert cloned_ctx._dd_origin == ctx._dd_origin assert cloned_ctx._current_span == ctx._current_span assert cloned_ctx._trace == []
def test_get_trace(self): # it should return the internal trace structure # if the context is finished ctx = Context() span = Span(tracer=None, name='fake_span') ctx.add_span(span) ctx.close_span(span) trace, sampled = ctx.get() assert 1 == len(trace) assert span == trace[0] assert sampled is True # the context should be empty assert 0 == len(ctx._trace) assert 0 == ctx._finished_spans assert ctx._current_span is None assert ctx._sampled is True
def test_get_trace(self): # it should return the internal trace structure # if the context is finished ctx = Context() span = Span(tracer=None, name='fake_span') ctx.add_span(span) ctx.close_span(span) trace, sampled = ctx.get() eq_(1, len(trace)) eq_(span, trace[0]) ok_(sampled is True) # the context should be empty eq_(0, len(ctx._trace)) eq_(0, ctx._finished_spans) ok_(ctx._current_span is None) ok_(ctx._sampled is True)
def test_add_span(self): ctx = Context() span = Span(tracer=None, name="fake_span") ctx.add_span(span) assert ctx == span.context assert ctx.span_id == span.span_id assert ctx.get_current_span() == span assert ctx.get_current_root_span() == span span2 = Span(tracer=None, name="fake_span2") span2.parent_id = span.span_id span2._parent = span ctx.add_span(span2) assert ctx == span2.context assert ctx.span_id == span2.span_id assert ctx.get_current_span() == span2 assert ctx.get_current_root_span() == span
def test_clone(self): ctx = Context() ctx.sampling_priority = 2 # manually create a root-child trace root = Span(tracer=None, name='root') child = Span(tracer=None, name='child_1', trace_id=root.trace_id, parent_id=root.span_id) child._parent = root ctx.add_span(root) ctx.add_span(child) cloned_ctx = ctx.clone() eq_(cloned_ctx._parent_trace_id, ctx._parent_trace_id) eq_(cloned_ctx._parent_span_id, ctx._parent_span_id) eq_(cloned_ctx._sampled, ctx._sampled) eq_(cloned_ctx._sampling_priority, ctx._sampling_priority) eq_(cloned_ctx._current_span, ctx._current_span) eq_(cloned_ctx._trace, []) eq_(cloned_ctx._finished_spans, 0)
def test_log_unfinished_spans_when_ok(self, log): # if the unfinished spans logging is enabled but the trace is finished, don't log anything tracer = get_dummy_tracer() ctx = Context() # manually create a root-child trace root = Span(tracer=tracer, name='root') child = Span(tracer=tracer, name='child_1', trace_id=root.trace_id, parent_id=root.span_id) child._parent = root ctx.add_span(root) ctx.add_span(child) # close the trace child.finish() root.finish() # the logger has never been invoked to print unfinished spans for call, _ in log.call_args_list: msg = call[0] assert 'the trace has %d unfinished spans' not in msg
def test_get_report_hostname_disabled(self, get_hostname): get_hostname.return_value = 'test-hostname' with self.override_global_config(dict(report_hostname=False)): # Create a context and add a span and finish it ctx = Context() span = Span(tracer=None, name='fake_span') ctx.add_span(span) span.finish() # Assert that we have not added the tag to the span yet assert span.get_tag(HOSTNAME_KEY) is None # Assert that retrieving the trace does not set the tag trace, _ = ctx.get() assert trace[0].get_tag(HOSTNAME_KEY) is None assert span.get_tag(HOSTNAME_KEY) is None
def test_partial_flush_remaining(self): """ When calling `Context.get` When partial flushing is enabled When we have some unfinished spans We keep the unfinished spans around """ tracer = get_dummy_tracer() ctx = Context() # Create a root span with 5 children, all of the children are finished, the root is not root = Span(tracer=tracer, name='root') ctx.add_span(root) for i in range(10): child = Span(tracer=tracer, name='child_{}'.format(i), trace_id=root.trace_id, parent_id=root.span_id) child._parent = root ctx.add_span(child) # CLose the first 5 only if i < 5: child._finished = True ctx.close_span(child) with self.override_partial_flush(ctx, enabled=True, min_spans=5): trace, sampled = ctx.get() # Assert partially flushed spans self.assertTrue(len(trace), 5) self.assertIsNotNone(sampled) self.assertEqual( set(['child_0', 'child_1', 'child_2', 'child_3', 'child_4']), set([span.name for span in trace])) # Assert remaining unclosed spans self.assertEqual(len(ctx._trace), 6) self.assertEqual(ctx._finished_spans, 0) self.assertEqual( set([ 'root', 'child_5', 'child_6', 'child_7', 'child_8', 'child_9' ]), set([span.name for span in ctx._trace]), )
def test_log_unfinished_spans_when_ok(self, log): # if the unfinished spans logging is enabled but the trace is finished, don't log anything tracer = get_dummy_tracer() tracer.debug_logging = True ctx = Context() # manually create a root-child trace root = Span(tracer=tracer, name='root') child = Span(tracer=tracer, name='child_1', trace_id=root.trace_id, parent_id=root.span_id) child._parent = root ctx.add_span(root) ctx.add_span(child) # close the trace child.finish() root.finish() # the logger has never been invoked to print unfinished spans for call, _ in log.call_args_list: msg = call[0] ok_('the trace has %d unfinished spans' not in msg)
def test_clone(self): ctx = Context() ctx.sampling_priority = 2 # manually create a root-child trace root = Span(tracer=None, name='root') child = Span(tracer=None, name='child_1', trace_id=root.trace_id, parent_id=root.span_id) child._parent = root ctx.add_span(root) ctx.add_span(child) cloned_ctx = ctx.clone() eq_(cloned_ctx._parent_trace_id, ctx._parent_trace_id) eq_(cloned_ctx._parent_span_id, ctx._parent_span_id) eq_(cloned_ctx._sampled, ctx._sampled) eq_(cloned_ctx._sampling_priority, ctx._sampling_priority) eq_(cloned_ctx._dd_origin, ctx._dd_origin) eq_(cloned_ctx._current_span, ctx._current_span) eq_(cloned_ctx._trace, []) eq_(cloned_ctx._finished_spans, 0)
def test_partial_flush_too_many(self): """ When calling `Context.get` When partial flushing is enabled When we have more than the minimum number of spans needed to flush We return the finished spans """ tracer = get_dummy_tracer() ctx = Context() # Create a root span with 5 children, all of the children are finished, the root is not root = Span(tracer=tracer, name='root') ctx.add_span(root) for i in range(5): child = Span(tracer=tracer, name='child_{}'.format(i), trace_id=root.trace_id, parent_id=root.span_id) child._parent = root child._finished = True ctx.add_span(child) ctx.close_span(child) with self.override_partial_flush(ctx, enabled=True, min_spans=1): trace, sampled = ctx.get() self.assertIsNotNone(trace) self.assertIsNotNone(sampled) self.assertEqual(len(trace), 5) self.assertEqual( set(['child_0', 'child_1', 'child_2', 'child_3', 'child_4']), set([span.name for span in trace])) # Ensure we clear/reset internal stats as expected self.assertEqual(ctx._finished_spans, 0) self.assertEqual(ctx._trace, [root]) with self.override_partial_flush(ctx, enabled=True, min_spans=5): trace, sampled = ctx.get() self.assertIsNone(trace) self.assertIsNone(sampled)
def test_log_unfinished_spans_disabled(self, log): # the trace finished status logging is disabled tracer = get_dummy_tracer() tracer.debug_logging = False ctx = Context() # manually create a root-child trace root = Span(tracer=tracer, name='root') child_1 = Span(tracer=tracer, name='child_1', trace_id=root.trace_id, parent_id=root.span_id) child_2 = Span(tracer=tracer, name='child_2', trace_id=root.trace_id, parent_id=root.span_id) child_1._parent = root child_2._parent = root ctx.add_span(root) ctx.add_span(child_1) ctx.add_span(child_2) # close only the parent root.finish() ok_(ctx.is_finished() is False) # the logger has never been invoked to print unfinished spans for call, _ in log.call_args_list: msg = call[0] ok_('the trace has %d unfinished spans' not in msg)
def test_current_span(self): # it should return the current active span ctx = Context() span = Span(tracer=None, name='fake_span') ctx.add_span(span) eq_(span, ctx.get_current_span())