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
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ There are several options for exporting metrics.

Metrics are usuall exposed over HTTP, to be read by the Prometheus server. For example:

Python 2:

```python
from prometheus_client import MetricsHandler
from BaseHTTPServer import HTTPServer
Expand All @@ -124,6 +126,16 @@ httpd = HTTPServer(server_address, MetricsHandler)
httpd.serve_forever()
```

Python 3:

```python
from prometheus_client import MetricsHandler
from http.server import HTTPServer
server_address = ('', 8000)
httpd = HTTPServer(server_address, MetricsHandler)
httpd.serve_forever()
```

Visit [http://localhost:8000/](http://localhost:8000/) to view the metrics.

## Node exporter textfile collector
Expand Down
24 changes: 15 additions & 9 deletions prometheus_client/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
#!/usr/bin/python

from __future__ import unicode_literals

import copy
import re
import os
import time
import threading
from contextlib import contextmanager
from BaseHTTPServer import BaseHTTPRequestHandler
try:
from BaseHTTPServer import BaseHTTPRequestHandler
except ImportError:
# Python 3
from http.server import BaseHTTPRequestHandler
from functools import wraps
from threading import Lock

Expand Down Expand Up @@ -116,7 +122,7 @@ def remove(self, *labelvalues):
def _samples(self):
with self._lock:
metrics = self._metrics.copy()
for labels, metric in metrics.iteritems():
for labels, metric in metrics.items():
for suffix, _, value in metric._samples():
yield (suffix, dict(zip(self._labelnames, labels)), value)

Expand Down Expand Up @@ -298,18 +304,18 @@ def generate_latest(registry=REGISTRY):
'''Returns the metrics from the registry in latest text format as a string.'''
output = []
for metric in registry.collect():
output.append(u'# HELP %s %s' % (
output.append('# HELP {0} {1}'.format(
metric._name, metric._documentation.replace('\\', r'\\').replace('\n', r'\n')))
output.append(u'\n# TYPE %s %s\n' % (metric._name, metric._type))
output.append('\n# TYPE {0} {1}\n'.format(metric._name, metric._type))
for name, labels, value in metric._samples:
if labels:
labelstr = u'{%s}' % ','.join(
[u'%s="%s"' % (
labelstr = '{{{0}}}'.format(','.join(
['{0}="{1}"'.format(
k, v.replace('\\', r'\\').replace('\n', r'\n').replace('\'', r'\''))
for k, v in labels.items()])
for k, v in labels.items()]))
else:
labelstr = u''
output.append(u'%s%s %s\n' % (name, labelstr, value))
labelstr = ''
output.append('{0}{1} {2}\n'.format(name, labelstr, value))
return ''.join(output).encode('utf-8')


Expand Down
78 changes: 42 additions & 36 deletions tests/test_client.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from __future__ import unicode_literals
import unittest

from prometheus_client import Gauge, Counter, Summary
Expand All @@ -9,11 +10,11 @@ def setUp(self):
self.counter = Counter('c', 'help', registry=self.registry)

def test_increment(self):
self.assertEquals(0, self.registry.get_sample_value('c'))
self.assertEqual(0, self.registry.get_sample_value('c'))
self.counter.inc()
self.assertEquals(1, self.registry.get_sample_value('c'))
self.assertEqual(1, self.registry.get_sample_value('c'))
self.counter.inc(7)
self.assertEquals(8, self.registry.get_sample_value('c'))
self.assertEqual(8, self.registry.get_sample_value('c'))

def test_negative_increment_raises(self):
self.assertRaises(ValueError, self.counter.inc, -1)
Expand All @@ -29,79 +30,79 @@ def f(r):
f(False)
except TypeError:
pass
self.assertEquals(0, self.registry.get_sample_value('c'))
self.assertEqual(0, self.registry.get_sample_value('c'))
try:
f(True)
except ValueError:
raised = True
self.assertEquals(1, self.registry.get_sample_value('c'))
self.assertEqual(1, self.registry.get_sample_value('c'))

def test_block_decorator(self):
with self.counter.count_exceptions():
pass
self.assertEquals(0, self.registry.get_sample_value('c'))
self.assertEqual(0, self.registry.get_sample_value('c'))
raised = False
try:
with self.counter.count_exceptions():
raise ValueError
except:
raised = True
self.assertTrue(raised)
self.assertEquals(1, self.registry.get_sample_value('c'))
self.assertEqual(1, self.registry.get_sample_value('c'))

class TestGauge(unittest.TestCase):
def setUp(self):
self.registry = CollectorRegistry()
self.gauge = Gauge('g', 'help', registry=self.registry)

def test_gauge(self):
self.assertEquals(0, self.registry.get_sample_value('g'))
self.assertEqual(0, self.registry.get_sample_value('g'))
self.gauge.inc()
self.assertEquals(1, self.registry.get_sample_value('g'))
self.assertEqual(1, self.registry.get_sample_value('g'))
self.gauge.dec(3)
self.assertEquals(-2, self.registry.get_sample_value('g'))
self.assertEqual(-2, self.registry.get_sample_value('g'))
self.gauge.set(9)
self.assertEquals(9, self.registry.get_sample_value('g'))
self.assertEqual(9, self.registry.get_sample_value('g'))

def test_function_decorator(self):
self.assertEquals(0, self.registry.get_sample_value('g'))
self.assertEqual(0, self.registry.get_sample_value('g'))
@self.gauge.track_inprogress()
def f():
self.assertEquals(1, self.registry.get_sample_value('g'))
self.assertEqual(1, self.registry.get_sample_value('g'))
f()
self.assertEquals(0, self.registry.get_sample_value('g'))
self.assertEqual(0, self.registry.get_sample_value('g'))

def test_block_decorator(self):
self.assertEquals(0, self.registry.get_sample_value('g'))
self.assertEqual(0, self.registry.get_sample_value('g'))
with self.gauge.track_inprogress():
self.assertEquals(1, self.registry.get_sample_value('g'))
self.assertEquals(0, self.registry.get_sample_value('g'))
self.assertEqual(1, self.registry.get_sample_value('g'))
self.assertEqual(0, self.registry.get_sample_value('g'))

class TestSummary(unittest.TestCase):
def setUp(self):
self.registry = CollectorRegistry()
self.summary = Summary('s', 'help', registry=self.registry)

def test_summary(self):
self.assertEquals(0, self.registry.get_sample_value('s_count'))
self.assertEquals(0, self.registry.get_sample_value('s_sum'))
self.assertEqual(0, self.registry.get_sample_value('s_count'))
self.assertEqual(0, self.registry.get_sample_value('s_sum'))
self.summary.observe(10)
self.assertEquals(1, self.registry.get_sample_value('s_count'))
self.assertEquals(10, self.registry.get_sample_value('s_sum'))
self.assertEqual(1, self.registry.get_sample_value('s_count'))
self.assertEqual(10, self.registry.get_sample_value('s_sum'))

def test_function_decorator(self):
self.assertEquals(0, self.registry.get_sample_value('s_count'))
self.assertEqual(0, self.registry.get_sample_value('s_count'))
@self.summary.time()
def f():
pass
f()
self.assertEquals(1, self.registry.get_sample_value('s_count'))
self.assertEqual(1, self.registry.get_sample_value('s_count'))

def test_block_decorator(self):
self.assertEquals(0, self.registry.get_sample_value('s_count'))
self.assertEqual(0, self.registry.get_sample_value('s_count'))
with self.summary.time():
pass
self.assertEquals(1, self.registry.get_sample_value('s_count'))
self.assertEqual(1, self.registry.get_sample_value('s_count'))

class TestMetricWrapper(unittest.TestCase):
def setUp(self):
Expand All @@ -111,18 +112,18 @@ def setUp(self):

def test_child(self):
self.counter.labels('x').inc()
self.assertEquals(1, self.registry.get_sample_value('c', {'l': 'x'}))
self.assertEqual(1, self.registry.get_sample_value('c', {'l': 'x'}))
self.two_labels.labels('x', 'y').inc(2)
self.assertEquals(2, self.registry.get_sample_value('two', {'a': 'x', 'b': 'y'}))
self.assertEqual(2, self.registry.get_sample_value('two', {'a': 'x', 'b': 'y'}))

def test_remove(self):
self.counter.labels('x').inc()
self.counter.labels('y').inc(2)
self.assertEquals(1, self.registry.get_sample_value('c', {'l': 'x'}))
self.assertEquals(2, self.registry.get_sample_value('c', {'l': 'y'}))
self.assertEqual(1, self.registry.get_sample_value('c', {'l': 'x'}))
self.assertEqual(2, self.registry.get_sample_value('c', {'l': 'y'}))
self.counter.remove('x')
self.assertEquals(None, self.registry.get_sample_value('c', {'l': 'x'}))
self.assertEquals(2, self.registry.get_sample_value('c', {'l': 'y'}))
self.assertEqual(None, self.registry.get_sample_value('c', {'l': 'x'}))
self.assertEqual(2, self.registry.get_sample_value('c', {'l': 'y'}))

def test_incorrect_label_count_raises(self):
self.assertRaises(ValueError, self.counter.labels)
Expand All @@ -133,7 +134,7 @@ def test_incorrect_label_count_raises(self):
def test_namespace_subsystem_concatenated(self):
c = Counter('c', 'help', namespace='a', subsystem='b', registry=self.registry)
c.inc()
self.assertEquals(1, self.registry.get_sample_value('a_b_c'))
self.assertEqual(1, self.registry.get_sample_value('a_b_c'))

def test_invalid_names_raise(self):
self.assertRaises(ValueError, Counter, '', 'help')
Expand All @@ -150,22 +151,27 @@ def setUp(self):
def test_counter(self):
c = Counter('cc', 'A counter', registry=self.registry)
c.inc()
self.assertEquals('# HELP cc A counter\n# TYPE cc counter\ncc 1.0\n', generate_latest(self.registry))
self.assertEqual(b'# HELP cc A counter\n# TYPE cc counter\ncc 1.0\n', generate_latest(self.registry))

def test_gauge(self):
g = Gauge('gg', 'A gauge', registry=self.registry)
g.set(17)
self.assertEquals('# HELP gg A gauge\n# TYPE gg gauge\ngg 17.0\n', generate_latest(self.registry))
self.assertEqual(b'# HELP gg A gauge\n# TYPE gg gauge\ngg 17.0\n', generate_latest(self.registry))

def test_summary(self):
s = Summary('ss', 'A summary', ['a', 'b'], registry=self.registry)
s.labels('c', 'd').observe(17)
self.assertEquals('# HELP ss A summary\n# TYPE ss summary\nss_count{a="c",b="d"} 1.0\nss_sum{a="c",b="d"} 17.0\n', generate_latest(self.registry))
self.assertEqual(b'# HELP ss A summary\n# TYPE ss summary\nss_count{a="c",b="d"} 1.0\nss_sum{a="c",b="d"} 17.0\n', generate_latest(self.registry))

def test_unicode(self):
c = Counter('cc', '\u4500', ['l'], registry=self.registry)
c.labels('\u4500').inc()
self.assertEqual(b'# HELP cc \xe4\x94\x80\n# TYPE cc counter\ncc{l="\xe4\x94\x80"} 1.0\n', generate_latest(self.registry))

def test_escaping(self):
c = Counter('cc', 'A\ncount\\er', ['a'], registry=self.registry)
c.labels('\\x\n').inc(1)
self.assertEquals('# HELP cc A\\ncount\\\\er\n# TYPE cc counter\ncc{a="\\\\x\\n"} 1.0\n', generate_latest(self.registry))
self.assertEqual(b'# HELP cc A\\ncount\\\\er\n# TYPE cc counter\ncc{a="\\\\x\\n"} 1.0\n', generate_latest(self.registry))


if __name__ == '__main__':
Expand Down