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

Refactor Meter and MeterProvider #2296

Merged
merged 13 commits into from
Dec 8, 2021
1 change: 0 additions & 1 deletion opentelemetry-api/src/opentelemetry/util/_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
def _time_ns() -> int:
return int(time() * 1e9)


ocelotl marked this conversation as resolved.
Show resolved Hide resolved
else:
from time import time_ns

Expand Down
2 changes: 2 additions & 0 deletions opentelemetry-sdk/setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ include_package_data = True
install_requires =
opentelemetry-api == 1.7.1
opentelemetry-semantic-conventions == 0.26b1
# FIXME Remove when 3.6 is no longer supported
ocelotl marked this conversation as resolved.
Show resolved Hide resolved
dataclasses == 0.8; python_version < '3.7'

[options.packages.find]
where = src
Expand Down
192 changes: 95 additions & 97 deletions opentelemetry-sdk/src/opentelemetry/sdk/_metrics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,37 @@
# See the License for the specific language governing permissions and
# limitations under the License.

# pylint: disable=function-redefined,too-many-ancestors

from abc import ABC, abstractmethod
from atexit import register, unregister
from logging import getLogger
from typing import Optional
from threading import Lock
from typing import Optional, Sequence

from opentelemetry._metrics import Meter as APIMeter
from opentelemetry._metrics import MeterProvider as APIMeterProvider
from opentelemetry._metrics import _DefaultMeter
from opentelemetry._metrics.instrument import Counter as APICounter
ocelotl marked this conversation as resolved.
Show resolved Hide resolved
from opentelemetry._metrics.instrument import Histogram as APIHistogram
from opentelemetry._metrics.instrument import (
ObservableCounter as APIObservableCounter,
)
from opentelemetry._metrics.instrument import (
ObservableGauge as APIObservableGauge,
)
from opentelemetry._metrics.instrument import (
ObservableUpDownCounter as APIObservableUpDownCounter,
)
from opentelemetry._metrics.instrument import UpDownCounter as APIUpDownCounter
from opentelemetry.sdk._metrics.instrument import (
Counter,
Histogram,
ObservableCounter,
ObservableGauge,
ObservableUpDownCounter,
UpDownCounter,
)
from opentelemetry.sdk._metrics.metric_exporter import MetricExporter
from opentelemetry.sdk._metrics.metric_reader import MetricReader
from opentelemetry.sdk._metrics.view import View
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.util.instrumentation import InstrumentationInfo

Expand All @@ -46,95 +59,96 @@ def __init__(
self._instrumentation_info = instrumentation_info
self._meter_provider = meter_provider

def create_counter(self, name, unit=None, description=None) -> Counter:
# FIXME implement this method
pass
def create_counter(self, name, unit=None, description=None) -> APICounter:
return Counter(self._instrumentation_info, name, unit, description)

def create_up_down_counter(
self, name, unit=None, description=None
) -> UpDownCounter:
# FIXME implement this method
pass
) -> APIUpDownCounter:
return UpDownCounter(
self._instrumentation_info, name, unit, description
)

def create_observable_counter(
self, name, callback, unit=None, description=None
) -> ObservableCounter:
# FIXME implement this method
pass
) -> APIObservableCounter:
return ObservableCounter(
ocelotl marked this conversation as resolved.
Show resolved Hide resolved
self._instrumentation_info, name, unit, description
ocelotl marked this conversation as resolved.
Show resolved Hide resolved
)

def create_histogram(self, name, unit=None, description=None) -> Histogram:
# FIXME implement this method
pass
def create_histogram(
self, name, unit=None, description=None
) -> APIHistogram:
return Histogram(self._instrumentation_info, name, unit, description)

def create_observable_gauge(
self, name, callback, unit=None, description=None
) -> ObservableGauge:
# FIXME implement this method
pass
) -> APIObservableGauge:
return ObservableGauge(
ocelotl marked this conversation as resolved.
Show resolved Hide resolved
self._instrumentation_info, name, unit, description
)

def create_observable_up_down_counter(
self, name, callback, unit=None, description=None
) -> ObservableUpDownCounter:
# FIXME implement this method
pass
) -> APIObservableUpDownCounter:
return ObservableUpDownCounter(
ocelotl marked this conversation as resolved.
Show resolved Hide resolved
self._instrumentation_info, name, unit, description
)


class MeterProvider(APIMeterProvider):
"""See `opentelemetry._metrics.MeterProvider`."""

