Skip to content

Commit 1918856

Browse files
authored
Merge pull request #3490 from yuvipanda/prometheus-intro
Add a /metrics endpoint for Prometheus Metrics
2 parents f111f9a + 7a3c0f3 commit 1918856

File tree

4 files changed

+53
-2
lines changed

4 files changed

+53
-2
lines changed

notebook/base/handlers.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from jinja2 import TemplateNotFound
2929
from tornado import web, gen, escape, httputil
3030
from tornado.log import app_log
31+
import prometheus_client
3132

3233
from notebook._sysinfo import get_sys_info
3334

@@ -809,6 +810,16 @@ def get(self):
809810
url = sep.join([self._url, self.request.query])
810811
self.redirect(url, permanent=self._permanent)
811812

813+
class PrometheusMetricsHandler(IPythonHandler):
814+
"""
815+
Return prometheus metrics for this notebook server
816+
"""
817+
@web.authenticated
818+
def get(self):
819+
self.set_header('Content-Type', prometheus_client.CONTENT_TYPE_LATEST)
820+
self.write(prometheus_client.generate_latest(prometheus_client.REGISTRY))
821+
822+
812823
#-----------------------------------------------------------------------------
813824
# URL pattern fragments for re-use
814825
#-----------------------------------------------------------------------------
@@ -825,4 +836,5 @@ def get(self):
825836
(r".*/", TrailingSlashHandler),
826837
(r"api", APIVersionHandler),
827838
(r'/(robots\.txt|favicon\.ico)', web.StaticFileHandler),
839+
(r'/metrics', PrometheusMetricsHandler)
828840
]

notebook/log.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import json
99
from tornado.log import access_log
10+
from .metrics import prometheus_log_method
1011

1112
def log_request(handler):
1213
"""log a bit more information about each request than tornado's default
@@ -45,4 +46,4 @@ def log_request(handler):
4546
# log all headers if it caused an error
4647
log_method(json.dumps(dict(request.headers), indent=2))
4748
log_method(msg.format(**ns))
48-
49+
prometheus_log_method(handler)

notebook/metrics.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""
2+
Prometheus metrics exported by Jupyter Notebook Server
3+
4+
Read https://prometheus.io/docs/practices/naming/ for naming
5+
conventions for metrics & labels.
6+
"""
7+
8+
from prometheus_client import Histogram
9+
10+
# This is a fairly standard name for HTTP duration latency reporting
11+
HTTP_REQUEST_DURATION_SECONDS = Histogram(
12+
'http_request_duration_seconds',
13+
'duration in seconds for all HTTP requests',
14+
['method', 'handler', 'status_code'],
15+
)
16+
17+
def prometheus_log_method(handler):
18+
"""
19+
Tornado log handler for recording RED metrics.
20+
21+
We record the following metrics:
22+
Rate - the number of requests, per second, your services are serving.
23+
Errors - the number of failed requests per second.
24+
Duration - The amount of time each request takes expressed as a time interval.
25+
26+
We use a fully qualified name of the handler as a label,
27+
rather than every url path to reduce cardinality.
28+
29+
This function should be either the value of or called from a function
30+
that is the 'log_function' tornado setting. This makes it get called
31+
at the end of every request, allowing us to record the metrics we need.
32+
"""
33+
HTTP_REQUEST_DURATION_SECONDS.labels(
34+
method=handler.request.method,
35+
handler='{}.{}'.format(handler.__class__.__module__, type(handler).__name__),
36+
status_code=handler.get_status()
37+
).observe(handler.request.request_time())

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@
9090
'nbconvert',
9191
'ipykernel', # bless IPython kernel for now
9292
'Send2Trash',
93-
'terminado>=0.8.1'
93+
'terminado>=0.8.1',
94+
'prometheus_client'
9495
],
9596
extras_require = {
9697
'test:python_version == "2.7"': ['mock'],

0 commit comments

Comments
 (0)