Skip to content

Commit 14ce513

Browse files
lzchenc24t
authored andcommitted
Implement direct calling convention of metric instruments (#224)
1 parent f803b30 commit 14ce513

File tree

6 files changed

+137
-44
lines changed

6 files changed

+137
-44
lines changed

docs/conf.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@
5252
# http://www.sphinx-doc.org/en/master/config.html#confval-nitpicky
5353
# Sphinx will warn about all references where the target cannot be found.
5454
nitpicky = True
55-
nitpick_ignore = []
55+
# Sphinx does not recognize generic type TypeVars
56+
# Container supposedly were fixed, but does not work
57+
# https://github.com/sphinx-doc/sphinx/pull/3744
58+
nitpick_ignore = [("py:class", "ValueT"), ("py:class", "typing.Tuple")]
5659

5760
# Add any paths that contain templates here, relative to this directory.
5861
templates_path = ["_templates"]

examples/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,15 @@
3131
)
3232

3333
label_values = ("staging",)
34+
35+
# Direct metric usage
36+
counter.add(label_values, 25)
37+
38+
# Handle usage
3439
counter_handle = counter.get_handle(label_values)
3540
counter_handle.add(100)
41+
42+
# Record batch usage
3643
meter.record_batch(label_values, [(counter, 50)])
3744
print(counter_handle.data)
3845

opentelemetry-api/src/opentelemetry/metrics/__init__.py

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
2727
2828
"""
29-
from abc import ABC, abstractmethod
3029
from typing import Callable, Optional, Sequence, Tuple, Type, TypeVar
3130

3231
from opentelemetry.util import loader
@@ -43,27 +42,38 @@ class DefaultMetricHandle:
4342

4443
class CounterHandle:
4544
def add(self, value: ValueT) -> None:
46-
"""Increases the value of the handle by ``value``"""
45+
"""Increases the value of the handle by ``value``.
46+
47+
Args:
48+
value: The value to record to the handle.
49+
"""
4750

4851

4952
class GaugeHandle:
5053
def set(self, value: ValueT) -> None:
51-
"""Sets the current value of the handle to ``value``."""
54+
"""Sets the current value of the handle to ``value``.
55+
56+
Args:
57+
value: The value to record to the handle.
58+
"""
5259

5360

5461
class MeasureHandle:
5562
def record(self, value: ValueT) -> None:
56-
"""Records the given ``value`` to this handle."""
63+
"""Records the given ``value`` to this handle.
64+
65+
Args:
66+
value: The value to record to the handle.
67+
"""
5768

5869

59-
class Metric(ABC):
70+
class Metric:
6071
"""Base class for various types of metrics.
6172
6273
Metric class that inherit from this class are specialized with the type of
6374
handle that the metric holds.
6475
"""
6576

66-
@abstractmethod
6777
def get_handle(self, label_values: Sequence[str]) -> "object":
6878
"""Gets a handle, used for repeated-use of metrics instruments.
6979
@@ -83,6 +93,11 @@ class DefaultMetric(Metric):
8393
"""The default Metric used when no Metric implementation is available."""
8494

8595
def get_handle(self, label_values: Sequence[str]) -> "DefaultMetricHandle":
96+
"""Gets a `DefaultMetricHandle`.
97+
98+
Args:
99+
label_values: The label values associated with the handle.
100+
"""
86101
return DefaultMetricHandle()
87102

88103

@@ -93,6 +108,14 @@ def get_handle(self, label_values: Sequence[str]) -> "CounterHandle":
93108
"""Gets a `CounterHandle`."""
94109
return CounterHandle()
95110

111+
def add(self, label_values: Sequence[str], value: ValueT) -> None:
112+
"""Increases the value of the counter by ``value``.
113+
114+
Args:
115+
label_values: The label values associated with the metric.
116+
value: The value to add to the counter metric.
117+
"""
118+
96119

97120
class Gauge(Metric):
98121
"""A gauge type metric that expresses a pre-calculated value.
@@ -107,6 +130,14 @@ def get_handle(self, label_values: Sequence[str]) -> "GaugeHandle":
107130
"""Gets a `GaugeHandle`."""
108131
return GaugeHandle()
109132

133+
def set(self, label_values: Sequence[str], value: ValueT) -> None:
134+
"""Sets the value of the gauge to ``value``.
135+
136+
Args:
137+
label_values: The label values associated with the metric.
138+
value: The value to set the gauge metric to.
139+
"""
140+
110141

111142
class Measure(Metric):
112143
"""A measure type metric that represent raw stats that are recorded.
@@ -120,6 +151,14 @@ def get_handle(self, label_values: Sequence[str]) -> "MeasureHandle":
120151
"""Gets a `MeasureHandle` with a float value."""
121152
return MeasureHandle()
122153

154+
def record(self, label_values: Sequence[str], value: ValueT) -> None:
155+
"""Records the ``value`` to the measure.
156+
157+
Args:
158+
label_values: The label values associated with the metric.
159+
value: The value to record to this measure metric.
160+
"""
161+
123162

