Repository: prometheus/client_python
Pull request #223 (2017-12-12 16:42:28)

Fix: #222. Support custom registry for MetricsHandler.
## What does this PR do?
  
Add `registry` parameter to start_http_server, so you can serve data
from a custom registry.

## How it's implemented

Added:
 - the optional named parameter `registry` to `start_http_server`
 - create a  MetricHandler tied to the passed `registry` via MetricHandler.factory 

## How do you use it
Create a new or a multiprocess registry
Pass `registry=myregistry` to start_http_server.

```
from prometheus_client import multiprocess, start_http_server
from prometheus_client import Counter
...
# Serve myregistry metrics.
myregistry = CollectorRegistry()
start_http_server(8000, registry=myregistry)

# Server core.REGISTRY metrics
start_http_server(8001) 
...
```


brian-brazil: Why are you opening a new PR?
ioggstream: I split the patch in two. This is just the custom registry part. No multiprocess.

Patch:
diff --git a/prometheus_client/exposition.py b/prometheus_client/exposition.py
index 2d54e45..b79b0d0 100644
--- a/prometheus_client/exposition.py
+++ b/prometheus_client/exposition.py
@@ -83,9 +83,10 @@ def generate_latest(registry=core.REGISTRY):
 
 class MetricsHandler(BaseHTTPRequestHandler):
     """HTTP handler that gives metrics from ``core.REGISTRY``."""
+    registry = core.REGISTRY
 
     def do_GET(self):
-        registry = core.REGISTRY
+        registry = self.registry
         params = parse_qs(urlparse(self.path).query)
         if 'name[]' in params:
             registry = registry.restricted_registry(params['name[]'])
@@ -102,14 +103,30 @@ class MetricsHandler(BaseHTTPRequestHandler):
     def log_message(self, format, *args):
         """Log nothing."""
 
+    @staticmethod
+    def factory(registry):
+        """Returns a dynamic MetricsHandler class tied
+           to the passed registry.
+        """
+        # This implementation relies on MetricsHandler.registry
+        #  (defined above and defaulted to core.REGISTRY).
+
+        # As we have unicode_literals, we need to create a str()
+        #  object for type().
+        cls_name = str('MetricsHandler')
+        MyMetricsHandler = type(cls_name, (MetricsHandler, object),
+                                {"registry": registry})
+        return MyMetricsHandler
+
 
 class _ThreadingSimpleServer(ThreadingMixIn, HTTPServer):
     """Thread per request HTTP server."""
 
 
-def start_http_server(port, addr=''):
+def start_http_server(port, addr='', registry=core.REGISTRY):
     """Starts an HTTP server for prometheus metrics as a daemon thread"""
-    httpd = _ThreadingSimpleServer((addr, port), MetricsHandler)
+    CustomMetricsHandler = MetricsHandler.factory(registry)
+    httpd = _ThreadingSimpleServer((addr, port), CustomMetricsHandler)
     t = threading.Thread(target=httpd.serve_forever)
     t.daemon = True
     t.start()
diff --git a/prometheus_client/multiprocess.py b/prometheus_client/multiprocess.py
index c097ced..6a878b5 100644
--- a/prometheus_client/multiprocess.py
+++ b/prometheus_client/multiprocess.py
@@ -9,6 +9,7 @@ import shelve
 
 from . import core
 
+
 class MultiProcessCollector(object):
     """Collector for files for multi-process mode."""
     def __init__(self, registry, path=None):


Test patch:
diff --git a/tests/test_exposition.py b/tests/test_exposition.py
index 3b1fb16..a4cc385 100644
--- a/tests/test_exposition.py
+++ b/tests/test_exposition.py
@@ -13,7 +13,8 @@ from prometheus_client import Gauge, Counter, Summary, Histogram, Metric
 from prometheus_client import CollectorRegistry, generate_latest
 from prometheus_client import push_to_gateway, pushadd_to_gateway, delete_from_gateway
 from prometheus_client import CONTENT_TYPE_LATEST, instance_ip_grouping_key
-from prometheus_client.exposition import default_handler, basic_auth_handler
+from prometheus_client import core
+from prometheus_client.exposition import default_handler, basic_auth_handler, MetricsHandler
 
 try:
     from BaseHTTPServer import BaseHTTPRequestHandler
@@ -195,6 +196,9 @@ class TestPushGateway(unittest.TestCase):
     def test_instance_ip_grouping_key(self):
         self.assertTrue('' != instance_ip_grouping_key()['instance'])
 
+    def test_metrics_handler(self):
+        MyHandler = MetricsHandler.factory(core.REGISTRY)
+
 
 if __name__ == '__main__':
     unittest.main()