Back to index

moin  1.9.0~rc2
profiler.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 """
00003     werkzeug.contrib.profiler
00004     ~~~~~~~~~~~~~~~~~~~~~~~~~
00005 
00006     This module provides a simple WSGI profiler middleware for finding
00007     bottlenecks in web application.  It uses the :mod:`profile` or
00008     :mod:`cProfile` module to do the profiling and writes the stats to the
00009     stream provided (defaults to stderr).
00010 
00011     Example usage::
00012 
00013         from werkzeug.contrib.profiler import ProfilerMiddleware
00014         app = ProfilerMiddleware(app)
00015 
00016     :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
00017     :license: BSD, see LICENSE for more details.
00018 """
00019 import sys
00020 try:
00021     try:
00022         from cProfile import Profile
00023     except ImportError:
00024         from profile import Profile
00025     from pstats import Stats
00026     available = True
00027 except ImportError:
00028     available = False
00029 
00030 
00031 class MergeStream(object):
00032     """An object that redirects `write` calls to multiple streams.
00033     Use this to log to both `sys.stdout` and a file::
00034 
00035         f = open('profiler.log', 'w')
00036         stream = MergeStream(sys.stdout, f)
00037         profiler = ProfilerMiddleware(app, stream)
00038     """
00039 
00040     def __init__(self, *streams):
00041         if not streams:
00042             raise TypeError('at least one stream must be given')
00043         self.streams = streams
00044 
00045     def write(self, data):
00046         for stream in self.streams:
00047             stream.write(data)
00048 
00049 
00050 class ProfilerMiddleware(object):
00051     """Simple profiler middleware.  Wraps a WSGI application and profiles
00052     a request.  This intentionally buffers the response so that timings are
00053     more exact.
00054 
00055     For the exact meaning of `sort_by` and `restrictions` consult the
00056     :mod:`profile` documentation.
00057 
00058     :param app: the WSGI application to profile.
00059     :param stream: the stream for the profiled stats.  defaults to stderr.
00060     :param sort_by: a tuple of columns to sort the result by.
00061     :param restrictions: a tuple of profiling strictions.
00062     """
00063 
00064     def __init__(self, app, stream=None,
00065                  sort_by=('time', 'calls'), restrictions=()):
00066         if not available:
00067             raise RuntimeError('the profiler is not available because '
00068                                'profile or pstat is not installed.')
00069         self._app = app
00070         self._stream = stream or sys.stdout
00071         self._sort_by = sort_by
00072         self._restrictions = restrictions
00073 
00074     def __call__(self, environ, start_response):
00075         response_body = []
00076 
00077         def catching_start_response(status, headers, exc_info=None):
00078             start_response(status, headers, exc_info)
00079             return response_body.append
00080 
00081         def runapp():
00082             appiter = self._app(environ, catching_start_response)
00083             response_body.extend(appiter)
00084             if hasattr(appiter, 'close'):
00085                 appiter.close()
00086 
00087         p = Profile()
00088         p.runcall(runapp)
00089         body = ''.join(response_body)
00090         stats = Stats(p)
00091         stats.sort_stats(*self._sort_by)
00092 
00093         self._stream.write('-' * 80)
00094         self._stream.write('\nPATH: %r\n' % environ.get('PATH_INFO'))
00095         stats.print_stats(*self._restrictions)
00096         self._stream.write('-' * 80 + '\n\n')
00097 
00098         return [body]
00099 
00100 
00101 def make_action(app_factory, hostname='localhost', port=5000,
00102                 threaded=False, processes=1, stream=None,
00103                 sort_by=('time', 'calls'), restrictions=()):
00104     """Return a new callback for :mod:`werkzeug.script` that starts a local
00105     server with the profiler enabled::
00106 
00107         from werkzeug.contrib import profiler
00108         action_profile = profiler.make_action(make_app)
00109     """
00110     def action(hostname=('h', hostname), port=('p', port),
00111                threaded=threaded, processes=processes):
00112         """Start a new development server."""
00113         from werkzeug.serving import run_simple
00114         app = ProfilerMiddleware(app_factory(), stream, sort_by, restrictions)
00115         run_simple(hostname, port, app, False, None, threaded, processes)
00116     return action