Skip to content

Commit 5ed8f28

Browse files
committed
Add Django exporter (#1088)
Signed-off-by: Julie Rymer <rymerjulie.pro@gmail.com>
1 parent 1783ca8 commit 5ed8f28

File tree

7 files changed

+149
-1
lines changed

7 files changed

+149
-1
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
title: Django
3+
weight: 5
4+
---
5+
6+
To use Prometheus with [Django](https://www.djangoproject.com/) you can use the provided view class
7+
to add a metrics endpoint to your app.
8+
9+
```python
10+
# urls.py
11+
12+
from django.urls import path
13+
from prometheus_client.django import PrometheusDjangoView
14+
15+
urlpatterns = [
16+
# ... any other urls that you want
17+
path("metrics/", PrometheusDjangoView.as_view(), name="prometheus-metrics"),
18+
# ... still more urls
19+
]
20+
```
21+
22+
By default, Multiprocessing support is activated if environment variable `PROMETHEUS_MULTIPROC_DIR` is set.
23+
You can override this through the view arguments:
24+
25+
```python
26+
from django.conf import settings
27+
28+
urlpatterns = [
29+
path(
30+
"metrics/",
31+
PrometheusDjangoView.as_view(
32+
multiprocess_mode=settings.YOUR_SETTING # or any boolean value
33+
),
34+
name="prometheus-metrics",
35+
),
36+
]
37+
```
38+
39+
Full multiprocessing instructions are provided [here]({{< ref "/multiprocess" >}}).
40+
41+
# django-prometheus
42+
43+
The included `PrometheusDjangoView` is useful if you want to define your own metrics from scratch.
44+
45+
An external package called [django-prometheus](https://github.com/django-commons/django-prometheus/)
46+
can be used instead if you want to get a bunch of ready-made monitoring metrics for your Django application
47+
and easily benefit from utilities such as models monitoring.

mypy.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[mypy]
2-
exclude = prometheus_client/decorator.py|prometheus_client/twisted|tests/test_twisted.py
2+
exclude = prometheus_client/decorator.py|prometheus_client/twisted|tests/test_twisted.py|prometheus_client/django|tests/test_django.py
33
implicit_reexport = False
44
disallow_incomplete_defs = True
55

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from .exposition import PrometheusDjangoView
2+
3+
__all__ = [
4+
"PrometheusDjangoView",
5+
]
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import os
2+
3+
from django.http import HttpResponse
4+
from django.views import View
5+
6+
import prometheus_client
7+
from prometheus_client import multiprocess
8+
from prometheus_client.exposition import _bake_output
9+
from tests.test_exposition import registry
10+
11+
12+
class PrometheusDjangoView(View):
13+
multiprocess_mode: bool = "PROMETHEUS_MULTIPROC_DIR" in os.environ or "prometheus_multiproc_dir" in os.environ
14+
registry: prometheus_client.CollectorRegistry = None
15+
16+
def get(self, request, *args, **kwargs):
17+
if self.registry is None:
18+
if self.multiprocess_mode:
19+
self.registry = prometheus_client.CollectorRegistry()
20+
multiprocess.MultiProcessCollector(registry)
21+
else:
22+
self.registry = prometheus_client.REGISTRY
23+
accept_header = request.headers.get("Accept")
24+
accept_encoding_header = request.headers.get("Accept-Encoding")
25+
# Bake output
26+
status, headers, output = _bake_output(
27+
registry=self.registry,
28+
accept_header=accept_header,
29+
accept_encoding_header=accept_encoding_header,
30+
params=request.GET,
31+
disable_compression=False,
32+
)
33+
status = int(status.split(" ")[0])
34+
return HttpResponse(
35+
output,
36+
status=status,
37+
headers=headers,
38+
)
39+
40+
def options(self, request, *args, **kwargs):
41+
return HttpResponse(
42+
status=200,
43+
headers={"Allow": "OPTIONS,GET"},
44+
)

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ twisted = [
4747
aiohttp = [
4848
"aiohttp",
4949
]
50+
django = [
51+
"django",
52+
]
5053

5154
[project.urls]
5255
Homepage = "https://github.com/prometheus/client_python"

tests/test_django.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from unittest import skipUnless
2+
3+
from prometheus_client import CollectorRegistry, Counter, generate_latest
4+
from prometheus_client.openmetrics.exposition import ALLOWUTF8
5+
6+
try:
7+
import django
8+
from django.test import RequestFactory, TestCase
9+
10+
from prometheus_client.django import PrometheusDjangoView
11+
12+
HAVE_DJANGO = True
13+
except ImportError:
14+
from unittest import TestCase
15+
16+
HAVE_DJANGO = False
17+
18+
else:
19+
from django.conf import settings
20+
21+
if not settings.configured:
22+
settings.configure(
23+
DATABASES={
24+
"default": {
25+
"ENGINE": "django.db.backends.sqlite3",
26+
'NAME': ':memory:'
27+
}
28+
},
29+
INSTALLED_APPS=[],
30+
)
31+
django.setup()
32+
33+
34+
class MetricsResourceTest(TestCase):
35+
@skipUnless(HAVE_DJANGO, "Don't have django installed.")
36+
def setUp(self):
37+
self.registry = CollectorRegistry()
38+
self.factory = RequestFactory()
39+
40+
def test_reports_metrics(self):
41+
c = Counter('cc', 'A counter', registry=self.registry)
42+
c.inc()
43+
44+
request = self.factory.get("/metrics")
45+
46+
response = PrometheusDjangoView.as_view(registry=self.registry)(request)
47+
self.assertEqual(response.status_code, 200)
48+
self.assertEqual(response.content, generate_latest(self.registry, ALLOWUTF8))

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ deps =
1010
attrs
1111
{py3.9,pypy3.9}: twisted
1212
{py3.9,pypy3.9}: aiohttp
13+
{py3.9,pypy3.9}: django
1314
commands = coverage run --parallel -m pytest {posargs}
1415

1516
[testenv:py3.9-nooptionals]

0 commit comments

Comments
 (0)