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: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,13 @@ c.labels('get', '/').inc()
c.labels('post', '/submit').inc()
```

Labels can also be provided as a dict:
Labels can also be passed as keyword-arguments:

```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()
c.labels(method='get', endpoint='/').inc()
c.labels(method='post', endpoint='/submit').inc()
```

### Process Collector
Expand Down Expand Up @@ -369,7 +369,7 @@ REGISTRY.register(CustomCollector())
## Parser

The Python client supports parsing the Promeheus text format.
This is intended for advanced use cases where you have servers
This is intended for advanced use cases where you have servers
exposing Prometheus metrics and need to get them into some other
system.

Expand All @@ -379,4 +379,3 @@ for family in text_string_to_metric_families("my_gauge 1.0\n"):
for sample in family.samples:
print("Name: {0} Labels: {1} Value: {2}".format(*sample))
```

11 changes: 7 additions & 4 deletions prometheus_client/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def __init__(self, wrappedClass, name, labelnames, **kwargs):
if l.startswith('__'):
raise ValueError('Invalid label metric name: ' + l)

def labels(self, *labelvalues):
def labels(self, *labelvalues, **labelkwargs):
'''Return the child for the given labelset.

All metrics can have labels, allowing grouping of related time series.
Expand All @@ -276,10 +276,13 @@ def labels(self, *labelvalues):
See the best practices on [naming](http://prometheus.io/docs/practices/naming/)
and [labels](http://prometheus.io/docs/practices/instrumentation/#use-labels).
'''
if len(labelvalues) == 1 and type(labelvalues[0]) == dict:
if sorted(labelvalues[0].keys()) != sorted(self._labelnames):
if labelvalues and labelkwargs:
raise ValueError("Can't pass both *args and **kwargs")

if labelkwargs:
if sorted(labelkwargs) != sorted(self._labelnames):
raise ValueError('Incorrect label names')
labelvalues = tuple([unicode(labelvalues[0][l]) for l in self._labelnames])
labelvalues = tuple([unicode(labelkwargs[l]) for l in self._labelnames])
else:
if len(labelvalues) != len(self._labelnames):
raise ValueError('Incorrect label count')
Expand Down
27 changes: 14 additions & 13 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def test_incorrect_label_count_raises(self):

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

self.counter.remove(None)
Expand All @@ -259,26 +259,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()})
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()
def test_labels_by_kwarg(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.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, {})
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)
self.assertRaises(ValueError, self.two_labels.labels, {'a': 'x'}, b='y')

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