diff --git a/prometheus_client/openmetrics/parser.py b/prometheus_client/openmetrics/parser.py index 0c5c9c41..01d6feed 100644 --- a/prometheus_client/openmetrics/parser.py +++ b/prometheus_client/openmetrics/parser.py @@ -472,7 +472,8 @@ def text_fd_to_metric_families(fd): allowed_names = [] eof = False - seen_names = set() + seen_names = {} + seen_metadata = {} type_suffixes = { 'counter': ['_total', '_created'], 'summary': ['', '_count', '_sum', '_created'], @@ -484,10 +485,15 @@ def text_fd_to_metric_families(fd): def build_metric(name, documentation, typ, unit, samples): if typ is None: typ = 'unknown' + metadata = (typ, unit) + if name in seen_metadata and seen_metadata[name] != metadata: + raise ValueError("Clashing name: " + name) + seen_metadata[name] = metadata for suffix in set(type_suffixes.get(typ, []) + [""]): - if name + suffix in seen_names: + sample_name = name + suffix + if sample_name in seen_names and seen_names[sample_name] != name: raise ValueError("Clashing name: " + name + suffix) - seen_names.add(name + suffix) + seen_names[sample_name] = name if documentation is None: documentation = '' if unit is None: diff --git a/tests/openmetrics/test_parser.py b/tests/openmetrics/test_parser.py index 79a2158c..c22d8e80 100644 --- a/tests/openmetrics/test_parser.py +++ b/tests/openmetrics/test_parser.py @@ -485,6 +485,29 @@ def test_untyped(self): ] self.assertEqual([m], list(families)) + def test_repeated_metric_family(self): + families = list(text_string_to_metric_families("""# TYPE go_gc_duration_seconds summary +go_gc_duration_seconds{instance="a",quantile="0.0"} 1.0 +go_gc_duration_seconds_sum{instance="a"} 1.0 +go_gc_duration_seconds_count{instance="a"} 1.0 +# TYPE up gauge +up{instance="a"} 1.0 +# TYPE go_gc_duration_seconds summary +go_gc_duration_seconds{instance="b",quantile="0.0"} 2.0 +go_gc_duration_seconds_sum{instance="b"} 2.0 +go_gc_duration_seconds_count{instance="b"} 1.0 +# EOF +""")) + + self.assertEqual( + ["go_gc_duration_seconds", "up", "go_gc_duration_seconds"], + [family.name for family in families], + ) + self.assertEqual( + {"instance": "b", "quantile": "0.0"}, + families[2].samples[0].labels, + ) + def test_type_help_switched(self): families = text_string_to_metric_families("""# HELP a help # TYPE a counter