Skip to content

Commit 331de2e

Browse files
committed
Handle min, max and sum in explicit bucket histogram aggregator
1 parent 2c8e893 commit 331de2e

File tree

4 files changed

+99
-28
lines changed

4 files changed

+99
-28
lines changed

opentelemetry-sdk/src/opentelemetry/sdk/_metrics/aggregation.py

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@
1414

1515
from abc import ABC, abstractmethod
1616
from collections import OrderedDict
17+
from logging import getLogger
1718
from math import inf
1819

20+
from opentelemetry._metrics.instrument import _Monotonic
1921
from opentelemetry.util._time import _time_ns
2022

23+
_logger = getLogger(__name__)
24+
2125

2226
class Aggregation(ABC):
2327
@property
@@ -37,8 +41,7 @@ class NoneAggregation(Aggregation):
3741
This aggregation drops all instrument measurements.
3842
"""
3943

40-
def __init__(self):
41-
super().__init__()
44+
def __init__(self, instrument):
4245
self._value = None
4346

4447
def aggregate(self, value):
@@ -50,8 +53,7 @@ class SumAggregation(Aggregation):
5053
This aggregation collects data for the SDK sum metric point.
5154
"""
5255

53-
def __init__(self):
54-
super().__init__()
56+
def __init__(self, instrument):
5557
self._value = 0
5658

5759
def aggregate(self, value):
@@ -64,8 +66,7 @@ class LastValueAggregation(Aggregation):
6466
This aggregation collects data for the SDK sum metric point.
6567
"""
6668

67-
def __init__(self):
68-
super().__init__()
69+
def __init__(self, instrument):
6970
self._value = None
7071
self._timestamp = _time_ns()
7172

@@ -82,14 +83,52 @@ class ExplicitBucketHistogramAggregation(Aggregation):
8283

8384
def __init__(
8485
self,
86+
instrument,
87+
*args,
8588
boundaries=(0, 5, 10, 25, 50, 75, 100, 250, 500, 1000, inf),
8689
record_min_max=True,
8790
):
8891
super().__init__()
8992
self._boundaries = boundaries
9093
self._value = OrderedDict([(key, 0) for key in boundaries])
94+
self._min = inf
95+
self._max = -inf
96+
self._sum = 0
97+
self._instrument = instrument
98+
self._record_min_max = record_min_max
99+
100+
@property
101+
def min(self):
102+
if not self._record_min_max:
103+
_logger.warning("Min is not being recorded")
104+
105+
return self._min
106+
107+
@property
108+
def max(self):
109+
if not self._record_min_max:
110+
_logger.warning("Max is not being recorded")
111+
112+
return self._max
113+
114+
@property
115+
def sum(self):
116+
if isinstance(self._instrument, _Monotonic):
117+
return self._sum
118+
119+
_logger.warning(
120+
"Sum is not filled out when the associated "
121+
"instrument is not monotonic"
122+
)
91123

92124
def aggregate(self, value):
125+
if self._record_min_max:
126+
self._min = min(self._min, value)
127+
self._max = max(self._max, value)
128+
129+
if isinstance(self._instrument, _Monotonic):
130+
self._sum += value
131+
93132
for key in self._value.keys():
94133

95134
if value < key:

opentelemetry-sdk/src/opentelemetry/sdk/_metrics/instrument.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,10 @@ def __init__(
4646
self._attributes_aggregations = {}
4747
self._aggregation = aggregation
4848
self._aggregation_config = aggregation_config
49-
aggregation(**aggregation_config)
49+
aggregation(self, **aggregation_config)
5050

5151

5252
class _Synchronous(_Instrument):
53-
5453
def add(self, amount, attributes=None):
5554

5655
if attributes is None:
@@ -59,8 +58,8 @@ def add(self, amount, attributes=None):
5958
attributes = frozenset(attributes.items())
6059
if attributes not in self._attributes_aggregations.keys():
6160

62-
self._attributes_aggregations[attributes] = (
63-
self._aggregation(**self._aggregation_config)
61+
self._attributes_aggregations[attributes] = self._aggregation(
62+
self, **self._aggregation_config
6463
)
6564
self._attributes_aggregations[attributes].aggregate(amount)
6665

@@ -79,7 +78,7 @@ def __init__(
7978
unit=unit,
8079
description=description,
8180
aggregation=aggregation,
82-
aggregation_config=aggregation_config
81+
aggregation_config=aggregation_config,
8382
)
8483

8584

@@ -97,7 +96,7 @@ def __init__(
9796
unit=unit,
9897
description=description,
9998
aggregation=aggregation,
100-
aggregation_config=aggregation_config
99+
aggregation_config=aggregation_config,
101100
)
102101

103102

@@ -116,7 +115,7 @@ def __init__(
116115
unit=unit,
117116
description=description,
118117
aggregation=aggregation,
119-
aggregation_config=aggregation_config
118+
aggregation_config=aggregation_config,
120119
)
121120

122121

@@ -135,7 +134,7 @@ def __init__(
135134
unit=unit,
136135
description=description,
137136
aggregation=aggregation,
138-
aggregation_config=aggregation_config
137+
aggregation_config=aggregation_config,
139138
)
140139

141140

@@ -153,7 +152,7 @@ def __init__(
153152
unit=unit,
154153
description=description,
155154
aggregation=aggregation,
156-
aggregation_config=aggregation_config
155+
aggregation_config=aggregation_config,
157156
)
158157

159158

@@ -172,5 +171,5 @@ def __init__(
172171
unit=unit,
173172
description=description,
174173
aggregation=aggregation,
175-
aggregation_config=aggregation_config
174+
aggregation_config=aggregation_config,
176175
)

opentelemetry-sdk/tests/metrics/test_aggregation.py

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
# limitations under the License.
1414

1515

16+
from logging import WARNING
1617
from math import inf
1718
from unittest import TestCase
19+
from unittest.mock import Mock
1820

1921
from opentelemetry.sdk._metrics.aggregation import (
2022
ExplicitBucketHistogramAggregation,
@@ -30,7 +32,7 @@ def test_aggregate(self):
3032
`NoneAggregation` drops all measurements.
3133
"""
3234

