diff --git a/README.md b/README.md index fedfd1e0..434baa20 100644 --- a/README.md +++ b/README.md @@ -181,3 +181,18 @@ write_to_textfile('/configured/textfile/path/raid.prom', registry) ``` A separate registry is used, as the default registry may contain other metrics. + +## Exporting to a Pushgateway + +The [Pushgateway](https://github.com/prometheus/pushgateway) +allows ephemeral and batch jobs to expose their metrics to Prometheus. + +```python +from prometheus_client import CollectorRegistry,Gauge,push_to_gateway +registry = CollectorRegistry() +g = Gauge('job_finished_ok_at', 'last time a batch job successfully finished', registry=registry) +g.set_to_current_time() +push_to_gateway(registry, job='batchA', host='pushgateway.mydomain') +``` + +A separate registry is used, as the default registry may contain other metrics. diff --git a/prometheus_client/__init__.py b/prometheus_client/__init__.py index 04075779..65bb7324 100644 --- a/prometheus_client/__init__.py +++ b/prometheus_client/__init__.py @@ -7,6 +7,14 @@ import os import time import threading + +try: + from urllib2 import urlopen, quote +except ImportError: + # Python 3 + from urllib.request import urlopen + from urllib.parse import quote + try: from BaseHTTPServer import BaseHTTPRequestHandler except ImportError: @@ -15,6 +23,7 @@ from functools import wraps from threading import Lock + __all__ = ['Counter', 'Gauge', 'Summary', 'Histogram'] _METRIC_NAME_RE = re.compile(r'^[a-zA-Z_:][a-zA-Z0-9_:]*$') @@ -434,6 +443,38 @@ def do_GET(self): self.wfile.write(generate_latest(REGISTRY)) +def build_pushgateway_url(job, instance=None, host='localhost', port=9091): + ''' + Build a valid pushgateway url + ''' + + if instance: + instancestr = '/instances/{}'.format(instance) + else: + instancestr = '' + + url = 'http://{}:{}/metrics/jobs/{}{}'.format(host, port, + quote(job), + quote(instancestr)) + return url + + +def push_to_gateway_url(url, registry, timeout=None): + '''Push metrics to the given url''' + + resp = urlopen(url, data=generate_latest(registry), timeout=timeout) + if resp.code >= 400: + raise IOError("error pushing to pushgateway: {0} {1}".format( + resp.code, resp.msg)) + + +def push_to_gateway(registry, job, instance=None, host='localhost', port=9091, timeout=None): + '''Push metrics to a pushgateway''' + + url = build_pushgateway_url(job, instance, host, port) + push_to_gateway_url(url, registry, timeout) + + def write_to_textfile(path, registry): '''Write metrics to the given path. diff --git a/tests/test_client.py b/tests/test_client.py index a28f3f4a..049729b8 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -3,6 +3,7 @@ from prometheus_client import Gauge, Counter, Summary, Histogram from prometheus_client import CollectorRegistry, generate_latest +from prometheus_client import build_pushgateway_url class TestCounter(unittest.TestCase): @@ -266,5 +267,26 @@ def test_escaping(self): self.assertEqual(b'# HELP cc A\\ncount\\\\er\n# TYPE cc counter\ncc{a="\\\\x\\n\\""} 1.0\n', generate_latest(self.registry)) +class TestBuildPushgatewayUrl(unittest.TestCase): + def test_job_instance(self): + expected = 'http://localhost:9091/metrics/jobs/foojob/instances/fooinstance' + + url = build_pushgateway_url('foojob', 'fooinstance') + self.assertEqual(url, expected) + + def test_host_port(self): + expected = 'http://foohost:9092/metrics/jobs/foojob' + + url = build_pushgateway_url('foojob', host='foohost', port=9092) + self.assertEqual(url, expected) + + def test_url_escaping(self): + expected = 'http://localhost:9091/metrics/jobs/foo%20job' + + url = build_pushgateway_url('foo job') + self.assertEqual(url, expected) + + + if __name__ == '__main__': unittest.main()