Skip to content

Commit 489d34e

Browse files
committed
Add default aggregation
Fixes #2537
1 parent 95aeecc commit 489d34e

File tree

5 files changed

+150
-69
lines changed

5 files changed

+150
-69
lines changed

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

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,11 @@ def consume_measurement(self, measurement: Measurement) -> None:
6565
if attributes not in self._attributes_aggregation:
6666
with self._lock:
6767
if attributes not in self._attributes_aggregation:
68-
if self._view._aggregation:
69-
aggregation = (
70-
self._view._aggregation._create_aggregation(
71-
self._instrument
72-
)
73-
)
74-
else:
75-
aggregation = self._instrument._default_aggregation
76-
self._attributes_aggregation[attributes] = aggregation
68+
self._attributes_aggregation[
69+
attributes
70+
] = self._view._aggregation._create_aggregation(
71+
self._instrument
72+
)
7773

7874
self._attributes_aggregation[attributes].aggregate(measurement)
7975

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

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,13 @@
2222

2323
from opentelemetry._metrics.instrument import (
2424
Asynchronous,
25+
Counter,
2526
Instrument,
27+
ObservableCounter,
28+
ObservableGauge,
29+
ObservableUpDownCounter,
2630
Synchronous,
31+
UpDownCounter,
2732
_Monotonic,
2833
)
2934
from opentelemetry.sdk._metrics.measurement import Measurement
@@ -54,6 +59,48 @@ def collect(self) -> Optional[_PointVarT]:
5459
pass
5560

5661

