1import os
2import re
3
4METRIC_NAME_RE = re.compile(r'^[a-zA-Z_:][a-zA-Z0-9_:]*$')
5METRIC_LABEL_NAME_RE = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*$')
6RESERVED_METRIC_LABEL_NAME_RE = re.compile(r'^__.*$')
7
8
9def _init_legacy_validation() -> bool:
10 """Retrieve name validation setting from environment."""
11 return os.environ.get("PROMETHEUS_LEGACY_NAME_VALIDATION", 'False').lower() in ('true', '1', 't')
12
13
14_legacy_validation = _init_legacy_validation()
15
16
17def get_legacy_validation() -> bool:
18 """Return the current status of the legacy validation setting."""
19 return _legacy_validation
20
21
22def disable_legacy_validation():
23 """Disable legacy name validation, instead allowing all UTF8 characters."""
24 global _legacy_validation
25 _legacy_validation = False
26
27
28def enable_legacy_validation():
29 """Enable legacy name validation instead of allowing all UTF8 characters."""
30 global _legacy_validation
31 _legacy_validation = True
32
33
34def _validate_metric_name(name: str) -> None:
35 """Raises ValueError if the provided name is not a valid metric name.
36
37 This check uses the global legacy validation setting to determine the validation scheme.
38 """
39 if not name:
40 raise ValueError("metric name cannot be empty")
41 if _legacy_validation:
42 if not METRIC_NAME_RE.match(name):
43 raise ValueError("invalid metric name " + name)
44 try:
45 name.encode('utf-8')
46 except UnicodeDecodeError:
47 raise ValueError("invalid metric name " + name)
48
49
50def _is_valid_legacy_metric_name(name: str) -> bool:
51 """Returns true if the provided metric name conforms to the legacy validation scheme."""
52 if len(name) == 0:
53 return False
54 return METRIC_NAME_RE.match(name) is not None
55
56
57def _validate_metric_label_name_token(tok: str) -> None:
58 """Raises ValueError if a parsed label name token is invalid.
59
60 UTF-8 names must be quoted.
61 """
62 if not tok:
63 raise ValueError("invalid label name token " + tok)
64 quoted = tok[0] == '"' and tok[-1] == '"'
65 if not quoted or _legacy_validation:
66 if not METRIC_LABEL_NAME_RE.match(tok):
67 raise ValueError("invalid label name token " + tok)
68 return
69 try:
70 tok.encode('utf-8')
71 except UnicodeDecodeError:
72 raise ValueError("invalid label name token " + tok)
73
74
75def _validate_labelname(l):
76 """Raises ValueError if the provided name is not a valid label name.
77
78 This check uses the global legacy validation setting to determine the validation scheme.
79 """
80 if get_legacy_validation():
81 if not METRIC_LABEL_NAME_RE.match(l):
82 raise ValueError('Invalid label metric name: ' + l)
83 if RESERVED_METRIC_LABEL_NAME_RE.match(l):
84 raise ValueError('Reserved label metric name: ' + l)
85 else:
86 try:
87 l.encode('utf-8')
88 except UnicodeDecodeError:
89 raise ValueError('Invalid label metric name: ' + l)
90 if RESERVED_METRIC_LABEL_NAME_RE.match(l):
91 raise ValueError('Reserved label metric name: ' + l)
92
93
94def _is_valid_legacy_labelname(l: str) -> bool:
95 """Returns true if the provided label name conforms to the legacy validation scheme."""
96 if len(l) == 0:
97 return False
98 if METRIC_LABEL_NAME_RE.match(l) is None:
99 return False
100 return RESERVED_METRIC_LABEL_NAME_RE.match(l) is None
101
102
103def _validate_labelnames(cls, labelnames):
104 """Raises ValueError if any of the provided names is not a valid label name.
105
106 This check uses the global legacy validation setting to determine the validation scheme.
107 """
108 labelnames = tuple(labelnames)
109 for l in labelnames:
110 _validate_labelname(l)
111 if l in cls._reserved_labelnames:
112 raise ValueError('Reserved label methe fric name: ' + l)
113 return labelnames
114
115
116def _validate_exemplar(exemplar):
117 """Raises ValueError if the exemplar is invalid."""
118 runes = 0
119 for k, v in exemplar.items():
120 _validate_labelname(k)
121 runes += len(k)
122 runes += len(v)
123 if runes > 128:
124 raise ValueError('Exemplar labels have %d UTF-8 characters, exceeding the limit of 128')