Skip to content

Commit 22b561d

Browse files
aabmassocelotl
authored andcommitted
Return proxy instruments from ProxyMeter (open-telemetry#2169)
1 parent 162acb9 commit 22b561d

File tree

2 files changed

+233
-53
lines changed

2 files changed

+233
-53
lines changed

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__)
@@ -70,18 +79,33 @@ def get_meter(
7079
return _DefaultMeter(name, version=version, schema_url=schema_url)
7180

7281

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

86110

87111
class Meter(ABC):
@@ -225,51 +249,109 @@ def create_observable_up_down_counter(
225249
self._secure_instrument_name(name)
226250

227251

228-
class ProxyMeter(Meter):
252+
class _ProxyMeter(Meter):
229253
def __init__(
230254
self,
231255
name,
232256
version=None,
233257
schema_url=None,
234258
):
235259
super().__init__(name, version=version, schema_url=schema_url)
260+
self._lock = Lock()
261+
self._instruments: List[_ProxyInstrument] = []
236262
self._real_meter: Optional[Meter] = None
237-
self._noop_meter = _DefaultMeter(
238-
name, version=version, schema_url=schema_url
263+
264+
def on_set_meter_provider(self, meter_provider: MeterProvider) -> None:
265+
"""Called when a real meter provider is set on the creating _ProxyMeterProvider
266+
267+
Creates a real backing meter for this instance and notifies all created
268+
instruments so they can create real backing instruments.
269+
"""
270+
real_meter = meter_provider.get_meter(
271+
self._name, self._version, self._schema_url
239272
)
240273

241-
@property
242-
def _meter(self) -> Meter:
243-
if self._real_meter is not None:
244-
return self._real_meter
245-
246-
if _METER_PROVIDER:
247-
self._real_meter = _METER_PROVIDER.get_meter(
248-
self._name,
249-
self._version,
250-
)
251-
return self._real_meter
252-
return self._noop_meter
274+
with self._lock:
275+
self._real_meter = real_meter
276+
# notify all proxy instruments of the new meter so they can create
277+
# real instruments to back themselves
278+
for instrument in self._instruments:
279+
instrument.on_meter_set(real_meter)
253280

254-
def create_counter(self, *args, **kwargs) -> Counter:
255-
return self._meter.create_counter(*args, **kwargs)
281+
def create_counter(self, name, unit="", description="") -> Counter:
282+
with self._lock:
283+
if self._real_meter:
284+
return self._real_meter.create_counter(name, unit, description)
285+
proxy = _ProxyCounter(name, unit, description)
286+
self._instruments.append(proxy)
287+
return proxy
256288

257-
def create_up_down_counter(self, *args, **kwargs) -> UpDownCounter:
258-
return self._meter.create_up_down_counter(*args, **kwargs)
289+
def create_up_down_counter(
290+
self, name, unit="", description=""
291+
) -> UpDownCounter:
292+
with self._lock:
293+
if self._real_meter:
294+
return self._real_meter.create_up_down_counter(
295+
name, unit, description
296+
)
297+
proxy = _ProxyUpDownCounter(name, unit, description)
298+
self._instruments.append(proxy)
299+
return proxy
259300

260-
def create_observable_counter(self, *args, **kwargs) -> ObservableCounter:
261-
return self._meter.create_observable_counter(*args, **kwargs)
301+
def create_observable_counter(
302+
self, name, callback, unit="", description=""
303+
) -> ObservableCounter:
304+
with self._lock:
305+
if self._real_meter:
306+
return self._real_meter.create_observable_counter(
307+
name, callback, unit, description
308+
)
309+
proxy = _ProxyObservableCounter(
310+
name, callback, unit=unit, description=description
311+
)
312+
self._instruments.append(proxy)
313+
return proxy
262314

263-
def create_histogram(self, *args, **kwargs) -> Histogram:
264-
return self._meter.create_histogram(*args, **kwargs)
315+
def create_histogram(self, name, unit="", description="") -> Histogram:
316+
with self._lock:
317+
if self._real_meter:
318+
return self._real_meter.create_histogram(
319+
name, unit, description
320+
)
321+
proxy = _ProxyHistogram(name, unit, description)
322+
self._instruments.append(proxy)
323+
return proxy
265324

266-
def create_observable_gauge(self, *args, **kwargs) -> ObservableGauge:
267-
return self._meter.create_observable_gauge(*args, **kwargs)
325+
def create_observable_gauge(
326+
self, name, callback, unit="", description=""
327+
) -> ObservableGauge:
328+
with self._lock:
329+
if self._real_meter:
330+
return self._real_meter.create_observable_gauge(
331+
name, callback, unit, description
332+
)
333+
proxy = _ProxyObservableGauge(
334+
name, callback, unit=unit, description=description
335+
)
336+
self._instruments.append(proxy)
337+
return proxy
268338

269339
def create_observable_up_down_counter(
270-
self, *args, **kwargs
340+
self, name, callback, unit="", description=""
271341
) -> ObservableUpDownCounter:
272-
return self._meter.create_observable_up_down_counter(*args, **kwargs)
342+
with self._lock:
343+
if self._real_meter:
344+
return self._real_meter.create_observable_up_down_counter(
345+
name,
346+
callback,
347+
unit,
348+
description,
349+
)
350+
proxy = _ProxyObservableUpDownCounter(
351+
name, callback, unit=unit, description=description
352+
)
353+
self._instruments.append(proxy)
354+
return proxy
273355

274356

275357
class _DefaultMeter(Meter):
@@ -329,8 +411,9 @@ def create_observable_up_down_counter(
329411
)
330412

331413

332-
_METER_PROVIDER = None
333-
_PROXY_METER_PROVIDER = None
414+
_METER_PROVIDER_SET_ONCE = Once()
415+
_METER_PROVIDER: Optional[MeterProvider] = None
416+
_PROXY_METER_PROVIDER = _ProxyMeterProvider()
334417

335418

336419
def get_meter(
@@ -350,35 +433,40 @@ def get_meter(
350433
return meter_provider.get_meter(name, version)
351434

352435

436+
def _set_meter_provider(meter_provider: MeterProvider, log: bool) -> None:
437+
def set_mp() -> None:
438+
global _METER_PROVIDER # pylint: disable=global-statement
439+
_METER_PROVIDER = meter_provider
440+
441+
# gives all proxies real instruments off the newly set meter provider
442+
_PROXY_METER_PROVIDER.on_set_meter_provider(meter_provider)
443+
444+
did_set = _METER_PROVIDER_SET_ONCE.do_once(set_mp)
445+
446+
if log and not did_set:
447+
_logger.warning("Overriding of current MeterProvider is not allowed")
448+
449+
353450
def set_meter_provider(meter_provider: MeterProvider) -> None:
354451
"""Sets the current global :class:`~.MeterProvider` object.
355452
356453
This can only be done once, a warning will be logged if any furter attempt
357454
is made.
358455
"""
359-
global _METER_PROVIDER # pylint: disable=global-statement
360-
361-
if _METER_PROVIDER is not None:
362-
_logger.warning("Overriding of current MeterProvider is not allowed")
363-
return
364-
365-
_METER_PROVIDER = meter_provider
456+
_set_meter_provider(meter_provider, log=True)
366457

367458

368459
def get_meter_provider() -> MeterProvider:
369460
"""Gets the current global :class:`~.MeterProvider` object."""
370-
# pylint: disable=global-statement
371-
global _METER_PROVIDER
372-
global _PROXY_METER_PROVIDER
373461

374462
if _METER_PROVIDER is None:
375463
if OTEL_PYTHON_METER_PROVIDER not in environ.keys():
376-
if _PROXY_METER_PROVIDER is None:
377-
_PROXY_METER_PROVIDER = ProxyMeterProvider()
378464
return _PROXY_METER_PROVIDER
379465

380-
_METER_PROVIDER = cast(
381-
"MeterProvider",
382-
_load_provider(OTEL_PYTHON_METER_PROVIDER, "meter_provider"),
466+
meter_provider: MeterProvider = _load_provider(
467+
OTEL_PYTHON_METER_PROVIDER, "meter_provider"
383468
)
384-
return _METER_PROVIDER
469+
_set_meter_provider(meter_provider, log=False)
470+
471+
# _METER_PROVIDER will have been set by one thread
472+
return cast("MeterProvider", _METER_PROVIDER)

0 commit comments

Comments
 (0)