8000 hooray got a simple thing to work · prometheus/client_python@1d76bea · GitHub
[go: up one dir, main page]

Skip to content

Commit 1d76bea

Browse files
committed
hooray got a simple thing to work
1 parent 37cd873 commit 1d76bea

File tree

4 files changed

+93
-10
lines changed

4 files changed

+93
-10
lines changed

prometheus_client/metrics.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
from . import values # retain this import style for testability
1212
from .context_managers import ExceptionCounter, InprogressTracker, Timer
1313
from .metrics_core import (
14-
Metric, METRIC_LABEL_NAME_RE, METRIC_NAME_RE,
15-
RESERVED_METRIC_LABEL_NAME_RE,
14+
get_legacy_validation, Metric, METRIC_LABEL_NAME_RE, METRIC_NAME_RE,
15+
RESERVED_METRIC_LABEL_NAME_RE
1616
)
1717
from .registry import Collector, CollectorRegistry, REGISTRY
1818
from .samples import Exemplar, Sample
@@ -39,10 +39,19 @@ def _build_full_name(metric_type, name, namespace, subsystem, unit):
3939

4040

4141
def _validate_labelname(l):
42-
if not METRIC_LABEL_NAME_RE.match(l):
43-
raise ValueError('Invalid label metric name: ' + l)
44-
if RESERVED_METRIC_LABEL_NAME_RE.match(l):
45-
raise ValueError('Reserved label metric name: ' + l)
42+
print("status???? ", get_legacy_validation())
43+
if get_legacy_validation():
44+
if not METRIC_LABEL_NAME_RE.match(l):
45+
raise ValueError('Invalid label metric name: ' + l)
46+
if RESERVED_METRIC_LABEL_NAME_RE.match(l):
47+
raise ValueError('Reserved label metric name: ' + l)
48+
else:
49+
try:
50+
l.encode('utf-8')
51+
except UnicodeDecodeError:
52+
raise ValueError('Invalid label metric name: ' + l)
53+
if RESERVED_METRIC_LABEL_NAME_RE.match(l):
54+
raise ValueError('Reserved label metric name: ' + l)
4655

4756

4857
def _validate_labelnames(cls, labelnames):
@@ -83,6 +92,7 @@ def enable_created_metrics():
8392
_use_created = True
8493

8594

