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 1, 2021
1 parent 3fdfa6d commit 9f75d73
Show file tree
Hide file tree
Showing 24 changed files with 2,052 additions and 6 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,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
1 change: 1 addition & 0 deletions docs/api/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ OpenTelemetry Python API
baggage
context
trace
metrics
environment_variables
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 @@ -125,7 +125,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
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,14 @@
"""
.. envvar:: OTEL_PYTHON_TRACER_PROVIDER
"""

OTEL_PYTHON_METER_PROVIDER = "OTEL_PYTHON_METER_PROVIDER"
"""
.. envvar:: OTEL_PYTHON_METER_PROVIDER
"""

OTEL_METRICS_EXPORTER = "OTEL_METRICS_EXPORTER"
"""
.. envvar:: OTEL_METRICS_EXPORTER
"""
140 changes: 140 additions & 0 deletions opentelemetry-api/src/opentelemetry/metrics/instrument.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# 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 logging import getLogger
from re import ASCII
from re import compile as compile_
from typing import Callable

_logger = getLogger(__name__)


class Instrument(ABC):

_name_regex = compile_(r"[a-zA-Z][-.\w]{0,62}", ASCII)

@abstractmethod
def __init__(self, name, unit="", description=""):

if self._name_regex.fullmatch(name) is None:
raise Exception("Invalid instrument name {}".format(name))

if len(unit) > 63 or any(ord(character) > 127 for character in unit):
raise Exception("unit must be 63 characters or shorter")


class Synchronous(Instrument):
pass


class Asynchronous(Instrument):
@abstractmethod
def __init__(self, name, callback, unit="", description=""):

if not isinstance(callback, Callable):
raise Exception("callback must be callable")

super().__init__(name, unit=unit, description=description)


class Adding(Instrument):
pass


class Grouping(Instrument):
pass


class Monotonic(Adding):
pass


class NonMonotonic(Adding):
pass


class Counter(Monotonic, Synchronous):
@abstractmethod
def add(self, amount, **attributes):
if amount < 0:
raise Exception("Amount must be non-negative")


class DefaultCounter(Counter):
def __init__(self, name, unit="", description=""):
super().__init__(name, unit=unit, description=description)

def add(self, amount, **attributes):
return super().add(amount, **attributes)


class UpDownCounter(NonMonotonic, Synchronous):
@abstractmethod
def add(self, amount, **attributes):
pass


class DefaultUpDownCounter(UpDownCounter):
def __init__(self, name, unit="", description=""):
super().__init__(name, unit=unit, description=description)

def add(self, amount, **attributes):
return super().add(amount, **attributes)


class ObservableCounter(Monotonic, Asynchronous):
pass


class DefaultObservableCounter(ObservableCounter):
def __init__(self, name, callback, unit="", description=""):
super().__init__(name, callback, unit=unit, description=description)


class ObservableUpDownCounter(NonMonotonic, Asynchronous):
pass


class DefaultObservableUpDownCounter(ObservableUpDownCounter):
def __init__(self, name, callback, unit="", description=""):
super().__init__(name, callback, unit=unit, description=description)


class Histogram(Grouping, Synchronous):
@abstractmethod
def record(self, amount, **attributes):
pass


class DefaultHistogram(Histogram):
def __init__(self, name, unit="", description=""):
super().__init__(name, unit=unit, description=description)

def record(self, amount, **attributes):
return super().record(amount, **attributes)


class ObservableGauge(Grouping, Asynchronous):
pass


class DefaultObservableGauge(ObservableGauge):
def __init__(self, name, callback, unit="", description=""):
super().__init__(name, callback, unit=unit, description=description)
Loading

0 comments on commit 9f75d73

Please sign in to comment.