So you want to find out why your Pylons app is running slowly? Well most likely it has to do with your SQL queries, and the best way to see what’s going on and how long each request is taking is to install Dozer (by benbangert of Pylons), and load it up with a TimerProxy (by zzzeek of SQLAlchemy).

Sound like fun? Well, here’s how to do it.

Install Dozer:

sudo easy_install -U http://www.bitbucket.org/bbangert/dozer/get/b748d3e1cc87.gz

Add this to your middleware:

# Add this to your middleware.py, right before return app
    if asbool(config['debug']):
        from dozer import Logview
        app = Logview(app, config)

Add this to your development.ini

# Add to development.ini
logview.sqlalchemy = #faa
logview.pylons.templating = #bfb

(you can customize the colors here)

Next, modify your configuration ini as well as you like to configure what shows up in the log. Note that I have root set to INFO which will squelch a lot of messages. Change this to DEBUG to see more of what’s going on in each request.

# Logging configuration
[loggers]
keys = root, YOURPROJ

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = INFO
handlers = console

[logger_YOURPROJ]
level = DEBUG
handlers =
qualname = YOURPROJ.lib

[logger_sqlalchemy]
level = INFO
handlers =
qualname = sqlalchemy.engine

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

Add this file to /lib/

querytimer.py

from sqlalchemy.interfaces import ConnectionProxy
import time

import logging
log = logging.getLogger(__name__)

class TimerProxy(ConnectionProxy):
    def cursor_execute(self, execute, cursor, statement, parameters, context, executemany):
        now = time.time()
        try:
            return execute(cursor, statement, parameters, context)
        finally:
            total = time.time() - now
            log.debug("Query: %s" % statement)
            log.debug("Total Time: %f" % total)

Okay, one last thing, modify your SQLAlchemy engine in environment.py to this:

engine = engine_from_config(config, 'sqlalchemy.', proxy=TimerProxy())

and add an import at the top:

from YOURPROJ.lib.querytimer import TimerProxy

So that’s it! Restart paster, and load up a request in your web browser. There will now be a bar at the top that you can click on and see all the requests.

If you want to run TimerProxy on it’s own (that is without Pylons and Dozer, see zzzeek’s post on “Timing All Queries“.


3 Responses to

“Profiling SQL in Pylons with Dozer”

  1. martinknyc Says:

    Really cool ! thank’s for posting that

  2. Marius Gedminas Says:

    Your screenshot looks much prettier than what I get from upstream Dozer. Would you mind sharing the CSS with us?

  3. oz49erfan Says:

    I had an issue importing PIL. so I had to do this:

    http://groups.google.com/group/pylons-discuss/msg/0b085996384d4fe3

    I experienced an issue with installing PIL v.1.1.7 for py2.6 on mac osx
    10.6. The egg on pypi was mis-organized. It installed fine but I would
    receive errors when I tried to import PIL. It was easily rectified by
    adding a PIL directory below the installed egg directory. Maybe you are
    experiencing the same problem?
    Here is what PIL looked like straight from pypi:
    Under the following directory was only one directory, EGG-INFO, and then all
    of the *.py files that make up the PIL library.
    site-packages/PIL-1.1.7-py2.6-macosx-10.6-universal.egg/
    But for the importer to properly recognize the package it should look like
    the following with all the *.py files in the PIL directory:
    site-packages/PIL-1.1.7-py2.6-macosx-10.6-universal.egg/
    /EGG-INFO
    /PIL
    Therefore, just create a PIL directory and move all the .py files there.

Leave a Comment...




Comment: