def test_origin(self):
        context = trace_api.SpanContext(
            trace_id=0x000000000000000000000000DEADBEEF,
            span_id=trace_api.INVALID_SPAN,
            is_remote=True,
            trace_state=trace_api.TraceState(
                {datadog.constants.DD_ORIGIN: "origin-service"}),
        )

        root_span = trace.Span(name="root", context=context, parent=None)
        child_span = trace.Span(name="child",
                                context=context,
                                parent=root_span)
        root_span.start()
        child_span.start()
        child_span.end()
        root_span.end()

        # pylint: disable=protected-access
        exporter = datadog.DatadogSpanExporter()
        datadog_spans = [
            span.to_dict()
            for span in exporter._translate_to_datadog([root_span, child_span])
        ]

        self.assertEqual(len(datadog_spans), 2)

        actual = [
            span["meta"].get(datadog.constants.DD_ORIGIN)
            if "meta" in span else None for span in datadog_spans
        ]
        expected = ["origin-service", None]
        self.assertListEqual(actual, expected)
示例#2
0
    def test_span(self):
        with self.assertRaises(Exception):
            # pylint: disable=no-value-for-parameter
            span = trace.Span()

        span = trace.Span("name", INVALID_SPAN_CONTEXT)
        self.assertEqual(span.get_context(), INVALID_SPAN_CONTEXT)
        self.assertIs(span.is_recording(), True)
