diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index f5617eac282..3d3d5375fe4 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -48,9 +48,6 @@ class MeterProvider(ABC): - def __init__(self): - super().__init__() - @abstractmethod def get_meter( self, @@ -268,9 +265,7 @@ def create_observable_gauge(self, *args, **kwargs) -> ObservableGauge: def create_observable_up_down_counter( self, *args, **kwargs ) -> ObservableUpDownCounter: - return self._meter.create_observable_up_down_counter( - *args, **kwargs - ) + return self._meter.create_observable_up_down_counter(*args, **kwargs) class _DefaultMeter(Meter): @@ -284,9 +279,7 @@ def create_up_down_counter( super().create_up_down_counter( name, unit=unit, description=description ) - return DefaultUpDownCounter( - name, unit=unit, description=description - ) + return DefaultUpDownCounter(name, unit=unit, description=description) def create_observable_counter( self, name, callback, unit="", description="" diff --git a/opentelemetry-api/src/opentelemetry/metrics/meter.py b/opentelemetry-api/src/opentelemetry/metrics/meter.py deleted file mode 100644 index 45c6f91b5fc..00000000000 --- a/opentelemetry-api/src/opentelemetry/metrics/meter.py +++ /dev/null @@ -1,216 +0,0 @@ -# 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 diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py new file mode 100644 index 00000000000..300ea2d2063 --- /dev/null +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -0,0 +1,112 @@ +# 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=function-redefined,too-many-ancestors + +from opentelemetry.metrics import Meter, MeterProvider +from opentelemetry.metrics.measurement import Measurement +from opentelemetry.sdk.metrics.instrument import ( + Counter, + Histogram, + ObservableCounter, + ObservableGauge, + ObservableUpDownCounter, + UpDownCounter, +) +from opentelemetry.sdk.resources import Resource +from opentelemetry.util.types import Attributes + + +class Measurement(Measurement): + def __init__(self, value, **attributes: Attributes): + self._value = value + self._attributes = attributes + super().__init__(value, **attributes) + + @property + def value(self): + return self._value + + @property + def attributes(self): + return self._attributes + + +class Meter(Meter): + def __init__(self, name, version=None, schema_url=None): + + super().__init__(name, version=version, schema_url=schema_url) + + def create_counter(self, name, unit=None, description=None) -> Counter: + # pylint: disable=abstract-class-instantiated + return Counter(name, unit=unit, description=description) + + def create_up_down_counter( + self, name, unit=None, description=None + ) -> UpDownCounter: + # pylint: disable=abstract-class-instantiated + return UpDownCounter(name, unit=unit, description=description) + + def create_observable_counter( + self, name, callback, unit=None, description=None + ) -> ObservableCounter: + # pylint: disable=abstract-class-instantiated + return ObservableCounter( + name, callback, unit=unit, description=description + ) + + def create_histogram(self, name, unit=None, description=None) -> Histogram: + # pylint: disable=abstract-class-instantiated + return Histogram(name, unit=unit, description=description) + + def create_observable_gauge( + self, name, callback, unit=None, description=None + ) -> ObservableGauge: + # pylint: disable=abstract-class-instantiated + return ObservableGauge( + name, callback, unit=unit, description=description + ) + + def create_observable_up_down_counter( + self, name, callback, unit=None, description=None + ) -> ObservableUpDownCounter: + # pylint: disable=abstract-class-instantiated + return ObservableUpDownCounter( + name, callback, unit=unit, description=description + ) + + +class MeterProvider(MeterProvider): + def __init__(self, resource=None): + if resource is None: + self._resource = Resource() + else: + self._resource = resource + + def add_pipeline(self): + pass + + def configure_views(self): + pass + + def configure_timint(self): + pass + + def get_meter( + self, + name, + version=None, + schema_url=None, + ) -> Meter: + return Meter(name, version=version, schema_url=schema_url) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/aggregator.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/aggregator.py index 460982b1e98..996a593504a 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/aggregator.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/aggregator.py @@ -32,14 +32,17 @@ def _get_initial_value(self): def aggregate(self, value): if self.__class__.__bases__[0] is Aggregator: + # pylint: disable=attribute-defined-outside-init self._value = self._aggregate(value) else: for parent_class in self.__class__.__bases__: + # pylint: disable=no-member + # pylint: disable=protected-access getattr(self, parent_class._get_value_name()).aggregate(value) @abstractmethod - def _aggregate(self, new_value): + def _aggregate(self, value): pass @@ -147,6 +150,7 @@ class LastAggregator(Aggregator): def __init__(self, *args, **kwargs): if self.__class__ == LastAggregator: + # pylint: disable=assignment-from-none self._value = self._get_initial_value() setattr( self.__class__, "value", property(lambda self: self._value) @@ -217,7 +221,7 @@ def _aggregate(self, value): for bucket in self._value: if value < bucket.lower.value: - _logger.warning("Value %s below lower histogram bound" % value) + _logger.warning("Value %s below lower histogram bound", value) break if (bucket.upper.inclusive and value <= bucket.upper.value) or ( @@ -228,7 +232,7 @@ def _aggregate(self, value): else: - _logger.warning("Value %s over upper histogram bound" % value) + _logger.warning("Value {value} over upper histogram bound") return self._value @@ -263,10 +267,10 @@ def _get_initial_value(self): def _aggregate(self, value): if value < self._lower_bound: - _logger.warning("Value %s below lower set bound" % value) + _logger.warning("Value %s below lower set bound", value) elif value > self._upper_bound: - _logger.warning("Value %s over upper set bound" % value) + _logger.warning("Value %s over upper set bound", value) else: self._value.add(value) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export.py index 019417aabab..1c48d491494 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export.py @@ -17,8 +17,8 @@ from threading import Event, Lock, Thread from typing import Sequence, Tuple +from opentelemetry.metrics import Meter from opentelemetry.metrics.instrument import Instrument -from opentelemetry.metrics.meter import Meter from opentelemetry.sdk.metrics.aggregator import Aggregator from opentelemetry.sdk.resources import Resource diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/meter.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/meter.py deleted file mode 100644 index 9518ae2b8f6..00000000000 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/meter.py +++ /dev/null @@ -1,213 +0,0 @@ -# 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=function-redefined,too-many-ancestors - -from contextlib import contextmanager -from threading import Lock -from typing import Sequence - -from opentelemetry.context import attach, detach, set_value -from opentelemetry.metrics.meter import Measurement, Meter, MeterProvider -from opentelemetry.sdk.metrics.export import Record -from opentelemetry.sdk.metrics.instrument import ( - Asynchronous, - Counter, - Histogram, - Instrument, - ObservableCounter, - ObservableGauge, - ObservableUpDownCounter, - UpDownCounter, -) -from opentelemetry.sdk.resources import Resource -from opentelemetry.sdk.util import get_dict_as_key -from opentelemetry.util.types import Attributes - - -class Measurement(Measurement): - def __init__(self, value, **attributes: Attributes): - self._value = value - self._attributes = attributes - super().__init__(value, **attributes) - - @property - def value(self): - return self._value - - @property - def attributes(self): - return self._attributes - - -class Meter(Meter): - - # pylint: disable=no-self-use - def __init__(self): - - self._batch_map = {} - self._lock = Lock() - self._instruments = [] - self._views = [] - self._stateful = False - - def create_counter(self, name, unit=None, description=None) -> Counter: - return Counter(name, unit=unit, description=description) - - def create_up_down_counter( - self, name, unit=None, description=None - ) -> UpDownCounter: - return UpDownCounter(name, unit=unit, description=description) - - def create_observable_counter( - self, name, callback, unit=None, description=None - ) -> ObservableCounter: - return ObservableCounter( - name, callback, unit=unit, description=description - ) - - def create_histogram(self, name, unit=None, description=None) -> Histogram: - return Histogram(name, unit=unit, description=description) - - def create_observable_gauge( - self, name, callback, unit=None, description=None - ) -> ObservableGauge: - return ObservableGauge( - name, callback, unit=unit, description=description - ) - - def create_observable_up_down_counter( - self, name, callback, unit=None, description=None - ) -> ObservableUpDownCounter: - return ObservableUpDownCounter( - name, callback, unit=unit, description=description - ) - - @contextmanager - def get_records(self) -> Sequence[Record]: - """Gets all the records created with this `Meter` for export. - - Creates checkpoints of the current values in - each aggregator belonging to the metrics that were created with this - meter instance. - """ - # pylint: disable=too-many-branches - try: - with self._lock: - for instrument in self._instruments: - if not instrument.enabled: - continue - if isinstance(instrument, Instrument): - to_remove = [] - for ( - labels, - bound_instrument, - ) in instrument.bound_instruments.items(): - for view_data in bound_instrument.view_datas: - self._populate_batch_map( - instrument, - view_data.labels, - view_data.aggregator, - ) - - if bound_instrument.ref_count() == 0: - to_remove.append(labels) - - # Remove handles that were released - for labels in to_remove: - del instrument.bound_instruments[labels] - elif isinstance(instrument, Asynchronous): - if not instrument.run(): - continue - - for ( - labels, - aggregator, - ) in instrument.aggregators.items(): - self._populate_batch_map( - instrument, labels, aggregator - ) - - token = attach(set_value("suppress_instrumentation", True)) - records = [] - # pylint: disable=W0612 - for ( - (instrument, aggregator_type, _, labels), - aggregator, - ) in self._batch_map.items(): - records.append( - Record(instrument, labels, aggregator, self._resource) - ) - - yield records - - finally: - - detach(token) - - if not self._stateful: - self._batch_map = {} - - def _populate_batch_map(self, instrument, labels, aggregator) -> None: - """Stores record information to be ready for exporting.""" - # Checkpoints the current aggregator value to be collected for export - aggregator.take_checkpoint() - - # The uniqueness of a batch record is defined by a specific metric - # using an aggregator type with a specific set of labels. - # If two aggregators are the same but with different configs, they are still two valid unique records - # (for example, two histogram views with different buckets) - key = ( - instrument, - aggregator.__class__, - get_dict_as_key(aggregator.config), - labels, - ) - - batch_value = self._batch_map.get(key) - - if batch_value: - # Update the stored checkpointed value if exists. The call to merge - # here combines only identical records (same key). - batch_value.merge(aggregator) - return - - # create a copy of the aggregator and update - # it with the current checkpointed value for long-term storage - aggregator = aggregator.__class__(config=aggregator.config) - aggregator.merge(aggregator) - - self._batch_map[key] = aggregator - - -class MeterProvider(MeterProvider): - def __init__(self, resource=None): - self._resource = Resource() - - def add_pipeline(self): - pass - - def configure_views(self): - pass - - def configure_timint(self): - pass - - def get_meter( - self, - name, - version=None, - schema_url=None, - ) -> Meter: - return Meter() diff --git a/tests/util/src/opentelemetry/test/__init__.py b/tests/util/src/opentelemetry/test/__init__.py index ebce7268d42..9b7d7cfac39 100644 --- a/tests/util/src/opentelemetry/test/__init__.py +++ b/tests/util/src/opentelemetry/test/__init__.py @@ -27,10 +27,12 @@ def __enter__(self): def __exit__(self, type_, value, tb): # pylint: disable=invalid-name if value is not None and type_ in self._exception_types: + # Using a single-use variable here because the f string can't + # contain a backslash. + formatted_tb = "\n".join(format_tb(tb)) + self._test_case.fail( - "Unexpected exception was raised:\n{}".format( - "\n".join(format_tb(tb)) - ) + f"Unexpected exception was raised:\n{formatted_tb}" ) return True