Skip to content

Commit

Permalink
Rename "handle" to "bound metric" (#470)
Browse files Browse the repository at this point in the history
bound metric is the new name, as dictated in the spec.
  • Loading branch information
lzchen committed Mar 10, 2020
1 parent d5f3a7f commit 4b6a52d
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 181 deletions.
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,20 +77,20 @@ from opentelemetry.sdk.metrics.export.controller import PushController
metrics.set_preferred_meter_provider_implementation(lambda _: MeterProvider())
meter = metrics.get_meter(__name__)
exporter = ConsoleMetricsExporter()
controller = PushController(meter, exporter, 5)
controller = PushController(meter=meter, exporter=exporter, interval=5)

counter = meter.create_metric(
"available memory",
"available memory",
"bytes",
int,
Counter,
("environment",),
name="available memory",
description="available memory",
unit="bytes",
value_type=int,
metric_type=Counter,
label_keys=("environment",),
)

label_values = ("staging",)
counter_handle = counter.get_handle(label_values)
counter_handle.add(100)
label_set = meter.get_label_set({"environment": "staging"})
bound_counter = counter.bind(label_set)
bound_counter.add(100)
```

See the [API documentation](https://open-telemetry.github.io/opentelemetry-python/) for more detail, and the [examples folder](./examples) for a more sample code.
Expand Down
16 changes: 9 additions & 7 deletions docs/examples/metrics/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,15 @@
# The meter takes a dictionary of key value pairs
label_set = meter.get_label_set({"environment": "staging"})

# Handle usage
# You can record metrics with metric handles. Handles are created by passing in
# a labelset. A handle is essentially metric data that corresponds to a specific
# set of labels. Therefore, getting a handle using the same set of labels will
# yield the same metric handle.
counter_handle = counter.get_handle(label_set)
counter_handle.add(100)
# Bound instrument usage

# You can record metrics with bound metric instruments. Bound metric
# instruments are created by passing in a labelset. A bound metric instrument
# is essentially metric data that corresponds to a specific set of labels.
# Therefore, getting a bound metric instrument using the same set of labels
# will yield the same bound metric instrument.
bound_counter = counter.bind(label_set)
bound_counter.add(100)

# Direct metric usage
# You can record metrics directly using the metric instrument. You pass in a
Expand Down
105 changes: 53 additions & 52 deletions opentelemetry-api/src/opentelemetry/metrics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,54 +37,54 @@
ValueT = TypeVar("ValueT", int, float)


class DefaultMetricHandle:
"""The default MetricHandle.
class DefaultBoundInstrument:
"""The default bound metric instrument.
Used when no MetricHandle implementation is available.
Used when no bound instrument implementation is available.
"""

def add(self, value: ValueT) -> None:
"""No-op implementation of `CounterHandle` add.
"""No-op implementation of `BoundCounter` add.
Args:
value: The value to add to the handle.
value: The value to add to the bound metric instrument.
"""

def record(self, value: ValueT) -> None:
"""No-op implementation of `MeasureHandle` record.
"""No-op implementation of `BoundMeasure` record.
Args:
value: The value to record to the handle.
value: The value to record to the bound metric instrument.
"""


class CounterHandle:
class BoundCounter:
def add(self, value: ValueT) -> None:
"""Increases the value of the handle by ``value``.
"""Increases the value of the bound counter by ``value``.
Args:
value: The value to add to the handle.
value: The value to add to the bound counter.
"""


class MeasureHandle:
class BoundMeasure:
def record(self, value: ValueT) -> None:
"""Records the given ``value`` to this handle.
"""Records the given ``value`` to this bound measure.
Args:
value: The value to record to the handle.
value: The value to record to the bound measure.
"""


class LabelSet(abc.ABC):
"""A canonicalized set of labels useful for preaggregation
Re-usable LabelSet objects provide a potential optimization for scenarios
where handles might not be effective. For example, if the LabelSet will be
re-used but only used once per metrics, handles do not offer any
optimization. It may best to pre-compute a canonicalized LabelSet once and
re-use it with the direct calling convention. LabelSets are immutable and
should be opaque in implementation.
where bound metric instruments might not be effective. For example, if the
LabelSet will be re-used but only used once per metrics, bound metric
instruments do not offer any optimization. It may best to pre-compute a
canonicalized LabelSet once and re-use it with the direct calling
convention. LabelSets are immutable and should be opaque in implementation.
"""


Expand All @@ -99,66 +99,67 @@ class Metric(abc.ABC):
"""Base class for various types of metrics.
Metric class that inherit from this class are specialized with the type of
handle that the metric holds.
bound metric instrument that the metric holds.
"""

@abc.abstractmethod
def get_handle(self, label_set: LabelSet) -> "object":
"""Gets a handle, used for repeated-use of metrics instruments.
def bind(self, label_set: LabelSet) -> "object":
"""Gets a bound metric instrument.
Handles are useful to reduce the cost of repeatedly recording a metric
with a pre-defined set of label values. All metric kinds (counter,
measure) support declaring a set of required label keys. The
values corresponding to these keys should be specified in every handle.
"Unspecified" label values, in cases where a handle is requested but
a value was not provided are permitted.
Bound metric instruments are useful to reduce the cost of repeatedly
recording a metric with a pre-defined set of label values. All metric
kinds (counter, measure) support declaring a set of required label
keys. The values corresponding to these keys should be specified in
every bound metric instrument. "Unspecified" label values, in cases
where a bound metric instrument is requested but a value was not
provided are permitted.
Args:
label_set: `LabelSet` to associate with the returned handle.
label_set: `LabelSet` to associate with the bound instrument.
"""


class DefaultMetric(Metric):
"""The default Metric used when no Metric implementation is available."""

def get_handle(self, label_set: LabelSet) -> "DefaultMetricHandle":
"""Gets a `DefaultMetricHandle`.
def bind(self, label_set: LabelSet) -> "DefaultBoundInstrument":
"""Gets a `DefaultBoundInstrument`.
Args:
label_set: `LabelSet` to associate with the returned handle.
label_set: `LabelSet` to associate with the bound instrument.
"""
return DefaultMetricHandle()
return DefaultBoundInstrument()

def add(self, value: ValueT, label_set: LabelSet) -> None:
"""No-op implementation of `Counter` add.
Args:
value: The value to add to the counter metric.
label_set: `LabelSet` to associate with the returned handle.
label_set: `LabelSet` to associate with the bound instrument.
"""

def record(self, value: ValueT, label_set: LabelSet) -> None:
"""No-op implementation of `Measure` record.
Args:
value: The value to record to this measure metric.
label_set: `LabelSet` to associate with the returned handle.
label_set: `LabelSet` to associate with the bound instrument.
"""


class Counter(Metric):
"""A counter type metric that expresses the computation of a sum."""

def get_handle(self, label_set: LabelSet) -> "CounterHandle":
"""Gets a `CounterHandle`."""
return CounterHandle()
def bind(self, label_set: LabelSet) -> "BoundCounter":
"""Gets a `BoundCounter`."""
return BoundCounter()

def add(self, value: ValueT, label_set: LabelSet) -> None:
"""Increases the value of the counter by ``value``.
Args:
value: The value to add to the counter metric.
label_set: `LabelSet` to associate with the returned handle.
label_set: `LabelSet` to associate with the returned bound counter.
"""


Expand All @@ -168,21 +169,22 @@ class Measure(Metric):
Measure metrics represent raw statistics that are recorded.
"""

def get_handle(self, label_set: LabelSet) -> "MeasureHandle":
"""Gets a `MeasureHandle` with a float value."""
return MeasureHandle()
def bind(self, label_set: LabelSet) -> "BoundMeasure":
"""Gets a `BoundMeasure`."""
return BoundMeasure()

def record(self, value: ValueT, label_set: LabelSet) -> None:
"""Records the ``value`` to the measure.
Args:
value: The value to record to this measure metric.
label_set: `LabelSet` to associate with the returned handle.
label_set: `LabelSet` to associate with the returned bound measure.
"""


class Observer(abc.ABC):
"""An observer type metric instrument used to capture a current set of values.
"""An observer type metric instrument used to capture a current set of
values.
Observer instruments are asynchronous, a callback is invoked with the
Expand Down Expand Up @@ -283,16 +285,15 @@ def record_batch(
) -> None:
"""Atomically records a batch of `Metric` and value pairs.
Allows the functionality of acting upon multiple metrics with
a single API call. Implementations should find metric and handles that
match the key-value pairs in the label tuples.
Allows the functionality of acting upon multiple metrics with a single
API call. Implementations should find bound metric instruments that
match the key-value pairs in the labelset.
Args:
label_set: The `LabelSet` associated with all measurements in
the batch. A measurement is a tuple, representing the `Metric`
being recorded and the corresponding value to record.
record_tuples: A sequence of pairs of `Metric` s and the
corresponding value to record for that metric.
Args: label_set: The `LabelSet` associated with all measurements in the
batch. A measurement is a tuple, representing the `Metric` being
recorded and the corresponding value to record. record_tuples: A
sequence of pairs of `Metric` s and the corresponding value to
record for that metric.
"""

@abc.abstractmethod
Expand Down
30 changes: 16 additions & 14 deletions opentelemetry-api/tests/metrics/test_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@ class TestMetrics(unittest.TestCase):
def test_default(self):
default = metrics.DefaultMetric()
default_ls = metrics.DefaultLabelSet()
handle = default.get_handle(default_ls)
self.assertIsInstance(handle, metrics.DefaultMetricHandle)
bound_metric_instr = default.bind(default_ls)
self.assertIsInstance(
bound_metric_instr, metrics.DefaultBoundInstrument
)

def test_counter(self):
counter = metrics.Counter()
label_set = metrics.LabelSet()
handle = counter.get_handle(label_set)
self.assertIsInstance(handle, metrics.CounterHandle)
bound_counter = counter.bind(label_set)
self.assertIsInstance(bound_counter, metrics.BoundCounter)

def test_counter_add(self):
counter = metrics.Counter()
Expand All @@ -39,21 +41,21 @@ def test_counter_add(self):
def test_measure(self):
measure = metrics.Measure()
label_set = metrics.LabelSet()
handle = measure.get_handle(label_set)
self.assertIsInstance(handle, metrics.MeasureHandle)
bound_measure = measure.bind(label_set)
self.assertIsInstance(bound_measure, metrics.BoundMeasure)

def test_measure_record(self):
measure = metrics.Measure()
label_set = metrics.LabelSet()
measure.record(1, label_set)

def test_default_handle(self):
metrics.DefaultMetricHandle()
def test_default_bound_metric(self):
metrics.DefaultBoundInstrument()

def test_counter_handle(self):
handle = metrics.CounterHandle()
handle.add(1)
def test_bound_counter(self):
bound_counter = metrics.BoundCounter()
bound_counter.add(1)

def test_measure_handle(self):
handle = metrics.MeasureHandle()
handle.record(1)
def test_bound_measure(self):
bound_measure = metrics.BoundMeasure()
bound_measure.record(1)

0 comments on commit 4b6a52d

Please sign in to comment.