示例#3
0
    def test_start_span(self):
        """Start twice, end a not started"""
        span = trace.Span("name", mock.Mock(spec=trace_api.SpanContext))

        # end not started span
        self.assertRaises(RuntimeError, span.end)

        span.start()
        start_time = span.start_time
        with self.assertLogs(level=WARNING):
            span.start()
        self.assertEqual(start_time, span.start_time)

        self.assertIs(span.status, None)

        # status
        new_status = trace_api.status.Status(
            trace_api.status.StatusCanonicalCode.CANCELLED, "Test description"
        )
        span.set_status(new_status)
        self.assertIs(
            span.status.canonical_code,
            trace_api.status.StatusCanonicalCode.CANCELLED,
        )
        self.assertIs(span.status.description, "Test description")
    def test_sampling_priority_auto_reject(self):
        """Test sampling priority rejected."""
        parent_context = get_current_span(
            FORMAT.extract(
                get_as_list,
                {
                    FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
                    FORMAT.PARENT_ID_KEY: self.serialized_parent_id,
                    FORMAT.SAMPLING_PRIORITY_KEY: str(constants.AUTO_REJECT),
                },
            )).get_context()

        self.assertEqual(parent_context.trace_flags, constants.AUTO_REJECT)

        child = trace.Span(
            "child",
            trace_api.SpanContext(
                parent_context.trace_id,
                trace_api.RandomIdsGenerator().generate_span_id(),
                is_remote=False,
                trace_flags=parent_context.trace_flags,
                trace_state=parent_context.trace_state,
            ),
            parent=parent_context,
        )

        child_carrier = {}
        child_context = set_span_in_context(child)
        FORMAT.inject(dict.__setitem__, child_carrier, context=child_context)

        self.assertEqual(
            child_carrier[FORMAT.SAMPLING_PRIORITY_KEY],
            str(constants.AUTO_REJECT),
        )
    def test_to_json(self):
        context = trace_api.SpanContext(
            trace_id=0x000000000000000000000000DEADBEEF,
            span_id=0x00000000DEADBEF0,
            is_remote=False,
            trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED),
        )
        span = trace.Span("span-name", context)

        self.assertEqual(
            span.to_json(),
            """{
    "name": "span-name",
    "context": {
        "trace_id": "0x000000000000000000000000deadbeef",
        "span_id": "0x00000000deadbef0",
        "trace_state": "{}"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": null,
    "start_time": null,
    "end_time": null,
    "attributes": {},
    "events": [],
    "links": [],
    "resource": {}
}""",
        )
        self.assertEqual(
            span.to_json(indent=None),
            '{"name": "span-name", "context": {"trace_id": "0x000000000000000000000000deadbeef", "span_id": "0x00000000deadbef0", "trace_state": "{}"}, "kind": "SpanKind.INTERNAL", "parent_id": null, "start_time": null, "end_time": null, "attributes": {}, "events": [], "links": [], "resource": {}}',
        )
    def test_sampling_rate(self):
        context = trace_api.SpanContext(
            trace_id=0x000000000000000000000000DEADBEEF,
            span_id=0x34BF92DEEFC58C92,
            is_remote=False,
            trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED),
        )
        sampler = sampling.TraceIdRatioBased(0.5)

        span = trace.Span(name="sampled",
                          context=context,
                          parent=None,
                          sampler=sampler)
        span.start()
        span.end()

        # pylint: disable=protected-access
        exporter = datadog.DatadogSpanExporter()
        datadog_spans = [
            span.to_dict() for span in exporter._translate_to_datadog([span])
        ]

        self.assertEqual(len(datadog_spans), 1)

        actual = [
            span["metrics"].get(datadog.constants.SAMPLE_RATE_METRIC_KEY)
            if "metrics" in span else None for span in datadog_spans
        ]
        expected = [0.5]
        self.assertListEqual(actual, expected)
    def _span_to_otel_span(self, span):
        # create out-of-band span (it would be better to create a SpanView)

        # context for current span
        context = trace_api.SpanContext(
            trace_id=span.trace_id, span_id=span.span_id
        )

        # parent (if one is set)
        parent = None
        if span.parent_id is not None:
            parent = trace_api.SpanContext(
                trace_id=span.trace_id, span_id=span.parent_id
            )

        # attributes
        attributes = {}
        # ddog members to opentelemetry attributes.
        # https://github.com/DataDog/dd-trace-py/blob/1f04d0fcfb3974611967004a22882b55db77433e/oteltrace/opentracer/span.py#L113
        # TODO: use constants
        if span.span_type is not None:
            # TODO(Mauricio): OpenTracing maps to 'span.type', I think
            # component is the right one for OpenTelemetry
            attributes['component'] = span.span_type

        if span.service is not None:
            attributes['service.name'] = span.service

        if span.resource is not None:
            attributes['resource.name'] = span.resource

        if span.context.sampling_priority is not None:
            attributes['sampling.priority'] = span.context.sampling_priority

        # tags
        for tag in span.meta.items():
            key = tag[0]
            value = tag[1]

            if key == 'out.host':
                key = 'peer.hostname'
            elif key == 'out.port':
                key = 'peer.port'

            attributes[key] = value

        # build span with all that info
        otel_span = trace.Span(
            name=span.name,
            context=context,
            parent=parent,
            attributes=attributes
            # TODO: attributes, events? links?
        )

        otel_span.start_time = int(span.start * 10**9)
        otel_span.end_time = int((span.start + span.duration) * 10**9)

        return otel_span
def _create_start_and_end_span(name, span_processor):
    span = trace.Span(
        name,
        mock.Mock(spec=trace_api.SpanContext),
        span_processor=span_processor,
    )
    span.start()
    span.end()
 def test_span_override_start_and_end_time(self):
     """Span sending custom start_time and end_time values"""
     span = trace.Span("name", mock.Mock(spec=trace_api.SpanContext))
     start_time = 123
     span.start(start_time)
     self.assertEqual(start_time, span.start_time)
     end_time = 456
     span.end(end_time)
     self.assertEqual(end_time, span.end_time)
