def setUp(self): self.tracer_source = trace.TracerSource() self.tracer = self.tracer_source.get_tracer(__name__) self.memory_exporter = InMemorySpanExporter() span_processor = export.SimpleExportSpanProcessor(self.memory_exporter) self.tracer_source.add_span_processor(span_processor) self.exec_scenario()
def test_sampling_attributes(self): decision_attributes = { "sampler-attr": "sample-val", "attr-in-both": "decision-attr", } tracer_source = trace.TracerSource( sampling.StaticSampler( sampling.Decision(sampled=True, attributes=decision_attributes))) self.tracer = tracer_source.get_tracer(__name__) with self.tracer.start_as_current_span("root2") as root: self.assertEqual(len(root.attributes), 2) self.assertEqual(root.attributes["sampler-attr"], "sample-val") self.assertEqual(root.attributes["attr-in-both"], "decision-attr") attributes = { "attr-key": "val", "attr-key2": "val2", "attr-in-both": "span-attr", } with self.tracer.start_as_current_span("root2", attributes=attributes) as root: self.assertEqual(len(root.attributes), 4) self.assertEqual(root.attributes["attr-key"], "val") self.assertEqual(root.attributes["attr-key2"], "val2") self.assertEqual(root.attributes["sampler-attr"], "sample-val") self.assertEqual(root.attributes["attr-in-both"], "decision-attr")
def test_error_status(self): try: with trace.TracerSource().get_tracer(__name__).start_span( "root") as root: raise Exception("unknown") except Exception: # pylint: disable=broad-except pass self.assertIs(root.status.canonical_code, StatusCanonicalCode.UNKNOWN) self.assertEqual(root.status.description, "Exception: unknown")
def test_sampler_no_sampling(self): tracer_source = trace.TracerSource(sampling.ALWAYS_OFF) tracer = tracer_source.get_tracer(__name__) # Check that the default tracer creates no-op spans if the sampler # decides not to sampler root_span = tracer.start_span(name="root span", parent=None) self.assertIsInstance(root_span, trace_api.DefaultSpan) child_span = tracer.start_span(name="child span", parent=root_span) self.assertIsInstance(child_span, trace_api.DefaultSpan)
def test_tracer(self): tracer = trace.TracerSource().get_tracer(__name__) with tracer.start_span("test") as span: self.assertNotEqual(span.get_context(), INVALID_SPAN_CONTEXT) self.assertNotEqual(span, INVALID_SPAN) self.assertIs(span.is_recording_events(), True) with tracer.start_span("test2") as span2: self.assertNotEqual(span2.get_context(), INVALID_SPAN_CONTEXT) self.assertNotEqual(span2, INVALID_SPAN) self.assertIs(span2.is_recording_events(), True)
def test_span_processor_for_source(self): tracer_source = trace.TracerSource() tracer1 = tracer_source.get_tracer("instr1") tracer2 = tracer_source.get_tracer("instr2", "1.3b3") span1 = tracer1.start_span("s1") span2 = tracer2.start_span("s2") # pylint:disable=protected-access self.assertIs(span1.span_processor, tracer_source._active_span_processor) self.assertIs(span2.span_processor, tracer_source._active_span_processor)
def test_invalid_instrumentation_info(self): tracer_source = trace.TracerSource() tracer1 = tracer_source.get_tracer("") tracer2 = tracer_source.get_tracer(None) self.assertEqual(tracer1.instrumentation_info, tracer2.instrumentation_info) self.assertIsInstance(tracer1.instrumentation_info, trace.InstrumentationInfo) span1 = tracer1.start_span("foo") self.assertTrue(span1.is_recording_events()) self.assertEqual(tracer1.instrumentation_info.version, "") self.assertEqual(tracer1.instrumentation_info.name, "ERROR:MISSING MODULE NAME")
def test_instrumentation_info(self): tracer_source = trace.TracerSource() tracer1 = tracer_source.get_tracer("instr1") tracer2 = tracer_source.get_tracer("instr2", "1.3b3") span1 = tracer1.start_span("s1") span2 = tracer2.start_span("s2") self.assertEqual(span1.instrumentation_info, trace.InstrumentationInfo("instr1", "")) self.assertEqual( span2.instrumentation_info, trace.InstrumentationInfo("instr2", "1.3b3"), ) self.assertEqual(span2.instrumentation_info.version, "1.3b3") self.assertEqual(span2.instrumentation_info.name, "instr2") self.assertLess(span1.instrumentation_info, span2.instrumentation_info) # Check sortability.
def test_simple_span_processor(self): tracer_source = trace.TracerSource() tracer = tracer_source.get_tracer(__name__) spans_names_list = [] my_exporter = MySpanExporter(destination=spans_names_list) span_processor = export.SimpleExportSpanProcessor(my_exporter) tracer_source.add_span_processor(span_processor) with tracer.start_as_current_span("foo"): with tracer.start_as_current_span("bar"): with tracer.start_as_current_span("xxx"): pass self.assertListEqual(["xxx", "bar", "foo"], spans_names_list) span_processor.shutdown() self.assertTrue(my_exporter.is_shutdown)
def test_simple_span_processor_no_context(self): """Check that we process spans that are never made active. SpanProcessors should act on a span's start and end events whether or not it is ever the active span. """ tracer_source = trace.TracerSource() tracer = tracer_source.get_tracer(__name__) spans_names_list = [] my_exporter = MySpanExporter(destination=spans_names_list) span_processor = export.SimpleExportSpanProcessor(my_exporter) tracer_source.add_span_processor(span_processor) with tracer.start_span("foo"): with tracer.start_span("bar"): with tracer.start_span("xxx"): pass self.assertListEqual(["xxx", "bar", "foo"], spans_names_list)
def test_add_span_processor_after_span_creation(self): tracer_source = trace.TracerSource() tracer = tracer_source.get_tracer(__name__) spans_calls_list = [] # filled by MySpanProcessor expected_list = [] # filled by hand # Span processors are created but not added to the tracer yet sp = MySpanProcessor("SP1", spans_calls_list) with tracer.start_as_current_span("foo"): with tracer.start_as_current_span("bar"): with tracer.start_as_current_span("baz"): # add span processor after spans have been created tracer_source.add_span_processor(sp) expected_list.append(span_event_end_fmt("SP1", "baz")) expected_list.append(span_event_end_fmt("SP1", "bar")) expected_list.append(span_event_end_fmt("SP1", "foo")) self.assertListEqual(spans_calls_list, expected_list)
def test_span_processor(self): tracer_source = trace.TracerSource() tracer = tracer_source.get_tracer(__name__) spans_calls_list = [] # filled by MySpanProcessor expected_list = [] # filled by hand # Span processors are created but not added to the tracer yet sp1 = MySpanProcessor("SP1", spans_calls_list) sp2 = MySpanProcessor("SP2", spans_calls_list) with tracer.start_as_current_span("foo"): with tracer.start_as_current_span("bar"): with tracer.start_as_current_span("baz"): pass # at this point lists must be empty self.assertEqual(len(spans_calls_list), 0) # add single span processor tracer_source.add_span_processor(sp1) with tracer.start_as_current_span("foo"): expected_list.append(span_event_start_fmt("SP1", "foo")) with tracer.start_as_current_span("bar"): expected_list.append(span_event_start_fmt("SP1", "bar")) with tracer.start_as_current_span("baz"): expected_list.append(span_event_start_fmt("SP1", "baz")) expected_list.append(span_event_end_fmt("SP1", "baz")) expected_list.append(span_event_end_fmt("SP1", "bar")) expected_list.append(span_event_end_fmt("SP1", "foo")) self.assertListEqual(spans_calls_list, expected_list) spans_calls_list.clear() expected_list.clear() # go for multiple span processors tracer_source.add_span_processor(sp2) with tracer.start_as_current_span("foo"): expected_list.append(span_event_start_fmt("SP1", "foo")) expected_list.append(span_event_start_fmt("SP2", "foo")) with tracer.start_as_current_span("bar"): expected_list.append(span_event_start_fmt("SP1", "bar")) expected_list.append(span_event_start_fmt("SP2", "bar")) with tracer.start_as_current_span("baz"): expected_list.append(span_event_start_fmt("SP1", "baz")) expected_list.append(span_event_start_fmt("SP2", "baz")) expected_list.append(span_event_end_fmt("SP1", "baz")) expected_list.append(span_event_end_fmt("SP2", "baz")) expected_list.append(span_event_end_fmt("SP1", "bar")) expected_list.append(span_event_end_fmt("SP2", "bar")) expected_list.append(span_event_end_fmt("SP1", "foo")) expected_list.append(span_event_end_fmt("SP2", "foo")) # compare if two lists are the same self.assertListEqual(spans_calls_list, expected_list)
def test_shutdown(self): tracer_source = trace.TracerSource() mock_processor1 = mock.Mock(spec=trace.SpanProcessor) tracer_source.add_span_processor(mock_processor1) mock_processor2 = mock.Mock(spec=trace.SpanProcessor) tracer_source.add_span_processor(mock_processor2) tracer_source.shutdown() self.assertEqual(mock_processor1.shutdown.call_count, 1) self.assertEqual(mock_processor2.shutdown.call_count, 1) shutdown_python_code = """ import atexit from unittest import mock from opentelemetry.sdk import trace mock_processor = mock.Mock(spec=trace.SpanProcessor) def print_shutdown_count(): print(mock_processor.shutdown.call_count) # atexit hooks are called in inverse order they are added, so do this before # creating the tracer atexit.register(print_shutdown_count) tracer_source = trace.TracerSource({tracer_parameters}) tracer_source.add_span_processor(mock_processor) {tracer_shutdown} """ def run_general_code(shutdown_on_exit, explicit_shutdown): tracer_parameters = "" tracer_shutdown = "" if not shutdown_on_exit: tracer_parameters = "shutdown_on_exit=False" if explicit_shutdown: tracer_shutdown = "tracer_source.shutdown()" return subprocess.check_output([ # use shutil to avoid calling python outside the # virtualenv on windows. shutil.which("python"), "-c", shutdown_python_code.format( tracer_parameters=tracer_parameters, tracer_shutdown=tracer_shutdown, ), ]) # test default shutdown_on_exit (True) out = run_general_code(True, False) self.assertTrue(out.startswith(b"1")) # test that shutdown is called only once even if Tracer.shutdown is # called explicitely out = run_general_code(True, True) self.assertTrue(out.startswith(b"1")) # test shutdown_on_exit=False out = run_general_code(False, False) self.assertTrue(out.startswith(b"0"))
def new_tracer() -> trace_api.Tracer: return trace.TracerSource().get_tracer(__name__)