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

Commit e72a4dc

Browse files
authored
Add Metric and supporting classes (#347)
* Add TimeSeries * Add ValueDistribution and related classes * Add Metric and TS type check * Improve TimeSeries point class check and test * Update for @mayurkale22's comments on #347 and improve test coverage. * Change TS arg order, make start_timestamp nullable and add a check for cumulative metrics with missing start timestamps. * Move noqa to bottom of docstring * Lift value type mapping out of check function
1 parent 7eaf980 commit e72a4dc

9 files changed

Lines changed: 632 additions & 13 deletions

File tree

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Copyright 2018, OpenCensus 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 opencensus.metrics.export import metric_descriptor
16+
from opencensus.metrics.export import value
17+
18+
19+
DESCRIPTOR_VALUE = {
20+
metric_descriptor.MetricDescriptorType.GAUGE_INT64:
21+
value.ValueLong,
22+
metric_descriptor.MetricDescriptorType.CUMULATIVE_INT64:
23+
value.ValueLong,
24+
metric_descriptor.MetricDescriptorType.GAUGE_DOUBLE:
25+
value.ValueDouble,
26+
metric_descriptor.MetricDescriptorType.CUMULATIVE_DOUBLE:
27+
value.ValueDouble,
28+
metric_descriptor.MetricDescriptorType.GAUGE_DISTRIBUTION:
29+
value.ValueDistribution,
30+
metric_descriptor.MetricDescriptorType.CUMULATIVE_DISTRIBUTION:
31+
value.ValueDistribution,
32+
metric_descriptor.MetricDescriptorType.SUMMARY:
33+
value.ValueSummary,
34+
}
35+
36+
37+
class Metric(object):
38+
"""A collection of time series data and label metadata.
39+
40+
This class implements the spec for v1 Metrics as of opencensus-proto
41+
release v0.0.2. See opencensus-proto for details:
42+
43+
https://github.com/census-instrumentation/opencensus-proto/blob/24333298e36590ea0716598caacc8959fc393c48/src/opencensus/proto/metrics/v1/metrics.proto#33
44+
45+
Defines a Metric which has one or more timeseries.
46+
47+
:type descriptor: class: '~opencensus.metrics.export.metric_descriptor.MetricDescriptor'
48+
:param descriptor: The metric's descriptor.
49+
50+
:type timeseries: list(:class: '~opencensus.metrics.export.time_series.TimeSeries')
51+
:param timeseries: One or more timeseries for a single metric, where each
52+
timeseries has one or more points.
53+
""" # noqa
54+
55+
def __init__(self, descriptor, time_series):
56+
if not time_series:
57+
raise ValueError("time_series must not be empty or null")
58+
if descriptor is None:
59+
raise ValueError("descriptor must not be null")
60+
self._time_series = time_series
61+
self._descriptor = descriptor
62+
self._check_type()
63+
64+
@property
65+
def time_series(self):
66+
return self._time_series
67+
68+
@property
69+
def descriptor(self):
70+
return self._descriptor
71+
72+
def _check_type(self):
73+
"""Check that point value types match the descriptor type."""
74+
check_type = DESCRIPTOR_VALUE.get(self.descriptor.type)
75+
if check_type is None:
76+
raise ValueError("Unknown metric descriptor type")
77+
for ts in self.time_series:
78+
if not ts.check_points_type(check_type):
79+
raise ValueError("Invalid point value type")
80+
81+
def _check_start_timestamp(self):
82+
"""Check that starting timestamp exists for cumulative metrics."""
83+
if self.descriptor.type in (
84+
metric_descriptor.MetricDescriptorType.CUMULATIVE_INT64,
85+
metric_descriptor.MetricDescriptorType.CUMULATIVE_DOUBLE,
86+
metric_descriptor.MetricDescriptorType.CUMULATIVE_DISTRIBUTION,
87+
):
88+
for ts in self.time_series:
89+
if ts.start_timestamp is None:
90+
raise ValueError("time_series.start_timestamp must exist "
91+
"for cumulative metrics")

opencensus/metrics/export/metric_descriptor.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,24 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
1615
import six
1716

17+
from opencensus.metrics.export.value import ValueDistribution
18+
from opencensus.metrics.export.value import ValueSummary
19+
1820

1921
class _MetricDescriptorTypeMeta(type):
2022
"""Helper for `x in MetricDescriptorType`."""
2123

2224
def __contains__(cls, item):
23-
return item in {MetricDescriptorType.GAUGE_INT64,
24-
MetricDescriptorType.GAUGE_DOUBLE,
25-
MetricDescriptorType.GAUGE_DISTRIBUTION,
26-
MetricDescriptorType.CUMULATIVE_INT64,
27-
MetricDescriptorType.CUMULATIVE_DOUBLE,
28-
MetricDescriptorType.CUMULATIVE_DISTRIBUTION}
25+
return item in {
26+
MetricDescriptorType.GAUGE_INT64,
27+
MetricDescriptorType.GAUGE_DOUBLE,
28+
MetricDescriptorType.GAUGE_DISTRIBUTION,
29+
MetricDescriptorType.CUMULATIVE_INT64,
30+
MetricDescriptorType.CUMULATIVE_DOUBLE,
31+
MetricDescriptorType.CUMULATIVE_DISTRIBUTION
32+
}
2933

3034

3135
@six.add_metaclass(_MetricDescriptorTypeMeta)
@@ -77,6 +81,22 @@ class MetricDescriptorType(object):
7781
# is not recommended, since it cannot be aggregated.
7882
SUMMARY = 7
7983

84+
@classmethod
85+
def to_type_class(cls, metric_descriptor_type):
86+
type_map = {
87+
cls.GAUGE_INT64: int,
88+
cls.GAUGE_DOUBLE: float,
89+
cls.GAUGE_DISTRIBUTION: ValueDistribution,
90+
cls.CUMULATIVE_INT64: int,
91+
cls.CUMULATIVE_DOUBLE: float,
92+
cls.CUMULATIVE_DISTRIBUTION: ValueDistribution,
93+
cls.SUMMARY: ValueSummary
94+
}
95+
try:
96+
return type_map[metric_descriptor_type]
97+
except KeyError:
98+
raise ValueError("Unknown MetricDescriptorType value")
99+
80100

81101
class MetricDescriptor(object):
82102
"""Defines a metric type and its schema.
@@ -105,6 +125,7 @@ class MetricDescriptor(object):
105125
:type label_keys: list(:class: '~opencensus.metrics.label_key.LabelKey')
106126
:param label_keys: The label keys associated with the metric descriptor.
107127
"""
128+
108129
def __init__(self, name, description, unit, type_, label_keys):
109130
if type_ not in MetricDescriptorType:
110131
raise ValueError("Invalid type")
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Copyright 2018, OpenCensus 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 opencensus.metrics.export import metric_descriptor
16+
17+
18+
class TimeSeries(object):
19+
"""Time series data for a given metric and time interval.
20+
21+
This class implements the spec for v1 TimeSeries structs as of
22+
opencensus-proto release v0.0.2. See opencensus-proto for details:
23+
24+
https://github.com/census-instrumentation/opencensus-proto/blob/24333298e36590ea0716598caacc8959fc393c48/src/opencensus/proto/metrics/v1/metrics.proto#L112
25+
26+
A TimeSeries is a collection of data points that describes the time-varying
27+
values of a metric.
28+
29+
:type label_values: list(:class:
30+
'~opencensus.metrics.label_value.LabelValue')
31+
:param label_values: The set of label values that uniquely identify this
32+
timeseries.
33+
34+
:type points: list(:class: '~opencensus.metrics.export.point.Point')
35+
:param points: The data points of this timeseries.
36+
37+
:type start_timestamp: str
38+
:param start_timestamp: The time when the cumulative value was reset to
39+
zero, must be set for cumulative metrics.
40+
""" # noqa
41+
42+
def __init__(self, label_values, points, start_timestamp):
43+
if not label_values:
44+
raise ValueError("label_values must not be null or empty")
45+
if not points:
46+
raise ValueError("points must not be null or empty")
47+
self._label_values = label_values
48+
self._points = points
49+
self._start_timestamp = start_timestamp
50+
51+
@property
52+
def start_timestamp(self):
53+
return self._start_timestamp
54+
55+
@property
56+
def label_values(self):
57+
return self._label_values
58+
59+
@property
60+
def points(self):
61+
return self._points
62+
63+
def check_points_type(self, type_):
64+
"""Check that each point's value is an instance `type_`.
65+
66+
:type type_: type
67+
:param type_: Type to check against.
68+
"""
69+
type_class = (
70+
metric_descriptor.MetricDescriptorType.to_type_class(type_))
71+
for point in self.points:
72+
if not isinstance(point.value.value, type_class):
73+
return False
74+
return True

0 commit comments

Comments
 (0)