aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/prometheus_client/metrics_core.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/prometheus_client/metrics_core.py')
-rw-r--r--.venv/lib/python3.12/site-packages/prometheus_client/metrics_core.py418
1 files changed, 418 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/prometheus_client/metrics_core.py b/.venv/lib/python3.12/site-packages/prometheus_client/metrics_core.py
new file mode 100644
index 00000000..7226d920
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/prometheus_client/metrics_core.py
@@ -0,0 +1,418 @@
+import re
+from typing import Dict, List, Optional, Sequence, Tuple, Union
+
+from .samples import Exemplar, Sample, Timestamp
+
+METRIC_TYPES = (
+ 'counter', 'gauge', 'summary', 'histogram',
+ 'gaugehistogram', 'unknown', 'info', 'stateset',
+)
+METRIC_NAME_RE = re.compile(r'^[a-zA-Z_:][a-zA-Z0-9_:]*$')
+METRIC_LABEL_NAME_RE = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*$')
+RESERVED_METRIC_LABEL_NAME_RE = re.compile(r'^__.*$')
+
+
+class Metric:
+ """A single metric family and its samples.
+
+ This is intended only for internal use by the instrumentation client.
+
+ Custom collectors should use GaugeMetricFamily, CounterMetricFamily
+ and SummaryMetricFamily instead.
+ """
+
+ def __init__(self, name: str, documentation: str, typ: str, unit: str = ''):
+ if unit and not name.endswith("_" + unit):
+ name += "_" + unit
+ if not METRIC_NAME_RE.match(name):
+ raise ValueError('Invalid metric name: ' + name)
+ self.name: str = name
+ self.documentation: str = documentation
+ self.unit: str = unit
+ if typ == 'untyped':
+ typ = 'unknown'
+ if typ not in METRIC_TYPES:
+ raise ValueError('Invalid metric type: ' + typ)
+ self.type: str = typ
+ self.samples: List[Sample] = []
+
+ def add_sample(self, name: str, labels: Dict[str, str], value: float, timestamp: Optional[Union[Timestamp, float]] = None, exemplar: Optional[Exemplar] = None) -> None:
+ """Add a sample to the metric.
+
+ Internal-only, do not use."""
+ self.samples.append(Sample(name, labels, value, timestamp, exemplar))
+
+ def __eq__(self, other: object) -> bool:
+ return (isinstance(other, Metric)
+ and self.name == other.name
+ and self.documentation == other.documentation
+ and self.type == other.type
+ and self.unit == other.unit
+ and self.samples == other.samples)
+
+ def __repr__(self) -> str:
+ return "Metric({}, {}, {}, {}, {})".format(
+ self.name,
+ self.documentation,
+ self.type,
+ self.unit,
+ self.samples,
+ )
+
+ def _restricted_metric(self, names):
+ """Build a snapshot of a metric with samples restricted to a given set of names."""
+ samples = [s for s in self.samples if s[0] in names]
+ if samples:
+ m = Metric(self.name, self.documentation, self.type)
+ m.samples = samples
+ return m
+ return None
+
+
+class UnknownMetricFamily(Metric):
+ """A single unknown metric and its samples.
+ For use by custom collectors.
+ """
+
+ def __init__(self,
+ name: str,
+ documentation: str,
+ value: Optional[float] = None,
+ labels: Optional[Sequence[str]] = None,
+ unit: str = '',
+ ):
+ Metric.__init__(self, name, documentation, 'unknown', unit)
+ if labels is not None and value is not None:
+ raise ValueError('Can only specify at most one of value and labels.')
+ if labels is None:
+ labels = []
+ self._labelnames = tuple(labels)
+ if value is not None:
+ self.add_metric([], value)
+
+ def add_metric(self, labels: Sequence[str], value: float, timestamp: Optional[Union[Timestamp, float]] = None) -> None:
+ """Add a metric to the metric family.
+ Args:
+ labels: A list of label values
+ value: The value of the metric.
+ """
+ self.samples.append(Sample(self.name, dict(zip(self._labelnames, labels)), value, timestamp))
+
+
+# For backward compatibility.
+UntypedMetricFamily = UnknownMetricFamily
+
+
+class CounterMetricFamily(Metric):
+ """A single counter and its samples.
+
+ For use by custom collectors.
+ """
+
+ def __init__(self,
+ name: str,
+ documentation: str,
+ value: Optional[float] = None,
+ labels: Optional[Sequence[str]] = None,
+ created: Optional[float] = None,
+ unit: str = '',
+ ):
+ # Glue code for pre-OpenMetrics metrics.
+ if name.endswith('_total'):
+ name = name[:-6]
+ Metric.__init__(self, name, documentation, 'counter', unit)
+ if labels is not None and value is not None:
+ raise ValueError('Can only specify at most one of value and labels.')
+ if labels is None:
+ labels = []
+ self._labelnames = tuple(labels)
+ if value is not None:
+ self.add_metric([], value, created)
+
+ def add_metric(self,
+ labels: Sequence[str],
+ value: float,
+ created: Optional[float] = None,
+ timestamp: Optional[Union[Timestamp, float]] = None,
+ ) -> None:
+ """Add a metric to the metric family.
+
+ Args:
+ labels: A list of label values
+ value: The value of the metric
+ created: Optional unix timestamp the child was created at.
+ """
+ self.samples.append(Sample(self.name + '_total', dict(zip(self._labelnames, labels)), value, timestamp))
+ if created is not None:
+ self.samples.append(Sample(self.name + '_created', dict(zip(self._labelnames, labels)), created, timestamp))
+
+
+class GaugeMetricFamily(Metric):
+ """A single gauge and its samples.
+
+ For use by custom collectors.
+ """
+
+ def __init__(self,
+ name: str,
+ documentation: str,
+ value: Optional[float] = None,
+ labels: Optional[Sequence[str]] = None,
+ unit: str = '',
+ ):
+ Metric.__init__(self, name, documentation, 'gauge', unit)
+ if labels is not None and value is not None:
+ raise ValueError('Can only specify at most one of value and labels.')
+ if labels is None:
+ labels = []
+ self._labelnames = tuple(labels)
+ if value is not None:
+ self.add_metric([], value)
+
+ def add_metric(self, labels: Sequence[str], value: float, timestamp: Optional[Union[Timestamp, float]] = None) -> None:
+ """Add a metric to the metric family.
+
+ Args:
+ labels: A list of label values
+ value: A float
+ """
+ self.samples.append(Sample(self.name, dict(zip(self._labelnames, labels)), value, timestamp))
+
+
+class SummaryMetricFamily(Metric):
+ """A single summary and its samples.
+
+ For use by custom collectors.
+ """
+
+ def __init__(self,
+ name: str,
+ documentation: str,
+ count_value: Optional[int] = None,
+ sum_value: Optional[float] = None,
+ labels: Optional[Sequence[str]] = None,
+ unit: str = '',
+ ):
+ Metric.__init__(self, name, documentation, 'summary', unit)
+ if (sum_value is None) != (count_value is None):
+ raise ValueError('count_value and sum_value must be provided together.')
+ if labels is not None and count_value is not None:
+ raise ValueError('Can only specify at most one of value and labels.')
+ if labels is None:
+ labels = []
+ self._labelnames = tuple(labels)
+ # The and clause is necessary only for typing, the above ValueError will raise if only one is set.
+ if count_value is not None and sum_value is not None:
+ self.add_metric([], count_value, sum_value)
+
+ def add_metric(self,
+ labels: Sequence[str],
+ count_value: int,
+ sum_value: float,
+ timestamp:
+ Optional[Union[float, Timestamp]] = None
+ ) -> None:
+ """Add a metric to the metric family.
+
+ Args:
+ labels: A list of label values
+ count_value: The count value of the metric.
+ sum_value: The sum value of the metric.
+ """
+ self.samples.append(Sample(self.name + '_count', dict(zip(self._labelnames, labels)), count_value, timestamp))
+ self.samples.append(Sample(self.name + '_sum', dict(zip(self._labelnames, labels)), sum_value, timestamp))
+
+
+class HistogramMetricFamily(Metric):
+ """A single histogram and its samples.
+
+ For use by custom collectors.
+ """
+
+ def __init__(self,
+ name: str,
+ documentation: str,
+ buckets: Optional[Sequence[Union[Tuple[str, float], Tuple[str, float, Exemplar]]]] = None,
+ sum_value: Optional[float] = None,
+ labels: Optional[Sequence[str]] = None,
+ unit: str = '',
+ ):
+ Metric.__init__(self, name, documentation, 'histogram', unit)
+ if sum_value is not None and buckets is None:
+ raise ValueError('sum value cannot be provided without buckets.')
+ if labels is not None and buckets is not None:
+ raise ValueError('Can only specify at most one of buckets and labels.')
+ if labels is None:
+ labels = []
+ self._labelnames = tuple(labels)
+ if buckets is not None:
+ self.add_metric([], buckets, sum_value)
+
+ def add_metric(self,
+ labels: Sequence[str],
+ buckets: Sequence[Union[Tuple[str, float], Tuple[str, float, Exemplar]]],
+ sum_value: Optional[float],
+ timestamp: Optional[Union[Timestamp, float]] = None) -> None:
+ """Add a metric to the metric family.
+
+ Args:
+ labels: A list of label values
+ buckets: A list of lists.
+ Each inner list can be a pair of bucket name and value,
+ or a triple of bucket name, value, and exemplar.
+ The buckets must be sorted, and +Inf present.
+ sum_value: The sum value of the metric.
+ """
+ for b in buckets:
+ bucket, value = b[:2]
+ exemplar = None
+ if len(b) == 3:
+ exemplar = b[2] # type: ignore
+ self.samples.append(Sample(
+ self.name + '_bucket',
+ dict(list(zip(self._labelnames, labels)) + [('le', bucket)]),
+ value,
+ timestamp,
+ exemplar,
+ ))
+ # Don't include sum and thus count if there's negative buckets.
+ if float(buckets[0][0]) >= 0 and sum_value is not None:
+ # +Inf is last and provides the count value.
+ self.samples.append(
+ Sample(self.name + '_count', dict(zip(self._labelnames, labels)), buckets[-1][1], timestamp))
+ self.samples.append(
+ Sample(self.name + '_sum', dict(zip(self._labelnames, labels)), sum_value, timestamp))
+
+
+
+class GaugeHistogramMetricFamily(Metric):
+ """A single gauge histogram and its samples.
+
+ For use by custom collectors.
+ """
+
+ def __init__(self,
+ name: str,
+ documentation: str,
+ buckets: Optional[Sequence[Tuple[str, float]]] = None,
+ gsum_value: Optional[float] = None,
+ labels: Optional[Sequence[str]] = None,
+ unit: str = '',
+ ):
+ Metric.__init__(self, name, documentation, 'gaugehistogram', unit)
+ if labels is not None and buckets is not None:
+ raise ValueError('Can only specify at most one of buckets and labels.')
+ if labels is None:
+ labels = []
+ self._labelnames = tuple(labels)
+ if buckets is not None:
+ self.add_metric([], buckets, gsum_value)
+
+ def add_metric(self,
+ labels: Sequence[str],
+ buckets: Sequence[Tuple[str, float]],
+ gsum_value: Optional[float],
+ timestamp: Optional[Union[float, Timestamp]] = None,
+ ) -> None:
+ """Add a metric to the metric family.
+
+ Args:
+ labels: A list of label values
+ buckets: A list of pairs of bucket names and values.
+ The buckets must be sorted, and +Inf present.
+ gsum_value: The sum value of the metric.
+ """
+ for bucket, value in buckets:
+ self.samples.append(Sample(
+ self.name + '_bucket',
+ dict(list(zip(self._labelnames, labels)) + [('le', bucket)]),
+ value, timestamp))
+ # +Inf is last and provides the count value.
+ self.samples.extend([
+ Sample(self.name + '_gcount', dict(zip(self._labelnames, labels)), buckets[-1][1], timestamp),
+ # TODO: Handle None gsum_value correctly. Currently a None will fail exposition but is allowed here.
+ Sample(self.name + '_gsum', dict(zip(self._labelnames, labels)), gsum_value, timestamp), # type: ignore
+ ])
+
+
+class InfoMetricFamily(Metric):
+ """A single info and its samples.
+
+ For use by custom collectors.
+ """
+
+ def __init__(self,
+ name: str,
+ documentation: str,
+ value: Optional[Dict[str, str]] = None,
+ labels: Optional[Sequence[str]] = None,
+ ):
+ Metric.__init__(self, name, documentation, 'info')
+ if labels is not None and value is not None:
+ raise ValueError('Can only specify at most one of value and labels.')
+ if labels is None:
+ labels = []
+ self._labelnames = tuple(labels)
+ if value is not None:
+ self.add_metric([], value)
+
+ def add_metric(self,
+ labels: Sequence[str],
+ value: Dict[str, str],
+ timestamp: Optional[Union[Timestamp, float]] = None,
+ ) -> None:
+ """Add a metric to the metric family.
+
+ Args:
+ labels: A list of label values
+ value: A dict of labels
+ """
+ self.samples.append(Sample(
+ self.name + '_info',
+ dict(dict(zip(self._labelnames, labels)), **value),
+ 1,
+ timestamp,
+ ))
+
+
+class StateSetMetricFamily(Metric):
+ """A single stateset and its samples.
+
+ For use by custom collectors.
+ """
+
+ def __init__(self,
+ name: str,
+ documentation: str,
+ value: Optional[Dict[str, bool]] = None,
+ labels: Optional[Sequence[str]] = None,
+ ):
+ Metric.__init__(self, name, documentation, 'stateset')
+ if labels is not None and value is not None:
+ raise ValueError('Can only specify at most one of value and labels.')
+ if labels is None:
+ labels = []
+ self._labelnames = tuple(labels)
+ if value is not None:
+ self.add_metric([], value)
+
+ def add_metric(self,
+ labels: Sequence[str],
+ value: Dict[str, bool],
+ timestamp: Optional[Union[Timestamp, float]] = None,
+ ) -> None:
+ """Add a metric to the metric family.
+
+ Args:
+ labels: A list of label values
+ value: A dict of string state names to booleans
+ """
+ labels = tuple(labels)
+ for state, enabled in sorted(value.items()):
+ v = (1 if enabled else 0)
+ self.samples.append(Sample(
+ self.name,
+ dict(zip(self._labelnames + (self.name,), labels + (state,))),
+ v,
+ timestamp,
+ ))