Beispiel #1
0
 def test_fields(self):
     tracer = trace.TracerProvider().get_tracer("sdk_tracer_provider")
     mock_set_in_carrier = Mock()
     with tracer.start_as_current_span("parent"):
         with tracer.start_as_current_span("child"):
             FORMAT.inject(mock_set_in_carrier, {})
     inject_fields = set()
     for call in mock_set_in_carrier.mock_calls:
         inject_fields.add(call[1][1])
     self.assertEqual(FORMAT.fields, inject_fields)
 def test_tracer(self):
     tracer = trace.TracerProvider().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)
Beispiel #3
0
    def test_override_error_status(self):
        def error_status_test(context):
            with self.assertRaises(AssertionError):
                with context as root:
                    root.set_status(
                        trace_api.status.Status(
                            StatusCanonicalCode.UNAVAILABLE,
                            "Error: Unavailable",
                        ))
                    raise AssertionError("unknown")

            self.assertIs(root.status.canonical_code,
                          StatusCanonicalCode.UNAVAILABLE)
            self.assertEqual(root.status.description, "Error: Unavailable")

        error_status_test(
            trace.TracerProvider().get_tracer(__name__).start_span("root"))
        error_status_test(trace.TracerProvider().get_tracer(
            __name__).start_as_current_span("root"))
Beispiel #4
0
    def test_tracer_provider_accepts_concurrent_multi_span_processor(self):
        span_processor = trace.ConcurrentMultiSpanProcessor(2)
        tracer_provider = trace.TracerProvider(
            active_span_processor=span_processor
        )

        # pylint: disable=protected-access
        self.assertEqual(
            span_processor, tracer_provider._active_span_processor
        )
    def test_sampler_no_sampling(self):
        tracer_provider = trace.TracerProvider(sampling.ALWAYS_OFF)
        tracer = tracer_provider.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_last_status_wins(self):
        def error_status_test(context):
            with self.assertRaises(AssertionError):
                with context as root:
                    root.set_status(trace_api.status.Status(StatusCode.OK))
                    raise AssertionError("unknown")

            self.assertIs(root.status.status_code, StatusCode.ERROR)
            self.assertEqual(
                root.status.description, "AssertionError: unknown"
            )

        error_status_test(
            trace.TracerProvider().get_tracer(__name__).start_span("root")
        )
        error_status_test(
            trace.TracerProvider()
            .get_tracer(__name__)
            .start_as_current_span("root")
        )
    def test_span_processor_accepts_parent_context(self):
        span_processor = mock.Mock(
            wraps=datadog.DatadogExportSpanProcessor(self.exporter))
        tracer_provider = trace.TracerProvider()
        tracer_provider.add_span_processor(span_processor)
        tracer = tracer_provider.get_tracer(__name__)

        context = Context()
        span = tracer.start_span("foo", context=context)

        span_processor.on_start.assert_called_once_with(span,
                                                        parent_context=context)
    def test_error_status(self):
        def error_status_test(context):
            with self.assertRaises(AssertionError):
                with context as root:
                    raise AssertionError("unknown")

            self.assertIs(
                root.status.canonical_code, StatusCanonicalCode.UNKNOWN
            )
            self.assertEqual(
                root.status.description, "AssertionError: unknown"
            )

        error_status_test(
            trace.TracerProvider().get_tracer(__name__).start_span("root")
        )
        error_status_test(
            trace.TracerProvider()
            .get_tracer(__name__)
            .start_as_current_span("root")
        )
    def test_concurrent_server_spans(self):
        """Check that concurrent RPC calls don't interfere with each other.

        This is the same check as test_sequential_server_spans except that the
        RPCs are concurrent. Two handlers are invoked at the same time on two
        separate threads. Each one should see a different active span and
        context.
        """

        tracer_provider = trace_sdk.TracerProvider()
        tracer = tracer_provider.get_tracer(__name__)

        interceptor = server_interceptor(tracer)

        # Capture the currently active span in each thread
        active_spans_in_handler = []
        latch = get_latch(2)

        def handler(request, context):
            latch()
            active_spans_in_handler.append(tracer.get_current_span())
            return b""

        server = grpc.server(
            futures.ThreadPoolExecutor(max_workers=2),
            options=(("grpc.so_reuseport", 0), ),
        )
        server = intercept_server(server, interceptor)
        server.add_generic_rpc_handlers((UnaryUnaryRpcHandler(handler), ))

        port = server.add_insecure_port("[::]:0")
        channel = grpc.insecure_channel("localhost:{:d}".format(port))

        try:
            server.start()
            # Interleave calls so spans are active on each thread at the same
            # time
            with futures.ThreadPoolExecutor(max_workers=2) as tpe:
                f1 = tpe.submit(channel.unary_unary(""), b"")
                f2 = tpe.submit(channel.unary_unary(""), b"")
            futures.wait((f1, f2))
        finally:
            server.stop(None)

        self.assertEqual(len(active_spans_in_handler), 2)
        # pylint:disable=unbalanced-tuple-unpacking
        span1, span2 = active_spans_in_handler
        # Spans should belong to separate traces, and each should be a root
        # span
        self.assertNotEqual(span1.context.span_id, span2.context.span_id)
        self.assertNotEqual(span1.context.trace_id, span2.context.trace_id)
        self.assertIsNone(span1.parent)
        self.assertIsNone(span1.parent)
    def test_span_processor_for_source(self):
        tracer_provider = trace.TracerProvider()
        tracer1 = tracer_provider.get_tracer("instr1")
        tracer2 = tracer_provider.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_provider._active_span_processor)
        self.assertIs(span2.span_processor,
                      tracer_provider._active_span_processor)
