From 63eee291e2e1e22ea771f7b16295a32ad182cabe Mon Sep 17 00:00:00 2001 From: Emil Madsen Date: Fri, 14 Feb 2020 21:47:45 +0100 Subject: [PATCH] Added ASGI application Signed-off-by: Emil Madsen --- README.md | 26 +++++++++++++++++++++++++ prometheus_client/__init__.py | 5 +++++ prometheus_client/asgi.py | 34 +++++++++++++++++++++++++++++++++ prometheus_client/exposition.py | 8 ++++++++ 4 files changed, 73 insertions(+) create mode 100644 prometheus_client/asgi.py diff --git a/README.md b/README.md index c7161fd4..cf4eafa7 100644 --- a/README.md +++ b/README.md @@ -306,6 +306,32 @@ from prometheus_client import start_wsgi_server start_wsgi_server(8000) ``` +#### ASGI + +To use Prometheus with [ASGI](http://asgi.readthedocs.org/en/latest/), there is +`make_asgi_app` which creates an ASGI application. + +Save the snippet below in a `myapp.py` file + +```python +from prometheus_client import make_asgi_app + +app = make_asgi_app() +``` +Such an application can be useful when integrating Prometheus metrics with ASGI +apps. + +The app can be used to serve the metrics through an ASGI implementation, such +as [daphne](https://github.com/django/daphne) or +[uvicorn](https://www.uvicorn.org/). +```bash +# Install daphne if you do not have it +pip install daphne +daphne myapp:app +``` + +Visit http://localhost:8000/ to see the metrics + #### Flask To use Prometheus with [Flask](http://flask.pocoo.org/) we need to serve metrics through a Prometheus WSGI application. This can be achieved using [Flask's application dispatching](http://flask.pocoo.org/docs/latest/patterns/appdispatch/). Below is a working example. diff --git a/prometheus_client/__init__.py b/prometheus_client/__init__.py index 67f493f8..f176ad09 100644 --- a/prometheus_client/__init__.py +++ b/prometheus_client/__init__.py @@ -24,6 +24,11 @@ generate_latest = exposition.generate_latest MetricsHandler = exposition.MetricsHandler make_wsgi_app = exposition.make_wsgi_app +try: + # Python >3.5 only + make_asgi_app = exposition.make_asgi_app +except: + pass start_http_server = exposition.start_http_server start_wsgi_server = exposition.start_wsgi_server write_to_textfile = exposition.write_to_textfile diff --git a/prometheus_client/asgi.py b/prometheus_client/asgi.py new file mode 100644 index 00000000..6ef94f5b --- /dev/null +++ b/prometheus_client/asgi.py @@ -0,0 +1,34 @@ +from urllib.parse import parse_qs + +from .exposition import choose_encoder +from .registry import REGISTRY + + +def make_asgi_app(registry=REGISTRY): + """Create a ASGI app which serves the metrics from a registry.""" + + async def prometheus_app(scope, receive, send): + assert scope.get("type") == "http" + params = parse_qs(scope.get('query_string', b'')) + r = registry + accept_header = "Accept: " + ",".join([ + value.decode("utf8") for (name, value) in scope.get('headers') + if name.decode("utf8") == 'accept' + ]) + encoder, content_type = choose_encoder(accept_header) + if 'name[]' in params: + r = r.restricted_registry(params['name[]']) + output = encoder(r) + + payload = await receive() + if payload.get("type") == "http.request": + await send( + { + "type": "http.response.start", + "status": 200, + "headers": [[b"Content-Type", content_type.encode('utf8')]], + } + ) + await send({"type": "http.response.body", "body": output}) + + return prometheus_app diff --git a/prometheus_client/exposition.py b/prometheus_client/exposition.py index 6911ba75..0e278242 100644 --- a/prometheus_client/exposition.py +++ b/prometheus_client/exposition.py @@ -31,6 +31,7 @@ PYTHON26_OR_OLDER = sys.version_info < (2, 7) PYTHON376_OR_NEWER = sys.version_info > (3, 7, 5) + def make_wsgi_app(registry=REGISTRY): """Create a WSGI app which serves the metrics from a registry.""" @@ -378,3 +379,10 @@ def instance_ip_grouping_key(): with closing(socket.socket(socket.AF_INET, socket.SOCK_DGRAM)) as s: s.connect(('localhost', 0)) return {'instance': s.getsockname()[0]} + + +try: + # Python >3.5 only + from .asgi import make_asgi_app +except: + pass