Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metrics Implementation #160

Merged
merged 96 commits into from
Sep 27, 2019
Merged
Show file tree
Hide file tree
Changes from 90 commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
6ca4274
Create functions
lzchen Jul 29, 2019
b23cec1
fix lint
lzchen Jul 31, 2019
981eece
Fix lint
lzchen Jul 31, 2019
8ea9709
fix typing
lzchen Jul 31, 2019
00b4f11
Remove options, constructors, seperate labels
lzchen Aug 6, 2019
34c87ce
Consistent naming for float and int
lzchen Aug 6, 2019
df8ae34
Abstract time series
lzchen Aug 6, 2019
a2561ac
Use ABC
lzchen Aug 6, 2019
1ece493
Fix typo
lzchen Aug 6, 2019
ce9268a
Fix docs
lzchen Aug 6, 2019
f5f9f01
seperate measure classes
lzchen Aug 8, 2019
74a1815
Add examples
lzchen Aug 8, 2019
0a0b8ee
fix lint
lzchen Aug 8, 2019
555bf50
Update to RFC 0003
lzchen Aug 14, 2019
d6b1113
Add spancontext, measurebatch
lzchen Aug 14, 2019
c819109
Merge branch 'master' of https://github.com/open-telemetry/openteleme…
lzchen Aug 15, 2019
18cfc71
Fix docs
lzchen Aug 15, 2019
f646555
Merge branch 'master' of https://github.com/open-telemetry/openteleme…
lzchen Aug 19, 2019
a44cb47
Fix comments
lzchen Aug 22, 2019
94eaad9
fix lint
lzchen Aug 22, 2019
fc251b2
fix lint
lzchen Aug 22, 2019
262f312
fix lint
lzchen Aug 22, 2019
66c0a56
skip examples
lzchen Aug 22, 2019
e2c4a7e
white space
lzchen Aug 22, 2019
2fb7646
fix spacing
lzchen Aug 22, 2019
eb711cb
fix imports
lzchen Aug 22, 2019
baa3a32
fix imports
lzchen Aug 22, 2019
5c30a9c
Merge branch 'master' of https://github.com/open-telemetry/openteleme…
lzchen Sep 3, 2019
211b20c
LabelValues to str
lzchen Sep 3, 2019
bffe040
Black formatting
lzchen Sep 3, 2019
0759b9a
fix isort
lzchen Sep 3, 2019
44d62f8
Remove aggregation
lzchen Sep 12, 2019
c5ab2df
Fix names
lzchen Sep 12, 2019
50d2de5
Remove aggregation from docs
lzchen Sep 12, 2019
d79bc7d
Fix lint
lzchen Sep 12, 2019
7fb7cdf
metric changes
lzchen Sep 13, 2019
79322f5
Typing
lzchen Sep 13, 2019
db242f0
Merge branch 'master' of https://github.com/open-telemetry/openteleme…
lzchen Sep 13, 2019
f9c7cc3
Fix lint
lzchen Sep 13, 2019
4465929
Fix lint
lzchen Sep 13, 2019
695e596
Add space
lzchen Sep 13, 2019
ee43e39
Fix lint
lzchen Sep 13, 2019
e59f7c4
fix comments
lzchen Sep 16, 2019
9946028
handle, recordbatch
lzchen Sep 16, 2019
e1a3101
docs
lzchen Sep 16, 2019
47d5209
Update recordbatch
lzchen Sep 16, 2019
2553b89
black
lzchen Sep 16, 2019
15c2894
Merge branch 'master' of https://github.com/open-telemetry/openteleme…
lzchen Sep 19, 2019
ebcc567
Fix typo
lzchen Sep 19, 2019
41ee12c
remove ValueType
lzchen Sep 19, 2019
62862a2
fix lint
lzchen Sep 19, 2019
b99dd3f
sdk
lzchen Sep 19, 2019
dda2966
metrics
lzchen Sep 20, 2019
38e5130
example
lzchen Sep 20, 2019
006bd89
counter
lzchen Sep 21, 2019
ae94dea
Tests
lzchen Sep 22, 2019
eed2cce
merge
lzchen Sep 22, 2019
a16556e
Address comments
lzchen Sep 23, 2019
3b0d251
ADd tests
lzchen Sep 23, 2019
8f2a216
Fix typing and examples
lzchen Sep 24, 2019
4f4e385
black
lzchen Sep 24, 2019
c0f1d10
fix lint
lzchen Sep 24, 2019
bbdc9df
remove override
lzchen Sep 24, 2019
bc617c0
Fix tests
lzchen Sep 24, 2019
081556b
mypy
lzchen Sep 24, 2019
77650be
fix lint
lzchen Sep 24, 2019
d1b8c32
fix type
lzchen Sep 24, 2019
1a8a037
fix typing
lzchen Sep 24, 2019
5c58f93
fix tests
lzchen Sep 24, 2019
1886a7e
isort
lzchen Sep 24, 2019
90fbbbb
isort
lzchen Sep 24, 2019
f602d70
isort
lzchen Sep 24, 2019
3d6654b
isort
lzchen Sep 24, 2019
6cd6ede
noop
lzchen Sep 25, 2019
d8e9a64
lint
lzchen Sep 25, 2019
311b1c4
lint
lzchen Sep 25, 2019
8ea1bd7
fix tuple typing
lzchen Sep 26, 2019
417231d
Merge branch 'metrics' of https://github.com/lzchen/opentelemetry-pyt…
lzchen Sep 26, 2019
1090a6c
fix type
lzchen Sep 26, 2019
18e6a11
black
lzchen Sep 26, 2019
4b03cb7
address comments
lzchen Sep 26, 2019
cedcd1e
fix type
lzchen Sep 26, 2019
77854c9
fix lint
lzchen Sep 26, 2019
605c947
remove imports
lzchen Sep 26, 2019
031bd1f
default tests
lzchen Sep 26, 2019
071b25b
fix lint
lzchen Sep 26, 2019
00eaea3
usse sequence
lzchen Sep 27, 2019
554b7b1
remove ellipses
lzchen Sep 27, 2019
7668de4
remove ellipses
lzchen Sep 27, 2019
61bef29
black
lzchen Sep 27, 2019
b26abba
Merge branch 'master' of https://github.com/open-telemetry/openteleme…
lzchen Sep 27, 2019
6564a3a
Fix typo
lzchen Sep 27, 2019
1083d7e
fix example
lzchen Sep 27, 2019
248e53e
fix type
lzchen Sep 27, 2019
56df6ac
fix type
lzchen Sep 27, 2019
2130ce8
address comments
lzchen Sep 27, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions docs/opentelemetry.metrics.handle.rst