def test_inject_empty_context(benchmark):
    tracer = trace.TracerProvider().get_tracer("sdk_tracer_provider")
    with tracer.start_as_current_span("Root Span"):
        with tracer.start_as_current_span("Child Span"):
            benchmark(
                FORMAT.inject,
                {
                    FORMAT.TRACE_ID_KEY: "bdb5b63237ed38aea578af665aa5aa60",
                    FORMAT.SPAN_ID_KEY: "00000000000000000c32d953d73ad225",
                    FORMAT.SAMPLED_KEY: "1",
                },
            )
Beispiel #12
0
    def test_on_start_accepts_context(self):
        # pylint: disable=no-self-use
        tracer_provider = trace.TracerProvider()
        tracer = tracer_provider.get_tracer(__name__)

        exporter = MySpanExporter([])
        span_processor = mock.Mock(wraps=export.SimpleSpanProcessor(exporter))
        tracer_provider.add_span_processor(span_processor)

        context = Context()
        span = tracer.start_span("foo", context=context)
        span_processor.on_start.assert_called_once_with(span,
                                                        parent_context=context)
    def test_sampler_with_env(self):
        # pylint: disable=protected-access
        reload(trace)
        tracer_provider = trace.TracerProvider()
        self.assertIsInstance(tracer_provider.sampler, sampling.StaticSampler)
        self.assertEqual(tracer_provider.sampler._decision,
                         sampling.Decision.DROP)

        tracer = tracer_provider.get_tracer(__name__)

        root_span = tracer.start_span(name="root span", context=None)
        # Should be no-op
        self.assertIsInstance(root_span, trace_api.DefaultSpan)
