Skip to content

Commit 03b3c9a

Browse files
aabmassocelotl
authored andcommitted
Make measurement a concrete class (#2153)
* Make Measurement a concrete class * comments * update changelog
1 parent 6592f1d commit 03b3c9a

File tree

5 files changed

+118
-70
lines changed

5 files changed

+118
-70
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
([#2207](https://github.com/open-telemetry/opentelemetry-python/pull/2207))
1111
- remove `X-B3-ParentSpanId` for B3 propagator as per OpenTelemetry specification
1212
([#2237](https://github.com/open-telemetry/opentelemetry-python/pull/2237))
13+
- Make Measurement a concrete class
14+
([#2153](https://github.com/open-telemetry/opentelemetry-python/pull/2153))
1315
- Add metrics API
1416
([#1887](https://github.com/open-telemetry/opentelemetry-python/pull/1887))
1517

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

+27-14
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,41 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
# pylint: disable=too-many-ancestors
16-
# type:ignore
15+
from typing import Union
1716

17+
from opentelemetry.util.types import Attributes
1818

19-
from abc import ABC, abstractmethod
2019

20+
class Measurement:
21+
"""A measurement observed in an asynchronous instrument
22+
23+
Return/yield instances of this class from asynchronous instrument callbacks.
24+
25+
Args:
26+
value: The float or int measured value
27+
attributes: The measurement's attributes
28+
"""
29+
30+
def __init__(
31+
self, value: Union[int, float], attributes: Attributes = None
32+
) -> None:
33+
self._value = value
34+
self._attributes = attributes
2135

22-
class Measurement(ABC):
2336
@property
24-
def value(self):
37+
def value(self) -> Union[float, int]:
2538
return self._value
2639

2740
@property
28-
def attributes(self):
41+
def attributes(self) -> Attributes:
2942
return self._attributes
3043

31-
@abstractmethod
32-
def __init__(self, value, attributes=None):
33-
self._value = value
34-
self._attributes = attributes
35-
44+
def __eq__(self, other: object) -> bool:
45+
return (
46+
isinstance(other, Measurement)
47+
and self.value == other.value
48+
and self.attributes == other.attributes
49+
)
3650

37-
class DefaultMeasurement(Measurement):
38-
def __init__(self, value, attributes=None):
39-
super().__init__(value, attributes=attributes)
51+
def __repr__(self) -> str:
52+
return f"Measurement(value={self.value}, attributes={self.attributes})"

opentelemetry-api/tests/metrics/integration_test/test_cpu_time.py

+36-44
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,6 @@
2323
# FIXME Test that the instrument methods can be called concurrently safely.
2424

2525

26-
class ChildMeasurement(Measurement):
27-
def __init__(self, value, attributes=None):
28-
super().__init__(value, attributes=attributes)
29-
30-
def __eq__(self, o: Measurement) -> bool:
31-
return self.value == o.value and self.attributes == o.attributes
32-
33-
3426
class TestCpuTimeIntegration(TestCase):
3527
"""Integration test of scraping CPU time from proc stat with an observable
3628
counter"""
@@ -48,24 +40,24 @@ class TestCpuTimeIntegration(TestCase):
4840
softirq 1644603067 0 166540056 208 309152755 8936439 0 1354908 935642970 13 222975718\n"""
4941

5042
measurements_expected = [
51-
ChildMeasurement(6150, {"cpu": "cpu0", "state": "user"}),
52-
ChildMeasurement(3177, {"cpu": "cpu0", "state": "nice"}),
53-
ChildMeasurement(5946, {"cpu": "cpu0", "state": "system"}),
54-
ChildMeasurement(891264, {"cpu": "cpu0", "state": "idle"}),
55-
ChildMeasurement(1296, {"cpu": "cpu0", "state": "iowait"}),
56-
ChildMeasurement(0, {"cpu": "cpu0", "state": "irq"}),
57-
ChildMeasurement(8343, {"cpu": "cpu0", "state": "softirq"}),
58-
ChildMeasurement(421, {"cpu": "cpu0", "state": "guest"}),
59-
ChildMeasurement(0, {"cpu": "cpu0", "state": "guest_nice"}),
60-
ChildMeasurement(5882, {"cpu": "cpu1", "state": "user"}),
61-
ChildMeasurement(3491, {"cpu": "cpu1", "state": "nice"}),
62-
ChildMeasurement(6404, {"cpu": "cpu1", "state": "system"}),
63-
ChildMeasurement(891564, {"cpu": "cpu1", "state": "idle"}),
64-
ChildMeasurement(1244, {"cpu": "cpu1", "state": "iowait"}),
65-
ChildMeasurement(0, {"cpu": "cpu1", "state": "irq"}),
66-
ChildMeasurement(2410, {"cpu": "cpu1", "state": "softirq"}),
67-
ChildMeasurement(418, {"cpu": "cpu1", "state": "guest"}),
68-
ChildMeasurement(0, {"cpu": "cpu1", "state": "guest_nice"}),
43+
Measurement(6150, {"cpu": "cpu0", "state": "user"}),
44+
Measurement(3177, {"cpu": "cpu0", "state": "nice"}),
45+
Measurement(5946, {"cpu": "cpu0", "state": "system"}),
46+
Measurement(891264, {"cpu": "cpu0", "state": "idle"}),
47+
Measurement(1296, {"cpu": "cpu0", "state": "iowait"}),
48+
Measurement(0, {"cpu": "cpu0", "state": "irq"}),
49+
Measurement(8343, {"cpu": "cpu0", "state": "softirq"}),
50+
Measurement(421, {"cpu": "cpu0", "state": "guest"}),
51+
Measurement(0, {"cpu": "cpu0", "state": "guest_nice"}),
52+
Measurement(5882, {"cpu": "cpu1", "state": "user"}),
53+
Measurement(3491, {"cpu": "cpu1", "state": "nice"}),
54+
Measurement(6404, {"cpu": "cpu1", "state": "system"}),
55+
Measurement(891564, {"cpu": "cpu1", "state": "idle"}),
56+
Measurement(1244, {"cpu": "cpu1", "state": "iowait"}),
57+
Measurement(0, {"cpu": "cpu1", "state": "irq"}),
58+
Measurement(2410, {"cpu": "cpu1", "state": "softirq"}),
59+
Measurement(418, {"cpu": "cpu1", "state": "guest"}),
60+
Measurement(0, {"cpu": "cpu1", "state": "guest_nice"}),
6961
]
7062

7163
def test_cpu_time_callback(self):
@@ -78,31 +70,31 @@ def cpu_time_callback() -> Iterable[Measurement]:
7870
if not line.startswith("cpu"):
7971
break
8072
cpu, *states = line.split()
81-
yield ChildMeasurement(
73+
yield Measurement(
8274
int(states[0]) // 100, {"cpu": cpu, "state": "user"}
8375
)
84-
yield ChildMeasurement(
76+
yield Measurement(
8577
int(states[1]) // 100, {"cpu": cpu, "state": "nice"}
8678
)
87-
yield ChildMeasurement(
79+
yield Measurement(
8880
int(states[2]) // 100, {"cpu": cpu, "state": "system"}
8981
)
90-
yield ChildMeasurement(
82+
yield Measurement(
9183
int(states[3]) // 100, {"cpu": cpu, "state": "idle"}
9284
)
93-
yield ChildMeasurement(
85+
yield Measurement(
9486
int(states[4]) // 100, {"cpu": cpu, "state": "iowait"}
9587
)
96-
yield ChildMeasurement(
88+
yield Measurement(
9789
int(states[5]) // 100, {"cpu": cpu, "state": "irq"}
9890
)
99-
yield ChildMeasurement(
91+
yield Measurement(
10092
int(states[6]) // 100, {"cpu": cpu, "state": "softirq"}
10193
)
102-
yield ChildMeasurement(
94+
yield Measurement(
10395
int(states[7]) // 100, {"cpu": cpu, "state": "guest"}
10496
)
105-
yield ChildMeasurement(
97+
yield Measurement(
10698
int(states[8]) // 100, {"cpu": cpu, "state": "guest_nice"}
10799
)
108100

@@ -130,54 +122,54 @@ def cpu_time_generator() -> Generator[
130122
break
131123
cpu, *states = line.split()
132124
measurements.append(
133-
ChildMeasurement(
125+
Measurement(
134126
int(states[0]) // 100,
135127
{"cpu": cpu, "state": "user"},
136128
)
137129
)
138130
measurements.append(
139-
ChildMeasurement(
131+
Measurement(
140132
int(states[1]) // 100,
141133
{"cpu": cpu, "state": "nice"},
142134
)
143135
)
144136
measurements.append(
145-
ChildMeasurement(
137+
Measurement(
146138
int(states[2]) // 100,
147139
{"cpu": cpu, "state": "system"},
148140
)
149141
)
150142
measurements.append(
151-
ChildMeasurement(
143+
Measurement(
152144
int(states[3]) // 100,
153145
{"cpu": cpu, "state": "idle"},
154146
)
155147
)
156148
measurements.append(
157-
ChildMeasurement(
149+
Measurement(
158150
int(states[4]) // 100,
159151
{"cpu": cpu, "state": "iowait"},
160152
)
161153
)
162154
measurements.append(
163-
ChildMeasurement(
155+
Measurement(
164156
int(states[5]) // 100, {"cpu": cpu, "state": "irq"}
165157
)
166158
)
167159
measurements.append(
168-
ChildMeasurement(
160+
Measurement(
169161
int(states[6]) // 100,
170162
{"cpu": cpu, "state": "softirq"},
171163
)
172164
)
173165
measurements.append(
174-
ChildMeasurement(
166+
Measurement(
175167
int(states[7]) // 100,
176168
{"cpu": cpu, "state": "guest"},
177169
)
178170
)
179171
measurements.append(
180-
ChildMeasurement(
172+
Measurement(
181173
int(states[8]) // 100,
182174
{"cpu": cpu, "state": "guest_nice"},
183175
)

opentelemetry-api/tests/metrics/test_instruments.py

+7-12
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,6 @@ def __init__(self, name, *args, unit="", description="", **kwargs):
4545
)
4646

4747

48-
class ChildMeasurement(Measurement):
49-
def __init__(self, value, attributes=None):
50-
super().__init__(value, attributes=attributes)
51-
52-
5348
class TestInstrument(TestCase):
5449
def test_instrument_has_name(self):
5550
"""
@@ -341,8 +336,8 @@ def callback():
341336
list(observable_counter.callback())
342337

343338
def callback():
344-
yield [ChildMeasurement(1), ChildMeasurement(2)]
345-
yield [ChildMeasurement(-1)]
339+
yield [Measurement(1), Measurement(2)]
340+
yield [Measurement(-1)]
346341

347342
observable_counter = DefaultObservableCounter("name", callback())
348343

@@ -382,7 +377,7 @@ def callback_invalid_return():
382377
list(observable_counter.callback())
383378

384379
def callback_valid():
385-
return [ChildMeasurement(1), ChildMeasurement(2)]
380+
return [Measurement(1), Measurement(2)]
386381

387382
observable_counter = DefaultObservableCounter("name", callback_valid)
388383

@@ -391,7 +386,7 @@ def callback_valid():
391386
list(observable_counter.callback())
392387

393388
def callback_one_invalid():
394-
return [ChildMeasurement(1), ChildMeasurement(-2)]
389+
return [Measurement(1), Measurement(-2)]
395390

396391
observable_counter = DefaultObservableCounter(
397392
"name", callback_one_invalid
@@ -578,7 +573,7 @@ def callback():
578573
list(observable_gauge.callback())
579574

580575
def callback():
581-
yield [ChildMeasurement(1), ChildMeasurement(-1)]
576+
yield [Measurement(1), Measurement(-1)]
582577

583578
observable_gauge = DefaultObservableGauge("name", callback())
584579
with self.assertRaises(AssertionError):
@@ -786,8 +781,8 @@ def test_observable_up_down_counter_callback(self):
786781
)
787782

788783
def callback():
789-
yield ChildMeasurement(1)
790-
yield ChildMeasurement(-1)
784+
yield Measurement(1)
785+
yield Measurement(-1)
791786

792787
with self.assertRaises(AssertionError):
793788
with self.assertLogs(level=ERROR):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from unittest import TestCase
16+
17+
from opentelemetry.metrics.measurement import Measurement
18+
19+
20+
class TestMeasurement(TestCase):
21+
def test_measurement_init(self):
22+
try:
23+
# int
24+
Measurement(321, {"hello": "world"})
25+
26+
# float
27+
Measurement(321.321, {"hello": "world"})
28+
except Exception: # pylint: disable=broad-except
29+
self.fail(
30+
"Unexpected exception raised when instantiating Measurement"
31+
)
32+
33+
def test_measurement_equality(self):
34+
self.assertEqual(
35+
Measurement(321, {"hello": "world"}),
36+
Measurement(321, {"hello": "world"}),
37+
)
38+
39+
self.assertNotEqual(
40+
Measurement(321, {"hello": "world"}),
41+
Measurement(321.321, {"hello": "world"}),
42+
)
43+
self.assertNotEqual(
44+
Measurement(321, {"baz": "world"}),
45+
Measurement(321, {"hello": "world"}),
46+
)

0 commit comments

Comments
 (0)