62+
class _AggregationFactory(ABC):
63+
@abstractmethod
64+
def _create_aggregation(self, instrument: Instrument) -> _Aggregation:
65+
"""Creates an aggregation"""
66+
67+
68+
class DefaultAggregation(_AggregationFactory):
69+
def _create_aggregation(self, instrument: Instrument) -> _Aggregation:
70+
71+
# pylint: disable=too-many-return-statements
72+
if isinstance(instrument, Counter):
73+
return _SumAggregation(
74+
instrument_is_monotonic=True,
75+
instrument_temporality=AggregationTemporality.DELTA,
76+
)
77+
if isinstance(instrument, UpDownCounter):
78+
return _SumAggregation(
79+
instrument_is_monotonic=False,
80+
instrument_temporality=AggregationTemporality.DELTA,
81+
)
82+
83+
if isinstance(instrument, ObservableCounter):
84+
return _SumAggregation(
85+
instrument_is_monotonic=True,
86+
instrument_temporality=AggregationTemporality.CUMULATIVE,
87+
)
88+
89+
if isinstance(instrument, ObservableUpDownCounter):
90+
return _SumAggregation(
91+
instrument_is_monotonic=False,
92+
instrument_temporality=AggregationTemporality.CUMULATIVE,
93+
)
94+
95+
if isinstance(instrument, Histogram):
96+
return _ExplicitBucketHistogramAggregation()
97+
98+
if isinstance(instrument, ObservableGauge):
99+
return _LastValueAggregation()
100+
101+
return None
102+
103+
57104
class _SumAggregation(_Aggregation[Sum]):
58105
def __init__(
59106
self,
@@ -327,12 +374,6 @@ def _convert_aggregation_temporality(
327374
return None
328375

329376

330-
class _AggregationFactory(ABC):
331-
@abstractmethod
332-
def _create_aggregation(self, instrument: Instrument) -> _Aggregation:
333-
"""Creates an aggregation"""
334-
335-
336377
class ExplicitBucketHistogramAggregation(_AggregationFactory):
337378
def __init__(
338379
self,

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

Lines changed: 5 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
# pylint: disable=too-many-ancestors
1616

1717
import logging
18-
from abc import ABC, abstractmethod
1918
from typing import Callable, Dict, Generator, Iterable, Union
2019

2120
from opentelemetry._metrics.instrument import CallbackT
@@ -32,28 +31,14 @@
3231
)
3332
from opentelemetry._metrics.instrument import UpDownCounter as APIUpDownCounter
3433
from opentelemetry._metrics.measurement import Measurement as APIMeasurement
35-
from opentelemetry.sdk._metrics.aggregation import (
36-
_Aggregation,
37-
_ExplicitBucketHistogramAggregation,
38-
_LastValueAggregation,
39-
_SumAggregation,
40-
)
4134
from opentelemetry.sdk._metrics.measurement import Measurement
4235
from opentelemetry.sdk._metrics.measurement_consumer import MeasurementConsumer
43-
from opentelemetry.sdk._metrics.point import AggregationTemporality
4436
from opentelemetry.sdk.util.instrumentation import InstrumentationInfo
4537

4638
_logger = logging.getLogger(__name__)
4739

4840

49-
class _Instrument(ABC):
50-
@property
51-
@abstractmethod
52-
def _default_aggregation(self) -> _Aggregation:
53-
pass
54-
55-
56-
class _Synchronous(_Instrument):
41+
class _Synchronous:
5742
def __init__(
5843
self,
5944
name: str,
@@ -70,7 +55,7 @@ def __init__(
7055
super().__init__(name, unit=unit, description=description)
7156

7257

73-
class _Asynchronous(_Instrument):
58+
class _Asynchronous:
7459
def __init__(
7560
self,
7661
name: str,
@@ -108,13 +93,6 @@ def callback(self) -> Iterable[Measurement]:
10893

10994

11095
class Counter(_Synchronous, APICounter):
111-
@property
112-
def _default_aggregation(self) -> _Aggregation:
113-
return _SumAggregation(
114-
instrument_is_monotonic=True,
115-
instrument_temporality=AggregationTemporality.DELTA,
116-
)
117-
11896
def add(
11997
self, amount: Union[int, float], attributes: Dict[str, str] = None
12098
):
@@ -129,13 +107,6 @@ def add(
129107

130108

131109
class UpDownCounter(_Synchronous, APIUpDownCounter):
132-
@property
133-
def _default_aggregation(self) -> _Aggregation:
134-
return _SumAggregation(
135-
instrument_is_monotonic=False,
136-
instrument_temporality=AggregationTemporality.DELTA,
137-
)
138-
139110
def add(
140111
self, amount: Union[int, float], attributes: Dict[str, str] = None
141112
):
@@ -145,28 +116,14 @@ def add(
145116

146117

147118
class ObservableCounter(_Asynchronous, APIObservableCounter):
148-
@property
149-
def _default_aggregation(self) -> _Aggregation:
150-
return _SumAggregation(
151-
instrument_is_monotonic=True,
152-
instrument_temporality=AggregationTemporality.CUMULATIVE,
153-
)
119+
pass
154120

155121

156122
class ObservableUpDownCounter(_Asynchronous, APIObservableUpDownCounter):
157-
@property
158-
def _default_aggregation(self) -> _Aggregation:
159-
return _SumAggregation(
160-
instrument_is_monotonic=False,
161-
instrument_temporality=AggregationTemporality.CUMULATIVE,
162-
)
123+
pass
163124

164125

165126
class Histogram(_Synchronous, APIHistogram):
166-
@property
167-
def _default_aggregation(self) -> _Aggregation:
168-
return _ExplicitBucketHistogramAggregation()
169-
170127
def record(
171128
self, amount: Union[int, float], attributes: Dict[str, str] = None
172129
):
@@ -182,6 +139,4 @@ def record(
182139

183140

184141
class ObservableGauge(_Asynchronous, APIObservableGauge):
185-
@property
186-
def _default_aggregation(self) -> _Aggregation:
187-
return _LastValueAggregation()
142+
pass

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121
from typing_extensions import final
2222

2323
from opentelemetry._metrics.instrument import Instrument
24-
from opentelemetry.sdk._metrics.aggregation import _AggregationFactory
24+
from opentelemetry.sdk._metrics.aggregation import (
25+
DefaultAggregation,
26+
_AggregationFactory,
27+
)
2528

2629
_logger = getLogger(__name__)
2730

@@ -86,8 +89,8 @@ class the instrument must be to match the view.
8689
8790
aggregation: This is a metric stream customizing attribute: the
8891
aggregatation instance to use when data is aggregated for the
89-
corresponding metrics stream. If `None` the default aggregation
90-
of the instrument will be used.
92+
corresponding metrics stream. If `None` an instance of
93+
`DefaultAggregation` will be used.
9194
9295
This class is not intended to be subclassed by the user.
9396
"""
@@ -127,7 +130,7 @@ class the instrument must be to match the view.
127130

128131
self._description = description
129132
self._attribute_keys = attribute_keys
130-
self._aggregation = aggregation
133+
self._aggregation = aggregation or DefaultAggregation()
131134

132135
# pylint: disable=too-many-return-statements
133136
# pylint: disable=too-many-branches

opentelemetry-sdk/tests/metrics/test_aggregation.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from opentelemetry.sdk._metrics import instrument
2424
from opentelemetry.sdk._metrics.aggregation import (
2525
AggregationTemporality,
26+
DefaultAggregation,
2627
ExplicitBucketHistogramAggregation,
2728
LastValueAggregation,
2829
SumAggregation,
@@ -31,6 +32,13 @@
3132
_LastValueAggregation,
3233
_SumAggregation,
3334
)
35+
from opentelemetry.sdk._metrics.instrument import (
36+
Counter,
37+
ObservableCounter,
38+
ObservableGauge,
39+
ObservableUpDownCounter,
40+
UpDownCounter,
41+
)
3442
from opentelemetry.sdk._metrics.measurement import Measurement
3543
from opentelemetry.sdk._metrics.point import Gauge, Histogram, Sum
3644
from opentelemetry.util.types import Attributes
@@ -817,3 +825,81 @@ def test_last_value_factory(self):
817825
self.assertIsInstance(aggregation, _LastValueAggregation)
818826
aggregation2 = factory._create_aggregation(counter)
819827
self.assertNotEqual(aggregation, aggregation2)
828+
829+
830+
class TestDefaultAggregation(TestCase):
831+
@classmethod
832+
def setUpClass(cls):
833+
cls.default_aggregation = DefaultAggregation()
834+
835+
def test_counter(self):
836+
837+
aggregation = self.default_aggregation._create_aggregation(
838+
Counter(Mock(), Mock(), Mock())
839+
)
840+
self.assertIsInstance(aggregation, _SumAggregation)
841+
self.assertTrue(aggregation._instrument_is_monotonic)
842+
self.assertEqual(
843+
aggregation._instrument_temporality, AggregationTemporality.DELTA
844+
)
845+
846+
def test_up_down_counter(self):
847+
848+
aggregation = self.default_aggregation._create_aggregation(
849+
UpDownCounter(Mock(), Mock(), Mock())
850+
)
851+
self.assertIsInstance(aggregation, _SumAggregation)
852+
self.assertFalse(aggregation._instrument_is_monotonic)
853+
self.assertEqual(
854+
aggregation._instrument_temporality, AggregationTemporality.DELTA
855+
)
856+
857+
def test_observable_counter(self):
858+
859+
aggregation = self.default_aggregation._create_aggregation(
860+
ObservableCounter(Mock(), Mock(), Mock(), Mock())
861+
)
862+
self.assertIsInstance(aggregation, _SumAggregation)
863+
self.assertTrue(aggregation._instrument_is_monotonic)
864+
self.assertEqual(
865+
aggregation._instrument_temporality,
866+
AggregationTemporality.CUMULATIVE,
867+
)
868+
869+
def test_observable_up_down_counter(self):
870+
871+
aggregation = self.default_aggregation._create_aggregation(
872+
ObservableUpDownCounter(Mock(), Mock(), Mock(), Mock())
873+
)
874+
self.assertIsInstance(aggregation, _SumAggregation)
875+
self.assertFalse(aggregation._instrument_is_monotonic)
876+
self.assertEqual(
877+
aggregation._instrument_temporality,
878+
AggregationTemporality.CUMULATIVE,
879+
)
880+
881+
def test_histogram(self):
882+
883+
aggregation = self.default_aggregation._create_aggregation(
884+
Histogram(
885+
Mock(),
886+
Mock(),
887+
Mock(),
888+
Mock(),
889+
Mock(),
890+
Mock(),
891+
)
892+
)
893+
self.assertIsInstance(aggregation, _ExplicitBucketHistogramAggregation)
894+
895+
def test_observable_gauge(self):
896+
897+
aggregation = self.default_aggregation._create_aggregation(
898+
ObservableGauge(
899+
Mock(),
900+
Mock(),
901+
Mock(),
902+
Mock(),
903+
)
904+
)
905+
self.assertIsInstance(aggregation, _LastValueAggregation)

0 commit comments

Comments
 (0)