This file was deleted.

7 changes: 0 additions & 7 deletions docs/opentelemetry.metrics.rst
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
opentelemetry.metrics package
=============================

Submodules
----------

.. toctree::

opentelemetry.metrics.handle

Module contents
---------------

Expand Down
295 changes: 141 additions & 154 deletions opentelemetry-api/src/opentelemetry/metrics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,147 +27,44 @@

"""
from abc import ABC, abstractmethod
from typing import Dict, List, Tuple, Type, Union
from typing import Callable, Optional, Sequence, Tuple, Type, TypeVar

from opentelemetry.metrics.handle import (
CounterHandle,
GaugeHandle,
MeasureHandle,
)
from opentelemetry.trace import SpanContext
from opentelemetry.util import loader

ValueT = TypeVar("ValueT", int, float)
Copy link
Member

Choose a reason for hiding this comment

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

Not to bikeshed on type annotations, but you don't need this or MetricT below unless you want the generic type for something. E.g. to declare that a function's return type matches one of its argument's type. As I understand it Union[int, float] would do the same thing here.

Copy link
Contributor Author

@lzchen lzchen Sep 27, 2019

Choose a reason for hiding this comment

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

I think according to @Oberon00 , mypy cannot deduce the actual return type of the parameter/function if Union is used, although I'm not really sure now what is meant by that. As well, sphinx does not seem to be able to read TypeVar in the docs.

This comment, I was using Union previously.

Copy link
Member

@Oberon00 Oberon00 Sep 27, 2019

Choose a reason for hiding this comment

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

Yes that seems indeed unused. Maybe I mixed something up in that comment, I'll have a closer look later. But as this PR discussion is getting too long and this is certainly no blocker, I guess we can make a follow-up for that.

EDIT: See #160 (comment), they would make sense.


# pylint: disable=unused-argument
class Meter:
"""An interface to allow the recording of metrics.