124163
MetricT = TypeVar("MetricT", Counter, Gauge, Measure)
125164

@@ -145,8 +184,9 @@ def record_batch(
145184
match the key-value pairs in the label tuples.
146185
147186
Args:
148-
label_values: The values that will be matched against to record for
149-
the handles under each metric that has those labels.
187+
label_values: The label values associated with all measurements in
188+
the batch. A measurement is a tuple, representing the `Metric`
189+
being recorded and the corresponding value to record.
150190
record_tuples: A sequence of pairs of `Metric` s and the
151191
corresponding value to record for that metric.
152192
"""

opentelemetry-api/tests/metrics/test_metrics.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,28 @@ def test_counter(self):
4242
handle = counter.get_handle(("test", "test1"))
4343
self.assertIsInstance(handle, metrics.CounterHandle)
4444

45+
def test_counter_add(self):
46+
counter = metrics.Counter()
47+
counter.add(("value",), 1)
48+
4549
def test_gauge(self):
4650
gauge = metrics.Gauge()
4751
handle = gauge.get_handle(("test", "test1"))
4852
self.assertIsInstance(handle, metrics.GaugeHandle)
4953

54+
def test_gauge_set(self):
55+
gauge = metrics.Gauge()
56+
gauge.set(("value",), 1)
57+
5058
def test_measure(self):
5159
measure = metrics.Measure()
5260
handle = measure.get_handle(("test", "test1"))
5361
self.assertIsInstance(handle, metrics.MeasureHandle)
5462

63+
def test_measure_record(self):
64+
measure = metrics.Measure()
65+
measure.record(("value",), 1)
66+
5567
def test_default_handle(self):
5668
metrics.DefaultMetricHandle()
5769

opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,43 +44,34 @@ def _validate_update(self, value: metrics_api.ValueT) -> bool:
4444

4545

4646
class CounterHandle(metrics_api.CounterHandle, BaseHandle):
47-
def update(self, value: metrics_api.ValueT) -> None:
47+
def add(self, value: metrics_api.ValueT) -> None:
48+
"""See `opentelemetry.metrics.CounterHandle.add`."""
4849
if self._validate_update(value):
4950
if self.monotonic and value < 0:
5051
logger.warning("Monotonic counter cannot descend.")
5152
return
5253
self.data += value
5354

54-
def add(self, value: metrics_api.ValueT) -> None:
55-
"""See `opentelemetry.metrics.CounterHandle._add`."""
56-
self.update(value)
57-
5855

5956
class GaugeHandle(metrics_api.GaugeHandle, BaseHandle):
60-
def update(self, value: metrics_api.ValueT) -> None:
57+
def set(self, value: metrics_api.ValueT) -> None:
58+
"""See `opentelemetry.metrics.GaugeHandle.set`."""
6159
if self._validate_update(value):
6260
if self.monotonic and value < self.data:
6361
logger.warning("Monotonic gauge cannot descend.")
6462
return
6563
self.data = value
6664

67-
def set(self, value: metrics_api.ValueT) -> None:
68-
"""See `opentelemetry.metrics.GaugeHandle._set`."""
69-
self.update(value)
70-
7165

7266
class MeasureHandle(metrics_api.MeasureHandle, BaseHandle):
73-
def update(self, value: metrics_api.ValueT) -> None:
67+
def record(self, value: metrics_api.ValueT) -> None:
68+
"""See `opentelemetry.metrics.MeasureHandle.record`."""
7469
if self._validate_update(value):
7570
if self.monotonic and value < 0:
7671
logger.warning("Monotonic measure cannot accept negatives.")
7772
return
7873
# TODO: record
7974

80-
def record(self, value: metrics_api.ValueT) -> None:
81-
"""See `opentelemetry.metrics.MeasureHandle._record`."""
82-
self.update(value)
83-
8475

8576
class Metric(metrics_api.Metric):
8677
"""See `opentelemetry.metrics.Metric`."""
@@ -116,8 +107,10 @@ def get_handle(self, label_values: Sequence[str]) -> BaseHandle:
116107
self.handles[label_values] = handle
117108
return handle
118109

110+
UPDATE_FUNCTION = lambda x, y: None # noqa: E731
111+
119112

120-
class Counter(Metric):
113+
class Counter(Metric, metrics_api.Counter):
121114
"""See `opentelemetry.metrics.Counter`.
122115
123116
By default, counter values can only go up (monotonic). Negative inputs
@@ -147,8 +140,16 @@ def __init__(
147140
monotonic=monotonic,
148141
)
149142

143+
def add(
144+
self, label_values: Sequence[str], value: metrics_api.ValueT
145+
) -> None:
146+
"""See `opentelemetry.metrics.Counter.add`."""
147+
self.get_handle(label_values).add(value)
148+
149+
UPDATE_FUNCTION = add
150+
150151

151-
class Gauge(Metric):
152+
class Gauge(Metric, metrics_api.Gauge):
152153
"""See `opentelemetry.metrics.Gauge`.
153154
154155
By default, gauge values can go both up and down (non-monotonic).
@@ -177,8 +178,16 @@ def __init__(
177178
monotonic=monotonic,
178179
)
179180

181+
def set(
182+
self, label_values: Sequence[str], value: metrics_api.ValueT
183+
) -> None:
184+
"""See `opentelemetry.metrics.Gauge.set`."""
185+
self.get_handle(label_values).set(value)
186+
187+
UPDATE_FUNCTION = set
180188

181-
class Measure(Metric):
189+
190+
class Measure(Metric, metrics_api.Measure):
182191
"""See `opentelemetry.metrics.Measure`.
183192
184193
By default, measure metrics can accept both positive and negatives.
@@ -207,6 +216,14 @@ def __init__(
207216
monotonic=monotonic,
208217
)
209218

219+
def record(
220+
self, label_values: Sequence[str], value: metrics_api.ValueT
221+
) -> None:
222+
"""See `opentelemetry.metrics.Measure.record`."""
223+
self.get_handle(label_values).record(value)
224+
225+
UPDATE_FUNCTION = record
226+
210227

211228
class Meter(metrics_api.Meter):
212229
"""See `opentelemetry.metrics.Meter`."""
@@ -218,7 +235,7 @@ def record_batch(
218235
) -> None:
219236
"""See `opentelemetry.metrics.Meter.record_batch`."""
220237
for metric, value in record_tuples:
221-
metric.get_handle(label_values).update(value)
238+
metric.UPDATE_FUNCTION(label_values, value)
222239

223240
def create_metric(
224241
self,

opentelemetry-sdk/tests/metrics/test_metrics.py

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ def test_record_batch_exists(self):
5151
label_keys = ("key1",)
5252
label_values = ("value1",)
5353
counter = metrics.Counter("name", "desc", "unit", float, label_keys)
54+
counter.add(label_values, 1.0)
5455
handle = counter.get_handle(label_values)
55-
handle.update(1.0)
5656
record_tuples = [(counter, 1.0)]
5757
meter.record_batch(label_values, record_tuples)
5858
self.assertEqual(counter.get_handle(label_values), handle)
@@ -96,12 +96,35 @@ def test_get_handle(self):
9696
self.assertEqual(metric.handles.get(label_values), handle)
9797

9898

99-
class TestCounterHandle(unittest.TestCase):
100-
def test_update(self):
101-
handle = metrics.CounterHandle(float, True, False)
102-
handle.update(2.0)
103-
self.assertEqual(handle.data, 2.0)
99+
class TestCounter(unittest.TestCase):
100+
def test_add(self):
101+
metric = metrics.Counter("name", "desc", "unit", int, ("key",))
102+
handle = metric.get_handle(("value",))
103+
metric.add(("value",), 3)
104+
metric.add(("value",), 2)
105+
self.assertEqual(handle.data, 5)
104106

107+
108+
class TestGauge(unittest.TestCase):
109+
def test_set(self):
110+
metric = metrics.Gauge("name", "desc", "unit", int, ("key",))
111+
handle = metric.get_handle(("value",))
112+
metric.set(("value",), 3)
113+
self.assertEqual(handle.data, 3)
114+
metric.set(("value",), 2)
115+
self.assertEqual(handle.data, 2)
116+
117+
118+
class TestMeasure(unittest.TestCase):
119+
def test_record(self):
120+
metric = metrics.Measure("name", "desc", "unit", int, ("key",))
121+
handle = metric.get_handle(("value",))
122+
metric.record(("value",), 3)
123+
# Record not implemented yet
124+
self.assertEqual(handle.data, 0)
125+
126+
127+
class TestCounterHandle(unittest.TestCase):
105128
def test_add(self):
106129
handle = metrics.CounterHandle(int, True, False)
107130
handle.add(3)
@@ -128,11 +151,6 @@ def test_add_incorrect_type(self, logger_mock):
128151

129152

130153
class TestGaugeHandle(unittest.TestCase):
131-
def test_update(self):
132-
handle = metrics.GaugeHandle(float, True, False)
133-
handle.update(2.0)
134-
self.assertEqual(handle.data, 2.0)
135-
136154
def test_set(self):
137155
handle = metrics.GaugeHandle(int, True, False)
138156
handle.set(3)
@@ -159,14 +177,10 @@ def test_set_incorrect_type(self, logger_mock):
159177

160178

161179
class TestMeasureHandle(unittest.TestCase):
162-
def test_update(self):
163-
handle = metrics.MeasureHandle(float, False, False)
164-
handle.update(2.0)
165-
self.assertEqual(handle.data, 0)
166-
167180
def test_record(self):
168181
handle = metrics.MeasureHandle(int, False, False)
169182
handle.record(3)
183+
# Record not implemented yet
170184
self.assertEqual(handle.data, 0)
171185

172186
def test_record_disabled(self):

0 commit comments

Comments
 (0)