2019-11-12 16:09:47 +00:00
|
|
|
import logging
|
2019-11-13 19:50:33 +00:00
|
|
|
import time
|
|
|
|
import threading
|
2019-11-12 16:09:47 +00:00
|
|
|
|
2019-11-13 19:50:33 +00:00
|
|
|
from flask import g, request
|
|
|
|
from prometheus_client import push_to_gateway, REGISTRY, Histogram
|
2019-11-12 16:09:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2019-11-13 19:50:33 +00:00
|
|
|
|
|
|
|
request_duration = Histogram('quay_request_duration_seconds',
|
|
|
|
'seconds taken to process a request',
|
|
|
|
labelnames=['method', 'endpoint', 'status'],
|
|
|
|
buckets=[.01, .025, .05, .1, .25, .5, 1.0, 2.5, 5.0])
|
|
|
|
|
|
|
|
|
|
|
|
PROMETHEUS_PUSH_INTERVAL_SECONDS = 30
|
|
|
|
ONE_DAY_IN_SECONDS = 60 * 60 * 24
|
|
|
|
|
2019-11-12 16:09:47 +00:00
|
|
|
|
|
|
|
class PrometheusPlugin(object):
|
|
|
|
""" Application plugin for reporting metrics to Prometheus. """
|
|
|
|
def __init__(self, app=None):
|
|
|
|
self.app = app
|
|
|
|
if app is not None:
|
|
|
|
self.state = self.init_app(app)
|
|
|
|
else:
|
|
|
|
self.state = None
|
|
|
|
|
|
|
|
def init_app(self, app):
|
2019-11-13 19:50:33 +00:00
|
|
|
pusher = ThreadPusher(app)
|
|
|
|
pusher.start()
|
2019-11-12 16:09:47 +00:00
|
|
|
|
|
|
|
# register extension with app
|
|
|
|
app.extensions = getattr(app, 'extensions', {})
|
2019-11-13 19:50:33 +00:00
|
|
|
app.extensions['prometheus'] = pusher
|
|
|
|
return pusher
|
2019-11-12 16:09:47 +00:00
|
|
|
|
|
|
|
def __getattr__(self, name):
|
|
|
|
return getattr(self.state, name, None)
|
|
|
|
|
|
|
|
|
2019-11-13 19:50:33 +00:00
|
|
|
class ThreadPusher(threading.Thread):
|
|
|
|
def __init__(self, app):
|
|
|
|
super(ThreadPusher, self).__init__()
|
2019-11-12 16:09:47 +00:00
|
|
|
self.daemon = True
|
2019-11-13 19:50:33 +00:00
|
|
|
self._app = app
|
2019-11-12 16:09:47 +00:00
|
|
|
|
|
|
|
def run(self):
|
2019-11-13 20:11:59 +00:00
|
|
|
agg_url = self._app.config.get('PROMETHEUS_PUSHGATEWAY_URL')
|
2019-11-12 16:09:47 +00:00
|
|
|
while True:
|
2019-11-13 19:50:33 +00:00
|
|
|
if agg_url is None:
|
2019-11-13 20:11:59 +00:00
|
|
|
# Practically disable this worker, if there is no pushgateway.
|
2019-11-13 19:50:33 +00:00
|
|
|
time.sleep(ONE_DAY_IN_SECONDS)
|
|
|
|
continue
|
|
|
|
|
|
|
|
time.sleep(PROMETHEUS_PUSH_INTERVAL_SECONDS)
|
|
|
|
push_to_gateway(agg_url, job=self._app.config.get('PROMETHEUS_NAMESPACE', 'quay'),
|
|
|
|
registry=REGISTRY)
|
|
|
|
|
|
|
|
|
|
|
|
def timed_blueprint(bp):
|
|
|
|
"""
|
|
|
|
Decorates a blueprint to have its request duration tracked by Prometheus.
|
|
|
|
"""
|
|
|
|
def _time_before_request():
|
|
|
|
g._request_start_time = time.time()
|
|
|
|
bp.before_request(_time_before_request)
|
|
|
|
|
|
|
|
|
|
|
|
def _time_after_request():
|
|
|
|
def f(r):
|
|
|
|
start = getattr(g, '_request_start_time', None)
|
|
|
|
if start is None:
|
|
|
|
return r
|
|
|
|
dur = time.time() - start
|
|
|
|
request_duration.labels(request.method, request.endpoint, r.status_code).observe(dur)
|
|
|
|
return r
|
2019-11-12 16:09:47 +00:00
|
|
|
return f
|
2019-11-13 19:50:33 +00:00
|
|
|
bp.after_request(_time_after_request())
|
|
|
|
return bp
|