Skip to content
Merged
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
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,15 @@ c.labels('get', '/').inc()
c.labels('post', '/submit').inc()
```

Labels can also be provided as a dict:

```python
from prometheus_client import Counter
c = Counter('my_requests_total', 'HTTP Failures', ['method', 'endpoint'])
c.labels({'method': 'get', 'endpoint': '/'}).inc()
c.labels({'method': 'post', 'endpoint': '/submit'}).inc()
```

### Process Collector

The Python Client automatically exports metrics about process CPU usage, RAM,
Expand Down
21 changes: 17 additions & 4 deletions prometheus_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,23 @@ def __init__(self, wrappedClass, labelnames, **kwargs):
raise ValueError('Invalid label metric name: ' + l)

def labels(self, *labelvalues):
'''Return the child for the given labelset.'''
if len(labelvalues) != len(self._labelnames):
raise ValueError('Incorrect label count')
labelvalues = tuple([unicode(l) for l in labelvalues])
'''Return the child for the given labelset.

Labels can be provided as a tuple or as a dict:
c = Counter('c', 'counter', ['l', 'm'])
# Set labels by position
c.labels('0', '1').inc()
# Set labels by name
c.labels({'l': '0', 'm': '1'}).inc()
'''
if len(labelvalues) == 1 and type(labelvalues[0]) == dict:
if sorted(labelvalues[0].keys()) != sorted(self._labelnames):
raise ValueError('Incorrect label names')
labelvalues = tuple([unicode(labelvalues[0][l]) for l in self._labelnames])
else:
if len(labelvalues) != len(self._labelnames):
raise ValueError('Incorrect label count')
labelvalues = tuple([unicode(l) for l in labelvalues])
with self._lock:
if labelvalues not in self._metrics:
self._metrics[labelvalues] = self._wrappedClass(**self._kwargs)
Expand Down
18 changes: 17 additions & 1 deletion tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,8 @@ def test_incorrect_label_count_raises(self):

def test_labels_coerced_to_string(self):
self.counter.labels(None).inc()
self.assertEqual(1, self.registry.get_sample_value('c', {'l': 'None'}))
self.counter.labels({'l': None}).inc()
self.assertEqual(2, self.registry.get_sample_value('c', {'l': 'None'}))

self.counter.remove(None)
self.assertEqual(None, self.registry.get_sample_value('c', {'l': 'None'}))
Expand All @@ -243,12 +244,27 @@ def test_non_string_labels_raises(self):
class Test(object):
__str__ = None
self.assertRaises(TypeError, self.counter.labels, Test())
self.assertRaises(TypeError, self.counter.labels, {'l': Test()})

def test_namespace_subsystem_concatenated(self):
c = Counter('c', 'help', namespace='a', subsystem='b', registry=self.registry)
c.inc()
self.assertEqual(1, self.registry.get_sample_value('a_b_c'))

def test_labels_by_dict(self):
self.counter.labels({'l': 'x'}).inc()
self.assertEqual(1, self.registry.get_sample_value('c', {'l': 'x'}))
self.assertRaises(ValueError, self.counter.labels, {'l': 'x', 'm': 'y'})
self.assertRaises(ValueError, self.counter.labels, {'m': 'y'})
self.assertRaises(ValueError, self.counter.labels, {})
self.two_labels.labels({'a': 'x', 'b': 'y'}).inc()
self.assertEqual(1, self.registry.get_sample_value('two', {'a': 'x', 'b': 'y'}))
self.assertRaises(ValueError, self.two_labels.labels, {'a': 'x', 'b': 'y', 'c': 'z'})
self.assertRaises(ValueError, self.two_labels.labels, {'a': 'x', 'c': 'z'})
self.assertRaises(ValueError, self.two_labels.labels, {'b': 'y', 'c': 'z'})
self.assertRaises(ValueError, self.two_labels.labels, {'c': 'z'})
self.assertRaises(ValueError, self.two_labels.labels, {})

def test_invalid_names_raise(self):
self.assertRaises(ValueError, Counter, '', 'help')
self.assertRaises(ValueError, Counter, '^', 'help')
Expand Down