Skip to content
This repository was archived by the owner on Sep 17, 2025. It is now read-only.
91 changes: 91 additions & 0 deletions opencensus/metrics/export/metric.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Copyright 2018, OpenCensus Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from opencensus.metrics.export import metric_descriptor
from opencensus.metrics.export import value


DESCRIPTOR_VALUE = {
metric_descriptor.MetricDescriptorType.GAUGE_INT64:
value.ValueLong,
metric_descriptor.MetricDescriptorType.CUMULATIVE_INT64:
value.ValueLong,
metric_descriptor.MetricDescriptorType.GAUGE_DOUBLE:
value.ValueDouble,
metric_descriptor.MetricDescriptorType.CUMULATIVE_DOUBLE:
value.ValueDouble,
metric_descriptor.MetricDescriptorType.GAUGE_DISTRIBUTION:
value.ValueDistribution,
metric_descriptor.MetricDescriptorType.CUMULATIVE_DISTRIBUTION:
value.ValueDistribution,
metric_descriptor.MetricDescriptorType.SUMMARY:
value.ValueSummary,
}


class Metric(object):
"""A collection of time series data and label metadata.

This class implements the spec for v1 Metrics as of opencensus-proto
release v0.0.2. See opencensus-proto for details:

https://github.com/census-instrumentation/opencensus-proto/blob/24333298e36590ea0716598caacc8959fc393c48/src/opencensus/proto/metrics/v1/metrics.proto#33

Defines a Metric which has one or more timeseries.

:type descriptor: class: '~opencensus.metrics.export.metric_descriptor.MetricDescriptor'
:param descriptor: The metric's descriptor.

:type timeseries: list(:class: '~opencensus.metrics.export.time_series.TimeSeries')
:param timeseries: One or more timeseries for a single metric, where each
timeseries has one or more points.
""" # noqa

def __init__(self, descriptor, time_series):
if not time_series:
Comment thread
c24t marked this conversation as resolved.
raise ValueError("time_series must not be empty or null")
if descriptor is None:
raise ValueError("descriptor must not be null")
self._time_series = time_series
self._descriptor = descriptor
self._check_type()

@property
def time_series(self):
return self._time_series

@property
def descriptor(self):
return self._descriptor

def _check_type(self):
"""Check that point value types match the descriptor type."""
check_type = DESCRIPTOR_VALUE.get(self.descriptor.type)
if check_type is None:
raise ValueError("Unknown metric descriptor type")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

# This relation mapping could be put at the top of this file
DESCRIPTOR_VALUE = {
    metric_descriptor.MetricDescriptorType.GAUGE_INT64: value.ValueLong,
    metric_descriptor.MetricDescriptorType.CUMULATIVE_INT64: value.ValueLong,
    ...
}

check_type = DESCRIPTOR_VALUE.get(self.descriptor.type)
if check_type is None:
    raise ValueError("Unknown metric descriptor type")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, changed in 39d2dcb.

for ts in self.time_series:
if not ts.check_points_type(check_type):
raise ValueError("Invalid point value type")

def _check_start_timestamp(self):
"""Check that starting timestamp exists for cumulative metrics."""
if self.descriptor.type in (
metric_descriptor.MetricDescriptorType.CUMULATIVE_INT64,
metric_descriptor.MetricDescriptorType.CUMULATIVE_DOUBLE,
metric_descriptor.MetricDescriptorType.CUMULATIVE_DISTRIBUTION,
):
for ts in self.time_series:
if ts.start_timestamp is None:
raise ValueError("time_series.start_timestamp must exist "
"for cumulative metrics")
35 changes: 28 additions & 7 deletions opencensus/metrics/export/metric_descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.


import six

from opencensus.metrics.export.value import ValueDistribution
from opencensus.metrics.export.value import ValueSummary


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

def __contains__(cls, item):
return item in {MetricDescriptorType.GAUGE_INT64,
MetricDescriptorType.GAUGE_DOUBLE,
MetricDescriptorType.GAUGE_DISTRIBUTION,
MetricDescriptorType.CUMULATIVE_INT64,
MetricDescriptorType.CUMULATIVE_DOUBLE,
MetricDescriptorType.CUMULATIVE_DISTRIBUTION}
return item in {
MetricDescriptorType.GAUGE_INT64,
MetricDescriptorType.GAUGE_DOUBLE,
MetricDescriptorType.GAUGE_DISTRIBUTION,
MetricDescriptorType.CUMULATIVE_INT64,
MetricDescriptorType.CUMULATIVE_DOUBLE,
MetricDescriptorType.CUMULATIVE_DISTRIBUTION
}


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

@classmethod
def to_type_class(cls, metric_descriptor_type):
type_map = {
cls.GAUGE_INT64: int,
cls.GAUGE_DOUBLE: float,
cls.GAUGE_DISTRIBUTION: ValueDistribution,
cls.CUMULATIVE_INT64: int,
cls.CUMULATIVE_DOUBLE: float,
cls.CUMULATIVE_DISTRIBUTION: ValueDistribution,
cls.SUMMARY: ValueSummary
}
try:
return type_map[metric_descriptor_type]
except KeyError:
raise ValueError("Unknown MetricDescriptorType value")


class MetricDescriptor(object):
"""Defines a metric type and its schema.
Expand Down Expand Up @@ -105,6 +125,7 @@ class MetricDescriptor(object):
:type label_keys: list(:class: '~opencensus.metrics.label_key.LabelKey')
:param label_keys: The label keys associated with the metric descriptor.
"""

def __init__(self, name, description, unit, type_, label_keys):
if type_ not in MetricDescriptorType:
raise ValueError("Invalid type")
Expand Down
74 changes: 74 additions & 0 deletions opencensus/metrics/export/time_series.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Copyright 2018, OpenCensus Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from opencensus.metrics.export import metric_descriptor


class TimeSeries(object):
"""Time series data for a given metric and time interval.

This class implements the spec for v1 TimeSeries structs as of
opencensus-proto release v0.0.2. See opencensus-proto for details:

https://github.com/census-instrumentation/opencensus-proto/blob/24333298e36590ea0716598caacc8959fc393c48/src/opencensus/proto/metrics/v1/metrics.proto#L112

A TimeSeries is a collection of data points that describes the time-varying
values of a metric.

:type label_values: list(:class:
'~opencensus.metrics.label_value.LabelValue')
:param label_values: The set of label values that uniquely identify this
timeseries.

:type points: list(:class: '~opencensus.metrics.export.point.Point')
:param points: The data points of this timeseries.

:type start_timestamp: str
:param start_timestamp: The time when the cumulative value was reset to
zero, must be set for cumulative metrics.
""" # noqa

def __init__(self, label_values, points, start_timestamp):
if not label_values:
raise ValueError("label_values must not be null or empty")
if not points:
raise ValueError("points must not be null or empty")
self._label_values = label_values
self._points = points
self._start_timestamp = start_timestamp

@property
def start_timestamp(self):
return self._start_timestamp

@property
def label_values(self):
return self._label_values

@property
def points(self):
return self._points

def check_points_type(self, type_):
"""Check that each point's value is an instance `type_`.

:type type_: type
:param type_: Type to check against.
"""
type_class = (
metric_descriptor.MetricDescriptorType.to_type_class(type_))
for point in self.points:
if not isinstance(point.value.value, type_class):
return False
return True
Loading