Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename "handle" to "bound metric instrument" #470

Merged
merged 11 commits into from
Mar 10, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
bound metric to instrument
  • Loading branch information
lzchen committed Mar 9, 2020
commit 9b160a6ca2b0fbc2020272eeb4388a064df86151
9 changes: 5 additions & 4 deletions examples/metrics/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,11 @@
label_set = meter.get_label_set({"environment": "staging"})

# Handle usage
# You can record metrics with bound metrics. Bound metrics are created by
# passing in a labelset. A bound metric is essentially metric data that
# corresponds to a specific set of labels. Therefore, getting a bound metric
# using the same set of labels will yield the same bound metric.

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

Expand Down
56 changes: 27 additions & 29 deletions opentelemetry-api/src/opentelemetry/metrics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@
ValueT = TypeVar("ValueT", int, float)


class DefaultBoundMetric:
"""The default BoundMetric.
class DefaultBoundInstrument:
"""The default bound metric instrument.

Used when no BoundMetric implementation is available.
Used when no bound instrument implementation is available.
"""

def add(self, value: ValueT) -> None:
Expand Down Expand Up @@ -80,11 +80,11 @@ class LabelSet(abc.ABC):
"""A canonicalized set of labels useful for preaggregation

Re-usable LabelSet objects provide a potential optimization for scenarios
where bound metrics might not be effective. For example, if the LabelSet
will be re-used but only used once per metrics, bound metrics 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,35 +99,34 @@ class Metric(abc.ABC):
"""Base class for various types of metrics.

Metric class that inherit from this class are specialized with the type of
bound metric that the metric holds.
bound metric instrument that the metric holds.
"""

@abc.abstractmethod
def bind(self, label_set: LabelSet) -> "object":
"""Gets a bound metric, used for repeated-use of metrics instruments.
lzchen marked this conversation as resolved.
Show resolved Hide resolved

Bound metrics 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. "Unspecified" label values, in cases where a bound metric 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. "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 bound metric.
Args: label_set: `LabelSet` to associate with the returned bound metric.
"""


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

def bind(self, label_set: LabelSet) -> "DefaultBoundMetric":
"""Gets a `DefaultBoundMetric`.
def bind(self, label_set: LabelSet) -> "DefaultBoundInstrument":
"""Gets a `DefaultBoundInstrument`.

Args:
label_set: `LabelSet` to associate with the returned bound metric.
"""
return DefaultBoundMetric()
return DefaultBoundInstrument()

def add(self, value: ValueT, label_set: LabelSet) -> None:
"""No-op implementation of `Counter` add.
Expand Down Expand Up @@ -283,16 +282,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 bound metrics
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
22 changes: 11 additions & 11 deletions opentelemetry-api/tests/metrics/test_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ class TestMetrics(unittest.TestCase):
def test_default(self):
default = metrics.DefaultMetric()
default_ls = metrics.DefaultLabelSet()
bound_metric = default.bind(default_ls)
self.assertIsInstance(bound_metric, metrics.DefaultBoundMetric)
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()
bound_metric = counter.bind(label_set)
self.assertIsInstance(bound_metric, metrics.BoundCounter)
bound_counter = counter.bind(label_set)
self.assertIsInstance(bound_counter, metrics.BoundCounter)

def test_counter_add(self):
counter = metrics.Counter()
Expand All @@ -39,21 +39,21 @@ def test_counter_add(self):
def test_measure(self):
measure = metrics.Measure()
label_set = metrics.LabelSet()
bound_metric = measure.bind(label_set)
self.assertIsInstance(bound_metric, metrics.BoundMeasure)
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_bound_metric(self):
metrics.DefaultBoundMetric()
metrics.DefaultBoundInstrument()

def test_bound_counter(self):
bound_metric = metrics.BoundCounter()
bound_metric.add(1)
bound_counter = metrics.BoundCounter()
bound_counter.add(1)

def test_bound_measure(self):
bound_metric = metrics.BoundMeasure()
bound_metric.record(1)
bound_measure = metrics.BoundMeasure()
bound_measure.record(1)
48 changes: 25 additions & 23 deletions opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,18 @@ def __eq__(self, other):
return self._encoded == other._encoded


class BaseBoundMetric:
"""Class containing common behavior for all bound metrics.
class BaseBoundInstrument:
"""Class containing common behavior for all bound metric instruments.

