Skip to content

Commit

Permalink
Adds metric prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
ocelotl committed Sep 30, 2021
1 parent 2631508 commit 3489314
Show file tree
Hide file tree
Showing 21 changed files with 1,898 additions and 5 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#1877](https://github.com/open-telemetry/opentelemetry-python/pull/1877))
- Added support for CreateKey functionality.
([#1853](https://github.com/open-telemetry/opentelemetry-python/pull/1853))
- Add metrics
([#1887](https://github.com/open-telemetry/opentelemetry-python/pull/1887))

### Changed
- Updated get_tracer to return an empty string when passed an invalid name
Expand Down
10 changes: 10 additions & 0 deletions docs/examples/metrics/grocery/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Grocery
=======

This is the implementation of the grocery scenario.


Useful links
------------

.. _Grocery: https://github.com/open-telemetry/oteps/blob/main/text/metrics/0146-metrics-prototype-scenarios.md#scenario-1-grocery
94 changes: 94 additions & 0 deletions docs/examples/metrics/grocery/grocery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Copyright The OpenTelemetry 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 opentelemetry.metrics import get_meter_provider
from opentelemetry.sdk.metrics.export import ConsoleExporter

exporter = ConsoleExporter()

get_meter_provider().start_pipeline_equivalent(exporter, 5)

meter = get_meter_provider().get_meter()


order_counter = meter.create_counter(
name="orders",
description="number of orders",
unit="order",
value_type=int,
)

amount_counter = meter.create_counter(
name="amount",
description="amount paid",
unit="dollar",
value_type=int,
)

sold_items_counter = meter.create_counter(
name="sold items",
description="number of sold items",
unit="item",
value_type=int,
)

customers_in_store = meter.create_up_down_counter(
name="customers in store",
description="amount of customers present in store",
unit="customer",
value_type=int,
)


class GroceryStore:
def __init__(self, name):
self._name = name

def process_order(self, customer_name, potatoes=0, tomatoes=0):
order_counter.add(1, {"store": self._name, "customer": customer_name})

amount_counter.add(
(potatoes * 1) + (tomatoes * 3),
{"store": self._name, "customer": customer_name},
)

sold_items_counter.add(
potatoes,
{
"store": self._name,
"customer": customer_name,
"item": "potato",
},
)

sold_items_counter.add(
tomatoes,
{
"store": self._name,
"customer": customer_name,
"item": "tomato",
},
)

def enter_customer(self, customer_name, account_type):

customers_in_store.add(
1, {"store": self._name, "account_type": account_type}
)

def exit_customer(self, customer_name, account_type):

customers_in_store.add(
-1, {"store": self._name, "account_type": account_type}
)
5 changes: 4 additions & 1 deletion docs/getting_started/otlpcollector_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@

# otcollector.py

from opentelemetry import trace
from opentelemetry import metrics, trace
from opentelemetry.exporter.otlp.metrics_exporter import OTLPMetricsExporter
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
OTLPSpanExporter,
)
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PushController
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

Expand Down
1 change: 1 addition & 0 deletions docs/sdk/sdk.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ OpenTelemetry Python SDK

resources
trace
metrics
error_handler
environment_variables
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def _translate_key_values(key: Text, value: Any) -> KeyValue:
return KeyValue(key=key, value=_translate_value(value))


def get_resource_data(
def _get_resource_data(
sdk_resource_instrumentation_library_data: Dict[
SDKResource, ResourceDataT
],
Expand Down
216 changes: 216 additions & 0 deletions opentelemetry-api/src/opentelemetry/metrics/meter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
# Copyright The OpenTelemetry 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.

# pylint: disable=abstract-class-instantiated
# pylint: disable=too-many-ancestors
# pylint: disable=useless-super-delegation


from abc import ABC, abstractmethod
from functools import wraps
from logging import getLogger
from typing import cast

from opentelemetry.environment_variables import OTEL_PYTHON_METER_PROVIDER
from opentelemetry.metrics.instrument import (
Counter,
DefaultCounter,
DefaultHistogram,
DefaultObservableCounter,
DefaultObservableGauge,
DefaultObservableUpDownCounter,
DefaultUpDownCounter,
Histogram,
ObservableCounter,
ObservableGauge,
ObservableUpDownCounter,
UpDownCounter,
)
from opentelemetry.util._providers import _load_provider
from opentelemetry.util.types import Attributes

_logger = getLogger(__name__)


class Measurement(ABC):
@abstractmethod
def __init__(self, value, **attributes: Attributes):
pass


class DefaultMeasurement(Measurement):
def __init__(self, value, **attributes: Attributes):
super().__init__(value, **attributes)


class Meter(ABC):

# FIXME make unit and description be "" if unit or description are None
@abstractmethod
def create_counter(self, name, unit=None, description=None) -> Counter:
pass

@abstractmethod
def create_up_down_counter(
self, name, unit=None, description=None
) -> UpDownCounter:
pass

@abstractmethod
def create_observable_counter(
self, name, callback, unit=None, description=None
) -> ObservableCounter:
pass

@abstractmethod
def create_histogram(self, name, unit=None, description=None) -> Histogram:
pass

@abstractmethod
def create_observable_gauge(
self, name, callback, unit=None, description=None
) -> ObservableGauge:
pass

@abstractmethod
def create_observable_up_down_counter(
self, name, callback, unit=None, description=None
) -> ObservableUpDownCounter:
pass

@staticmethod
def check_unique_name(checker):
def wrapper_0(method):
@wraps(method)
def wrapper_1(self, name, unit=None, description=None):
checker(self, name)
return method(self, name, unit=unit, description=description)

return wrapper_1

return wrapper_0


class DefaultMeter(Meter):
def __init__(self):
self._instrument_names = set()

def _instrument_name_checker(self, name):

if name in self._instrument_names:
raise Exception("Instrument name {} has been used already")

self._instrument_names.add(name)

@Meter.check_unique_name(_instrument_name_checker)
def create_counter(self, name, unit=None, description=None) -> Counter:
return DefaultCounter(name, unit=unit, description=description)

@Meter.check_unique_name(_instrument_name_checker)
def create_up_down_counter(
self, name, unit=None, description=None
) -> UpDownCounter:
return DefaultUpDownCounter(name, unit=unit, description=description)

@Meter.check_unique_name(_instrument_name_checker)
def create_observable_counter(
self, name, callback, unit=None, description=None
) -> ObservableCounter:
return DefaultObservableCounter(
name,
callback,
unit=unit,
description=description,
)

@Meter.check_unique_name(_instrument_name_checker)
def create_histogram(self, name, unit=None, description=None) -> Histogram:
return DefaultHistogram(name, unit=unit, description=description)

@Meter.check_unique_name(_instrument_name_checker)
def create_observable_gauge(
self, name, callback, unit=None, description=None
) -> ObservableGauge:
return DefaultObservableGauge( # pylint: disable=abstract-class-instantiated
name,
callback,
unit=unit,
description=description,
)

@Meter.check_unique_name(_instrument_name_checker)
def create_observable_up_down_counter(
self, name, callback, unit=None, description=None
) -> ObservableUpDownCounter:
return DefaultObservableUpDownCounter( # pylint: disable=abstract-class-instantiated
name,
callback,
unit=unit,
description=description,
)


class MeterProvider(ABC):
"""
var
"""

@abstractmethod
def get_meter(
self,
name,
version=None,
schema_url=None,
) -> Meter:
"""
vpas
"""
pass


class DefaultMeterProvider(MeterProvider):
def get_meter(
self,
name,
version=None,
schema_url=None,
) -> Meter:
return DefaultMeter()


_METER_PROVIDER = None


def set_meter_provider(meter_provider: MeterProvider) -> None:
"""Sets the current global :class:`~.MeterProvider` object."""
global _METER_PROVIDER # pylint: disable=global-statement

if _METER_PROVIDER is not None:
_logger.warning("Overriding of current MeterProvider is not allowed")
return

_METER_PROVIDER = meter_provider


def get_meter_provider() -> MeterProvider:
"""Gets the current global :class:`~.MeterProvider` object."""
global _METER_PROVIDER # pylint: disable=global-statement

if _METER_PROVIDER is None:
_METER_PROVIDER = cast(
"MeterProvider",
_load_provider(OTEL_PYTHON_METER_PROVIDER, "meter_provider"),
)

return _METER_PROVIDER
Loading

0 comments on commit 3489314

Please sign in to comment.