Switch to side-by-side view

--- a/Allura/allura/lib/custom_middleware.py
+++ b/Allura/allura/lib/custom_middleware.py
@@ -1,21 +1,14 @@
 import os
 import re
 import logging
-from contextlib import contextmanager
-from threading import local
-from random import random
 
 import tg
-import pylons
 import pkg_resources
-import markdown
 from paste import fileapp
-from paste.deploy.converters import asbool
 from pylons.util import call_wsgi_application
-from tg.controllers import DecoratedController
+from timermiddleware import Timer, TimerMiddleware
 from webob import exc, Request
 
-from allura.lib.stats import timing, StatsRecord
 from allura.lib import helpers as h
 
 log = logging.getLogger(__name__)
@@ -151,57 +144,39 @@
             resp = req.get_response(self.app)
         return resp(environ, start_response)
 
-class StatsMiddleware(object):
+class AlluraTimerMiddleware(TimerMiddleware):
+    def timers(self):
+        import genshi
+        import jinja2
+        import markdown
+        import ming
+        import pymongo
+        import socket
+        import urllib2
 
-    def __init__(self, app, config):
-        self.app = app
-        self.config = config
-        self.log = logging.getLogger('stats')
-        self.active = False
-        try:
-            self.sample_rate = config.get('stats.sample_rate', 0.25)
-            self.debug = asbool(config.get('debug', 'false'))
-            self.instrument_pymongo()
-            self.instrument_template()
-            self.active = True
-        except KeyError:
-            self.sample_rate = 0
-
-    def instrument_pymongo(self):
-        import pymongo.collection
-        import ming.odm
-        timing('mongo').decorate(pymongo.collection.Collection,
-                                 'count find find_one')
-        timing('mongo').decorate(pymongo.cursor.Cursor,
-                                 'count distinct explain hint limit next rewind'
-                                 ' skip sort where')
-        timing('ming').decorate(ming.odm.odmsession.ODMSession,
-                                'flush find get')
-        timing('ming').decorate(ming.odm.odmsession.ODMCursor,
-                                'next')
-
-    def instrument_template(self):
-        import jinja2
-        import genshi.template
-        timing('template').decorate(genshi.template.Template,
-                                    '_prepare _parse generate')
-        timing('render').decorate(genshi.Stream,
-                                  'render')
-        timing('render').decorate(jinja2.Template,
-                                  'render')
-        timing('markdown').decorate(markdown.Markdown,
-                                    'convert')
-
-
-    def __call__(self, environ, start_response):
-        req = Request(environ)
-        req.environ['sf.stats'] = s = StatsRecord(req, random() < self.sample_rate)
-        with s.timing('total'):
-            resp = req.get_response(self.app, catch_exc_info=self.debug)
-            result = resp(environ, start_response)
-        if s.active:
-            self.log.info('Stats: %r', s)
-            from allura import model as M
-            M.Stats.make(s.asdict()).m.insert()
-        return result
-
+        return [
+            Timer('markdown', markdown.Markdown, 'convert'),
+            Timer('ming', ming.odm.odmsession.ODMCursor, 'next'),
+            Timer('ming', ming.odm.odmsession.ODMSession, 'flush', 'find',
+                'get'),
+            Timer('ming', ming.schema.Document, 'validate',
+                debug_each_call=False),
+            Timer('ming', ming.schema.FancySchemaItem, '_validate_required',
+                '_validate_fast_missing', '_validate_optional',
+                debug_each_call=False),
+            Timer('mongo', pymongo.collection.Collection, 'count', 'find',
+                'find_one'),
+            Timer('mongo', pymongo.cursor.Cursor, 'count', 'distinct',
+                'explain', 'hint', 'limit', 'next', 'rewind', 'skip',
+                'sort', 'where'),
+            Timer('jinja', jinja2.Template, 'render', 'stream', 'generate'),
+            # urlopen and socket io may or may not overlap partially
+            Timer('urlopen', urllib2, 'urlopen'),
+            Timer('render', genshi.Stream, 'render'),
+            Timer('socket_read', socket._fileobject, 'read', 'readline',
+                'readlines', debug_each_call=False),
+            Timer('socket_write', socket._fileobject, 'write', 'writelines',
+                'flush', debug_each_call=False),
+            Timer('template', genshi.template.Template, '_prepare', '_parse',
+                'generate'),
+        ]