def __init__(
self,
metric_exporters: Sequence[MetricExporter] = (),
lzchen marked this conversation as resolved.
Show resolved Hide resolved
metric_readers: Sequence[MetricReader] = (),
ocelotl marked this conversation as resolved.
Show resolved Hide resolved
views: Sequence[View] = (),
resource: Resource = Resource.create({}),
shutdown_on_exit: bool = True,
use_always_matching_view: bool = True,
):
self._resource = resource
self._lock = Lock()
self._atexit_handler = None
self.__use_always_matching_view = use_always_matching_view
aabmass marked this conversation as resolved.
Show resolved Hide resolved

if shutdown_on_exit:
self._atexit_handler = register(self.shutdown)

self._metric_readers = []
self._metric_exporters = []
self._views = []
self._shutdown = False
self.__metric_readers = metric_readers

def get_meter(
self,
name: str,
version: Optional[str] = None,
schema_url: Optional[str] = None,
) -> Meter:

if self._shutdown:
_logger.warning(
"A shutdown `MeterProvider` can not provide a `Meter`"
)
return _DefaultMeter(name, version=version, schema_url=schema_url)
for metric_reader in self._metric_readers:
metric_reader._register_meter_provider(self)

return Meter(InstrumentationInfo(name, version, schema_url), self)
self.__metric_exporters = metric_exporters
ocelotl marked this conversation as resolved.
Show resolved Hide resolved

def shutdown(self):
# FIXME implement a timeout
self.__views = views

if self._shutdown:
_logger.warning("shutdown can only be called once")
return False
if self.__use_always_matching_view:
self.__views = [*self.__views, View(instrument_name="*")]
ocelotl marked this conversation as resolved.
Show resolved Hide resolved

result = True
self.__resource = resource
self._shutdown = False

for metric_reader in self._metric_readers:
result = result and metric_reader.shutdown()
@property
def _metric_readers(self):
return self.__metric_readers

for metric_exporter in self._metric_exporters:
result = result and metric_exporter.shutdown()
@property
def _use_always_matching_view(self):
return self.__use_always_matching_view

self._shutdown = True
@property
def _resource(self):
return self.__resource

if self._atexit_handler is not None:
unregister(self._atexit_handler)
self._atexit_handler = None
@property
def _metric_exporters(self):
return self.__metric_exporters

return result
@property
def _views(self):
return self.__views

def force_flush(self) -> bool:

Expand All @@ -161,56 +175,40 @@ def force_flush(self) -> bool:

return metric_reader_result and metric_exporter_result

def register_metric_reader(self, metric_reader: "MetricReader") -> None:
# FIXME protect this method against race conditions
self._metric_readers.append(metric_reader)

def register_metric_exporter(
self, metric_exporter: "MetricExporter"
) -> None:
# FIXME protect this method against race conditions
self._metric_exporters.append(metric_exporter)

def register_view(self, view: "View") -> None:
# FIXME protect this method against race conditions
self._views.append(view)


class MetricReader(ABC):
def __init__(self):
self._shutdown = False

@abstractmethod
def collect(self):
pass

def shutdown(self):
# FIXME this will need a Once wrapper
self._shutdown = True
# FIXME implement a timeout

if self._shutdown:
_logger.warning("shutdown can only be called once")
return False

class MetricExporter(ABC):
def __init__(self):
self._shutdown = False
result = True

@abstractmethod
def export(self):
pass
for metric_reader in self._metric_readers:
result = result and metric_reader.shutdown()
ocelotl marked this conversation as resolved.
Show resolved Hide resolved

def shutdown(self):
# FIXME this will need a Once wrapper
self._shutdown = True
for metric_exporter in self._metric_exporters:
result = result and metric_exporter.shutdown()

self._shutdown = True

class View:
pass
if self._atexit_handler is not None:
unregister(self._atexit_handler)
self._atexit_handler = None

return result

class ConsoleMetricExporter(MetricExporter):
def export(self):
pass
def get_meter(
self,
name: str,
version: Optional[str] = None,
schema_url: Optional[str] = None,
) -> Meter:

if self._shutdown:
_logger.warning(
"A shutdown `MeterProvider` can not provide a `Meter`"
)
return _DefaultMeter(name, version=version, schema_url=schema_url)

class SDKMetricReader(MetricReader):
def collect(self):
pass
return Meter(InstrumentationInfo(name, version, schema_url), self)
aabmass marked this conversation as resolved.
Show resolved Hide resolved
Loading