Bound metrics are responsible for operating on data for metric instruments
for a specific set of labels.
Bound metric instruments are responsible for operating on data for metric
instruments for a specific set of labels.

Args: value_type: The type of values this bound metric holds (int, float).
enabled: True if the originating instrument is enabled. aggregator: The
aggregator for this bound metric. Will handle aggregation upon updates
and checkpointing of values for exporting.
Args:
value_type: The type of values for this bound instrument (int, float).
enabled: True if the originating instrument is enabled.
aggregator: The aggregator for this bound metric instrument. Will
handle aggregation upon updates and checkpointing of values for
exporting.
"""

def __init__(
Expand Down Expand Up @@ -93,14 +95,14 @@ def __repr__(self):
)


class BoundCounter(metrics_api.BoundCounter, BaseBoundMetric):
class BoundCounter(metrics_api.BoundCounter, BaseBoundInstrument):
def add(self, value: metrics_api.ValueT) -> None:
"""See `opentelemetry.metrics.BoundCounter.add`."""
if self._validate_update(value):
self.update(value)


class BoundMeasure(metrics_api.BoundMeasure, BaseBoundMetric):
class BoundMeasure(metrics_api.BoundMeasure, BaseBoundInstrument):
def record(self, value: metrics_api.ValueT) -> None:
"""See `opentelemetry.metrics.BoundMeasure.record`."""
if self._validate_update(value):
Expand All @@ -113,10 +115,10 @@ class Metric(metrics_api.Metric):
Also known as metric instrument. This is the class that is used to
represent a metric that is to be continuously recorded and tracked. Each
metric has a set of bound metrics that are created from the metric. See
`BaseBoundMetric` for information on bound metrics.
`BaseBoundInstrument` for information on bound metric instruments.
"""

BOUND_METRIC_TYPE = BaseBoundMetric
BOUND_INSTR_TYPE = BaseBoundInstrument

def __init__(
self,
Expand All @@ -135,20 +137,20 @@ def __init__(
self.meter = meter
self.label_keys = label_keys
self.enabled = enabled
self.bound_metrics = {}
self.bound_instruments = {}

def bind(self, label_set: LabelSet) -> BaseBoundMetric:
def bind(self, label_set: LabelSet) -> BaseBoundInstrument:
"""See `opentelemetry.metrics.Metric.bind`."""
bound_metric = self.bound_metrics.get(label_set)
if not bound_metric:
bound_metric = self.BOUND_METRIC_TYPE(
bound_instrument = self.bound_instruments.get(label_set)
if not bound_instrument:
bound_instrument = self.BOUND_INSTR_TYPE(
self.value_type,
self.enabled,
# Aggregator will be created based off type of metric
self.meter.batcher.aggregator_for(self.__class__),
)
self.bound_metrics[label_set] = bound_metric
return bound_metric
self.bound_instruments[label_set] = bound_instrument
return bound_instrument

def __repr__(self):
return '{}(name="{}", description="{}")'.format(
Expand All @@ -162,7 +164,7 @@ class Counter(Metric, metrics_api.Counter):
"""See `opentelemetry.metrics.Counter`.
"""

BOUND_METRIC_TYPE = BoundCounter
BOUND_INSTR_TYPE = BoundCounter

def add(self, value: metrics_api.ValueT, label_set: LabelSet) -> None:
"""See `opentelemetry.metrics.Counter.add`."""
Expand All @@ -174,7 +176,7 @@ def add(self, value: metrics_api.ValueT, label_set: LabelSet) -> None:
class Measure(Metric, metrics_api.Measure):
"""See `opentelemetry.metrics.Measure`."""

BOUND_METRIC_TYPE = BoundMeasure
BOUND_INSTR_TYPE = BoundMeasure

def record(self, value: metrics_api.ValueT, label_set: LabelSet) -> None:
"""See `opentelemetry.metrics.Measure.record`."""
Expand Down Expand Up @@ -294,9 +296,9 @@ def collect(self) -> None:
def _collect_metrics(self) -> None:
for metric in self.metrics:
if metric.enabled:
for label_set, bound_metric in metric.bound_metrics.items():
for label_set, bound_instr in metric.bound_instruments.items():
# TODO: Consider storing records in memory?
record = Record(metric, label_set, bound_metric.aggregator)
record = Record(metric, label_set, bound_instr.aggregator)
# Checkpoints the current aggregators
# Applies different batching logic based on type of batcher
self.batcher.process(record)
Expand Down
Loading