Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6b655ce

Browse files
authored
Refactor aggregations (open-telemetry#2308)
1 parent 5233793 commit 6b655ce

File tree

5 files changed

+415
-122
lines changed

5 files changed

+415
-122
lines changed

opentelemetry-sdk/setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ install_requires =
4646
opentelemetry-api == 1.8.0
4747
opentelemetry-semantic-conventions == 0.27b0
4848
setuptools >= 16.0
49+
dataclasses == 0.8; python_version < '3.7'
4950

5051
[options.packages.find]
5152
where = src

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

Lines changed: 138 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -14,124 +14,194 @@
1414

1515
from abc import ABC, abstractmethod
1616
from collections import OrderedDict
17+
from enum import IntEnum
1718
from logging import getLogger
1819
from math import inf
20+
from threading import Lock
21+
from typing import Generic, Optional, Sequence, TypeVar
1922

20-
from opentelemetry._metrics.instrument import _Monotonic
23+
from opentelemetry.sdk._metrics.measurement import Measurement
24+
from opentelemetry.sdk._metrics.point import Gauge, Histogram, PointT, Sum
2125
from opentelemetry.util._time import _time_ns
2226

27+
28+
class AggregationTemporality(IntEnum):
29+
UNSPECIFIED = 0
30+
DELTA = 1
31+
CUMULATIVE = 2
32+
33+
34+
_PointVarT = TypeVar("_PointVarT", bound=PointT)
35+
2336
_logger = getLogger(__name__)
2437

2538

26-
class Aggregation(ABC):
27-
@property
28-
def value(self):
29-
return self._value # pylint: disable=no-member
39+
class _InstrumentMonotonicityAwareAggregation:
40+
def __init__(self, instrument_is_monotonic: bool):
41+
self._instrument_is_monotonic = instrument_is_monotonic
42+
super().__init__()
43+
44+
45+
class Aggregation(ABC, Generic[_PointVarT]):
46+
def __init__(self):
47+
self._lock = Lock()
3048

3149
@abstractmethod
32-
def aggregate(self, value):
50+
def aggregate(self, measurement: Measurement) -> None:
3351
pass
3452

3553
@abstractmethod
36-
def make_point_and_reset(self):
54+
def collect(self) -> Optional[_PointVarT]:
55+
pass
56+
57+
58+
class SynchronousSumAggregation(
59+
_InstrumentMonotonicityAwareAggregation, Aggregation[Sum]
60+
):
61+
def __init__(self, instrument_is_monotonic: bool):
62+
super().__init__(instrument_is_monotonic)
63+
self._value = 0
64+
self._start_time_unix_nano = _time_ns()
65+
66+
def aggregate(self, measurement: Measurement) -> None:
67+
with self._lock:
68+
self._value = self._value + measurement.value
69+
70+
def collect(self) -> Optional[Sum]:
3771
"""
38-
Atomically return a point for the current value of the metric and reset the internal state.
72+
Atomically return a point for the current value of the metric and
73+
reset the aggregation value.
3974
"""
75+
now = _time_ns()
4076

77+
with self._lock:
78+
value = self._value
79+
start_time_unix_nano = self._start_time_unix_nano
4180

42-
class SumAggregation(Aggregation):
43-
"""
44-
This aggregation collects data for the SDK sum metric point.
45-
"""
81+
self._value = 0
82+
self._start_time_unix_nano = now + 1
4683

47-
def __init__(self, instrument):
48-
self._value = 0
84+
return Sum(
85+
aggregation_temporality=AggregationTemporality.DELTA,
86+
is_monotonic=self._instrument_is_monotonic,
87+
start_time_unix_nano=start_time_unix_nano,
88+
time_unix_nano=now,
89+
value=value,
90+
)
4991

50-
def aggregate(self, value):
51-
self._value = self._value + value
5292

53-
def make_point_and_reset(self):
54-
pass
93+
class AsynchronousSumAggregation(
94+
_InstrumentMonotonicityAwareAggregation, Aggregation[Sum]
95+
):
96+
def __init__(self, instrument_is_monotonic: bool):
97+
super().__init__(instrument_is_monotonic)
98+
self._value = None
99+
self._start_time_unix_nano = _time_ns()
55100

101+
def aggregate(self, measurement: Measurement) -> None:
102+
with self._lock:
103+
self._value = measurement.value
56104

57-
class LastValueAggregation(Aggregation):
105+
def collect(self) -> Optional[Sum]:
106+
"""
107+
Atomically return a point for the current value of the metric.
108+
"""
109+
if self._value is None:
110+
return None
111+
112+
return Sum(
113+
start_time_unix_nano=self._start_time_unix_nano,
114+
time_unix_nano=_time_ns(),
115+
value=self._value,
116+
aggregation_temporality=AggregationTemporality.CUMULATIVE,
117+
is_monotonic=self._instrument_is_monotonic,
118+
)
58119

59-
"""
60-
This aggregation collects data for the SDK sum metric point.
61-
"""
62120

63-
def __init__(self, instrument):
121+
class LastValueAggregation(Aggregation[Gauge]):
122+
def __init__(self):
123+
super().__init__()
64124
self._value = None
65-
self._timestamp = _time_ns()
66125

67-
def aggregate(self, value):
68-
self._value = value
69-
self._timestamp = _time_ns()
70-
71-
def make_point_and_reset(self):
72-
pass
126+
def aggregate(self, measurement: Measurement):
127+
with self._lock:
128+
self._value = measurement.value
73129

130+
def collect(self) -> Optional[Gauge]:
131+
"""
132+
Atomically return a point for the current value of the metric.
133+
"""
134+
if self._value is None:
135+
return None
74136

75-
class ExplicitBucketHistogramAggregation(Aggregation):
137+
return Gauge(
138+
time_unix_nano=_time_ns(),
139+
value=self._value,
140+
)
76141

77-
"""
78-
This aggregation collects data for the SDK sum metric point.
79-
"""
80142

143+
class ExplicitBucketHistogramAggregation(Aggregation[Histogram]):
81144
def __init__(
82145
self,
83-
instrument,
84-
*args,
85-
boundaries=(0, 5, 10, 25, 50, 75, 100, 250, 500, 1000),
86-
record_min_max=True,
146+
boundaries: Sequence[int] = (
147+
0,
148+
5,
149+
10,
150+
25,
151+
50,
152+
75,
153+
100,
154+
250,
155+
500,
156+
1000,
157+
),
158+
record_min_max: bool = True,
87159
):
88160
super().__init__()
89161
self._value = OrderedDict([(key, 0) for key in (*boundaries, inf)])
90162
self._min = inf
91163
self._max = -inf
92164
self._sum = 0
93-
self._instrument = instrument
94165
self._record_min_max = record_min_max
166+
self._start_time_unix_nano = _time_ns()
167+
self._boundaries = boundaries
95168

96-
@property
97-
def min(self):
98-
if not self._record_min_max:
99-
_logger.warning("Min is not being recorded")
100-
101-
return self._min
169+
def aggregate(self, measurement: Measurement) -> None:
102170

103-
@property
104-
def max(self):
105-
if not self._record_min_max:
106-
_logger.warning("Max is not being recorded")
171+
value = measurement.value
107172

108-
return self._max
109-
110-
@property
111-
def sum(self):
112-
if isinstance(self._instrument, _Monotonic):
113-
return self._sum
114-
115-
_logger.warning(
116-
"Sum is not filled out when the associated "
117-
"instrument is not monotonic"
118-
)
119-
return None
120-
121-
def aggregate(self, value):
122173
if self._record_min_max:
123174
self._min = min(self._min, value)
124175
self._max = max(self._max, value)
125176

126-
if isinstance(self._instrument, _Monotonic):
127-
self._sum += value
177+
self._sum += value
128178

129179
for key in self._value.keys():
130180

131181
if value < key:
132-
self._value[key] = self._value[key] + value
182+
self._value[key] = self._value[key] + 1
133183

134184
break
135185

136-
def make_point_and_reset(self):
137-
pass
186+
def collect(self) -> Optional[Histogram]:
187+
"""
188+
Atomically return a point for the current value of the metric.
189+
"""
190+
now = _time_ns()
191+
192+
with self._lock:
193+
value = self._value
194+
start_time_unix_nano = self._start_time_unix_nano
195+
196+
self._value = OrderedDict(
197+
[(key, 0) for key in (*self._boundaries, inf)]
198+
)
199+
self._start_time_unix_nano = now + 1
200+
201+
return Histogram(
202+
start_time_unix_nano=start_time_unix_nano,
203+
time_unix_nano=now,
204+
bucket_counts=tuple(value.values()),
205+
explicit_bounds=self._boundaries,
206+
aggregation_temporality=AggregationTemporality.DELTA,
207+
)

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from dataclasses import dataclass
16+
from typing import Union
1517

18+
from opentelemetry.util.types import Attributes
19+
20+
21+
@dataclass(frozen=True)
1622
class Measurement:
17-
pass
23+
value: Union[int, float]
24+
attributes: Attributes = None
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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 dataclasses import dataclass
16+
from typing import Sequence, Union
17+
18+
19+
@dataclass(frozen=True)
20+
class Sum:
21+
start_time_unix_nano: int
22+
time_unix_nano: int
23+
value: Union[int, float]
24+
aggregation_temporality: int
25+
is_monotonic: bool
26+
27+
28+
@dataclass(frozen=True)
29+
class Gauge:
30+
time_unix_nano: int
31+
value: Union[int, float]
32+
33+
34+
@dataclass(frozen=True)
35+
class Histogram:
36+
start_time_unix_nano: int
37+
time_unix_nano: int
38+
bucket_counts: Sequence[int]
39+
explicit_bounds: Sequence[float]
40+
aggregation_temporality: int
41+
42+
43+
PointT = Union[Sum, Gauge, Histogram]

0 commit comments

Comments
 (0)