forked from 0ad/0ad
84 lines
3.5 KiB
Python
84 lines
3.5 KiB
Python
# 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
|