示例#10
0
    def test_context_propagation(self):
        """Test the propagation of Datadog headers."""
        parent_context = get_span_from_context(
            FORMAT.extract(
                get_as_list,
                {
                    FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
                    FORMAT.PARENT_ID_KEY: self.serialized_parent_id,
                    FORMAT.SAMPLING_PRIORITY_KEY: str(constants.AUTO_KEEP),
                    FORMAT.ORIGIN_KEY: self.serialized_origin,
                },
            )
        ).get_context()

        self.assertEqual(
            parent_context.trace_id, int(self.serialized_trace_id)
        )
        self.assertEqual(
            parent_context.span_id, int(self.serialized_parent_id)
        )
        self.assertEqual(parent_context.trace_flags, constants.AUTO_KEEP)
        self.assertEqual(
            parent_context.trace_state.get(constants.DD_ORIGIN),
            self.serialized_origin,
        )
        self.assertTrue(parent_context.is_remote)

        child = trace.Span(
            "child",
            trace_api.SpanContext(
                parent_context.trace_id,
                trace.generate_span_id(),
                is_remote=False,
                trace_flags=parent_context.trace_flags,
                trace_state=parent_context.trace_state,
            ),
            parent=parent_context,
        )

        child_carrier = {}
        child_context = set_span_in_context(child)
        FORMAT.inject(dict.__setitem__, child_carrier, context=child_context)

        self.assertEqual(
            child_carrier[FORMAT.TRACE_ID_KEY], self.serialized_trace_id
        )
        self.assertEqual(
            child_carrier[FORMAT.PARENT_ID_KEY], str(child.context.span_id)
        )
        self.assertEqual(
            child_carrier[FORMAT.SAMPLING_PRIORITY_KEY],
            str(constants.AUTO_KEEP),
        )
        self.assertEqual(
            child_carrier.get(FORMAT.ORIGIN_KEY), self.serialized_origin
        )
    def setUp(self):
        # create and save span to be used in tests
        context = trace_api.SpanContext(
            trace_id=0x000000000000000000000000DEADBEEF,
            span_id=0x00000000DEADBEF0,
        )

        self._test_span = trace.Span("test_span", context=context)
        self._test_span.start()
        self._test_span.end()
示例#12
0
def get_child_parent_new_carrier(old_carrier):

    parent_context = FORMAT.extract(get_as_list, old_carrier)

    parent = trace.Span("parent", parent_context)
    child = trace.Span(
        "child",
        trace_api.SpanContext(
            parent_context.trace_id,
            trace.generate_span_id(),
            trace_flags=parent_context.trace_flags,
            trace_state=parent_context.trace_state,
        ),
        parent=parent,
    )

    new_carrier = {}
    FORMAT.inject(child, dict.__setitem__, new_carrier)

    return child, parent, new_carrier
示例#13
0
    def test_start_span(self):
        """Start twice, end a not started"""
        span = trace.Span("name", mock.Mock(spec=trace_api.SpanContext))

        # end not started span
        self.assertRaises(RuntimeError, span.end)

        span.start()
        start_time = span.start_time
        span.start()
        self.assertEqual(start_time, span.start_time)
示例#14
0
    def test_export(self):  # pylint: disable=no-self-use
        """Check that the console exporter prints spans."""
        exporter = export.ConsoleSpanExporter()

        # Mocking stdout interferes with debugging and test reporting, mock on
        # the exporter instance instead.
        span = trace.Span("span name", mock.Mock())
        with mock.patch.object(exporter, "out") as mock_stdout:
            exporter.export([span])
        mock_stdout.write.assert_called_once_with(str(span))
        self.assertEqual(mock_stdout.write.call_count, 1)
