From 0e5696b96923b2db742c3b2d16437763603f85bd Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 3 Jul 2016 11:32:02 -0400 Subject: [PATCH 1/2] Allow passing label values as keyword arguments --- README.md | 14 +++++++++++--- prometheus_client/core.py | 9 ++++++++- tests/test_core.py | 15 +++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5af4cc51..6ffa387e 100644 --- a/README.md +++ b/README.md @@ -185,7 +185,16 @@ 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() +``` + +Or as a dict: ```python from prometheus_client import Counter @@ -369,7 +378,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. @@ -379,4 +388,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)) ``` - diff --git a/prometheus_client/core.py b/prometheus_client/core.py index 66198025..b4c7fd16 100644 --- a/prometheus_client/core.py +++ b/prometheus_client/core.py @@ -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. @@ -276,10 +276,17 @@ 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 labelvalues and labelkwargs: + raise ValueError("Can't pass both *args and **kwargs") + 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]) + elif labelkwargs: + if sorted(labelkwargs) != sorted(self._labelnames): + raise ValueError('Incorrect label names') + labelvalues = tuple([unicode(labelkwargs[l]) for l in self._labelnames]) else: if len(labelvalues) != len(self._labelnames): raise ValueError('Incorrect label count') diff --git a/tests/test_core.py b/tests/test_core.py index 83b15e86..1b0d958c 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -280,6 +280,21 @@ def test_labels_by_dict(self): self.assertRaises(ValueError, self.two_labels.labels, {'c': 'z'}) self.assertRaises(ValueError, self.two_labels.labels, {}) + 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.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') + def test_invalid_names_raise(self): self.assertRaises(ValueError, Counter, '', 'help') self.assertRaises(ValueError, Counter, '^', 'help') From 882d5769da695bee7be5a9806b4b1d4762e23a8a Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 22 Jul 2016 09:31:20 -0400 Subject: [PATCH 2/2] Remove the by-dict support for labels --- README.md | 9 --------- prometheus_client/core.py | 6 +----- tests/test_core.py | 18 ++---------------- 3 files changed, 3 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 6ffa387e..67220f9e 100644 --- a/README.md +++ b/README.md @@ -194,15 +194,6 @@ c.labels(method='get', endpoint='/').inc() c.labels(method='post', endpoint='/submit').inc() ``` -Or 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, diff --git a/prometheus_client/core.py b/prometheus_client/core.py index b4c7fd16..1752b71d 100644 --- a/prometheus_client/core.py +++ b/prometheus_client/core.py @@ -279,11 +279,7 @@ def labels(self, *labelvalues, **labelkwargs): if labelvalues and labelkwargs: raise ValueError("Can't pass both *args and **kwargs") - 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]) - elif labelkwargs: + if labelkwargs: if sorted(labelkwargs) != sorted(self._labelnames): raise ValueError('Incorrect label names') labelvalues = tuple([unicode(labelkwargs[l]) for l in self._labelnames]) diff --git a/tests/test_core.py b/tests/test_core.py index 1b0d958c..d8d8311a 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -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) @@ -259,27 +259,13 @@ 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() - 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_labels_by_kwarg(self): self.counter.labels(l='x').inc() self.assertEqual(1, self.registry.get_sample_value('c', {'l': 'x'}))