`Metric` s are used for recording pre-defined aggregation (gauge and
counter), or raw values (measure) in which the aggregation and labels
for the exported metric are deferred.
"""

def record_batch(
self,
label_tuples: Dict[str, str],
record_tuples: List[Tuple["Metric", Union[float, int]]],
) -> None:
"""Atomically records a batch of `Metric` and value pairs.

Allows the functionality of acting upon multiple metrics with
a single API call. Implementations should find handles that match
the key-value pairs in the label tuples.

Args:
label_tuples: A collection of key value pairs that will be matched
against to record for the metric-handle that has those labels.
record_tuples: A list of pairs of `Metric` s and the
corresponding value to record for that metric.
"""


def create_counter(
name: str,
description: str,
unit: str,
value_type: Union[Type[float], Type[int]],
is_bidirectional: bool = False,
label_keys: List[str] = None,
span_context: SpanContext = None,
) -> Union["FloatCounter", "IntCounter"]:
"""Creates a counter metric with type value_type.

By default, counter values can only go up (unidirectional). The API
should reject negative inputs to unidirectional counter metrics.
Counter metrics have a bidirectional option to allow for negative
inputs.
class DefaultMetricHandle:
"""The default MetricHandle.

Args:
name: The name of the counter.
description: Human readable description of the metric.
unit: Unit of the metric values.
value_type: The type of values being recorded by the metric.
is_bidirectional: Set to true to allow negative inputs.
label_keys: list of keys for the labels with dynamic values.
Order of the list is important as the same order must be used
on recording when suppling values for these labels.
span_context: The `SpanContext` that identifies the `Span`
that the metric is associated with.

Returns: A new counter metric for values of the given value_type.
Used when no MetricHandle implementation is available.
"""


def create_gauge(
name: str,
description: str,
unit: str,
value_type: Union[Type[float], Type[int]],
is_unidirectional: bool = False,
label_keys: List[str] = None,
span_context: SpanContext = None,
) -> Union["FloatGauge", "IntGauge"]:
"""Creates a gauge metric with type value_type.
class CounterHandle:
def add(self, value: ValueT) -> None:
"""Increases the value of the handle by ``value``"""

By default, gauge values can go both up and down (bidirectional). The API
allows for an optional unidirectional flag, in which when set will reject
descending update values.

Args:
name: The name of the gauge.
description: Human readable description of the metric.
unit: Unit of the metric values.
value_type: The type of values being recorded by the metric.
is_unidirectional: Set to true to reject negative inputs.
label_keys: list of keys for the labels with dynamic values.
Order of the list is important as the same order must be used
on recording when suppling values for these labels.
span_context: The `SpanContext` that identifies the `Span`
that the metric is associated with.

Returns: A new gauge metric for values of the given value_type.
"""

class GaugeHandle:
def set(self, value: ValueT) -> None:
"""Sets the current value of the handle to ``value``."""

