25
25
from abc import ABC , abstractmethod
26
26
from logging import getLogger
27
27
from os import environ
28
- from typing import Optional , cast
28
+ from threading import Lock
29
+ from typing import List , Optional , cast
29
30
30
31
from opentelemetry .environment_variables import OTEL_PYTHON_METER_PROVIDER
31
32
from opentelemetry .metrics .instrument import (
41
42
ObservableGauge ,
42
43
ObservableUpDownCounter ,
43
44
UpDownCounter ,
45
+ _ProxyCounter ,
46
+ _ProxyHistogram ,
47
+ _ProxyInstrument ,
48
+ _ProxyObservableCounter ,
49
+ _ProxyObservableGauge ,
50
+ _ProxyObservableUpDownCounter ,
51
+ _ProxyUpDownCounter ,
44
52
)
53
+ from opentelemetry .util ._once import Once
45
54
from opentelemetry .util ._providers import _load_provider
46
55
47
56
_logger = getLogger (__name__ )
@@ -69,18 +78,33 @@ def get_meter(
69
78
return _DefaultMeter (name , version = version , schema_url = schema_url )
70
79
71
80
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
+
73
87
def get_meter (
74
88
self ,
75
89
name ,
76
90
version = None ,
77
91
schema_url = None ,
78
92
) -> "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 )
84
108
85
109
86
110
class Meter (ABC ):
@@ -215,51 +239,109 @@ def create_observable_up_down_counter(
215
239
pass
216
240
217
241
218
- class ProxyMeter (Meter ):
242
+ class _ProxyMeter (Meter ):
219
243
def __init__ (
220
244
self ,
221
245
name ,
222
246
version = None ,
223
247
schema_url = None ,
224
248
):
225
249
super ().__init__ (name , version = version , schema_url = schema_url )
250
+ self ._lock = Lock ()
251
+ self ._instruments : List [_ProxyInstrument ] = []
226
252
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
229
262
)
230
263
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 )
243
270
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
246
278
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
249
290
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
252
304
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
255
314
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
258
328
259
329
def create_observable_up_down_counter (
260
- self , * args , ** kwargs
330
+ self , name , callback , unit = "" , description = ""
261
331
) -> 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
263
345
264
346
265
347
class _DefaultMeter (Meter ):
@@ -319,8 +401,9 @@ def create_observable_up_down_counter(
319
401
)
320
402
321
403
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 ()
324
407
325
408
326
409
def get_meter (
@@ -340,35 +423,40 @@ def get_meter(
340
423
return meter_provider .get_meter (name , version )
341
424
342
425
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
+
343
440
def set_meter_provider (meter_provider : MeterProvider ) -> None :
344
441
"""Sets the current global :class:`~.MeterProvider` object.
345
442
346
443
This can only be done once, a warning will be logged if any furter attempt
347
444
is made.
348
445
"""
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 )
356
447
357
448
358
449
def get_meter_provider () -> MeterProvider :
359
450
"""Gets the current global :class:`~.MeterProvider` object."""
360
- # pylint: disable=global-statement
361
- global _METER_PROVIDER
362
- global _PROXY_METER_PROVIDER
363
451
364
452
if _METER_PROVIDER is None :
365
453
if OTEL_PYTHON_METER_PROVIDER not in environ .keys ():
366
- if _PROXY_METER_PROVIDER is None :
367
- _PROXY_METER_PROVIDER = ProxyMeterProvider ()
368
454
return _PROXY_METER_PROVIDER
369
455
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"
373
458
)
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