4242
4343import warnings
4444from abc import ABC , abstractmethod
45+ from dataclasses import dataclass
4546from logging import getLogger
4647from os import environ
4748from threading import Lock
48- from typing import List , Optional , Sequence , Set , Tuple , Union , cast
49+ from typing import Dict , List , Optional , Sequence , Tuple , Union , cast
4950
5051from opentelemetry .environment_variables import OTEL_PYTHON_METER_PROVIDER
5152from opentelemetry .metrics ._internal .instrument import (
@@ -177,6 +178,13 @@ def on_set_meter_provider(self, meter_provider: MeterProvider) -> None:
177178 meter .on_set_meter_provider (meter_provider )
178179
179180
181+ @dataclass
182+ class _InstrumentRegistrationStatus :
183+ already_registered : bool
184+ conflict : bool
185+ current_advisory : Optional [MetricsInstrumentAdvisory ]
186+
187+
180188class Meter (ABC ):
181189 """Handles instrument creation.
182190
@@ -194,7 +202,7 @@ def __init__(
194202 self ._name = name
195203 self ._version = version
196204 self ._schema_url = schema_url
197- self ._instrument_ids : Set [str ] = set ()
205+ self ._instrument_ids : Dict [str , str ] = {}
198206 self ._instrument_ids_lock = Lock ()
199207
200208 @property
@@ -218,38 +226,58 @@ def schema_url(self) -> Optional[str]:
218226 """
219227 return self ._schema_url
220228
221- def _is_instrument_registered (
222- self , name : str , type_ : type , unit : str , description : str
223- ) -> Tuple [bool , str ]:
229+ def _register_instrument (
230+ self ,
231+ name : str ,
232+ type_ : type ,
233+ unit : str ,
234+ description : str ,
235+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
236+ ) -> Tuple [str , _InstrumentRegistrationStatus ]:
224237 """
225- Check if an instrument with the same name, type, unit and description
226- has been registered already.
227-
228- Returns a tuple. The first value is `True` if the instrument has been
229- registered already, `False` otherwise. The second value is the
230- instrument id.
238+ Register an instrument with the name, type, unit and description as
239+ identifying keys and the advisory as value.
240+
241+ Returns a tuple. The first value is the instrument id.
242+ The second value is an `_InstrumentRegistrationStatus` where
243+ `already_registered` is `True` if the instrument has been registered
244+ already.
245+ If `conflict` is set to True the `current_advisory` attribute contains
246+ the registered instrument advisory.
231247 """
232248
233249 instrument_id = "," .join (
234250 [name .strip ().lower (), type_ .__name__ , unit , description ]
235251 )
236252
237- result = False
253+ already_registered , conflict = False , False
254+ current_advisory = None
238255
239256 with self ._instrument_ids_lock :
240- if instrument_id in self ._instrument_ids :
241- result = True
257+ # we are not using get because None is a valid value
258+ already_registered = instrument_id in self ._instrument_ids
259+ if already_registered :
260+ current_advisory = self ._instrument_ids [instrument_id ]
261+ conflict = current_advisory != advisory
242262 else :
243- self ._instrument_ids .add (instrument_id )
244-
245- return (result , instrument_id )
263+ self ._instrument_ids [instrument_id ] = advisory
264+
265+ return (
266+ instrument_id ,
267+ _InstrumentRegistrationStatus (
268+ already_registered = already_registered ,
269+ conflict = conflict ,
270+ current_advisory = current_advisory ,
271+ ),
272+ )
246273
247274 @abstractmethod
248275 def create_counter (
249276 self ,
250277 name : str ,
251278 unit : str = "" ,
252279 description : str = "" ,
280+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
253281 ) -> Counter :
254282 """Creates a `Counter` instrument
255283
@@ -266,6 +294,7 @@ def create_up_down_counter(
266294 name : str ,
267295 unit : str = "" ,
268296 description : str = "" ,
297+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
269298 ) -> UpDownCounter :
270299 """Creates an `UpDownCounter` instrument
271300
@@ -283,6 +312,7 @@ def create_observable_counter(
283312 callbacks : Optional [Sequence [CallbackT ]] = None ,
284313 unit : str = "" ,
285314 description : str = "" ,
315+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
286316 ) -> ObservableCounter :
287317 """Creates an `ObservableCounter` instrument
288318
@@ -395,6 +425,7 @@ def create_gauge( # type: ignore # pylint: disable=no-self-use
395425 name : str ,
396426 unit : str = "" ,
397427 description : str = "" ,
428+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
398429 ) -> Gauge :
399430 """Creates a ``Gauge`` instrument
400431
@@ -413,6 +444,7 @@ def create_observable_gauge(
413444 callbacks : Optional [Sequence [CallbackT ]] = None ,
414445 unit : str = "" ,
415446 description : str = "" ,
447+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
416448 ) -> ObservableGauge :
417449 """Creates an `ObservableGauge` instrument
418450
@@ -433,6 +465,7 @@ def create_observable_up_down_counter(
433465 callbacks : Optional [Sequence [CallbackT ]] = None ,
434466 unit : str = "" ,
435467 description : str = "" ,
468+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
436469 ) -> ObservableUpDownCounter :
437470 """Creates an `ObservableUpDownCounter` instrument
438471
@@ -481,6 +514,7 @@ def create_counter(
481514 name : str ,
482515 unit : str = "" ,
483516 description : str = "" ,
517+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
484518 ) -> Counter :
485519 with self ._lock :
486520 if self ._real_meter :
@@ -494,6 +528,7 @@ def create_up_down_counter(
494528 name : str ,
495529 unit : str = "" ,
496530 description : str = "" ,
531+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
497532 ) -> UpDownCounter :
498533 with self ._lock :
499534 if self ._real_meter :
@@ -510,6 +545,7 @@ def create_observable_counter(
510545 callbacks : Optional [Sequence [CallbackT ]] = None ,
511546 unit : str = "" ,
512547 description : str = "" ,
548+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
513549 ) -> ObservableCounter :
514550 with self ._lock :
515551 if self ._real_meter :
@@ -543,6 +579,7 @@ def create_gauge(
543579 name : str ,
544580 unit : str = "" ,
545581 description : str = "" ,
582+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
546583 ) -> Gauge :
547584 with self ._lock :
548585 if self ._real_meter :
@@ -557,6 +594,7 @@ def create_observable_gauge(
557594 callbacks : Optional [Sequence [CallbackT ]] = None ,
558595 unit : str = "" ,
559596 description : str = "" ,
597+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
560598 ) -> ObservableGauge :
561599 with self ._lock :
562600 if self ._real_meter :
@@ -575,6 +613,7 @@ def create_observable_up_down_counter(
575613 callbacks : Optional [Sequence [CallbackT ]] = None ,
576614 unit : str = "" ,
577615 description : str = "" ,
616+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
578617 ) -> ObservableUpDownCounter :
579618 with self ._lock :
580619 if self ._real_meter :
@@ -602,18 +641,22 @@ def create_counter(
602641 name : str ,
603642 unit : str = "" ,
604643 description : str = "" ,
644+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
605645 ) -> Counter :
606646 """Returns a no-op Counter."""
607- if self ._is_instrument_registered (
608- name , NoOpCounter , unit , description
609- )[0 ]:
647+ _ , status = self ._register_instrument (
648+ name , NoOpCounter , unit , description , advisory
649+ )
650+ if status .conflict :
610651 _logger .warning (
611652 "An instrument with name %s, type %s, unit %s and "
612- "description %s has been created already." ,
653+ "description %s has been created already with a "
654+ "different advisory value %s." ,
613655 name ,
614656 Counter .__name__ ,
615657 unit ,
616658 description ,
659+ status .current_advisory ,
617660 )
618661 return NoOpCounter (name , unit = unit , description = description )
619662
@@ -622,18 +665,22 @@ def create_gauge(
622665 name : str ,
623666 unit : str = "" ,
624667 description : str = "" ,
668+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
625669 ) -> Gauge :
626670 """Returns a no-op Gauge."""
627- if self ._is_instrument_registered (name , NoOpGauge , unit , description )[
628- 0
629- ]:
671+ _ , status = self ._register_instrument (
672+ name , NoOpGauge , unit , description , advisory
673+ )
674+ if status .conflict :
630675 _logger .warning (
631676 "An instrument with name %s, type %s, unit %s and "
632- "description %s has been created already." ,
677+ "description %s has been created already with a "
678+ "different advisory value %s." ,
633679 name ,
634680 Gauge .__name__ ,
635681 unit ,
636682 description ,
683+ status .current_advisory ,
637684 )
638685 return NoOpGauge (name , unit = unit , description = description )
639686
@@ -642,18 +689,22 @@ def create_up_down_counter(
642689 name : str ,
643690 unit : str = "" ,
644691 description : str = "" ,
692+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
645693 ) -> UpDownCounter :
646694 """Returns a no-op UpDownCounter."""
647- if self ._is_instrument_registered (
648- name , NoOpUpDownCounter , unit , description
649- )[0 ]:
695+ _ , status = self ._register_instrument (
696+ name , NoOpUpDownCounter , unit , description , advisory
697+ )
698+ if status .conflict :
650699 _logger .warning (
651700 "An instrument with name %s, type %s, unit %s and "
652- "description %s has been created already." ,
701+ "description %s has been created already with a "
702+ "different advisory value %s." ,
653703 name ,
654704 UpDownCounter .__name__ ,
655705 unit ,
656706 description ,
707+ status .current_advisory ,
657708 )
658709 return NoOpUpDownCounter (name , unit = unit , description = description )
659710
@@ -663,18 +714,22 @@ def create_observable_counter(
663714 callbacks : Optional [Sequence [CallbackT ]] = None ,
664715 unit : str = "" ,
665716 description : str = "" ,
717+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
666718 ) -> ObservableCounter :
667719 """Returns a no-op ObservableCounter."""
668- if self ._is_instrument_registered (
669- name , NoOpObservableCounter , unit , description
670- )[0 ]:
720+ _ , status = self ._register_instrument (
721+ name , NoOpObservableCounter , unit , description , advisory
722+ )
723+ if status .conflict :
671724 _logger .warning (
672725 "An instrument with name %s, type %s, unit %s and "
673- "description %s has been created already." ,
726+ "description %s has been created already with a "
727+ "different advisory value %s." ,
674728 name ,
675729 ObservableCounter .__name__ ,
676730 unit ,
677731 description ,
732+ status .current_advisory ,
678733 )
679734 return NoOpObservableCounter (
680735 name ,
@@ -691,16 +746,19 @@ def create_histogram(
691746 advisory : Optional [MetricsInstrumentAdvisory ] = None ,
692747 ) -> Histogram :
693748 """Returns a no-op Histogram."""
694- if self ._is_instrument_registered (
695- name , NoOpHistogram , unit , description
696- )[0 ]:
749+ _ , status = self ._register_instrument (
750+ name , NoOpHistogram , unit , description , advisory
751+ )
752+ if status .conflict :
697753 _logger .warning (
698754 "An instrument with name %s, type %s, unit %s and "
699- "description %s has been created already." ,
755+ "description %s has been created already with a "
756+ "different advisory value %s." ,
700757 name ,
701758 Histogram .__name__ ,
702759 unit ,
703760 description ,
761+ status .current_advisory ,
704762 )
705763 return NoOpHistogram (
706764 name , unit = unit , description = description , advisory = advisory
@@ -712,18 +770,22 @@ def create_observable_gauge(
712770 callbacks : Optional [Sequence [CallbackT ]] = None ,
713771 unit : str = "" ,
714772 description : str = "" ,
773+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
715774 ) -> ObservableGauge :
716775 """Returns a no-op ObservableGauge."""
717- if self ._is_instrument_registered (
718- name , NoOpObservableGauge , unit , description
719- )[0 ]:
776+ _ , status = self ._register_instrument (
777+ name , NoOpObservableGauge , unit , description , advisory
778+ )
779+ if status .conflict :
720780 _logger .warning (
721781 "An instrument with name %s, type %s, unit %s and "
722- "description %s has been created already." ,
782+ "description %s has been created already with a "
783+ "different advisory value %s." ,
723784 name ,
724785 ObservableGauge .__name__ ,
725786 unit ,
726787 description ,
788+ status .current_advisory ,
727789 )
728790 return NoOpObservableGauge (
729791 name ,
@@ -738,18 +800,22 @@ def create_observable_up_down_counter(
738800 callbacks : Optional [Sequence [CallbackT ]] = None ,
739801 unit : str = "" ,
740802 description : str = "" ,
803+ advisory : Optional [MetricsInstrumentAdvisory ] = None ,
741804 ) -> ObservableUpDownCounter :
742805 """Returns a no-op ObservableUpDownCounter."""
743- if self ._is_instrument_registered (
744- name , NoOpObservableUpDownCounter , unit , description
745- )[0 ]:
806+ _ , status = self ._register_instrument (
807+ name , NoOpObservableUpDownCounter , unit , description , advisory
808+ )
809+ if status .conflict :
746810 _logger .warning (
747811 "An instrument with name %s, type %s, unit %s and "
748- "description %s has been created already." ,
812+ "description %s has been created already with a "
813+ "different advisory value %s." ,
749814 name ,
750815 ObservableUpDownCounter .__name__ ,
751816 unit ,
752817 description ,
818+ status .current_advisory ,
753819 )
754820 return NoOpObservableUpDownCounter (
755821 name ,
0 commit comments