33-
none_aggregation = NoneAggregation()
35+
none_aggregation = NoneAggregation(Mock())
3436

3537
none_aggregation.aggregate(1)
3638
none_aggregation.aggregate(2)
@@ -45,7 +47,7 @@ def test_aggregate(self):
4547
`SumAggregation` collects data for sum metric points
4648
"""
4749

48-
sum_aggregation = SumAggregation()
50+
sum_aggregation = SumAggregation(Mock())
4951

5052
sum_aggregation.aggregate(1)
5153
sum_aggregation.aggregate(2)
@@ -61,7 +63,7 @@ def test_aggregate(self):
6163
temporality
6264
"""
6365

64-
last_value_aggregation = LastValueAggregation()
66+
last_value_aggregation = LastValueAggregation(Mock())
6567

6668
last_value_aggregation.aggregate(1)
6769
self.assertEqual(last_value_aggregation.value, 1)
@@ -80,7 +82,7 @@ def test_aggregate(self):
8082
"""
8183

8284
explicit_bucket_histogram_aggregation = (
83-
ExplicitBucketHistogramAggregation()
85+
ExplicitBucketHistogramAggregation(Mock())
8486
)
8587

8688
explicit_bucket_histogram_aggregation.aggregate(-1)
@@ -95,3 +97,38 @@ def test_aggregate(self):
9597
self.assertEqual(
9698
explicit_bucket_histogram_aggregation.value[inf], 9999
9799
)
100+
101+
def test_min_max(self):
102+
"""
103+
`record_min_max` indicates the aggregator to record the minimum and
104+
maximum value in the population
105+
"""
106+
107+
explicit_bucket_histogram_aggregation = (
108+
ExplicitBucketHistogramAggregation(Mock())
109+
)
110+
111+
explicit_bucket_histogram_aggregation.aggregate(-1)
112+
explicit_bucket_histogram_aggregation.aggregate(2)
113+
explicit_bucket_histogram_aggregation.aggregate(7)
114+
explicit_bucket_histogram_aggregation.aggregate(8)
115+
explicit_bucket_histogram_aggregation.aggregate(9999)
116+
117+
self.assertEqual(explicit_bucket_histogram_aggregation.min, -1)
118+
self.assertEqual(explicit_bucket_histogram_aggregation.max, 9999)
119+
120+
explicit_bucket_histogram_aggregation = (
121+
ExplicitBucketHistogramAggregation(Mock(), record_min_max=False)
122+
)
123+
124+
explicit_bucket_histogram_aggregation.aggregate(-1)
125+
explicit_bucket_histogram_aggregation.aggregate(2)
126+
explicit_bucket_histogram_aggregation.aggregate(7)
127+
explicit_bucket_histogram_aggregation.aggregate(8)
128+
explicit_bucket_histogram_aggregation.aggregate(9999)
129+
130+
with self.assertLogs(level=WARNING):
131+
self.assertEqual(explicit_bucket_histogram_aggregation.min, inf)
132+
133+
with self.assertLogs(level=WARNING):
134+
self.assertEqual(explicit_bucket_histogram_aggregation.max, -inf)

opentelemetry-sdk/tests/metrics/test_instrument.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,8 @@
1515

1616
from unittest import TestCase
1717

18-
from opentelemetry.sdk._metrics.instrument import (
19-
_Synchronous
20-
)
21-
from opentelemetry.sdk._metrics.aggregation import (
22-
SumAggregation
23-
)
18+
from opentelemetry.sdk._metrics.aggregation import SumAggregation
19+
from opentelemetry.sdk._metrics.instrument import _Synchronous
2420

2521

2622
class Test_Synchronous(TestCase):
@@ -38,11 +34,11 @@ def test_add(self):
3834
synchronous._attributes_aggregations[
3935
frozenset({("name0", "value0")})
4036
],
41-
SumAggregation
37+
SumAggregation,
4238
)
4339
self.assertIsInstance(
4440
synchronous._attributes_aggregations[
4541
frozenset({("name1", "value1")})
4642
],
47-
SumAggregation
43+
SumAggregation,
4844
)

0 commit comments

Comments
 (0)