Skip to content
This repository was archived by the owner on Sep 17, 2025. It is now read-only.

Commit 0d00458

Browse files
colincadamsc24t
authored andcommitted
Support int typed aggregations (#696)
and refactor supporting stats classes.
1 parent e6a1b9e commit 0d00458

File tree

15 files changed

+294
-436
lines changed

15 files changed

+294
-436
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Unreleased
44

5+
- Fix exporting int-valued stats with sum and lastvalue aggregations
6+
([#696](https://github.com/census-instrumentation/opencensus-python/pull/696))
57
- Fix cloud format propagator to use decimal span_id encoding instead of hex
68
([#719](https://github.com/census-instrumentation/opencensus-python/pull/719))
79

contrib/opencensus-ext-prometheus/opencensus/ext/prometheus/stats_exporter/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ def to_metric(self, desc, tag_values, agg_data):
206206
return metric
207207

208208
elif isinstance(agg_data,
209-
aggregation_data_module.SumAggregationDataFloat):
209+
aggregation_data_module.SumAggregationData):
210210
metric = UnknownMetricFamily(name=metric_name,
211211
documentation=metric_description,
212212
labels=label_keys)

contrib/opencensus-ext-prometheus/tests/test_prometheus_stats.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
# limitations under the License.
1414

1515
from datetime import datetime
16-
import copy
1716
import mock
1817
import unittest
1918

@@ -140,7 +139,8 @@ def test_collector_to_metric_count(self):
140139
collector.register_view(view)
141140
desc = collector.registered_views[list(REGISTERED_VIEW)[0]]
142141
metric = collector.to_metric(
143-
desc=desc, tag_values=[None], agg_data=agg.aggregation_data)
142+
desc=desc, tag_values=[None],
143+
agg_data=agg.new_aggregation_data(VIDEO_SIZE_MEASURE))
144144

145145
self.assertEqual(desc['name'], metric.name)
146146
self.assertEqual(desc['documentation'], metric.documentation)
@@ -158,7 +158,8 @@ def test_collector_to_metric_sum(self):
158158
collector.register_view(view)
159159
desc = collector.registered_views[list(REGISTERED_VIEW)[0]]
160160
metric = collector.to_metric(
161-
desc=desc, tag_values=[None], agg_data=agg.aggregation_data)
161+
desc=desc, tag_values=[None],
162+
agg_data=agg.new_aggregation_data(VIDEO_SIZE_MEASURE))
162163

163164
self.assertEqual(desc['name'], metric.name)
164165
self.assertEqual(desc['documentation'], metric.documentation)
@@ -176,7 +177,8 @@ def test_collector_to_metric_last_value(self):
176177
collector.register_view(view)
177178
desc = collector.registered_views[list(REGISTERED_VIEW)[0]]
178179
metric = collector.to_metric(
179-
desc=desc, tag_values=[None], agg_data=agg.aggregation_data)
180+
desc=desc, tag_values=[None],
181+
agg_data=agg.new_aggregation_data(VIDEO_SIZE_MEASURE))
180182

181183
self.assertEqual(desc['name'], metric.name)
182184
self.assertEqual(desc['documentation'], metric.documentation)
@@ -189,7 +191,8 @@ def test_collector_to_metric_histogram(self):
189191
collector = prometheus.Collector(options=options)
190192
collector.register_view(VIDEO_SIZE_VIEW)
191193
desc = collector.registered_views[list(REGISTERED_VIEW)[0]]
192-
distribution = copy.deepcopy(VIDEO_SIZE_DISTRIBUTION.aggregation_data)
194+
distribution = VIDEO_SIZE_DISTRIBUTION.new_aggregation_data(
195+
VIDEO_SIZE_MEASURE)
193196
distribution.add_sample(280.0 * MiB, None, None)
194197
metric = collector.to_metric(
195198
desc=desc,
@@ -243,7 +246,7 @@ def test_collector_collect(self):
243246
metric = collector.to_metric(
244247
desc=desc,
245248
tag_values=[tag_value_module.TagValue("value")],
246-
agg_data=agg.aggregation_data)
249+
agg_data=agg.new_aggregation_data(VIDEO_SIZE_MEASURE))
247250

248251
self.assertEqual(desc['name'], metric.name)
249252
self.assertEqual(desc['documentation'], metric.documentation)
@@ -262,7 +265,8 @@ def test_collector_collect_with_none_label_value(self):
262265
collector.register_view(view)
263266
desc = collector.registered_views['test3_new_view']
264267
metric = collector.to_metric(
265-
desc=desc, tag_values=[None], agg_data=agg.aggregation_data)
268+
desc=desc, tag_values=[None],
269+
agg_data=agg.new_aggregation_data(VIDEO_SIZE_MEASURE))
266270

