1
# -*- coding: utf-8 -*-
3
werkzeug.contrib.profiler
4
~~~~~~~~~~~~~~~~~~~~~~~~~
6
This module provides a simple WSGI profiler middleware for finding
7
bottlenecks in web application. It uses the :mod:`profile` or
8
:mod:`cProfile` module to do the profiling and writes the stats to the
9
stream provided (defaults to stderr).
13
from werkzeug.contrib.profiler import ProfilerMiddleware
14
app = ProfilerMiddleware(app)
16
:copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
17
:license: BSD, see LICENSE for more details.
22
from cProfile import Profile
24
from profile import Profile
25
from pstats import Stats
31
class MergeStream(object):
32
"""An object that redirects `write` calls to multiple streams.
33
Use this to log to both `sys.stdout` and a file::
35
f = open('profiler.log', 'w')
36
stream = MergeStream(sys.stdout, f)
37
profiler = ProfilerMiddleware(app, stream)
40
def __init__(self, *streams):
42
raise TypeError('at least one stream must be given')
43
self.streams = streams
45
def write(self, data):
46
for stream in self.streams:
50
class ProfilerMiddleware(object):
51
"""Simple profiler middleware. Wraps a WSGI application and profiles
52
a request. This intentionally buffers the response so that timings are
55
For the exact meaning of `sort_by` and `restrictions` consult the
56
:mod:`profile` documentation.
58
:param app: the WSGI application to profile.
59
:param stream: the stream for the profiled stats. defaults to stderr.
60
:param sort_by: a tuple of columns to sort the result by.
61
:param restrictions: a tuple of profiling strictions.
64
def __init__(self, app, stream=None,
65
sort_by=('time', 'calls'), restrictions=()):
67
raise RuntimeError('the profiler is not available because '
68
'profile or pstat is not installed.')
70
self._stream = stream or sys.stdout
71
self._sort_by = sort_by
72
self._restrictions = restrictions
74
def __call__(self, environ, start_response):
77
def catching_start_response(status, headers, exc_info=None):
78
start_response(status, headers, exc_info)
79
return response_body.append
82
appiter = self._app(environ, catching_start_response)
83
response_body.extend(appiter)
84
if hasattr(appiter, 'close'):
89
body = ''.join(response_body)
91
stats.sort_stats(*self._sort_by)
93
self._stream.write('-' * 80)
94
self._stream.write('\nPATH: %r\n' % environ.get('PATH_INFO'))
95
stats.print_stats(*self._restrictions)
96
self._stream.write('-' * 80 + '\n\n')
101
def make_action(app_factory, hostname='localhost', port=5000,
102
threaded=False, processes=1, stream=None,
103
sort_by=('time', 'calls'), restrictions=()):
104
"""Return a new callback for :mod:`werkzeug.script` that starts a local
105
server with the profiler enabled::
107
from werkzeug.contrib import profiler
108
action_profile = profiler.make_action(make_app)
110
def action(hostname=('h', hostname), port=('p', port),
111
threaded=threaded, processes=processes):
112
"""Start a new development server."""
113
from werkzeug.serving import run_simple
114
app = ProfilerMiddleware(app_factory(), stream, sort_by, restrictions)
115
run_simple(hostname, port, app, False, None, threaded, processes)