95+
8696
class MetricWrapperBase(Collector):
8797
_type: Optional[str] = None
8898
_reserved_labelnames: Sequence[str] = ()
@@ -139,8 +149,14 @@ def __init__(self: T,
139149
self._documentation = documentation
140150
self._unit = unit
141151

142-
if not METRIC_NAME_RE.match(self._name):
143-
raise ValueError('Invalid metric name: ' + self._name)
152+
if get_legacy_validation():
153+
if not METRIC_NAME_RE.match(self._name):
154+
raise ValueError('Invalid metric name2: ' + self._name)
155+
else:
156+
try:
157+
self._name.encode('utf-8')
158+
except UnicodeDecodeError:
159+
raise ValueError('Invalid metric name3: ' + self._name)
144160

145161
if self._is_parent():
146162
# Prepare the fields needed for child metrics.
@@ -292,7 +308,7 @@ def f():
292308
# Count only one type of exception
293309
with c.count_exceptions(ValueError):
294310
pass
295-
311+
296312
You can also reset the counter to zero in case your logical "process" restarts
297313
without restarting the actual python process.
298314

prometheus_client/metrics_core.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os
12
import re
23
from typing import Dict, List, Optional, Sequence, Tuple, Union
34

@@ -12,6 +13,45 @@
1213
RESERVED_METRIC_LABEL_NAME_RE = re.compile(r'^__.*$')
1314

1415

16+
def _init_legacy_validation() -> bool:
17+
print("getting value!", os.environ.get("PROMETHEUS_LEGACY_NAME_VALIDATION", 'False').lower() in ('true', '1', 't'))
18+
return os.environ.get("PROMETHEUS_LEGACY_NAME_VALIDATION", 'False').lower() in ('true', '1', 't')
19+
20+
21+
_legacy_validation = _init_legacy_validation()
22+
23+
24+
def get_legacy_validation() -> bool:
25+
global _legacy_validation
26+
return _legacy_validation
27+
28+
29+
def disable_legacy_validation():
30+
"""Disable legacy name validation, instead allowing all UTF8 characters."""
31+
global _legacy_validation
32+
_legacy_validation = False
33+
34+
35+
def enable_legacy_validation():
36+
"""Enable legacy name validation instead of allowing all UTF8 characters."""
37+
global _legacy_validation
38+
_legacy_validation = True
39+
40+
41+
def valid_metric_name(name: str) -> bool:
42+
global _legacy_validation
43+
if _legacy_validation:
44+
return METRIC_NAME_RE.match(name)
45+
else:
46+
if not name:
47+
return False
48+
try:
49+
name.encode('utf-8')
50+
return True
51+
except UnicodeDecodeError:
52+
return False
53+
54+
1555
class Metric:
1656
"""A single metric family and its samples.
1757
@@ -24,7 +64,7 @@ class Metric:
2464
def __init__(self, name: str, documentation: str, typ: str, unit: str = ''):
2565
if unit and not name.endswith("_" + unit):
2666
name += "_" + unit
27-
if not METRIC_NAME_RE.match(name):
67+
if not valid_metric_name(name):
2868
raise ValueError('Invalid metric name: ' + name)
2969
self.name: str = name
3070
self.documentation: str = documentation

prometheus_client/registry.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def register(self, collector: Collector) -> None:
4646
for name in names:
4747
self._names_to_collectors[name] = collector
4848
self._collector_to_names[collector] = names
49+
print("ok2?", self._names_to_collectors, self._collector_to_names)
4950

5051
def unregister(self, collector: Collector) -> None:
5152
"""Remove a collector from the registry."""
@@ -137,6 +138,7 @@ def get_sample_value(self, name: str, labels: Optional[Dict[str, str]] = None) -
137138
labels = {}
138139
for metric in self.collect():
139140
for s in metric.samples:
141+
print("ok?", s)
140142
if s.name == name and s.labels == labels:
141143
return s.value
142144
return None

tests/test_core.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
)
1515
from prometheus_client.decorator import getargspec
1616
from prometheus_client.metrics import _get_use_created
17+
from prometheus_client.metrics_core import (
18+
disable_legacy_validation, enable_legacy_validation
19+
)
1720

1821

1922
def is_locked(lock):
@@ -114,8 +117,12 @@ def test_inc_not_observable(self):
114117
assert_not_observable(counter.inc)
115118

116119
def test_exemplar_invalid_label_name(self):
120+
enable_legacy_validation()
117121
self.assertRaises(ValueError, self.counter.inc, exemplar={':o)': 'smile'})
118122
self.assertRaises(ValueError, self.counter.inc, exemplar={'1': 'number'})
123+
disable_legacy_validation()
124+
self.counter.inc(exemplar={':o)': 'smile'})
125+
self.counter.inc(exemplar={'1': 'number'})
119126

120127
def test_exemplar_unicode(self):
121128
# 128 characters should not raise, even using characters larger than 1 byte.
@@ -511,8 +518,12 @@ def test_block_decorator_with_label(self):
511518
self.assertEqual(1, value('hl_bucket', {'le': '+Inf', 'l': 'a'}))
512519

513520
def test_exemplar_invalid_label_name(self):
521+
enable_legacy_validation()
514522
self.assertRaises(ValueError, self.histogram.observe, 3.0, exemplar={':o)': 'smile'})
515523
self.assertRaises(ValueError, self.histogram.observe, 3.0, exemplar={'1': 'number'})
524+
disable_legacy_validation()
525+
self.histogram.observe(3.0, exemplar={':o)': 'smile'})
526+
self.histogram.observe(3.0, exemplar={'1': 'number'})
516527

517528
def test_exemplar_too_long(self):
518529
# 129 characters in total should fail.
@@ -655,6 +666,7 @@ def test_labels_by_kwarg(self):
655666
self.assertRaises(ValueError, self.two_labels.labels, {'a': 'x'}, b='y')
656667

657668
def test_invalid_names_raise(self):
669+
enable_legacy_validation()
658670
self.assertRaises(ValueError, Counter, '', 'help')
659671
self.assertRaises(ValueError, Counter, '^', 'help')
660672
self.assertRaises(ValueError, Counter, '', 'help', namespace='&')
@@ -663,6 +675,15 @@ def test_invalid_names_raise(self):
663675
self.assertRaises(ValueError, Counter, 'c_total', '', labelnames=['a:b'])
664676
self.assertRaises(ValueError, Counter, 'c_total', '', labelnames=['__reserved'])
665677
self.assertRaises(ValueError, Summary, 'c_total', '', labelnames=['quantile'])
678+
disable_legacy_validation()
679+
self.assertRaises(ValueError, Counter, '', 'help')
680+
# self.assertRaises(ValueError, Counter, '^', 'help')
681+
# self.assertRaises(ValueError, Counter, '', 'help', namespace='&')
682+
# self.assertRaises(ValueError, Counter, '', 'help', subsystem='(')
683+
# self.assertRaises(ValueError, Counter, 'c_total', '', labelnames=['^'])
684+
# self.assertRaises(ValueError, Counter, 'c_total', '', labelnames=['a:b'])
685+
self.assertRaises(ValueError, Counter, 'c_total', '', labelnames=['__reserved'])
686+
self.assertRaises(ValueError, Summary, 'c_total', '', labelnames=['quantile'])
666687

667688
def test_empty_labels_list(self):
668689
Histogram('h', 'help', [], registry=self.registry)
@@ -713,6 +734,10 @@ def test_untyped_unit(self):
713734
def test_counter(self):
714735
self.custom_collector(CounterMetricFamily('c_total', 'help', value=1))
715736
self.assertEqual(1, self.registry.get_sample_value('c_total', {}))
737+
738+
def test_counter_utf8(self):
739+
self.custom_collector(CounterMetricFamily('my.metric', 'help', value=1))
740+
self.assertEqual(1, self.registry.get_sample_value('my.metric_total', {}))
716741

717742
def test_counter_total(self):
718743
self.custom_collector(CounterMetricFamily('c_total', 'help', value=1))

0 commit comments

Comments
 (0)
0