示例#15
0
    def test_export_custom(self):  # pylint: disable=no-self-use
        """Check that console exporter uses custom io, formatter."""
        mock_span_str = mock.Mock(str)

        def formatter(span):  # pylint: disable=unused-argument
            return mock_span_str

        mock_stdout = mock.Mock()
        exporter = export.ConsoleSpanExporter(out=mock_stdout,
                                              formatter=formatter)
        exporter.export([trace.Span("span name", mock.Mock())])
        mock_stdout.write.assert_called_once_with(mock_span_str)
    def test_span_propagation(self):
        # ensure spans getter and setter works properly
        @self.app.task
        def fn_task():
            return 42

        # propagate and retrieve a Span
        task_id = "7c6731af-9533-40c3-83a9-25b58f0d837f"
        span = trace.Span("name", mock.Mock(spec=trace_api.SpanContext))
        utils.attach_span(fn_task, task_id, span)
        span_after = utils.retrieve_span(fn_task, task_id)
        self.assertIs(span, span_after)
 def test_record_error(self):
     span = trace.Span("name", mock.Mock(spec=trace_api.SpanContext))
     try:
         raise ValueError("invalid")
     except ValueError as err:
         span.record_error(err)
     error_event = span.events[0]
     self.assertEqual("error", error_event.name)
     self.assertEqual("invalid", error_event.attributes["error.message"])
     self.assertEqual("ValueError", error_event.attributes["error.type"])
     self.assertIn("ValueError: invalid",
                   error_event.attributes["error.stack"])
    def test_span_delete(self):
        # ensure the helper removes properly a propagated Span
        @self.app.task
        def fn_task():
            return 42

        # propagate a Span
        task_id = "7c6731af-9533-40c3-83a9-25b58f0d837f"
        span = trace.Span("name", mock.Mock(spec=trace_api.SpanContext))
        utils.attach_span(fn_task, task_id, span)
        # delete the Span
        utils.detach_span(fn_task, task_id)
        self.assertEqual(utils.retrieve_span(fn_task, task_id), (None, None))
def _create_start_and_end_span(name, span_processor):
    span = trace.Span(
        name,
        trace_api.SpanContext(
            0xDEADBEEF,
            0xDEADBEEF,
            is_remote=False,
            trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED),
        ),
        span_processor=span_processor,
    )
    span.start()
    span.end()
示例#20
0
    def test_return_code(self):
        span = trace.Span("name", mock.Mock(spec=trace_api.SpanContext))
        span_list = (span,)
        memory_exporter = InMemorySpanExporter()

        ret = memory_exporter.export(span_list)
        self.assertEqual(ret, export.SpanExportResult.SUCCESS)

        memory_exporter.shutdown()

        # after shutdown export should fail
        ret = memory_exporter.export(span_list)
        self.assertEqual(ret, export.SpanExportResult.FAILED_NOT_RETRYABLE)
示例#21
0
def get_child_parent_new_carrier(old_carrier):

    ctx = FORMAT.extract(get_as_list, old_carrier)
    parent_context = get_span_from_context(ctx).get_context()

    parent = trace.Span("parent", parent_context)
    child = trace.Span(
        "child",
        trace_api.SpanContext(
            parent_context.trace_id,
            trace.generate_span_id(),
            is_remote=False,
            trace_flags=parent_context.trace_flags,
            trace_state=parent_context.trace_state,
        ),
        parent=parent,
    )

    new_carrier = {}
    ctx = set_span_in_context(child)
    FORMAT.inject(dict.__setitem__, new_carrier, context=ctx)

    return child, parent, new_carrier
    def test_export(self):
        """Test that agent and/or collector are invoked"""
        # create and save span to be used in tests
        context = trace_api.SpanContext(
            trace_id=0x000000000000000000000000DEADBEEF,
            span_id=0x00000000DEADBEF0,
            is_remote=False,
        )

        test_span = trace.Span("test_span", context=context)
        test_span.start()
        test_span.end()

        self.exporter.export((test_span, ))

        self.assertEqual(self.exporter.agent_writer.write.call_count, 1)
