Skip to content

Support metric help text in multiprocess mode #866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions prometheus_client/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ def f():

def _metric_init(self) -> None:
self._value = values.ValueClass(self._type, self._name, self._name + '_total', self._labelnames,
self._labelvalues)
self._labelvalues, self._documentation)
self._created = time.time()

def inc(self, amount: float = 1, exemplar: Optional[Dict[str, str]] = None) -> None:
Expand Down Expand Up @@ -377,7 +377,7 @@ def __init__(self,
def _metric_init(self) -> None:
self._value = values.ValueClass(
self._type, self._name, self._name, self._labelnames, self._labelvalues,
multiprocess_mode=self._multiprocess_mode
self._documentation, multiprocess_mode=self._multiprocess_mode
)

def inc(self, amount: float = 1) -> None:
Expand Down Expand Up @@ -469,8 +469,8 @@ def create_response(request):

def _metric_init(self) -> None:
self._count = values.ValueClass(self._type, self._name, self._name + '_count', self._labelnames,
self._labelvalues)
self._sum = values.ValueClass(self._type, self._name, self._name + '_sum', self._labelnames, self._labelvalues)
self._labelvalues, self._documentation)
self._sum = values.ValueClass(self._type, self._name, self._name + '_sum', self._labelnames, self._labelvalues, self._documentation)
self._created = time.time()

def observe(self, amount: float) -> None:
Expand Down Expand Up @@ -583,14 +583,15 @@ def _metric_init(self) -> None:
self._buckets: List[values.ValueClass] = []
self._created = time.time()
bucket_labelnames = self._labelnames + ('le',)
self._sum = values.ValueClass(self._type, self._name, self._name + '_sum', self._labelnames, self._labelvalues)
self._sum = values.ValueClass(self._type, self._name, self._name + '_sum', self._labelnames, self._labelvalues, self._documentation)
for b in self._upper_bounds:
self._buckets.append(values.ValueClass(
self._type,
self._name,
self._name + '_bucket',
bucket_labelnames,
self._labelvalues + (floatToGoString(b),))
self._labelvalues + (floatToGoString(b),),
self._documentation)
)

def observe(self, amount: float, exemplar: Optional[Dict[str, str]] = None) -> None:
Expand Down
5 changes: 3 additions & 2 deletions prometheus_client/mmap_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import mmap
import os
import struct
from typing import List

_INITIAL_MMAP_SIZE = 1 << 16
_pack_integer_func = struct.Struct(b'i').pack
Expand Down Expand Up @@ -137,8 +138,8 @@ def close(self):
self._f = None


def mmap_key(metric_name, name, labelnames, labelvalues):
def mmap_key(metric_name: str, name: str, labelnames: List[str], labelvalues: List[str], help_text: str) -> str:
"""Format a key for use in the mmap file."""
# ensure labels are in consistent order for identity
labels = dict(zip(labelnames, labelvalues))
return json.dumps([metric_name, name, labels], sort_keys=True)
return json.dumps([metric_name, name, labels, help_text], sort_keys=True)
10 changes: 4 additions & 6 deletions prometheus_client/multiprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
except NameError: # Python >= 2.5
FileNotFoundError = IOError

MP_METRIC_HELP = 'Multiprocess metric'


class MultiProcessCollector:
"""Collector for files for multi-process mode."""
Expand Down Expand Up @@ -53,9 +51,9 @@ def _read_metrics(files):
def _parse_key(key):
val = key_cache.get(key)
if not val:
metric_name, name, labels = json.loads(key)
metric_name, name, labels, help_text = json.loads(key)
labels_key = tuple(sorted(labels.items()))
val = key_cache[key] = (metric_name, name, labels, labels_key)
val = key_cache[key] = (metric_name, name, labels, labels_key, help_text)
return val

for f in files:
Expand All @@ -71,11 +69,11 @@ def _parse_key(key):
continue
raise
for key, value, _ in file_values:
metric_name, name, labels, labels_key = _parse_key(key)
metric_name, name, labels, labels_key, help_text = _parse_key(key)

metric = metrics.get(metric_name)
if metric is None:
metric = Metric(metric_name, MP_METRIC_HELP, typ)
metric = Metric(metric_name, help_text, typ)
metrics[metric_name] = metric

if typ == 'gauge':
Expand Down
10 changes: 5 additions & 5 deletions prometheus_client/values.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class MutexValue:

_multiprocess = False

def __init__(self, typ, metric_name, name, labelnames, labelvalues, **kwargs):
def __init__(self, typ, metric_name, name, labelnames, labelvalues, help_text, **kwargs):
self._value = 0.0
self._exemplar = None
self._lock = Lock()
Expand Down Expand Up @@ -57,8 +57,8 @@ class MmapedValue:

_multiprocess = True

def __init__(self, typ, metric_name, name, labelnames, labelvalues, multiprocess_mode='', **kwargs):
self._params = typ, metric_name, name, labelnames, labelvalues, multiprocess_mode
def __init__(self, typ, metric_name, name, labelnames, labelvalues, help_text, multiprocess_mode='', **kwargs):
self._params = typ, metric_name, name, labelnames, labelvalues, help_text, multiprocess_mode
# This deprecation warning can go away in a few releases when removing the compatibility
if 'prometheus_multiproc_dir' in os.environ and 'PROMETHEUS_MULTIPROC_DIR' not in os.environ:
os.environ['PROMETHEUS_MULTIPROC_DIR'] = os.environ['prometheus_multiproc_dir']
Expand All @@ -69,7 +69,7 @@ def __init__(self, typ, metric_name, name, labelnames, labelvalues, multiprocess
values.append(self)

def __reset(self):
typ, metric_name, name, labelnames, labelvalues, multiprocess_mode = self._params
typ, metric_name, name, labelnames, labelvalues, help_text, multiprocess_mode = self._params
if typ == 'gauge':
file_prefix = typ + '_' + multiprocess_mode
else:
Expand All @@ -81,7 +81,7 @@ def __reset(self):

files[file_prefix] = MmapedDict(filename)
self._file = files[file_prefix]
self._key = mmap_key(metric_name, name, labelnames, labelvalues)
self._key = mmap_key(metric_name, name, labelnames, labelvalues, help_text)
self._value = self._file.read_value(self._key)

def __check_for_pid_change(self):
Expand Down
25 changes: 25 additions & 0 deletions tests/test_multiprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,31 @@ def add_label(key, value):

self.assertEqual(metrics['h'].samples, expected_histogram)

def test_collect_preserves_help(self):
pid = 0
values.ValueClass = MultiProcessValue(lambda: pid)
labels = {i: i for i in 'abcd'}

c = Counter('c', 'c help', labelnames=labels.keys(), registry=None)
g = Gauge('g', 'g help', labelnames=labels.keys(), registry=None)
h = Histogram('h', 'h help', labelnames=labels.keys(), registry=None)

c.labels(**labels).inc(1)
g.labels(**labels).set(1)
h.labels(**labels).observe(1)

pid = 1

c.labels(**labels).inc(1)
g.labels(**labels).set(1)
h.labels(**labels).observe(5)

metrics = {m.name: m for m in self.collector.collect()}

self.assertEqual(metrics['c'].documentation, 'c help')
self.assertEqual(metrics['g'].documentation, 'g help')
self.assertEqual(metrics['h'].documentation, 'h help')

def test_merge_no_accumulate(self):
pid = 0
values.ValueClass = MultiProcessValue(lambda: pid)
Expand Down