12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485 |
- # http://www.no-ack.org/2010/12/yet-another-profiling-middleware-for.html
- import os
- import re
- import tempfile
- from cStringIO import StringIO
- from django.conf import settings
- import hotshot
- import hotshot.stats
- COMMENT_SYNTAX = ((re.compile(r'^application/(.*\+)?xml|text/html$', re.I), '<!--', '-->'),
- (re.compile(r'^application/j(avascript|son)$', re.I), '/*', '*/'))
- class ProfileMiddleware(object):
- def process_view(self, request, callback, args, kwargs):
- # Create a profile, writing into a temporary file.
- filename = tempfile.mktemp()
- profile = hotshot.Profile(filename)
- try:
- try:
- # Profile the call of the view function.
- response = profile.runcall(callback, request, *args, **kwargs)
- # If we have got a 3xx status code, further
- # action needs to be taken by the user agent
- # in order to fulfill the request. So don't
- # attach any stats to the content, because of
- # the content is supposed to be empty and is
- # ignored by the user agent.
- if response.status_code // 100 == 3:
- return response
- # Detect the appropriate syntax based on the
- # Content-Type header.
- for regex, begin_comment, end_comment in COMMENT_SYNTAX:
- if regex.match(response['Content-Type'].split(';')[0].strip()):
- break
- else:
- # If the given Content-Type is not
- # supported, don't attach any stats to
- # the content and return the unchanged
- # response.
- return response
- # The response can hold an iterator, that
- # is executed when the content property
- # is accessed. So we also have to profile
- # the call of the content property.
- content = profile.runcall(response.__class__.content.fget, response)
- finally:
- profile.close()
- # Load the stats from the temporary file and
- # write them in a human readable format,
- # respecting some optional settings into a
- # StringIO object.
- stats = hotshot.stats.load(filename)
- if getattr(settings, 'PROFILE_MIDDLEWARE_STRIP_DIRS', False):
- stats.strip_dirs()
- if getattr(settings, 'PROFILE_MIDDLEWARE_SORT', None):
- stats.sort_stats(*settings.PROFILE_MIDDLEWARE_SORT)
- stats.stream = StringIO()
- stats.print_stats(*getattr(settings, 'PROFILE_MIDDLEWARE_RESTRICTIONS', []))
- finally:
- os.unlink(filename)
- # Construct an HTML/XML or Javascript comment, with
- # the formatted stats, written to the StringIO object
- # and attach it to the content of the response.
- comment = '\n%s\n\n%s\n\n%s\n' % (begin_comment, stats.stream.getvalue().strip(), end_comment)
- response.content = content + comment
- # If the Content-Length header is given, add the
- # number of bytes we have added to it. If the
- # Content-Length header is ommited or incorrect,
- # it remains so in order to don't change the
- # behaviour of the web server or user agent.
- if response.has_header('Content-Length'):
- response['Content-Length'] = int(response['Content-Length']) + len(comment)
- return response
|