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__ )
@@ -70,18 +79,33 @@ def get_meter(
70
79
return _DefaultMeter (name , version = version , schema_url = schema_url )
71
80
72
81
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
+
74
88
def get_meter (
75
89
self ,
76
90
name ,
77
91
version = None ,
78
92
schema_url = None ,
79
93
) -> "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 )
85
109
86
110
87
111
class Meter (ABC ):
@@ -225,51 +249,109 @@ def create_observable_up_down_counter(
225
249
self ._secure_instrument_name (name )
226
250
227
251
228
- class ProxyMeter (Meter ):
252
+ class _ProxyMeter (Meter ):
229
253
def __init__ (
230
254
self ,
231
255
name ,
232
256
version = None ,
233
257
schema_url = None ,
234
258
):
235
259
super ().__init__ (name , version = version , schema_url = schema_url )
260
+ self ._lock = Lock ()
261
+ self ._instruments : List [_ProxyInstrument ] = []
236
262
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
239
272
)
240
273
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 )
253
280
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
256
288
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
259
300
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
262
314
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
265
324
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
268
338
269
339
def create_observable_up_down_counter (
270
- self , * args , ** kwargs
340
+ self , name , callback , unit = "" , description = ""
271
341
) -> 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
273
355
274
356
275
357
class _DefaultMeter (Meter ):
@@ -329,8 +411,9 @@ def create_observable_up_down_counter(
329
411
)
330
412
331
413
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 ()
334
417
335
418
336
419
def get_meter (
@@ -350,35 +433,40 @@ def get_meter(
350
433
return meter_provider .get_meter (name , version )
351
434
352
435
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
+
353
450
def set_meter_provider (meter_provider : MeterProvider ) -> None :
354
451
"""Sets the current global :class:`~.MeterProvider` object.
355
452
356
453
This can only be done once, a warning will be logged if any furter attempt
357
454
is made.
358
455
"""
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 )
366
457
367
458
368
459
def get_meter_provider () -> MeterProvider :
369
460
"""Gets the current global :class:`~.MeterProvider` object."""
370
- # pylint: disable=global-statement
371
- global _METER_PROVIDER
372
- global _PROXY_METER_PROVIDER
373
461
374
462
if _METER_PROVIDER is None :
375
463
if OTEL_PYTHON_METER_PROVIDER not in environ .keys ():
376
- if _PROXY_METER_PROVIDER is None :
377
- _PROXY_METER_PROVIDER = ProxyMeterProvider ()
378
464
return _PROXY_METER_PROVIDER
379
465
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"
383
468
)
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