import collections import time import pygooglechart DEFAULT_BUCKETS = [ 2, 5, 10, 20, 50, 100, 200, 500, ] def _percent(counts): tot = float(sum(counts)) return [(x, 100 * x / tot) for x in counts] class LatencyProfilerMiddleware(object): def __init__(self, app, urls=None, buckets=None): self._app = app self._buckets = buckets or DEFAULT_BUCKETS self._n_buckets = len(self._buckets) + 1 self._urls = sorted(urls or [], key=lambda x: len(x), reverse=True) def _build_buckets(): return [0] * self._n_buckets self._data = collections.defaultdict(_build_buckets) def __call__(self, environ, start_response): start = time.time() url = environ['PATH_INFO'] if url.endswith('/__latency__'): return self.handler(environ, start_response) try: return self._app(environ, start_response) finally: elapsed_ms = 1000 * (time.time() - start) for url_prefix in self._urls: if url.startswith(url_prefix): url = url_prefix break for bkt, threshold in enumerate(self._buckets): if elapsed_ms < threshold: break else: bkt = self._n_buckets - 1 self._data[url][bkt] += 1 def handler(self, environ, start_response): headers = [('Content-type', 'text/html')] start_response('200 OK', headers) result = [ '<!doctype html>\n' '<html><head><title>Latency Breakdown</title>\n' '<style type="text/css">\n' 'body { background: white; font-family: sans-serif; }\n' 'tbody td { text-align: left; padding-right: 10px; }\n' 'thead th { font-weight: bold; text-align: left; color: white; ' 'background-color: #666; }\n' 'td.url { font-weight: bold; border-right: 1px solid #666; }\n' 'tbody tr { border-bottom: 1px solid #ccc; }\n' '</style></head><body>\n' '<h3>Latency report by URL</h3>\n' '<table cellspacing="0" cellpadding="0" border="0"><thead><tr>' '<th rowspan="2">URL</th>', '<th rowspan="2">Total</th>', '<th colspan="%d">Latencies (ms)</th>' % self._n_buckets, '<th rowspan="2"></th>', '</tr><tr>' ] for ms in self._buckets: result.append('<th><%d</th>' % ms) result.append('<th>>%d</th>' % ms) result.append('</tr></thead><tbody>') for url in sorted(self._data.keys()): values = self._data[url] result.append('<tr><td class="url">%s</td><td>%d</td>' % ( url, sum(values))) chart = pygooglechart.SparkLineChart(80, 25) chart.set_data(values) for count, pct in _percent(values): result.append('<td>%d (%.2g%%)</td>' % (count, pct)) result.append('<td><img src="%s" width="80" height="25"></td>' % chart.get_url()) result.append('</tr>') result.append('</tbody></table>') result.append('</body></html>\n') return result