From a7cfa2a297362e5ee7365465cdb5ed8a76624b6f Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sat, 23 Nov 2024 03:43:46 +0900 Subject: [PATCH] Use specific exception for duplicate timeseries Use sub-class of ValueError instead of ValueError, so that we can distinguish issues caused by wrong input (like invalid name format) from duplicate metrics being registered into the same registry. Signed-off-by: Takashi Kajinami --- prometheus_client/core.py | 3 ++- prometheus_client/registry.py | 14 ++++++++---- tests/test_core.py | 42 +++++++++++++++++------------------ 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/prometheus_client/core.py b/prometheus_client/core.py index 60f93ce1..045e90ab 100644 --- a/prometheus_client/core.py +++ b/prometheus_client/core.py @@ -4,7 +4,7 @@ HistogramMetricFamily, InfoMetricFamily, Metric, StateSetMetricFamily, SummaryMetricFamily, UnknownMetricFamily, UntypedMetricFamily, ) -from .registry import CollectorRegistry, REGISTRY +from .registry import CollectorRegistry, DuplicateTimeseries, REGISTRY from .samples import BucketSpan, Exemplar, NativeHistogram, Sample, Timestamp __all__ = ( @@ -12,6 +12,7 @@ 'CollectorRegistry', 'Counter', 'CounterMetricFamily', + 'DuplicateTimeseries', 'Enum', 'Exemplar', 'Gauge', diff --git a/prometheus_client/registry.py b/prometheus_client/registry.py index c2b55d15..d4cfc273 100644 --- a/prometheus_client/registry.py +++ b/prometheus_client/registry.py @@ -1,6 +1,6 @@ import copy from threading import Lock -from typing import Dict, Iterable, List, Optional, Protocol +from typing import Dict, Iterable, List, Optional, Protocol, Set from .metrics_core import Metric @@ -15,6 +15,14 @@ def collect(self) -> Iterable[Metric]: return [] +class DuplicateTimeseries(ValueError): + def __init__(self, duplicates: Set[str]): + msg = 'Duplicated timeseries in CollectorRegistry: {}'.format( + duplicates) + super().__init__(msg) + self.duplicates: Set[str] = duplicates + + class CollectorRegistry: """Metric collector registry. @@ -40,9 +48,7 @@ def register(self, collector: Collector) -> None: names = self._get_names(collector) duplicates = set(self._names_to_collectors).intersection(names) if duplicates: - raise ValueError( - 'Duplicated timeseries in CollectorRegistry: {}'.format( - duplicates)) + raise DuplicateTimeseries(duplicates) for name in names: self._names_to_collectors[name] = collector self._collector_to_names[collector] = names diff --git a/tests/test_core.py b/tests/test_core.py index cdf32bfa..cee4bfb0 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -7,8 +7,8 @@ from prometheus_client import metrics from prometheus_client.core import ( - CollectorRegistry, Counter, CounterMetricFamily, Enum, Gauge, - GaugeHistogramMetricFamily, GaugeMetricFamily, Histogram, + CollectorRegistry, Counter, CounterMetricFamily, DuplicateTimeseries, Enum, + Gauge, GaugeHistogramMetricFamily, GaugeMetricFamily, Histogram, HistogramMetricFamily, Info, InfoMetricFamily, Metric, Sample, StateSetMetricFamily, Summary, SummaryMetricFamily, UntypedMetricFamily, ) @@ -916,41 +916,41 @@ class TestCollectorRegistry(unittest.TestCase): def test_duplicate_metrics_raises(self): registry = CollectorRegistry() Counter('c_total', 'help', registry=registry) - self.assertRaises(ValueError, Counter, 'c_total', 'help', registry=registry) - self.assertRaises(ValueError, Gauge, 'c_total', 'help', registry=registry) - self.assertRaises(ValueError, Gauge, 'c_created', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Counter, 'c_total', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Gauge, 'c_total', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Gauge, 'c_created', 'help', registry=registry) Gauge('g_created', 'help', registry=registry) - self.assertRaises(ValueError, Gauge, 'g_created', 'help', registry=registry) - self.assertRaises(ValueError, Counter, 'g', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Gauge, 'g_created', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Counter, 'g', 'help', registry=registry) Summary('s', 'help', registry=registry) - self.assertRaises(ValueError, Summary, 's', 'help', registry=registry) - self.assertRaises(ValueError, Gauge, 's_created', 'help', registry=registry) - self.assertRaises(ValueError, Gauge, 's_sum', 'help', registry=registry) - self.assertRaises(ValueError, Gauge, 's_count', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Summary, 's', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Gauge, 's_created', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Gauge, 's_sum', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Gauge, 's_count', 'help', registry=registry) # We don't currently expose quantiles, but let's prevent future # clashes anyway. - self.assertRaises(ValueError, Gauge, 's', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Gauge, 's', 'help', registry=registry) Histogram('h', 'help', registry=registry) - self.assertRaises(ValueError, Histogram, 'h', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Histogram, 'h', 'help', registry=registry) # Clashes aggaint various suffixes. - self.assertRaises(ValueError, Summary, 'h', 'help', registry=registry) - self.assertRaises(ValueError, Gauge, 'h_count', 'help', registry=registry) - self.assertRaises(ValueError, Gauge, 'h_sum', 'help', registry=registry) - self.assertRaises(ValueError, Gauge, 'h_bucket', 'help', registry=registry) - self.assertRaises(ValueError, Gauge, 'h_created', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Summary, 'h', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Gauge, 'h_count', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Gauge, 'h_sum', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Gauge, 'h_bucket', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Gauge, 'h_created', 'help', registry=registry) # The name of the histogram itself is also taken. - self.assertRaises(ValueError, Gauge, 'h', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Gauge, 'h', 'help', registry=registry) Info('i', 'help', registry=registry) - self.assertRaises(ValueError, Gauge, 'i_info', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Gauge, 'i_info', 'help', registry=registry) def test_unregister_works(self): registry = CollectorRegistry() s = Summary('s', 'help', registry=registry) - self.assertRaises(ValueError, Gauge, 's_count', 'help', registry=registry) + self.assertRaises(DuplicateTimeseries, Gauge, 's_count', 'help', registry=registry) registry.unregister(s) Gauge('s_count', 'help', registry=registry)