267271
self.assertEqual(1, len(metric.samples))
268272
sample = metric.samples[0]

contrib/opencensus-ext-stackdriver/tests/test_stackdriver_stats.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,8 +1275,7 @@ def test_create_timeseries_disjoint_tags(self, monitoring_resoure_mock):
12751275

12761276
def test_create_timeseries_from_distribution(self):
12771277
"""Check for explicit 0-bound bucket for SD export."""
1278-
agg = aggregation_module.DistributionAggregation(
1279-
aggregation_type=aggregation_module.Type.DISTRIBUTION)
1278+
agg = aggregation_module.DistributionAggregation()
12801279

12811280
view = view_module.View(
12821281
name="example.org/test_view",
@@ -1328,8 +1327,7 @@ def test_create_timeseries_multiple_tags(self):
13281327
create_time_series_list should return a time series for each set of
13291328
values in the tag value aggregation map.
13301329
"""
1331-
agg = aggregation_module.CountAggregation(
1332-
aggregation_type=aggregation_module.Type.COUNT)
1330+
agg = aggregation_module.CountAggregation()
13331331

13341332
view = view_module.View(
13351333
name="example.org/test_view",
@@ -1375,12 +1373,10 @@ def test_create_timeseries_invalid_aggregation(self):
13751373
v_data = mock.Mock(spec=view_data_module.ViewData)
13761374
v_data.view.name = "example.org/base_view"
13771375
v_data.view.columns = [tag_key_module.TagKey('base_key')]
1378-
v_data.view.aggregation.aggregation_type = \
1379-
aggregation_module.Type.NONE
13801376
v_data.start_time = TEST_TIME_STR
13811377
v_data.end_time = TEST_TIME_STR
13821378

1383-
base_data = aggregation_data_module.BaseAggregationData(10)
1379+
base_data = None
13841380
v_data.tag_value_aggregation_data_map = {
13851381
(None,): base_data,
13861382
}

opencensus/stats/aggregation.py

Lines changed: 73 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -14,125 +14,78 @@
1414

1515
import logging
1616

17-
from opencensus.stats import bucket_boundaries
1817
from opencensus.stats import aggregation_data
18+
from opencensus.stats import measure as measure_module
19+
from opencensus.metrics.export.metric_descriptor import MetricDescriptorType
1920

2021

2122
logger = logging.getLogger(__name__)
2223

2324

24-
class Type(object):
25-
""" The type of aggregation function used on a View.
26-
27-
Attributes:
28-
NONE (int): The aggregation type of the view is 'unknown'.
29-
SUM (int): The aggregation type of the view is 'sum'.
30-
COUNT (int): The aggregation type of the view is 'count'.
31-
DISTRIBUTION (int): The aggregation type of the view is 'distribution'.
32-
LASTVALUE (int): The aggregation type of the view is 'lastvalue'.
33-
"""
34-
NONE = 0
35-
SUM = 1
36-
COUNT = 2
37-
DISTRIBUTION = 3
38-
LASTVALUE = 4
39-
40-
41-
class BaseAggregation(object):
42-
"""Aggregation describes how the data collected is aggregated by type of
43-
aggregation and buckets
44-
45-
:type buckets: list(:class: '~opencensus.stats.bucket_boundaries.
46-
BucketBoundaries')
47-
:param buckets: list of endpoints if the aggregation represents a
48-
distribution
49-
50-
:type aggregation_type: :class:`~opencensus.stats.aggregation.Type`
51-
:param aggregation_type: represents the type of this aggregation
52-
53-
"""
54-
def __init__(self, buckets=None, aggregation_type=Type.NONE):
55-
self._aggregation_type = aggregation_type
56-
self._buckets = buckets or []
57-
58-
@property
59-
def aggregation_type(self):
60-
"""The aggregation type of the current aggregation"""
61-
return self._aggregation_type
62-
63-
@property
64-
def buckets(self):
65-
"""The buckets of the current aggregation"""
66-
return self._buckets
67-
68-
69-
class SumAggregation(BaseAggregation):
70-
"""Sum Aggregation escribes that data collected and aggregated with this
25+
class SumAggregation(object):
26+
"""Sum Aggregation describes that data collected and aggregated with this
7127
method will be summed
7228
7329
:type sum: int or float
74-
:param sum: the sum of the data collected and aggregated
75-
76-
77-
:type aggregation_type: :class:`~opencensus.stats.aggregation.Type`
78-
:param aggregation_type: represents the type of this aggregation
30+
:param sum: the initial sum to be used in the aggregation
7931
8032
"""
81-
def __init__(self, sum=None, aggregation_type=Type.SUM):
82-
super(SumAggregation, self).__init__(aggregation_type=aggregation_type)
83-
self._sum = aggregation_data.SumAggregationDataFloat(
84-
sum_data=float(sum or 0))
85-
self.aggregation_data = self._sum
86-
87-
@property
88-
def sum(self):
89-
"""The sum of the current aggregation"""
90-
return self._sum
33+
def __init__(self, sum=None):
34+
self._initial_sum = sum or 0
35+
36+
def new_aggregation_data(self, measure):
37+
"""Get a new AggregationData for this aggregation."""
38+
value_type = MetricDescriptorType.to_type_class(
39+
self.get_metric_type(measure))
40+
return aggregation_data.SumAggregationData(
41+
value_type=value_type, sum_data=self._initial_sum)
42+
43+
@staticmethod
44+
def get_metric_type(measure):
45+
"""Get the MetricDescriptorType for the metric produced by this
46+
aggregation and measure.
47+
"""
48+
if isinstance(measure, measure_module.MeasureInt):
49+
return MetricDescriptorType.CUMULATIVE_INT64
50+
if isinstance(measure, measure_module.MeasureFloat):
51+
return MetricDescriptorType.CUMULATIVE_DOUBLE
52+
raise ValueError
9153

9254

93-
class CountAggregation(BaseAggregation):
55+
class CountAggregation(object):
9456
"""Describes that the data collected and aggregated with this method will
9557
be turned into a count value
9658
9759
:type count: int
98-
:param count: represents the count of this aggregation
99-
100-
:type aggregation_type: :class:`~opencensus.stats.aggregation.Type`
101-
:param aggregation_type: represents the type of this aggregation
60+
:param count: the initial count to be used in the aggregation
10261
10362
"""
104-
def __init__(self, count=0, aggregation_type=Type.COUNT):
105-
super(CountAggregation, self).__init__(
106-
aggregation_type=aggregation_type)
107-
self._count = aggregation_data.CountAggregationData(count)
108-
self.aggregation_data = self._count
63+
def __init__(self, count=0):
64+
self._initial_count = count
10965

110-
@property
111-
def count(self):
112-
"""The count of the current aggregation"""
113-
return self._count
66+
def new_aggregation_data(self, measure=None):
67+
"""Get a new AggregationData for this aggregation."""
68+
return aggregation_data.CountAggregationData(self._initial_count)
11469

70+
@staticmethod
71+
def get_metric_type(measure):
72+
"""Get the MetricDescriptorType for the metric produced by this
73+
aggregation and measure.
74+
"""
75+
return MetricDescriptorType.CUMULATIVE_INT64
11576

116-
class DistributionAggregation(BaseAggregation):
77+
78+
class DistributionAggregation(object):
11779
"""Distribution Aggregation indicates that the desired aggregation is a
11880
histogram distribution
11981
12082
:type boundaries: list(:class:'~opencensus.stats.bucket_boundaries.
12183
BucketBoundaries')
12284
:param boundaries: the bucket endpoints
12385
124-
:type distribution: histogram
125-
:param distribution: histogram of the values of the population
126-
127-
:type aggregation_type: :class:`~opencensus.stats.aggregation.Type`
128-
:param aggregation_type: represents the type of this aggregation
129-
13086
"""
13187

132-
def __init__(self,
133-
boundaries=None,
134-
distribution=None,
135-
aggregation_type=Type.DISTRIBUTION):
88+
def __init__(self, boundaries=None):
13689
if boundaries:
13790
if not all(boundaries[ii] < boundaries[ii + 1]
13891
for ii in range(len(boundaries) - 1)):
@@ -147,44 +100,46 @@ def __init__(self,
147100
ii)
148101
boundaries = boundaries[ii:]
149102

150-
super(DistributionAggregation, self).__init__(
151-
buckets=boundaries, aggregation_type=aggregation_type)
152-
self._boundaries = bucket_boundaries.BucketBoundaries(boundaries)
153-
self._distribution = distribution or {}
154-
self.aggregation_data = aggregation_data.DistributionAggregationData(
155-
0, 0, 0, None, boundaries)
103+
self._boundaries = boundaries
156104

157-
@property
158-
def boundaries(self):
159-
"""The boundaries of the current aggregation"""
160-
return self._boundaries
105+
def new_aggregation_data(self, measure=None):
106+
"""Get a new AggregationData for this aggregation."""
107+
return aggregation_data.DistributionAggregationData(
108+
0, 0, 0, None, self._boundaries)
161109

162-
@property
163-
def distribution(self):
164-
"""The distribution of the current aggregation"""
165-
return self._distribution
110+
@staticmethod
111+
def get_metric_type(measure):
112+
"""Get the MetricDescriptorType for the metric produced by this
113+
aggregation and measure.
114+
"""
115+
return MetricDescriptorType.CUMULATIVE_DISTRIBUTION
166116

167117

168-
class LastValueAggregation(BaseAggregation):
118+
class LastValueAggregation(object):
169119
"""Describes that the data collected with this method will
170120
overwrite the last recorded value
171121
172122
:type value: long
173-
:param value: represents the value of this aggregation
174-
175-
:type aggregation_type: :class:`~opencensus.stats.aggregation.Type`
176-
:param aggregation_type: represents the type of this aggregation
123+
:param count: the initial value to be used in the aggregation
177124
178125
"""
179-
def __init__(self, value=0, aggregation_type=Type.LASTVALUE):
180-
super(LastValueAggregation, self).__init__(
181-
aggregation_type=aggregation_type)
182-
self.aggregation_data = aggregation_data.LastValueAggregationData(
183-
value=value)
184-
self._value = value
185-
186-
@property
187-
def value(self):
188-
"""The current recorded value
126+
def __init__(self, value=0):
127+
self._initial_value = value
128+
129+
def new_aggregation_data(self, measure):
130+
"""Get a new AggregationData for this aggregation."""
131+
value_type = MetricDescriptorType.to_type_class(
132+
self.get_metric_type(measure))
133+
return aggregation_data.LastValueAggregationData(
134+
value=self._initial_value, value_type=value_type)
135+
136+
@staticmethod
137+
def get_metric_type(measure):
138+
"""Get the MetricDescriptorType for the metric produced by this
139+
aggregation and measure.
189140
"""
190-
return self._value
141+
if isinstance(measure, measure_module.MeasureInt):
142+
return MetricDescriptorType.GAUGE_INT64
143+
if isinstance(measure, measure_module.MeasureFloat):
144+
return MetricDescriptorType.GAUGE_DOUBLE
145+
raise ValueError

0 commit comments

Comments
 (0)