1 # http://www.no-ack.org/2010/12/yet-another-profiling-middleware-for.html
6 from cStringIO
import StringIO
8 from django
.conf
import settings
12 COMMENT_SYNTAX
= ((re
.compile(r
'^application/(.*\+)?xml|text/html$', re
.I
), '<!--', '-->'),
13 (re
.compile(r
'^application/j(avascript|son)$', re
.I
), '/*', '*/' ))
15 class ProfileMiddleware(object):
16 def process_view(self
, request
, callback
, args
, kwargs
):
17 # Create a profile, writing into a temporary file.
18 filename
= tempfile
.mktemp()
19 profile
= hotshot
.Profile(filename
)
23 # Profile the call of the view function.
24 response
= profile
.runcall(callback
, request
, *args
, **kwargs
)
26 # If we have got a 3xx status code, further
27 # action needs to be taken by the user agent
28 # in order to fulfill the request. So don't
29 # attach any stats to the content, because of
30 # the content is supposed to be empty and is
31 # ignored by the user agent.
32 if response
.status_code
// 100 == 3:
35 # Detect the appropriate syntax based on the
36 # Content-Type header.
37 for regex
, begin_comment
, end_comment
in COMMENT_SYNTAX
:
38 if regex
.match(response
['Content-Type'].split(';')[0].strip()):
41 # If the given Content-Type is not
42 # supported, don't attach any stats to
43 # the content and return the unchanged
47 # The response can hold an iterator, that
48 # is executed when the content property
49 # is accessed. So we also have to profile
50 # the call of the content property.
51 content
= profile
.runcall(response
.__class
__.content
.fget
, response
)
55 # Load the stats from the temporary file and
56 # write them in a human readable format,
57 # respecting some optional settings into a
59 stats
= hotshot
.stats
.load(filename
)
60 if getattr(settings
, 'PROFILE_MIDDLEWARE_STRIP_DIRS', False):
62 if getattr(settings
, 'PROFILE_MIDDLEWARE_SORT', None):
63 stats
.sort_stats(*settings
.PROFILE_MIDDLEWARE_SORT
)
64 stats
.stream
= StringIO()
65 stats
.print_stats(*getattr(settings
, 'PROFILE_MIDDLEWARE_RESTRICTIONS', []))
69 # Construct an HTML/XML or Javascript comment, with
70 # the formatted stats, written to the StringIO object
71 # and attach it to the content of the response.
72 comment
= '\n%s\n\n%s\n\n%s\n' % (begin_comment
, stats
.stream
.getvalue().strip(), end_comment
)
73 response
.content
= content
+ comment
75 # If the Content-Length header is given, add the
76 # number of bytes we have added to it. If the
77 # Content-Length header is ommited or incorrect,
78 # it remains so in order to don't change the
79 # behaviour of the web server or user agent.
80 if response
.has_header('Content-Length'):
81 response
['Content-Length'] = int(response
['Content-Length']) + len(comment
)