15
|
1 from __future__ import with_statement
|
|
2
|
|
3 import cProfile
|
|
4 import pstats
|
|
5 import cgi
|
|
6 import pprint
|
|
7 import threading
|
|
8
|
|
9 from StringIO import StringIO
|
|
10
|
|
11 class ProfilingMiddleware(object):
|
|
12 def __init__(self, app):
|
|
13 self.lock = threading.Lock()
|
|
14 self.app = app
|
|
15
|
|
16
|
|
17 def __call__(self, environ, start_response):
|
|
18 with self.lock:
|
|
19 profiler = cProfile.Profile()
|
|
20 def run_app(*a, **kw):
|
|
21 self.response = self.app(environ, start_response)
|
|
22
|
|
23 profiler.runcall(run_app, environ, start_response)
|
|
24
|
|
25 profiler.snapshot_stats()
|
|
26
|
|
27 stats = pstats.Stats(profiler)
|
|
28 stats.sort_stats('cumulative')
|
|
29
|
|
30 # Redirect output
|
|
31 out = StringIO()
|
|
32 stats.stream = out
|
|
33
|
|
34 stats.print_stats()
|
|
35
|
|
36 resp = ''.join(self.response)
|
|
37
|
|
38 # Lets at least only put this on html-like responses.
|
|
39 if resp.strip().startswith('<'):
|
|
40 ## The profiling info is just appended to the response.
|
|
41 ## Browsers don't mind this.
|
|
42 resp += '<pre style="text-align:left; border-top: 4px dashed red; padding: 1em;">'
|
|
43 resp += cgi.escape(out.getvalue(), True)
|
|
44
|
|
45 output = StringIO()
|
|
46 pprint.pprint(environ, output, depth=3)
|
|
47
|
|
48 resp += cgi.escape(output.getvalue(), True)
|
|
49 resp += '</pre>'
|
|
50
|
|
51 return resp
|
|
52
|
|
53
|