def create_measure(
name: str,
description: str,
unit: str,
value_type: Union[Type[float], Type[int]],
is_non_negative: bool = False,
label_keys: List[str] = None,
span_context: SpanContext = None,
) -> Union["FloatMeasure", "IntMeasure"]:
"""Creates a measure metric with type value_type.

Measure metrics represent raw statistics that are recorded. As an option,
measure metrics can be declared as non-negative. The API will reject
negative metric events for non-negative measures.

Args:
name: The name of the measure.
description: Human readable description of the metric.
unit: Unit of the metric values.
value_type: The type of values being recorded by the metric.
is_non_negative: Set to true to reject negative inputs.
label_keys: list of keys for the labels with dynamic values.
Order of the list is important as the same order must be used
on recording when suppling values for these labels.
span_context: The `SpanContext` that identifies the `Span`
that the metric is associated with.

Returns: A new measure metric for values of the given value_type.
"""
class MeasureHandle:
def record(self, value: ValueT) -> None:
"""Records the given ``value`` to this handle."""


class Metric(ABC):
"""Base class for various types of metrics.

Metric class that inherit from this class are specialized with the type of
time series that the metric holds.
handle that the metric holds.
"""

@abstractmethod
def get_handle(self, label_values: List[str]) -> "object":
def get_handle(self, label_values: Sequence[str]) -> "object":
"""Gets a handle, used for repeated-use of metrics instruments.

Handles are useful to reduce the cost of repeatedly recording a metric
Expand All @@ -178,60 +75,150 @@ def get_handle(self, label_values: List[str]) -> "object":
a value was not provided are permitted.

Args:
label_values: A list of label values that will be associated
with the return handle.
label_values: Values to associate with the returned handle.
"""

def remove_handle(self, label_values: List[str]) -> None:
"""Removes the handle from the Metric, if present.

The handle with matching label values will be removed.
class DefaultMetric(Metric):
"""The default Metric used when no Metric implementation is available."""

args:
label_values: The list of label values to match against.
"""
def get_handle(self, label_values: Sequence[str]) -> "DefaultMetricHandle":
return DefaultMetricHandle()

def clear(self) -> None:
"""Removes all handles from the `Metric`."""

class Counter(Metric):
"""A counter type metric that expresses the computation of a sum."""

class FloatCounter(Metric):
"""A counter type metric that holds float values."""
def get_handle(self, label_values: Sequence[str]) -> "CounterHandle":
"""Gets a `CounterHandle`."""
return CounterHandle()

def get_handle(self, label_values: List[str]) -> "CounterHandle":
"""Gets a `CounterHandle` with a float value."""

class Gauge(Metric):
"""A gauge type metric that expresses a pre-calculated value.

class IntCounter(Metric):
"""A counter type metric that holds int values."""
Gauge metrics have a value that is either ``Set`` by explicit
instrumentation or observed through a callback. This kind of metric
should be used when the metric cannot be expressed as a sum or because
the measurement interval is arbitrary.
"""

def get_handle(self, label_values: List[str]) -> "CounterHandle":
"""Gets a `CounterHandle` with an int value."""
def get_handle(self, label_values: Sequence[str]) -> "GaugeHandle":
"""Gets a `GaugeHandle`."""
return GaugeHandle()


class FloatGauge(Metric):
"""A gauge type metric that holds float values."""
class Measure(Metric):
"""A measure type metric that represent raw stats that are recorded.

def get_handle(self, label_values: List[str]) -> "GaugeHandle":
"""Gets a `GaugeHandle` with a float value."""
Measure metrics represent raw statistics that are recorded. By
default, measure metrics can accept both positive and negatives.
Negative inputs will be discarded when monotonic is True.
"""

def get_handle(self, label_values: Sequence[str]) -> "MeasureHandle":
"""Gets a `MeasureHandle` with a float value."""
return MeasureHandle()

class IntGauge(Metric):
"""A gauge type metric that holds int values."""

def get_handle(self, label_values: List[str]) -> "GaugeHandle":
"""Gets a `GaugeHandle` with an int value."""
MetricT = TypeVar("MetricT", Counter, Gauge, Measure)


