Skip to content

Commit a7d395e

Browse files
committed
Rework conflicting instrument registration check
First to take into account advisory and then to avoid printing warnings if there's not duplicate for already registered instrument.
1 parent c148618 commit a7d395e

File tree

4 files changed

+261
-91
lines changed

4 files changed

+261
-91
lines changed

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

Lines changed: 111 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@
4242

4343
import warnings
4444
from abc import ABC, abstractmethod
45+
from dataclasses import dataclass
4546
from logging import getLogger
4647
from os import environ
4748
from 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

5051
from opentelemetry.environment_variables import OTEL_PYTHON_METER_PROVIDER
5152
from 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+
180188
class 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

Comments
 (0)