diff --git a/.gitignore b/.gitignore
index 2eb6e784..44a7b3d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,3 +27,4 @@ htmlcov
.vscode/
.pytest_cache
pip-wheel-metadata
+docs/source/reference/metrics.rst
diff --git a/docs/Makefile b/docs/Makefile
index b20dc238..5e61789e 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -48,6 +48,7 @@ help:
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
@echo " coverage to run coverage check of the documentation (if enabled)"
@echo " spelling to run spell check on documentation"
+ @echo " metrics to generate documentation for metrics by inspecting the source code"
clean:
rm -rf $(BUILDDIR)/*
@@ -60,7 +61,12 @@ rest-api: source/_static/rest-api/index.html
source/_static/rest-api/index.html: rest-api.yml node_modules
npm run rest-api
-html: rest-api
+metrics: source/reference/metrics.rst
+
+source/reference/metrics.rst: generate-metrics.py
+ python3 generate-metrics.py
+
+html: rest-api metrics
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
diff --git a/docs/generate-metrics.py b/docs/generate-metrics.py
new file mode 100644
index 00000000..81304bef
--- /dev/null
+++ b/docs/generate-metrics.py
@@ -0,0 +1,57 @@
+import os
+from os.path import join
+
+from pytablewriter import RstSimpleTableWriter
+from pytablewriter.style import Style
+
+import jupyterhub.metrics
+
+HERE = os.path.abspath(os.path.dirname(__file__))
+
+
+class Generator:
+ @classmethod
+ def create_writer(cls, table_name, headers, values):
+ writer = RstSimpleTableWriter()
+ writer.table_name = table_name
+ writer.headers = headers
+ writer.value_matrix = values
+ writer.margin = 1
+ [writer.set_style(header, Style(align="center")) for header in headers]
+ return writer
+
+ def _parse_metrics(self):
+ table_rows = []
+ for name in dir(jupyterhub.metrics):
+ obj = getattr(jupyterhub.metrics, name)
+ if obj.__class__.__module__.startswith('prometheus_client.'):
+ for metric in obj.describe():
+ table_rows.append([metric.type, metric.name, metric.documentation])
+ return table_rows
+
+ def prometheus_metrics(self):
+ generated_directory = f"{HERE}/source/reference"
+ if not os.path.exists(generated_directory):
+ os.makedirs(generated_directory)
+
+ filename = f"{generated_directory}/metrics.rst"
+ table_name = ""
+ headers = ["Type", "Name", "Description"]
+ values = self._parse_metrics()
+ writer = self.create_writer(table_name, headers, values)
+
+ title = "List of Prometheus Metrics"
+ underline = "============================"
+ content = f"{title}\n{underline}\n{writer.dumps()}"
+ with open(filename, 'w') as f:
+ f.write(content)
+ print(f"Generated {filename}.")
+
+
+def main():
+ doc_generator = Generator()
+ doc_generator.prometheus_metrics()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 472fcc28..2d9da795 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -5,6 +5,7 @@ alabaster_jupyterhub
# 0.1.0 released.
https://github.com/jupyterhub/autodoc-traits/archive/75885ee24636efbfebfceed1043459715049cd84.zip
pydata-sphinx-theme
+pytablewriter>=0.56
recommonmark>=0.6
sphinx-copybutton
sphinx-jsonschema
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 63f758c9..e1e09b3e 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -217,10 +217,10 @@ intersphinx_mapping = {'https://docs.python.org/3/': None}
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
if on_rtd:
# readthedocs.org uses their theme by default, so no need to specify it
- # build rest-api, since RTD doesn't run make
+ # build both metrics and rest-api, since RTD doesn't run make
from subprocess import check_call as sh
- sh(['make', 'rest-api'], cwd=docs)
+ sh(['make', 'metrics', 'rest-api'], cwd=docs)
# -- Spell checking -------------------------------------------------------
diff --git a/docs/source/reference/index.rst b/docs/source/reference/index.rst
index 4008a723..ed478fa1 100644
--- a/docs/source/reference/index.rst
+++ b/docs/source/reference/index.rst
@@ -16,6 +16,7 @@ what happens under-the-hood when you deploy and configure your JupyterHub.
proxy
separate-proxy
rest
+ monitoring
database
templates
../events/index
diff --git a/docs/source/reference/monitoring.rst b/docs/source/reference/monitoring.rst
new file mode 100644
index 00000000..774656ec
--- /dev/null
+++ b/docs/source/reference/monitoring.rst
@@ -0,0 +1,20 @@
+Monitoring
+==========
+
+This section covers details on monitoring the state of your JupyterHub installation.
+
+JupyterHub expose the ``/metrics`` endpoint that returns text describing its current
+operational state formatted in a way `Prometheus `_ understands.
+
+Prometheus is a separate open source tool that can be configured to repeatedly poll
+JupyterHub's ``/metrics`` endpoint to parse and save its current state.
+
+By doing so, Prometheus can describe JupyterHub's evolving state over time.
+This evolving state can then be accessed through Prometheus that expose its underlying
+storage to those allowed to access it, and be presented with dashboards by a
+tool like `Grafana `_.
+
+.. toctree::
+ :maxdepth: 2
+
+ metrics