Skip to content

Commit

Permalink
Resources sdk (#464)
Browse files Browse the repository at this point in the history
Moving resources to the SDK, as per the specification.

Modifying the values it accepts to int, float, str, and bool, to match the spec.

Introducing an empty resource. To short-circuit the common empty situation.
  • Loading branch information
mauriciovasquezbernal authored Mar 9, 2020
1 parent 9af7951 commit d7d9b15
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 77 deletions.
55 changes: 0 additions & 55 deletions opentelemetry-api/src/opentelemetry/resources/__init__.py

This file was deleted.

Empty file.
13 changes: 12 additions & 1 deletion opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from opentelemetry import metrics as metrics_api
from opentelemetry.sdk.metrics.export.aggregate import Aggregator
from opentelemetry.sdk.metrics.export.batcher import Batcher, UngroupedBatcher
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.util.instrumentation import InstrumentationInfo
from opentelemetry.util import time_ns

Expand Down Expand Up @@ -289,12 +290,16 @@ class Meter(metrics_api.Meter):
"""

def __init__(
self, instrumentation_info: "InstrumentationInfo", stateful: bool,
self,
instrumentation_info: "InstrumentationInfo",
stateful: bool,
resource: Resource = Resource.create_empty(),
):
self.instrumentation_info = instrumentation_info
self.metrics = set()
self.observers = set()
self.batcher = UngroupedBatcher(stateful)
self.resource = resource

def collect(self) -> None:
"""Collects all the metrics created with this `Meter` for export.
Expand Down Expand Up @@ -400,6 +405,11 @@ def get_label_set(self, labels: Dict[str, str]):


class MeterProvider(metrics_api.MeterProvider):
def __init__(
self, resource: Resource = Resource.create_empty(),
):
self.resource = resource

def get_meter(
self,
instrumenting_module_name: str,
Expand All @@ -413,4 +423,5 @@ def get_meter(
instrumenting_module_name, instrumenting_library_version
),
stateful=stateful,
resource=self.resource,
)
41 changes: 25 additions & 16 deletions opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,42 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import opentelemetry.resources as resources
import typing

LabelValue = typing.Union[str, bool, int, float]
Labels = typing.Dict[str, LabelValue]

class Resource(resources.Resource):
def __init__(self, labels):
self._labels = labels

class Resource:
def __init__(self, labels: Labels):
self._labels = labels.copy()

@staticmethod
def create(labels):
def create(labels: Labels) -> "Resource":
if not labels:
return _EMPTY_RESOURCE
return Resource(labels)

@staticmethod
def create_empty() -> "Resource":
return _EMPTY_RESOURCE

@property
def labels(self):
return self._labels

def merge(self, other):
if other is None:
return self
if not self._labels:
return other
merged_labels = self.labels.copy()
for key, value in other.labels.items():
def labels(self) -> Labels:
return self._labels.copy()

def merge(self, other: "Resource") -> "Resource":
merged_labels = self.labels
# pylint: disable=protected-access
for key, value in other._labels.items():
if key not in merged_labels or merged_labels[key] == "":
merged_labels[key] = value
return Resource(merged_labels)

def __eq__(self, other: object) -> bool:
if not isinstance(other, Resource):
return False
return self.labels == other.labels
return self._labels == other._labels


_EMPTY_RESOURCE = Resource({})
8 changes: 6 additions & 2 deletions opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from opentelemetry import context as context_api
from opentelemetry import trace as trace_api
from opentelemetry.sdk import util
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.util import BoundedDict, BoundedList
from opentelemetry.sdk.util.instrumentation import InstrumentationInfo
from opentelemetry.trace import SpanContext, sampling
Expand Down Expand Up @@ -127,7 +128,7 @@ class Span(trace_api.Span):
remote, null if this is a root span
sampler: The sampler used to create this span
trace_config: TODO
resource: TODO
resource: Entity producing telemetry
attributes: The span's attributes to be exported
events: Timestamped events to be exported
links: Links to other spans to be exported
Expand All @@ -147,7 +148,7 @@ def __init__(
parent: trace_api.ParentSpan = None,
sampler: Optional[sampling.Sampler] = None,
trace_config: None = None, # TODO
resource: None = None, # TODO
resource: None = None,
attributes: types.Attributes = None, # TODO
events: Sequence[trace_api.Event] = None, # TODO
links: Sequence[trace_api.Link] = (),
Expand Down Expand Up @@ -486,6 +487,7 @@ def start_span( # pylint: disable=too-many-locals
context=context,
parent=parent,
sampler=self.source.sampler,
resource=self.source.resource,
attributes=span_attributes,
span_processor=self.source._active_span_processor, # pylint:disable=protected-access
kind=kind,
Expand Down Expand Up @@ -535,9 +537,11 @@ class TracerProvider(trace_api.TracerProvider):
def __init__(
self,
sampler: sampling.Sampler = trace_api.sampling.ALWAYS_ON,
resource: Resource = Resource.create_empty(),
shutdown_on_exit: bool = True,
):
self._active_span_processor = MultiSpanProcessor()
self.resource = resource
self.sampler = sampler
self._atexit_handler = None
if shutdown_on_exit:
Expand Down
21 changes: 19 additions & 2 deletions opentelemetry-sdk/tests/metrics/test_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,24 @@
from unittest import mock

from opentelemetry import metrics as metrics_api
from opentelemetry.sdk import metrics
from opentelemetry.sdk import metrics, resources
from opentelemetry.sdk.metrics import export


class TestMeterProvider(unittest.TestCase):
def test_resource(self):
resource = resources.Resource.create({})
meter_provider = metrics.MeterProvider(resource=resource)
meter = meter_provider.get_meter(__name__)
self.assertIs(meter.resource, resource)

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)


class TestMeter(unittest.TestCase):
def test_extends_api(self):
meter = metrics.MeterProvider().get_meter(__name__)
Expand Down Expand Up @@ -126,13 +140,16 @@ def test_record_batch_exists(self):
self.assertEqual(handle.aggregator.current, 2.0)

def test_create_metric(self):
meter = metrics.MeterProvider().get_meter(__name__)
resource = mock.Mock(spec=resources.Resource)
meter_provider = metrics.MeterProvider(resource=resource)
meter = meter_provider.get_meter(__name__)
counter = meter.create_metric(
"name", "desc", "unit", int, metrics.Counter, ()
)
self.assertIsInstance(counter, metrics.Counter)
self.assertEqual(counter.value_type, int)
self.assertEqual(counter.name, "name")
self.assertIs(counter.meter.resource, resource)

def test_create_measure(self):
meter = metrics.MeterProvider().get_meter(__name__)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,35 @@
# See the License for the specific language governing permissions and
# limitations under the License.

# pylint: disable=protected-access

import unittest

from opentelemetry.sdk import resources


class TestResources(unittest.TestCase):
def test_create(self):
labels = {
"service": "ui",
"version": 1,
"has_bugs": True,
"cost": 112.12,
}

resource = resources.Resource.create(labels)
self.assertIsInstance(resource, resources.Resource)
self.assertEqual(resource.labels, labels)

resource = resources.Resource.create_empty()
self.assertIs(resource, resources._EMPTY_RESOURCE)

resource = resources.Resource.create(None)
self.assertIs(resource, resources._EMPTY_RESOURCE)

resource = resources.Resource.create({})
self.assertIs(resource, resources._EMPTY_RESOURCE)

def test_resource_merge(self):
left = resources.Resource({"service": "ui"})
right = resources.Resource({"host": "service-host"})
Expand All @@ -41,3 +64,22 @@ def test_resource_merge_empty_string(self):
left.merge(right),
resources.Resource({"service": "ui", "host": "service-host"}),
)

def test_immutability(self):
labels = {
"service": "ui",
"version": 1,
"has_bugs": True,
"cost": 112.12,
}

labels_copy = labels.copy()

resource = resources.Resource.create(labels)
self.assertEqual(resource.labels, labels_copy)

resource.labels["has_bugs"] = False
self.assertEqual(resource.labels, labels_copy)

labels["cost"] = 999.91
self.assertEqual(resource.labels, labels_copy)
16 changes: 15 additions & 1 deletion opentelemetry-sdk/tests/trace/test_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from unittest import mock

from opentelemetry import trace as trace_api
from opentelemetry.sdk import trace
from opentelemetry.sdk import resources, trace
from opentelemetry.sdk.util.instrumentation import InstrumentationInfo
from opentelemetry.trace import sampling
from opentelemetry.trace.status import StatusCanonicalCode
Expand Down Expand Up @@ -364,6 +364,20 @@ def test_start_as_current_span_explicit(self):
self.assertIs(tracer.get_current_span(), root)
self.assertIsNotNone(child.end_time)

def test_explicit_span_resource(self):
resource = resources.Resource.create({})
tracer_provider = trace.TracerProvider(resource=resource)
tracer = tracer_provider.get_tracer(__name__)
span = tracer.start_span("root")
self.assertIs(span.resource, resource)

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.assertIs(span.resource, resources._EMPTY_RESOURCE)


class TestSpan(unittest.TestCase):
def setUp(self):
Expand Down

0 comments on commit d7d9b15

Please sign in to comment.