Skip to content

Commit 0243aa4

Browse files
authored
Return proxy instruments from ProxyMeter (#2169)
1 parent dacaff2 commit 0243aa4

File tree

6 files changed

+454
-180
lines changed

6 files changed

+454
-180
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.6.0-0.25b0...HEAD)
88

9+
- Return proxy instruments from ProxyMeter
10+
[[#2169](https://github.com/open-telemetry/opentelemetry-python/pull/2169)]
911
- Make Measurement a concrete class
1012
([#2153](https://github.com/open-telemetry/opentelemetry-python/pull/2153))
1113
- Add metrics API

docs/conf.py

+2
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@
9898
nitpick_ignore = [
9999
("py:class", "ValueT"),
100100
("py:class", "MetricT"),
101+
("py:class", "InstrumentT"),
102+
("py:obj", "opentelemetry.metrics.instrument.InstrumentT"),
101103
# Even if wrapt is added to intersphinx_mapping, sphinx keeps failing
102104
# with "class reference target not found: ObjectProxy".
103105
("py:class", "ObjectProxy"),

opentelemetry-api/src/opentelemetry/metrics/__init__.py

+140-52
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
from abc import ABC, abstractmethod
2626
from logging import getLogger
2727
from os import environ
28-
from typing import Optional, cast
28+
from threading import Lock
29+
from typing import List, Optional, cast
2930

3031
from opentelemetry.environment_variables import OTEL_PYTHON_METER_PROVIDER
3132
from opentelemetry.metrics.instrument import (
@@ -41,7 +42,15 @@
4142
ObservableGauge,
4243
ObservableUpDownCounter,
4344
UpDownCounter,
45+
_ProxyCounter,
46+
_ProxyHistogram,
47+
_ProxyInstrument,
48+
_ProxyObservableCounter,
49+
_ProxyObservableGauge,
50+
_ProxyObservableUpDownCounter,
51+
_ProxyUpDownCounter,
4452
)
53+
from opentelemetry.util._once import Once
4554
from opentelemetry.util._providers import _load_provider
4655

4756
_logger = getLogger(__name__)
@@ -69,18 +78,33 @@ def get_meter(
6978
return _DefaultMeter(name, version=version, schema_url=schema_url)
7079

7180

72-
class ProxyMeterProvider(MeterProvider):
81+
class _ProxyMeterProvider(MeterProvider):
82+
def __init__(self) -> None:
83+
self._lock = Lock()
84+
self._meters: List[_ProxyMeter] = []
85+
self._real_meter_provider: Optional[MeterProvider] = None
86+
7387
def get_meter(
7488
self,
7589
name,
7690
version=None,
7791
schema_url=None,
7892
) -> "Meter":
79-
if _METER_PROVIDER:
80-
return _METER_PROVIDER.get_meter(
81-
name, version=version, schema_url=schema_url
82-
)
83-
return ProxyMeter(name, version=version, schema_url=schema_url)
93+
with self._lock:
94+
if self._real_meter_provider is not None:
95+
return self._real_meter_provider.get_meter(
96+
name, version, schema_url
97+
)
98+
99+
meter = _ProxyMeter(name, version=version, schema_url=schema_url)
100+
self._meters.append(meter)
101+
return meter
102+
103+
def on_set_meter_provider(self, meter_provider: MeterProvider) -> None:
104+
with self._lock:
105+
self._real_meter_provider = meter_provider
106+
for meter in self._meters:
107+
meter.on_set_meter_provider(meter_provider)
84108

85109

86110
class Meter(ABC):
@@ -215,51 +239,109 @@ def create_observable_up_down_counter(
215239
pass
216240

217241

218-
class ProxyMeter(Meter):
242+
class _ProxyMeter(Meter):
219243
def __init__(
220244
self,
221245
name,
222246
version=None,
223247
schema_url=None,
224248
):
225249
super().__init__(name, version=version, schema_url=schema_url)
250+
self._lock = Lock()
251+
self._instruments: List[_ProxyInstrument] = []
226252
self._real_meter: Optional[Meter] = None
227-
self._noop_meter = _DefaultMeter(
228-
name, version=version, schema_url=schema_url
253+
254+
def on_set_meter_provider(self, meter_provider: MeterProvider) -> None:
255+
"""Called when a real meter provider is set on the creating _ProxyMeterProvider
256+
257+
Creates a real backing meter for this instance and notifies all created
258+
instruments so they can create real backing instruments.
259+
"""
260+
real_meter = meter_provider.get_meter(
261+
self._name, self._version, self._schema_url
229262
)
230263

231-
@property
232-
def _meter(self) -> Meter:
233-
if self._real_meter is not None:
234-
return self._real_meter
235-
236-
if _METER_PROVIDER:
237-
self._real_meter = _METER_PROVIDER.get_meter(
238-
self._name,
239-
self._version,
240-
)
241-
return self._real_meter
242-
return self._noop_meter
264+
with self._lock:
265+
self._real_meter = real_meter
266+
# notify all proxy instruments of the new meter so they can create
267+
# real instruments to back themselves
268+
for instrument in self._instruments:
269+
instrument.on_meter_set(real_meter)
243270

244-
def create_counter(self, *args, **kwargs) -> Counter:
245-
return self._meter.create_counter(*args, **kwargs)
271+
def create_counter(self, name, unit="", description="") -> Counter:
272+
with self._lock:
273+
if self._real_meter:
274+
return self._real_meter.create_counter(name, unit, description)
275+
proxy = _ProxyCounter(name, unit, description)
276+
self._instruments.append(proxy)
277+
return proxy
246278

247-
def create_up_down_counter(self, *args, **kwargs) -> UpDownCounter:
248-
return self._meter.create_up_down_counter(*args, **kwargs)
279+
def create_up_down_counter(
280+
self, name, unit="", description=""
281+
) -> UpDownCounter:
282+
with self._lock:
283+
if self._real_meter:
284+
return self._real_meter.create_up_down_counter(
285+
name, unit, description
286+
)
287+
proxy = _ProxyUpDownCounter(name, unit, description)
288+
self._instruments.append(proxy)
289+
return proxy
249290

250-
def create_observable_counter(self, *args, **kwargs) -> ObservableCounter:
251-
return self._meter.create_observable_counter(*args, **kwargs)
291+
def create_observable_counter(
292+
self, name, callback, unit="", description=""
293+
) -> ObservableCounter:
294+
with self._lock:
295+
if self._real_meter:
296+
return self._real_meter.create_observable_counter(
297+
name, callback, unit, description
298+
)
299+
proxy = _ProxyObservableCounter(
300+
name, callback, unit=unit, description=description
301+
)
302+
self._instruments.append(proxy)
303+
return proxy
252304

253-
def create_histogram(self, *args, **kwargs) -> Histogram:
254-
return self._meter.create_histogram(*args, **kwargs)
305+
def create_histogram(self, name, unit="", description="") -> Histogram:
306+
with self._lock:
307+
if self._real_meter:
308+
return self._real_meter.create_histogram(
309+
name, unit, description
310+
)
311+
proxy = _ProxyHistogram(name, unit, description)
312+
self._instruments.append(proxy)
313+
return proxy
255314

256-
def create_observable_gauge(self, *args, **kwargs) -> ObservableGauge:
257-
return self._meter.create_observable_gauge(*args, **kwargs)
315+
def create_observable_gauge(
316+
self, name, callback, unit="", description=""
317+
) -> ObservableGauge:
318+
with self._lock:
319+
if self._real_meter:
320+
return self._real_meter.create_observable_gauge(
321+
name, callback, unit, description
322+
)
323+
proxy = _ProxyObservableGauge(
324+
name, callback, unit=unit, description=description
325+
)
326+
self._instruments.append(proxy)
327+
return proxy
258328

259329
def create_observable_up_down_counter(
260-
self, *args, **kwargs
330+
self, name, callback, unit="", description=""
261331
) -> ObservableUpDownCounter:
262-
return self._meter.create_observable_up_down_counter(*args, **kwargs)
332+
with self._lock:
333+
if self._real_meter:
334+
return self._real_meter.create_observable_up_down_counter(
335+
name,
336+
callback,
337+
unit,
338+
description,
339+
)
340+
proxy = _ProxyObservableUpDownCounter(
341+
name, callback, unit=unit, description=description
342+
)
343+
self._instruments.append(proxy)
344+
return proxy
263345

264346

265347
class _DefaultMeter(Meter):
@@ -319,8 +401,9 @@ def create_observable_up_down_counter(
319401
)
320402

321403

322-
_METER_PROVIDER = None
323-
_PROXY_METER_PROVIDER = None
404+
_METER_PROVIDER_SET_ONCE = Once()
405+
_METER_PROVIDER: Optional[MeterProvider] = None
406+
_PROXY_METER_PROVIDER = _ProxyMeterProvider()
324407

325408

326409
def get_meter(
@@ -340,35 +423,40 @@ def get_meter(
340423
return meter_provider.get_meter(name, version)
341424

342425

426+
def _set_meter_provider(meter_provider: MeterProvider, log: bool) -> None:
427+
def set_mp() -> None:
428+
global _METER_PROVIDER # pylint: disable=global-statement
429+
_METER_PROVIDER = meter_provider
430+
431+
# gives all proxies real instruments off the newly set meter provider
432+
_PROXY_METER_PROVIDER.on_set_meter_provider(meter_provider)
433+
434+
did_set = _METER_PROVIDER_SET_ONCE.do_once(set_mp)
435+
436+
if log and not did_set:
437+
_logger.warning("Overriding of current MeterProvider is not allowed")
438+
439+
343440
def set_meter_provider(meter_provider: MeterProvider) -> None:
344441
"""Sets the current global :class:`~.MeterProvider` object.
345442
346443
This can only be done once, a warning will be logged if any furter attempt
347444
is made.
348445
"""
349-
global _METER_PROVIDER # pylint: disable=global-statement
350-
351-
if _METER_PROVIDER is not None:
352-
_logger.warning("Overriding of current MeterProvider is not allowed")
353-
return
354-
355-
_METER_PROVIDER = meter_provider
446+
_set_meter_provider(meter_provider, log=True)
356447

357448

358449
def get_meter_provider() -> MeterProvider:
359450
"""Gets the current global :class:`~.MeterProvider` object."""
360-
# pylint: disable=global-statement
361-
global _METER_PROVIDER
362-
global _PROXY_METER_PROVIDER
363451

364452
if _METER_PROVIDER is None:
365453
if OTEL_PYTHON_METER_PROVIDER not in environ.keys():
366-
if _PROXY_METER_PROVIDER is None:
367-
_PROXY_METER_PROVIDER = ProxyMeterProvider()
368454
return _PROXY_METER_PROVIDER
369455

370-
_METER_PROVIDER = cast(
371-
"MeterProvider",
372-
_load_provider(OTEL_PYTHON_METER_PROVIDER, "meter_provider"),
456+
meter_provider: MeterProvider = _load_provider(
457+
OTEL_PYTHON_METER_PROVIDER, "meter_provider"
373458
)
374-
return _METER_PROVIDER
459+
_set_meter_provider(meter_provider, log=False)
460+
461+
# _METER_PROVIDER will have been set by one thread
462+
return cast("MeterProvider", _METER_PROVIDER)

0 commit comments

Comments
 (0)