Skip to content

Commit a2f9742

Browse files
committed
Import python3-prometheus-client 0.8.0 source.
1 parent 570c4a3 commit a2f9742

29 files changed

Lines changed: 774 additions & 171 deletions

.travis.yml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
dist: xenial
12
sudo: false
23
cache:
34
directories:
@@ -9,6 +10,7 @@ matrix:
910
include:
1011
- python: "2.6"
1112
env: TOXENV=py26
13+
dist: trusty
1214
- python: "2.7"
1315
env: TOXENV=py27
1416
- python: "2.7"
@@ -21,11 +23,12 @@ matrix:
2123
env: TOXENV=py36
2224
- python: "3.7"
2325
env: TOXENV=py37
24-
dist: xenial
2526
sudo: true
26-
- python: "3.7"
27-
env: TOXENV=py37-nooptionals
28-
dist: xenial
27+
- python: "3.8"
28+
env: TOXENV=py38
29+
sudo: true
30+
- python: "3.8"
31+
env: TOXENV=py38-nooptionals
2932
sudo: true
3033
- python: "pypy"
3134
env: TOXENV=pypy

MANIFEST.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
recursive-include tests *
2+
prune tests/__pycache__

README.md

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,19 @@ from prometheus_client import start_wsgi_server
306306
start_wsgi_server(8000)
307307
```
308308

309+
#### ASGI
310+
311+
To use Prometheus with [ASGI](http://asgi.readthedocs.org/en/latest/), there is
312+
`make_asgi_app` which creates an ASGI application.
313+
314+
```python
315+
from prometheus_client import make_asgi_app
316+
317+
app = make_asgi_app()
318+
```
319+
Such an application can be useful when integrating Prometheus metrics with ASGI
320+
apps.
321+
309322
#### Flask
310323

311324
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.
@@ -314,7 +327,7 @@ Save the snippet below in a `myapp.py` file
314327

315328
```python
316329
from flask import Flask
317-
from werkzeug.wsgi import DispatcherMiddleware
330+
from werkzeug.middleware.dispatcher import DispatcherMiddleware
318331
from prometheus_client import make_wsgi_app
319332

320333
# Create my app
@@ -452,7 +465,7 @@ used to predetermine the names of time series a `CollectorRegistry` exposes and
452465
thus to detect collisions and duplicate registrations.
453466

454467
Usually custom collectors do not have to implement `describe`. If `describe` is
455-
not implemented and the CollectorRegistry was created with `auto_desribe=True`
468+
not implemented and the CollectorRegistry was created with `auto_describe=True`
456469
(which is the case for the default registry) then `collect` will be called at
457470
registration time instead of `describe`. If this could cause problems, either
458471
implement a proper `describe`, or if that's not practical have `describe`
@@ -476,31 +489,25 @@ This comes with a number of limitations:
476489

477490
There's several steps to getting this working:
478491

479-
**One**: Gunicorn deployment
492+
**1. Gunicorn deployment**:
480493

481494
The `prometheus_multiproc_dir` environment variable must be set to a directory
482495
that the client library can use for metrics. This directory must be wiped
483496
between Gunicorn runs (before startup is recommended).
484497

485-
Put the following in the config file:
486-
```python
487-
from prometheus_client import multiprocess
498+
This environment variable should be set from a start-up shell script,
499+
and not directly from Python (otherwise it may not propagate to child processes).
488500

489-
def child_exit(server, worker):
490-
multiprocess.mark_process_dead(worker.pid)
491-
```
501+
**2. Metrics collector**:
502+
503+
The application must initialize a new `CollectorRegistry`,
504+
and store the multi-process collector inside.
492505

493-
**Two**: Inside the application
494506
```python
495507
from prometheus_client import multiprocess
496-
from prometheus_client import generate_latest, CollectorRegistry, CONTENT_TYPE_LATEST, Gauge
497-
498-
# Example gauge.
499-
IN_PROGRESS = Gauge("inprogress_requests", "help", multiprocess_mode='livesum')
500-
508+
from prometheus_client import generate_latest, CollectorRegistry, CONTENT_TYPE_LATEST
501509

502510
# Expose metrics.
503-
@IN_PROGRESS.track_inprogress()
504511
def app(environ, start_response):
505512
registry = CollectorRegistry()
506513
multiprocess.MultiProcessCollector(registry)
@@ -514,19 +521,36 @@ def app(environ, start_response):
514521
return iter([data])
515522
```
516523

517-
**Three**: Instrumentation
524+
**3. Gunicorn configuration**:
518525

519-
Counters, Summarys and Histograms work as normal.
526+
The `gunicorn` configuration file needs to include the following function:
520527

521-
Gauges have several modes they can run in, which can be selected with the
522-
`multiprocess_mode` parameter.
528+
```python
529+
from prometheus_client import multiprocess
530+
531+
def child_exit(server, worker):
532+
multiprocess.mark_process_dead(worker.pid)
533+
```
534+
535+
**4. Metrics tuning (Gauge)**:
536+
537+
When `Gauge` metrics are used, additional tuning needs to be performed.
538+
Gauges have several modes they can run in, which can be selected with the `multiprocess_mode` parameter.
523539

524540
- 'all': Default. Return a timeseries per process alive or dead.
525541
- 'liveall': Return a timeseries per process that is still alive.
526542
- 'livesum': Return a single timeseries that is the sum of the values of alive processes.
527543
- 'max': Return a single timeseries that is the maximum of the values of all processes, alive or dead.
528544
- 'min': Return a single timeseries that is the minimum of the values of all processes, alive or dead.
529545

546+
```python
547+
from prometheus_client import Gauge
548+
549+
# Example gauge
550+
IN_PROGRESS = Gauge("inprogress_requests", "help", multiprocess_mode='livesum')
551+
```
552+
553+
530554
## Parser
531555

532556
The Python client supports parsing the Prometheus text format.

prometheus_client/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
generate_latest = exposition.generate_latest
2525
MetricsHandler = exposition.MetricsHandler
2626
make_wsgi_app = exposition.make_wsgi_app
27+
try:
28+
# Python >3.5 only
29+
make_asgi_app = exposition.make_asgi_app
30+
except:
31+
pass
2732
start_http_server = exposition.start_http_server
2833
start_wsgi_server = exposition.start_wsgi_server
2934
write_to_textfile = exposition.write_to_textfile

prometheus_client/asgi.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from urllib.parse import parse_qs
2+
3+
from .exposition import _bake_output
4+
from .registry import REGISTRY
5+
6+
7+
def make_asgi_app(registry=REGISTRY):
8+
"""Create a ASGI app which serves the metrics from a registry."""
9+
10+
async def prometheus_app(scope, receive, send):
11+
assert scope.get("type") == "http"
12+
# Prepare parameters
13+
params = parse_qs(scope.get('query_string', b''))
14+
accept_header = "Accept: " + ",".join([
15+
value.decode("utf8") for (name, value) in scope.get('headers')
16+
if name.decode("utf8") == 'accept'
17+
])
18+
# Bake output
19+
status, header, output = _bake_output(registry, accept_header, params)
20+
# Return output
21+
payload = await receive()
22+
if payload.get("type") == "http.request":
23+
await send(
24+
{
25+
"type": "http.response.start",
26+
"status": int(status.split(' ')[0]),
27+
"headers": [
28+
tuple(x.encode('utf8') for x in header)
29+
]
30+
}
31+
)
32+
await send({"type": "http.response.body", "body": output})
33+
34+
return prometheus_app

0 commit comments

Comments
 (0)