import datetime import logging import time from functools import wraps from Queue import Queue, Full from flask import g, request logger = logging.getLogger(__name__) class MetricQueue(object): def __init__(self): self._queue = None def enable(self, maxsize=10000): self._queue = Queue(maxsize) def put(self, name, value, **kwargs): if self._queue is None: logger.debug('No metric queue %s %s %s', name, value, kwargs) return try: kwargs.setdefault('timestamp', datetime.datetime.now()) kwargs.setdefault('dimensions', {}) self._queue.put_nowait((name, value, kwargs)) except Full: logger.error('Metric queue full') def get(self): return self._queue.get() def get_nowait(self): return self._queue.get_nowait() def time_blueprint(bp, metric_queue): bp.before_request(time_before_request) bp.after_request(time_after_request(bp.name, metric_queue)) def time_before_request(): g._request_start_time = time.time() def time_after_request(name, metric_queue): def f(r): start = getattr(g, '_request_start_time', None) if start is None: return r dur = time.time() - start dims = {'endpoint': request.endpoint} metric_queue.put('ResponseTime', dur, dimensions=dims, unit='Seconds') metric_queue.put('ResponseCode', r.status_code, dimensions=dims) if r.status_code >= 500: metric_queue.put('5XXResponse', 1, dimensions={'name': name}) elif r.status_code < 200 or r.status_code >= 300: metric_queue.put('Non200Response', 1, dimensions={'name': name}) return r return f def time_decorator(name, metric_queue): after = time_after_request(name, metric_queue) def decorator(func): @wraps(func) def wrapper(*args, **kwargs): time_before_request() rv = func(*args, **kwargs) after(rv) return rv return wrapper return decorator