1
# -*- coding: iso-8859-1 -*-
3
MoinMoin - WSGI middlewares for profiling
5
These have been ported from server_standalone to provide application
6
profiling for a WSGI application. They are implemented as WSGI
7
middlewares, so they can be plugged right in front of the MoinMoin
8
WSGI application. Attention has to be payed, that at the end of
9
profiling the `shutdown`-method has to be invoked, so that the
10
middlewares can write the reports to the filesystem.
12
TODO: in pre-WSGI MoinMoin those profilers where integrated in
13
the standalone server and also some other gateway interfaces.
14
In the near future the middlewares here could be again made
15
configurable automatically with command line switches or
16
wiki configuration options.
18
@copyright: 2008 MoinMoin:FlorianKrupicka,
19
@license: GNU GPL, see COPYING for details.
21
from werkzeug import get_current_url
23
from MoinMoin import log
24
logging = log.getLogger(__name__)
26
class ProfilerMiddleware(object):
27
""" Abstract base class for profiling middlewares.
29
Concrete implementations of this class should provide implementations
30
of `run_profile` and `shutdown`, the latter which should be called by
31
the code utilizing the profiler.
33
def __init__(self, app):
36
def profile(self, environ, start_response):
38
Profile the request. Exceptions encountered during the profile are
39
logged before being propagated for further handling.
41
method = environ.get('REQUEST_METHOD', 'GET')
42
url = get_current_url(environ)
43
logging.debug("Profiling call for '%s %s'", method, url)
45
res = self.run_profile(self.app, (environ, start_response))
47
logging.exception("Exception while profiling '%s %s'", method, url)
53
def run_profile(self, app, *args, **kwargs):
54
""" Override in subclasses.
56
Several profilers available for python use the same call signature,
57
therefore simplifying the implementation.
59
raise NotImplementedError()
62
""" Override in subclasses to clean up when server/script shuts down. """
65
class CProfileMiddleware(ProfilerMiddleware):
66
""" A profiler based on the the cProfile module from the standard lib. """
67
def __init__(self, app, filename):
68
super(CProfileMiddleware, self).__init__(app)
70
self._profile = cProfile.Profile()
71
self._filename = filename
72
self.run_profile = self._profile.runcall
75
self._profile.dump_stats(self._filename)
77
class HotshotMiddleware(ProfilerMiddleware):
78
""" A profiler based on the more recent hotshot module from the stdlib. """
79
def __init__(self, app, *args, **kwargs):
80
super(HotshotMiddleware, self).__init__(app)
82
self._profile = hotshot.Profile(*args, **kwargs)
83
self.run_profile = self._profile.runcall
88
class PycallgraphMiddleware(ProfilerMiddleware):
89
""" A call graphing middleware utilizing the pycallgraph 3rd party
90
module (available at http://pycallgraph.slowchop.com/). """
91
def __init__(self, app, filename):
92
super(PycallgraphMiddleware, self).__init__(app)
94
pycallgraph.settings['include_stdlib'] = False
95
self._filename = filename
96
globs = ['pycallgraph.*', 'unknown.*']
97
f = pycallgraph.GlobbingFilter(exclude=globs, max_depth=9999)
99
self.pycallgraph = pycallgraph
101
def run_profile(self, app, *args, **kwargs):
102
pycallgraph = self.pycallgraph
103
pycallgraph.start_trace(reset=True, filter_func=self._filter)
105
return app(*args, **kwargs)
107
pycallgraph.stop_trace()
110
fname = self._filename
111
pycallgraph = self.pycallgraph
112
if fname.endswith('.png'):
113
logging.info("Saving the rendered callgraph to '%s'", fname)
114
pycallgraph.make_dot_graph(fname)
115
elif fname.endswith('.dot'):
116
logging.info("Saving the raw callgraph to '%s'", fname)
117
pycallgraph.save_dot(fname)