diff --git a/docs/examples/datadog_exporter/client.py b/docs/examples/datadog_exporter/client.py index 2570c426d5f..a5ddf2843c4 100644 --- a/docs/examples/datadog_exporter/client.py +++ b/docs/examples/datadog_exporter/client.py @@ -21,14 +21,19 @@ DatadogExportSpanProcessor, DatadogSpanExporter, ) +from opentelemetry.sdk import resources from opentelemetry.sdk.trace import TracerProvider -trace.set_tracer_provider(TracerProvider()) +service_name = "example-client" + +resource = resources.Resource.create({"service.name": service_name}) + +trace.set_tracer_provider(TracerProvider(resource=resource)) trace.get_tracer_provider().add_span_processor( DatadogExportSpanProcessor( DatadogSpanExporter( - agent_url="http://localhost:8126", service="example-client" + agent_url="http://localhost:8126", service=service_name ) ) ) diff --git a/exporter/opentelemetry-exporter-datadog/CHANGELOG.md b/exporter/opentelemetry-exporter-datadog/CHANGELOG.md index 728c0810901..0fab809da95 100644 --- a/exporter/opentelemetry-exporter-datadog/CHANGELOG.md +++ b/exporter/opentelemetry-exporter-datadog/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- Add support for span resource labels and service name + ## Version 0.12b0 Released 2020-08-14 diff --git a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/constants.py b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/constants.py index 92d736c9181..2ae5386e848 100644 --- a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/constants.py +++ b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/constants.py @@ -6,3 +6,4 @@ SAMPLING_PRIORITY_KEY = "_sampling_priority_v1" ENV_KEY = "env" VERSION_KEY = "version" +SERVICE_NAME_TAG = "service.name" diff --git a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py index 37c78187f8e..2b36299989f 100644 --- a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py +++ b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py @@ -26,7 +26,13 @@ from opentelemetry.trace.status import StatusCanonicalCode # pylint:disable=relative-beyond-top-level -from .constants import DD_ORIGIN, ENV_KEY, SAMPLE_RATE_METRIC_KEY, VERSION_KEY +from .constants import ( + DD_ORIGIN, + ENV_KEY, + SAMPLE_RATE_METRIC_KEY, + SERVICE_NAME_TAG, + VERSION_KEY, +) logger = logging.getLogger(__name__) @@ -107,6 +113,7 @@ def shutdown(self): self.agent_writer.stop() self.agent_writer.join(self.agent_writer.exit_timeout) + # pylint: disable=too-many-locals def _translate_to_datadog(self, spans): datadog_spans = [] @@ -119,10 +126,16 @@ def _translate_to_datadog(self, spans): # duration. tracer = None + # extract resource attributes to be used as tags as well as potential service name + [ + resource_tags, + resource_service_name, + ] = _extract_tags_from_resource(span.resource) + datadog_span = DatadogSpan( tracer, _get_span_name(span), - service=self.service, + service=resource_service_name or self.service, resource=_get_resource(span), span_type=_get_span_type(span), trace_id=trace_id, @@ -140,7 +153,12 @@ def _translate_to_datadog(self, spans): datadog_span.set_tag("error.msg", exc_val) datadog_span.set_tag("error.type", exc_type) - datadog_span.set_tags(span.attributes) + # combine resource attributes and span attributes, don't modify existing span attributes + combined_span_tags = {} + combined_span_tags.update(resource_tags) + combined_span_tags.update(span.attributes) + + datadog_span.set_tags(combined_span_tags) # add configured env tag if self.env is not None: @@ -282,3 +300,19 @@ def _parse_tags_str(tags_str): parsed_tags[key] = value return parsed_tags + + +def _extract_tags_from_resource(resource): + """Parse tags from resource.attributes, except service.name which + has special significance within datadog""" + tags = {} + service_name = None + if not (resource and getattr(resource, "attributes", None)): + return [tags, service_name] + + for attribute_key, attribute_value in resource.attributes.items(): + if attribute_key == SERVICE_NAME_TAG: + service_name = attribute_value + else: + tags[attribute_key] = attribute_value + return [tags, service_name] diff --git a/exporter/opentelemetry-exporter-datadog/tests/test_datadog_exporter.py b/exporter/opentelemetry-exporter-datadog/tests/test_datadog_exporter.py index 73c8cb3bf82..e1973e5ac65 100644 --- a/exporter/opentelemetry-exporter-datadog/tests/test_datadog_exporter.py +++ b/exporter/opentelemetry-exporter-datadog/tests/test_datadog_exporter.py @@ -23,7 +23,7 @@ from opentelemetry import trace as trace_api from opentelemetry.exporter import datadog from opentelemetry.sdk import trace -from opentelemetry.sdk.trace import sampling +from opentelemetry.sdk.trace import Resource, sampling from opentelemetry.sdk.util.instrumentation import InstrumentationInfo @@ -144,6 +144,17 @@ def test_translate_to_datadog(self): # pylint: disable=invalid-name self.maxDiff = None + resource = Resource( + attributes={ + "key_resource": "some_resource", + "service.name": "resource_service_name", + } + ) + + resource_without_service = Resource( + attributes={"conflicting_key": "conflicting_value"} + ) + span_names = ("test1", "test2", "test3") trace_id = 0x6E0C63257DE34C926F9EFCD03927272E trace_id_low = 0x6F9EFCD03927272E @@ -183,18 +194,25 @@ def test_translate_to_datadog(self): parent=parent_context, kind=trace_api.SpanKind.CLIENT, instrumentation_info=instrumentation_info, + resource=Resource({}), ), trace.Span( name=span_names[1], context=parent_context, parent=None, instrumentation_info=instrumentation_info, + resource=resource_without_service, ), trace.Span( - name=span_names[2], context=other_context, parent=None, + name=span_names[2], + context=other_context, + parent=None, + resource=resource, ), ] + otel_spans[1].set_attribute("conflicting_key", "original_value") + otel_spans[0].start(start_time=start_times[0]) otel_spans[0].end(end_time=end_times[0]) @@ -234,7 +252,12 @@ def test_translate_to_datadog(self): duration=durations[1], error=0, service="test-service", - meta={"env": "test", "team": "testers", "version": "0.0.1"}, + meta={ + "env": "test", + "team": "testers", + "version": "0.0.1", + "conflicting_key": "original_value", + }, ), dict( trace_id=trace_id_low, @@ -245,8 +268,13 @@ def test_translate_to_datadog(self): start=start_times[2], duration=durations[2], error=0, - service="test-service", - meta={"env": "test", "team": "testers", "version": "0.0.1"}, + service="resource_service_name", + meta={ + "env": "test", + "team": "testers", + "version": "0.0.1", + "key_resource": "some_resource", + }, ), ]