diff --git a/exporter/opentelemetry-exporter-jaeger/tests/test_jaeger_exporter.py b/exporter/opentelemetry-exporter-jaeger/tests/test_jaeger_exporter.py index a8809f88aaf..f2717a8bcf4 100644 --- a/exporter/opentelemetry-exporter-jaeger/tests/test_jaeger_exporter.py +++ b/exporter/opentelemetry-exporter-jaeger/tests/test_jaeger_exporter.py @@ -215,9 +215,11 @@ def test_translate_to_jaeger(self): otel_spans[0].end(end_time=end_times[0]) otel_spans[1].start(start_time=start_times[1]) + otel_spans[1].resource = Resource({}) otel_spans[1].end(end_time=end_times[1]) otel_spans[2].start(start_time=start_times[2]) + otel_spans[2].resource = Resource({}) otel_spans[2].end(end_time=end_times[2]) # pylint: disable=protected-access diff --git a/exporter/opentelemetry-exporter-zipkin/tests/test_zipkin_exporter.py b/exporter/opentelemetry-exporter-zipkin/tests/test_zipkin_exporter.py index f6e24a1495a..220acb8fa41 100644 --- a/exporter/opentelemetry-exporter-zipkin/tests/test_zipkin_exporter.py +++ b/exporter/opentelemetry-exporter-zipkin/tests/test_zipkin_exporter.py @@ -165,6 +165,7 @@ def test_export(self): ] otel_spans[0].start(start_time=start_times[0]) + otel_spans[0].resource = Resource({}) # added here to preserve order otel_spans[0].set_attribute("key_bool", False) otel_spans[0].set_attribute("key_string", "hello_world") @@ -185,6 +186,7 @@ def test_export(self): otel_spans[2].end(end_time=end_times[2]) otel_spans[3].start(start_time=start_times[3]) + otel_spans[3].resource = Resource({}) otel_spans[3].end(end_time=end_times[3]) service_name = "test-service" @@ -295,6 +297,7 @@ def test_zero_padding(self): ) otel_span.start(start_time=start_time) + otel_span.resource = Resource({}) otel_span.end(end_time=end_time) service_name = "test-service" diff --git a/opentelemetry-sdk/CHANGELOG.md b/opentelemetry-sdk/CHANGELOG.md index acd1ce1d7d1..60116ec7313 100644 --- a/opentelemetry-sdk/CHANGELOG.md +++ b/opentelemetry-sdk/CHANGELOG.md @@ -8,6 +8,8 @@ ([#1034](https://github.com/open-telemetry/opentelemetry-python/pull/1034)) - Remove lazy Event and Link API from Span interface ([#1045](https://github.com/open-telemetry/opentelemetry-python/pull/1045)) +- Populate resource attributes as per semantic conventions + ([#1053](https://github.com/open-telemetry/opentelemetry-python/pull/1053)) ## Version 0.12b0 diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index f3d31195f62..092f456fafe 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -478,7 +478,7 @@ class MeterProvider(metrics_api.MeterProvider): def __init__( self, stateful=True, - resource: Resource = Resource.create_empty(), + resource: Resource = Resource.create({}), shutdown_on_exit: bool = True, ): self.stateful = stateful diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py index d9752e3b3cb..3053a188674 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py @@ -19,11 +19,22 @@ import typing from json import dumps +import pkg_resources + LabelValue = typing.Union[str, bool, int, float] Labels = typing.Dict[str, LabelValue] logger = logging.getLogger(__name__) +TELEMETRY_SDK_LANGUAGE = "telemetry.sdk.language" +TELEMETRY_SDK_NAME = "telemetry.sdk.name" +TELEMETRY_SDK_VERSION = "telemetry.sdk.version" + +OPENTELEMETRY_SDK_VERSION = pkg_resources.get_distribution( + "opentelemetry-sdk" +).version + + class Resource: def __init__(self, labels: Labels): self._labels = labels.copy() @@ -31,8 +42,8 @@ def __init__(self, labels: Labels): @staticmethod def create(labels: Labels) -> "Resource": if not labels: - return _EMPTY_RESOURCE - return Resource(labels) + return _DEFAULT_RESOURCE + return _DEFAULT_RESOURCE.merge(Resource(labels)) @staticmethod def create_empty() -> "Resource": @@ -60,6 +71,13 @@ def __hash__(self): _EMPTY_RESOURCE = Resource({}) +_DEFAULT_RESOURCE = Resource( + { + TELEMETRY_SDK_LANGUAGE: "python", + TELEMETRY_SDK_NAME: "opentelemetry", + TELEMETRY_SDK_VERSION: OPENTELEMETRY_SDK_VERSION, + } +) class ResourceDetector(abc.ABC): diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py index a082cffa7b4..e42f4ceb0b4 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py @@ -364,7 +364,7 @@ def __init__( parent: Optional[trace_api.SpanContext] = None, sampler: Optional[sampling.Sampler] = None, trace_config: None = None, # TODO - resource: Resource = Resource.create_empty(), + resource: Resource = Resource.create({}), attributes: types.Attributes = None, # TODO events: Sequence[Event] = None, # TODO links: Sequence[trace_api.Link] = (), @@ -821,7 +821,7 @@ class TracerProvider(trace_api.TracerProvider): def __init__( self, sampler: sampling.Sampler = sampling.DEFAULT_ON, - resource: Resource = Resource.create_empty(), + resource: Resource = Resource.create({}), shutdown_on_exit: bool = True, active_span_processor: Union[ SynchronousMultiSpanProcessor, ConcurrentMultiSpanProcessor diff --git a/opentelemetry-sdk/tests/metrics/test_metrics.py b/opentelemetry-sdk/tests/metrics/test_metrics.py index b854f2d5db9..01974765203 100644 --- a/opentelemetry-sdk/tests/metrics/test_metrics.py +++ b/opentelemetry-sdk/tests/metrics/test_metrics.py @@ -40,7 +40,7 @@ def test_resource_empty(self): meter_provider = metrics.MeterProvider() meter = meter_provider.get_meter(__name__) # pylint: disable=protected-access - self.assertIs(meter.resource, resources._EMPTY_RESOURCE) + self.assertIs(meter.resource, resources._DEFAULT_RESOURCE) def test_start_pipeline(self): exporter = mock.Mock() diff --git a/opentelemetry-sdk/tests/resources/test_resources.py b/opentelemetry-sdk/tests/resources/test_resources.py index 84d0cf2ae59..be677f19fcc 100644 --- a/opentelemetry-sdk/tests/resources/test_resources.py +++ b/opentelemetry-sdk/tests/resources/test_resources.py @@ -30,18 +30,28 @@ def test_create(self): "cost": 112.12, } + expected_labels = { + "service": "ui", + "version": 1, + "has_bugs": True, + "cost": 112.12, + resources.TELEMETRY_SDK_NAME: "opentelemetry", + resources.TELEMETRY_SDK_LANGUAGE: "python", + resources.TELEMETRY_SDK_VERSION: resources.OPENTELEMETRY_SDK_VERSION, + } + resource = resources.Resource.create(labels) self.assertIsInstance(resource, resources.Resource) - self.assertEqual(resource.labels, labels) + self.assertEqual(resource.labels, expected_labels) resource = resources.Resource.create_empty() self.assertIs(resource, resources._EMPTY_RESOURCE) resource = resources.Resource.create(None) - self.assertIs(resource, resources._EMPTY_RESOURCE) + self.assertIs(resource, resources._DEFAULT_RESOURCE) resource = resources.Resource.create({}) - self.assertIs(resource, resources._EMPTY_RESOURCE) + self.assertIs(resource, resources._DEFAULT_RESOURCE) def test_resource_merge(self): left = resources.Resource({"service": "ui"}) @@ -75,7 +85,14 @@ def test_immutability(self): "cost": 112.12, } + default_labels = { + resources.TELEMETRY_SDK_NAME: "opentelemetry", + resources.TELEMETRY_SDK_LANGUAGE: "python", + resources.TELEMETRY_SDK_VERSION: resources.OPENTELEMETRY_SDK_VERSION, + } + labels_copy = labels.copy() + labels_copy.update(default_labels) resource = resources.Resource.create(labels) self.assertEqual(resource.labels, labels_copy) diff --git a/opentelemetry-sdk/tests/trace/test_trace.py b/opentelemetry-sdk/tests/trace/test_trace.py index 1c272048cae..a6b4fa93e99 100644 --- a/opentelemetry-sdk/tests/trace/test_trace.py +++ b/opentelemetry-sdk/tests/trace/test_trace.py @@ -20,7 +20,7 @@ from opentelemetry import trace as trace_api from opentelemetry.sdk import resources, trace -from opentelemetry.sdk.trace import sampling +from opentelemetry.sdk.trace import Resource, sampling from opentelemetry.sdk.util.instrumentation import InstrumentationInfo from opentelemetry.trace.status import StatusCanonicalCode from opentelemetry.util import time_ns @@ -396,7 +396,7 @@ def test_default_span_resource(self): tracer = tracer_provider.get_tracer(__name__) span = tracer.start_span("root") # pylint: disable=protected-access - self.assertIs(span.resource, resources._EMPTY_RESOURCE) + self.assertIs(span.resource, resources._DEFAULT_RESOURCE) def test_span_context_remote_flag(self): tracer = new_tracer() @@ -922,6 +922,7 @@ def test_to_json(self): trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED), ) span = trace.Span("span-name", context) + span.resource = Resource({}) self.assertEqual( span.to_json(), diff --git a/tox.ini b/tox.ini index 424b1d694eb..0fbaabce83c 100644 --- a/tox.ini +++ b/tox.ini @@ -365,6 +365,10 @@ deps = -c {toxinidir}/dev-requirements.txt -r {toxinidir}/docs-requirements.txt +commands_pre = + pip install -e {toxinidir}/opentelemetry-api \ + -e {toxinidir}/opentelemetry-sdk + changedir = docs commands =