class FloatMeasure(Metric):
"""A measure type metric that holds float values."""
# pylint: disable=unused-argument
class Meter:
"""An interface to allow the recording of metrics.

def get_handle(self, label_values: List[str]) -> "MeasureHandle":
"""Gets a `MeasureHandle` with a float value."""
`Metric` s are used for recording pre-defined aggregation (gauge and
counter), or raw values (measure) in which the aggregation and labels
for the exported metric are deferred.
"""

def record_batch(
self,
label_values: Sequence[str],
record_tuples: Sequence[Tuple["Metric", ValueT]],
) -> None:
"""Atomically records a batch of `Metric` and value pairs.

Allows the functionality of acting upon multiple metrics with
a single API call. Implementations should find metric and handles that
match the key-value pairs in the label tuples.

Args:
label_values: The values that will be matched against to record for
the handles under each metric that has those labels.
record_tuples: A sequence of pairs of `Metric` s and the
corresponding value to record for that metric.
"""

def create_metric(
self,
name: str,
description: str,
unit: str,
value_type: Type[ValueT],
metric_type: Type[MetricT],
label_keys: Sequence[str] = None,
enabled: bool = True,
monotonic: bool = False,
Oberon00 marked this conversation as resolved.
Show resolved Hide resolved
) -> "Metric":
lzchen marked this conversation as resolved.
Show resolved Hide resolved
"""Creates a ``metric_kind`` metric with type ``value_type``.

Args:
name: The name of the counter.
lzchen marked this conversation as resolved.
Show resolved Hide resolved
description: Human-readable description of the metric.
unit: Unit of the metric values.
value_type: The type of values being recorded by the metric.
metric_type: The type of metric being created.
label_keys: The keys for the labels with dynamic values.
Order of the sequence is important as the same order must be
used on recording when suppling values for these labels.
enabled: Whether to report the metric by default.
monotonic: Whether to only allow non-negative values.

Returns: A new ``metric_type`` metric with values of ``value_type``.
"""
# pylint: disable=no-self-use
return DefaultMetric()


# Once https://github.com/python/mypy/issues/7092 is resolved,
# the following type definition should be replaced with
# from opentelemetry.util.loader import ImplementationFactory
ImplementationFactory = Callable[[Type[Meter]], Optional[Meter]]

_METER = None
_METER_FACTORY = None


def meter() -> Meter:
"""Gets the current global :class:`~.Meter` object.

If there isn't one set yet, a default will be loaded.
"""
global _METER, _METER_FACTORY # pylint:disable=global-statement

if _METER is None:
# pylint:disable=protected-access
_METER = loader._load_impl(Meter, _METER_FACTORY)
del _METER_FACTORY

return _METER


def set_preferred_meter_implementation(factory: ImplementationFactory) -> None:
"""Set the factory to be used to create the meter.

See :mod:`opentelemetry.util.loader` for details.

This function may not be called after a meter is already loaded.

Args:
factory: Callback that should create a new :class:`Meter` instance.
"""
global _METER, _METER_FACTORY # pylint:disable=global-statement

class IntMeasure(Metric):
"""A measure type metric that holds int values."""
if _METER:
raise RuntimeError("Meter already loaded.")

def get_handle(self, label_values: List[str]) -> "MeasureHandle":
"""Gets a `MeasureHandle` with an int value."""
_METER_FACTORY = factory
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,3 @@
# 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.

# pylint: skip-file
from opentelemetry import metrics

METER = metrics.Meter()
COUNTER = METER.create_counter(
"sum numbers",
"sum numbers over time",
"number",
metrics.ValueType.FLOAT,
["environment"],
)

# Metrics sent to some exporter
METRIC_TESTING = COUNTER.get_handle("Testing")
METRIC_STAGING = COUNTER.get_handle("Staging")

for i in range(100):
METRIC_STAGING.add(i)
Loading