Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

trs/wip/metrics #245

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions lib/id3c/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import logging
from flask import Flask
from . import config
from .metrics import metrics
from .routes import blueprints


Expand All @@ -17,6 +18,8 @@ def create_app():
for blueprint in blueprints:
app.register_blueprint(blueprint)

metrics.init_app(app)

LOG.debug(f"app root is {app.root_path}")
LOG.debug(f"app static directory is {app.static_folder}")

Expand Down
19 changes: 19 additions & 0 deletions lib/id3c/api/metrics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""
Web API metrics.
"""
import prometheus_flask_exporter
import prometheus_flask_exporter.multiprocess


if "prometheus_multiproc_dir" in os.environ:
FlaskMetrics = prometheus_flask_exporter.multiprocess.MultiprocessPrometheusMetrics
else:
FlaskMetrics = prometheus_flask_exporter.PrometheusMetrics


# This instance is used by both our routes and create_app().
metrics = FlaskMetrics(
app = None,
path = None,
defaults_prefix = prometheus_flask_exporter.NO_PREFIX,
default_latency_as_histogram = False)
28 changes: 27 additions & 1 deletion lib/id3c/api/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
import json
import logging
import pkg_resources
from flask import Blueprint, request, send_file
import prometheus_client
from flask import Blueprint, make_response, request, send_file

from ..metrics import DatabaseCollector
from . import datastore
from .metrics import metrics
from .utils.routes import authenticated_datastore_session_required, content_types_accepted, check_content_length


Expand All @@ -20,6 +24,28 @@
]


# Metrics exposition endpoint
@api_v1.route("/metrics", methods = ["GET"])
@metrics.do_not_track()
@authenticated_datastore_session_required
def expose_metrics(*, session):
"""
Exposes metrics for Prometheus.

Includes metrics collected from the Flask app, as well as the database.
"""
registry = prometheus_client.CollectorRegistry(auto_describe = True)

# Collect metrics from the app-wide registry, potentially from multiple
# server processes via files in prometheus_multiproc_dir.
registry.register(metrics.registry)

# Collect metrics from the database using the authenticated session.
registry.register(DatabaseCollector(session))

return make_response(prometheus_client.make_wsgi_app(registry))


@api_v1.route("/", methods = ['GET'])
@api_unversioned.route("/", methods = ['GET'])
def index():
Expand Down
56 changes: 56 additions & 0 deletions lib/id3c/metrics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""
Metrics handling functions.
"""
import logging
from prometheus_client.core import GaugeMetricFamily
from psycopg2.errors import InsufficientPrivilege
from .db import DatabaseSession


LOG = logging.getLogger(__name__)


class DatabaseCollector:
"""
Collects metrics from the database, using an existing *session*.
"""
def __init__(self, session: DatabaseSession):
self.session = session


def collect(self):
with self.session:
yield from self.estimated_row_total()


def estimated_row_total(self):
family = GaugeMetricFamily(
"id3c_estimated_row_total",
"Estimated number of rows in an ID3C database table",
labels = ("schema", "table"))

try:
metrics = self.session.fetch_all(
"""
select
ns.nspname as schema,
c.relname as table,
c.reltuples::bigint as estimated_row_count
from
pg_catalog.pg_class c
join pg_catalog.pg_namespace ns on (c.relnamespace = ns.oid)
where
ns.nspname in ('receiving', 'warehouse') and
c.relkind = 'r'
order by
schema,
"table"
""")
except InsufficientPrivilege as error:
LOG.error(f"Permission denied when collecting id3c_estimated_row_total metrics: {error}")
return

for metric in metrics:
family.add_metric((metric.schema, metric.table), metric.estimated_row_count)

yield family
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
"more-itertools",
"oauth2client >2.0.0,<4.0.0",
"pandas >=1.0.1,<2",
"prometheus_client",
"prometheus_flask_exporter",
"psycopg2 >=2.8,<3",
"pyyaml",
"requests",
Expand Down