def test_inject_empty_context(benchmark):
    tracer = trace.TracerProvider().get_tracer("sdk_tracer_provider")
    with tracer.start_as_current_span("Root Span"):
        with tracer.start_as_current_span("Child Span"):
            benchmark(
                FORMAT.inject,
                dict.__setitem__,
                {
                    FORMAT.TRACE_ID_KEY: "bdb5b63237ed38aea578af665aa5aa60",
                    FORMAT.SPAN_ID_KEY: "00000000000000000c32d953d73ad225",
                    FORMAT.PARENT_SPAN_ID_KEY:
                    "11fd79a30b0896cd285b396ae102dd76",
                    FORMAT.SAMPLED_KEY: "1",
                },
            )
 def test_invalid_instrumentation_info(self):
     tracer_provider = trace.TracerProvider()
     with self.assertLogs(level=ERROR):
         tracer1 = tracer_provider.get_tracer("")
     with self.assertLogs(level=ERROR):
         tracer2 = tracer_provider.get_tracer(None)
     self.assertEqual(tracer1.instrumentation_info,
                      tracer2.instrumentation_info)
     self.assertIsInstance(tracer1.instrumentation_info,
                           InstrumentationInfo)
     span1 = tracer1.start_span("foo")
     self.assertTrue(span1.is_recording())
     self.assertEqual(tracer1.instrumentation_info.version, "")
     self.assertEqual(tracer1.instrumentation_info.name,
                      "ERROR:MISSING MODULE NAME")
    def test_instrumentation_info(self):
        tracer_provider = trace.TracerProvider()
        tracer1 = tracer_provider.get_tracer("instr1")
        tracer2 = tracer_provider.get_tracer("instr2", "1.3b3")
        span1 = tracer1.start_span("s1")
        span2 = tracer2.start_span("s2")
        self.assertEqual(span1.instrumentation_info,
                         InstrumentationInfo("instr1", ""))
        self.assertEqual(span2.instrumentation_info,
                         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.
Beispiel #17
0
    def test_fields(self):
        """Make sure the fields attribute returns the fields used in inject"""

        tracer = trace.TracerProvider().get_tracer("sdk_tracer_provider")

        mock_setter = Mock()

        with tracer.start_as_current_span("parent"):
            with tracer.start_as_current_span("child"):
                FORMAT.inject({}, setter=mock_setter)

        inject_fields = set()

        for call in mock_setter.mock_calls:
            inject_fields.add(call[1][1])

        self.assertEqual(FORMAT.fields, inject_fields)
Beispiel #18
0
    def test_sampling_attributes(self):
        sampling_attributes = {
            "sampler-attr": "sample-val",
            "attr-in-both": "decision-attr",
        }
        tracer_provider = trace.TracerProvider(
            sampling.StaticSampler(sampling.Decision.RECORD_AND_SAMPLE, ))

        self.tracer = tracer_provider.get_tracer(__name__)

        with self.tracer.start_as_current_span(
                name="root2", attributes=sampling_attributes) 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")
            self.assertEqual(root.get_context().trace_flags,
                             trace_api.TraceFlags.SAMPLED)
    def test_simple_span_processor_not_sampled(self):
        tracer_provider = trace.TracerProvider(
            sampler=trace.sampling.ALWAYS_OFF)
        tracer = tracer_provider.get_tracer(__name__)

        spans_names_list = []

        my_exporter = MySpanExporter(destination=spans_names_list)
        span_processor = export.SimpleExportSpanProcessor(my_exporter)
        tracer_provider.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([], spans_names_list)
    def test_span_processor_lossless(self):
        """Test that no spans are lost when sending max_trace_size spans"""
        span_processor = datadog.DatadogExportSpanProcessor(self.exporter,
                                                            max_trace_size=128)
        tracer_provider = trace.TracerProvider()
        tracer_provider.add_span_processor(span_processor)
        tracer = tracer_provider.get_tracer(__name__)

        with tracer.start_as_current_span("root"):
            for _ in range(127):
                with tracer.start_span("foo"):
                    pass

        self.assertTrue(span_processor.force_flush())
        datadog_spans = get_spans(tracer, self.exporter)
        self.assertEqual(len(datadog_spans), 128)
        tracer_provider.shutdown()
    def test_log_record_trace_correlation(self):
        emitter_mock = Mock(spec=LogEmitter)
        logger = get_logger(log_emitter=emitter_mock)

        tracer = trace.TracerProvider().get_tracer(__name__)
        with tracer.start_as_current_span("test") as span:
            logger.critical("Critical message within span")

            args, _ = emitter_mock.emit.call_args_list[0]
            log_record = args[0]
            self.assertEqual(log_record.body, "Critical message within span")
            self.assertEqual(log_record.severity_text, "CRITICAL")
            self.assertEqual(log_record.severity_number, SeverityNumber.FATAL)
            span_context = span.get_span_context()
            self.assertEqual(log_record.trace_id, span_context.trace_id)
            self.assertEqual(log_record.span_id, span_context.span_id)
            self.assertEqual(log_record.trace_flags, span_context.trace_flags)
    def test_batch_span_processor_fork(self):
        # pylint: disable=invalid-name
        tracer_provider = trace.TracerProvider()
        tracer = tracer_provider.get_tracer(__name__)

        exporter = InMemorySpanExporter()
        span_processor = export.BatchSpanProcessor(
            exporter,
            max_queue_size=256,
            max_export_batch_size=64,
            schedule_delay_millis=10,
        )
        tracer_provider.add_span_processor(span_processor)
        with tracer.start_as_current_span("foo"):
            pass
        time.sleep(0.5)  # give some time for the exporter to upload spans

        self.assertTrue(span_processor.force_flush())
        self.assertEqual(len(exporter.get_finished_spans()), 1)
        exporter.clear()

        def child(conn):
            def _target():
                with tracer.start_as_current_span("span") as s:
                    s.set_attribute("i", "1")
                    with tracer.start_as_current_span("temp"):
                        pass

            self.run_with_many_threads(_target, 100)

            time.sleep(0.5)

            spans = exporter.get_finished_spans()
            conn.send(len(spans) == 200)
            conn.close()

        parent_conn, child_conn = multiprocessing.Pipe()
        p = multiprocessing.Process(target=child, args=(child_conn, ))
        p.start()
        self.assertTrue(parent_conn.recv())
        p.join()

        span_processor.shutdown()
    def test_simple_span_processor(self):
        tracer_provider = trace.TracerProvider()
        tracer = tracer_provider.get_tracer(__name__)

        spans_names_list = []

        my_exporter = MySpanExporter(destination=spans_names_list)
        span_processor = export.SimpleExportSpanProcessor(my_exporter)
        tracer_provider.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_sampler_no_sampling(self):
        tracer_provider = trace.TracerProvider(sampling.ALWAYS_OFF)
        tracer = tracer_provider.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", context=None)
        ctx = trace_api.set_span_in_context(root_span)
        self.assertIsInstance(root_span, trace_api.DefaultSpan)
        child_span = tracer.start_span(name="child span", context=ctx)
        self.assertIsInstance(child_span, trace_api.DefaultSpan)
        self.assertEqual(
            root_span.get_span_context().trace_flags,
            trace_api.TraceFlags.DEFAULT,
        )
        self.assertEqual(
            child_span.get_span_context().trace_flags,
            trace_api.TraceFlags.DEFAULT,
        )
    def test_span_processor_dropped_spans(self):
        """Test that spans are lost when exceeding max_trace_size spans"""
        span_processor = datadog.DatadogExportSpanProcessor(self.exporter,
                                                            max_trace_size=128)
        tracer_provider = trace.TracerProvider()
        tracer_provider.add_span_processor(span_processor)
        tracer = tracer_provider.get_tracer(__name__)

        with tracer.start_as_current_span("root"):
            for _ in range(127):
                with tracer.start_span("foo"):
                    pass
            with self.assertLogs(level=logging.WARNING):
                with tracer.start_span("one-too-many"):
                    pass

        self.assertTrue(span_processor.force_flush())
        datadog_spans = get_spans(tracer, self.exporter)
        self.assertEqual(len(datadog_spans), 128)
        tracer_provider.shutdown()
    def test_sequential_server_spans(self):
        """Check that sequential RPCs get separate server spans."""

        tracer_provider = trace_sdk.TracerProvider()
        tracer = tracer_provider.get_tracer(__name__)

        interceptor = server_interceptor(tracer)

        # Capture the currently active span in each thread
        active_spans_in_handler = []

        def handler(request, context):
            active_spans_in_handler.append(tracer.get_current_span())
            return b""

        server = grpc.server(
            futures.ThreadPoolExecutor(max_workers=1),
            options=(("grpc.so_reuseport", 0), ),
        )
        server = intercept_server(server, interceptor)
        server.add_generic_rpc_handlers((UnaryUnaryRpcHandler(handler), ))

        port = server.add_insecure_port("[::]:0")
        channel = grpc.insecure_channel("localhost:{:d}".format(port))

        try:
            server.start()
            channel.unary_unary("")(b"")
            channel.unary_unary("")(b"")
        finally:
            server.stop(None)

        self.assertEqual(len(active_spans_in_handler), 2)
        # pylint:disable=unbalanced-tuple-unpacking
        span1, span2 = active_spans_in_handler
        # Spans should belong to separate traces, and each should be a root
        # span
        self.assertNotEqual(span1.context.span_id, span2.context.span_id)
        self.assertNotEqual(span1.context.trace_id, span2.context.trace_id)
        self.assertIsNone(span1.parent)
        self.assertIsNone(span1.parent)
    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_provider = trace.TracerProvider()
        tracer = tracer_provider.get_tracer(__name__)

        spans_names_list = []

        my_exporter = MySpanExporter(destination=spans_names_list)
        span_processor = export.SimpleExportSpanProcessor(my_exporter)
        tracer_provider.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_span_processor_scheduled_delay(self):
        """Test that spans are exported each schedule_delay_millis"""
        delay = 300
        span_processor = datadog.DatadogExportSpanProcessor(
            self.exporter, schedule_delay_millis=delay)
        tracer_provider = trace.TracerProvider()
        tracer_provider.add_span_processor(span_processor)
        tracer = tracer_provider.get_tracer(__name__)

        with tracer.start_span("foo"):
            pass

        time.sleep(delay / (1e3 * 2))
        datadog_spans = get_spans(tracer, self.exporter, shutdown=False)
        self.assertEqual(len(datadog_spans), 0)

        time.sleep(delay / (1e3 * 2) + 0.01)
        datadog_spans = get_spans(tracer, self.exporter, shutdown=False)
        self.assertEqual(len(datadog_spans), 1)

        tracer_provider.shutdown()
 def test_default_span_resource(self):
     tracer_provider = trace.TracerProvider()
     tracer = tracer_provider.get_tracer(__name__)
     span = tracer.start_span("root")
     # pylint: disable=protected-access
     self.assertIsInstance(span.resource, resources.Resource)
     self.assertEqual(
         span.resource.attributes.get(resources.SERVICE_NAME),
         "unknown_service",
     )
     self.assertEqual(
         span.resource.attributes.get(resources.TELEMETRY_SDK_LANGUAGE),
         "python",
     )
     self.assertEqual(
         span.resource.attributes.get(resources.TELEMETRY_SDK_NAME),
         "opentelemetry",
     )
     self.assertEqual(
         span.resource.attributes.get(resources.TELEMETRY_SDK_VERSION),
         resources._OPENTELEMETRY_SDK_VERSION,
     )
    def test_batch_span_processor_not_sampled(self):
        tracer_provider = trace.TracerProvider(
            sampler=trace.sampling.ALWAYS_OFF)
        tracer = tracer_provider.get_tracer(__name__)
        spans_names_list = []

        my_exporter = MySpanExporter(destination=spans_names_list,
                                     max_export_batch_size=128)
        span_processor = export.BatchExportSpanProcessor(
            my_exporter,
            max_queue_size=256,
            max_export_batch_size=64,
            schedule_delay_millis=100,
        )
        tracer_provider.add_span_processor(span_processor)
        with tracer.start_as_current_span("foo"):
            pass
        time.sleep(0.05)  # give some time for the exporter to upload spans

        self.assertTrue(span_processor.force_flush())
        self.assertEqual(len(spans_names_list), 0)
        span_processor.shutdown()