示例#23
0
    def test_max_tag_length(self):
        service_name = "test-service"

        span_context = trace_api.SpanContext(
            0x0E0C63257DE34C926F9EFCD03927272E,
            0x04BF92DEEFC58C92,
            is_remote=False,
            trace_flags=TraceFlags(TraceFlags.SAMPLED),
        )

        span = trace.Span(name="test-span", context=span_context,)

        span.start()
        span.resource = Resource({})
        # added here to preserve order
        span.set_attribute("k1", "v" * 500)
        span.set_attribute("k2", "v" * 50)
        span.set_status(
            Status(StatusCanonicalCode.UNKNOWN, "Example description")
        )
        span.end()

        exporter = ZipkinSpanExporter(service_name)
        mock_post = MagicMock()
        with patch("requests.post", mock_post):
            mock_post.return_value = MockResponse(200)
            status = exporter.export([span])
            self.assertEqual(SpanExportResult.SUCCESS, status)

        _, kwargs = mock_post.call_args  # pylint: disable=E0633

        tags = json.loads(kwargs["data"])[0]["tags"]
        self.assertEqual(len(tags["k1"]), 128)
        self.assertEqual(len(tags["k2"]), 50)

        exporter = ZipkinSpanExporter(service_name, max_tag_value_length=2)
        mock_post = MagicMock()
        with patch("requests.post", mock_post):
            mock_post.return_value = MockResponse(200)
            status = exporter.export([span])
            self.assertEqual(SpanExportResult.SUCCESS, status)

        _, kwargs = mock_post.call_args  # pylint: disable=E0633
        tags = json.loads(kwargs["data"])[0]["tags"]
        self.assertEqual(len(tags["k1"]), 2)
        self.assertEqual(len(tags["k2"]), 2)
    def test_set_attributes_from_context_empty_keys(self):
        # it should not extract empty keys
        context = {
            "correlation_id": None,
            "exchange": "",
            "timelimit": (None, None),
            "retries": 0,
        }

        span = trace.Span("name", mock.Mock(spec=trace_api.SpanContext))
        utils.set_attributes_from_context(span, context)

        self.assertEqual(len(span.attributes), 0)
        # edge case: `timelimit` can also be a list of None values
        context = {
            "timelimit": [None, None],
        }

        utils.set_attributes_from_context(span, context)

        self.assertEqual(len(span.attributes), 0)
    def test_set_attributes_from_context(self):
        # it should extract only relevant keys
        context = {
            "correlation_id": "44b7f305",
            "delivery_info": {
                "eager": True
            },
            "eta": "soon",
            "expires": "later",
            "hostname": "localhost",
            "id": "44b7f305",
            "reply_to": "44b7f305",
            "retries": 4,
            "timelimit": ("now", "later"),
            "custom_meta": "custom_value",
            "routing_key": "celery",
        }

        span = trace.Span("name", mock.Mock(spec=trace_api.SpanContext))
        utils.set_attributes_from_context(span, context)

        self.assertEqual(span.attributes.get("messaging.message_id"),
                         "44b7f305")
        self.assertEqual(span.attributes.get("messaging.conversation_id"),
                         "44b7f305")
        self.assertEqual(span.attributes.get("messaging.destination"),
                         "celery")

        self.assertEqual(span.attributes["celery.delivery_info"],
                         str({"eager": True}))
        self.assertEqual(span.attributes.get("celery.eta"), "soon")
        self.assertEqual(span.attributes.get("celery.expires"), "later")
        self.assertEqual(span.attributes.get("celery.hostname"), "localhost")

        self.assertEqual(span.attributes.get("celery.reply_to"), "44b7f305")
        self.assertEqual(span.attributes.get("celery.retries"), 4)
        self.assertEqual(span.attributes.get("celery.timelimit"),
                         ("now", "later"))
        self.assertNotIn("custom_meta", span.attributes)
    def test_export(self):
        mock_client = mock.MagicMock()
        mock_export = mock.MagicMock()
        mock_client.Export = mock_export
        host_name = "testHostName"
        collector_exporter = OpenCensusSpanExporter(client=mock_client,
                                                    host_name=host_name)

        trace_id = 0x6E0C63257DE34C926F9EFCD03927272E
        span_id = 0x34BF92DEEFC58C92
        span_context = trace_api.SpanContext(
            trace_id,
            span_id,
            is_remote=False,
            trace_flags=TraceFlags(TraceFlags.SAMPLED),
        )
        otel_spans = [
            trace.Span(
                name="test1",
                context=span_context,
                kind=trace_api.SpanKind.CLIENT,
            )
        ]
        result_status = collector_exporter.export(otel_spans)
        self.assertEqual(SpanExportResult.SUCCESS, result_status)

        # pylint: disable=unsubscriptable-object
        export_arg = mock_export.call_args[0]
        service_request = next(export_arg[0])
        output_spans = getattr(service_request, "spans")
        output_node = getattr(service_request, "node")
        self.assertEqual(len(output_spans), 1)
        self.assertIsNotNone(getattr(output_node, "library_info"))
        self.assertIsNotNone(getattr(output_node, "service_info"))
        output_identifier = getattr(output_node, "identifier")
        self.assertEqual(getattr(output_identifier, "host_name"),
                         "testHostName")
    def test_export(self):

        span_names = ("test1", "test2", "test3")
        trace_id = 0x6E0C63257DE34C926F9EFCD03927272E
        span_id = 0x34BF92DEEFC58C92
        parent_id = 0x1111111111111111
        other_id = 0x2222222222222222

        base_time = 683647322 * 10**9  # in ns
        start_times = (
            base_time,
            base_time + 150 * 10**6,
            base_time + 300 * 10**6,
        )
        durations = (50 * 10**6, 100 * 10**6, 200 * 10**6)
        end_times = (
            start_times[0] + durations[0],
            start_times[1] + durations[1],
            start_times[2] + durations[2],
        )

        span_context = trace_api.SpanContext(
            trace_id,
            span_id,
            trace_options=TraceOptions(TraceOptions.SAMPLED),
        )
        parent_context = trace_api.SpanContext(trace_id, parent_id)
        other_context = trace_api.SpanContext(trace_id, other_id)

        event_attributes = {
            "annotation_bool": True,
            "annotation_string": "annotation_test",
            "key_float": 0.3,
        }

        event_timestamp = base_time + 50 * 10**6
        event = trace_api.Event(
            name="event0",
            timestamp=event_timestamp,
            attributes=event_attributes,
        )

        link_attributes = {"key_bool": True}

        link = trace_api.Link(context=other_context,
                              attributes=link_attributes)

        otel_spans = [
            trace.Span(
                name=span_names[0],
                context=span_context,
                parent=parent_context,
                events=(event, ),
                links=(link, ),
            ),
            trace.Span(name=span_names[1], context=parent_context,
                       parent=None),
            trace.Span(name=span_names[2], context=other_context, parent=None),
        ]

        otel_spans[0].start_time = start_times[0]
        # added here to preserve order
        otel_spans[0].set_attribute("key_bool", False)
        otel_spans[0].set_attribute("key_string", "hello_world")
        otel_spans[0].set_attribute("key_float", 111.22)
        otel_spans[0].end_time = end_times[0]

        otel_spans[1].start_time = start_times[1]
        otel_spans[1].end_time = end_times[1]

        otel_spans[2].start_time = start_times[2]
        otel_spans[2].end_time = end_times[2]

        service_name = "test-service"
        local_endpoint = {
            "serviceName": service_name,
            "port": 9411,
        }

        exporter = ZipkinSpanExporter(service_name)
        expected = [
            {
                "traceId":
                format(trace_id, "x"),
                "id":
                format(span_id, "x"),
                "name":
                span_names[0],
                "timestamp":
                start_times[0] // 10**3,
                "duration":
                durations[0] // 10**3,
                "localEndpoint":
                local_endpoint,
                "kind":
                None,
                "tags": {
                    "key_bool": "False",
                    "key_string": "hello_world",
                    "key_float": "111.22",
                },
                "annotations": [{
                    "timestamp": event_timestamp // 10**3,
                    "value": "event0",
                }],
                "debug":
                1,
                "parentId":
                format(parent_id, "x"),
            },
            {
                "traceId": format(trace_id, "x"),
                "id": format(parent_id, "x"),
                "name": span_names[1],
                "timestamp": start_times[1] // 10**3,
                "duration": durations[1] // 10**3,
                "localEndpoint": local_endpoint,
                "kind": None,
                "tags": None,
                "annotations": None,
            },
            {
                "traceId": format(trace_id, "x"),
                "id": format(other_id, "x"),
                "name": span_names[2],
                "timestamp": start_times[2] // 10**3,
                "duration": durations[2] // 10**3,
                "localEndpoint": local_endpoint,
                "kind": None,
                "tags": None,
                "annotations": None,
            },
        ]

        mock_post = MagicMock()
        with patch("requests.post", mock_post):
            mock_post.return_value = MockResponse(200)
            status = exporter.export(otel_spans)
            self.assertEqual(SpanExportResult.SUCCESS, status)

        mock_post.assert_called_with(
            url="http://localhost:9411/api/v2/spans",
            data=json.dumps(expected),
            headers={"Content-Type": "application/json"},
        )
    def test_translate_to_datadog(self):
        # pylint: disable=invalid-name
        self.maxDiff = None

        span_names = ("test1", "test2", "test3")
        trace_id = 0x6E0C63257DE34C926F9EFCD03927272E
        trace_id_low = 0x6F9EFCD03927272E
        span_id = 0x34BF92DEEFC58C92
        parent_id = 0x1111111111111111
        other_id = 0x2222222222222222

        base_time = 683647322 * 10**9  # in ns
        start_times = (
            base_time,
            base_time + 150 * 10**6,
            base_time + 300 * 10**6,
        )
        durations = (50 * 10**6, 100 * 10**6, 200 * 10**6)
        end_times = (
            start_times[0] + durations[0],
            start_times[1] + durations[1],
            start_times[2] + durations[2],
        )

        span_context = trace_api.SpanContext(trace_id,
                                             span_id,
                                             is_remote=False)
        parent_context = trace_api.SpanContext(trace_id,
                                               parent_id,
                                               is_remote=False)
        other_context = trace_api.SpanContext(trace_id,
                                              other_id,
                                              is_remote=False)

        instrumentation_info = InstrumentationInfo(__name__, "0")

        otel_spans = [
            trace.Span(
                name=span_names[0],
                context=span_context,
                parent=parent_context,
                kind=trace_api.SpanKind.CLIENT,
                instrumentation_info=instrumentation_info,
            ),
            trace.Span(
                name=span_names[1],
                context=parent_context,
                parent=None,
                instrumentation_info=instrumentation_info,
            ),
            trace.Span(
                name=span_names[2],
                context=other_context,
                parent=None,
            ),
        ]

        otel_spans[0].start(start_time=start_times[0])
        otel_spans[0].end(end_time=end_times[0])

        otel_spans[1].start(start_time=start_times[1])
        otel_spans[1].end(end_time=end_times[1])

        otel_spans[2].start(start_time=start_times[2])
        otel_spans[2].end(end_time=end_times[2])

        # pylint: disable=protected-access
        exporter = datadog.DatadogSpanExporter()
        datadog_spans = [
            span.to_dict()
            for span in exporter._translate_to_datadog(otel_spans)
        ]

        expected_spans = [
            dict(
                trace_id=trace_id_low,
                parent_id=parent_id,
                span_id=span_id,
                name="tests.test_datadog_exporter.CLIENT",
                resource=span_names[0],
                start=start_times[0],
                duration=durations[0],
                error=0,
                service="test-service",
                meta={
                    "env": "test",
                    "team": "testers"
                },
            ),
            dict(
                trace_id=trace_id_low,
                parent_id=0,
                span_id=parent_id,
                name="tests.test_datadog_exporter.INTERNAL",
                resource=span_names[1],
                start=start_times[1],
                duration=durations[1],
                error=0,
                service="test-service",
                meta={
                    "env": "test",
                    "team": "testers",
                    "version": "0.0.1"
                },
            ),
            dict(
                trace_id=trace_id_low,
                parent_id=0,
                span_id=other_id,
                name=span_names[2],
                resource=span_names[2],
                start=start_times[2],
                duration=durations[2],
                error=0,
                service="test-service",
                meta={
                    "env": "test",
                    "team": "testers",
                    "version": "0.0.1"
                },
            ),
        ]

        self.assertEqual(datadog_spans, expected_spans)
 def test_disallow_direct_span_creation(self):
     with self.assertRaises(TypeError):
         # pylint: disable=abstract-class-instantiated
         trace.Span("name", mock.Mock(spec=trace_api.SpanContext))
 def test_basic_span(self):
     span = trace.Span("name", mock.Mock(spec=trace_api.SpanContext))
     self.assertEqual(span.name, "name")