Logging connection/cursor implementation inspired by #90.

This commit is contained in:
Federico Di Gregorio 2006-01-20 04:07:23 +00:00
parent 202c002c5c
commit 81d86e9244
3 changed files with 120 additions and 0 deletions

View File

@ -1,6 +1,11 @@
Compiling and installing psycopg Compiling and installing psycopg
******************************** ********************************
** Important note: if you plan to use psyopg2 in a multithreaed application
make sure that your libpq has been compiled with the --with-thread-safety
option. psycopg2 will work correctly even with a non-thread-safe libpq but
libpq will leak memory.
While psycopg 1.x used autoconf for its build process psycopg 2 switched to While psycopg 1.x used autoconf for its build process psycopg 2 switched to
the more pythoning setup.py. Currently both psycopg's author and distutils the more pythoning setup.py. Currently both psycopg's author and distutils
have some limitations so the file setup.cfg is almost unused and most build have some limitations so the file setup.cfg is almost unused and most build

14
README
View File

@ -23,3 +23,17 @@ the DBAPI-2.0 is available in the files located in the doc/ direcory.
psycopg is free software ("free as in freedom" but I like beer too.) psycopg is free software ("free as in freedom" but I like beer too.)
Licensing information is available in the LICENSE file. Licensing information is available in the LICENSE file.
Contributors
------------
A short list of contributors to psycopg2 follows (if you feel you belong
to this list and you can't find yourself here just drop me a mail):
* Kudos to piro for all the documentation work.
* Peter Fein contributed a logging connection/cursor class that even if it
was not used directly heavily influenced the implementation currently in
psycopg2.extras.

View File

@ -17,11 +17,20 @@ and classes untill a better place in the distribution is found.
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details. # for more details.
import os
import time
try:
import logging
except:
logging = None
from psycopg2.extensions import cursor as _cursor from psycopg2.extensions import cursor as _cursor
from psycopg2.extensions import connection as _connection from psycopg2.extensions import connection as _connection
from psycopg2.extensions import register_adapter as _RA from psycopg2.extensions import register_adapter as _RA
from psycopg2.extensions import adapt as _A from psycopg2.extensions import adapt as _A
class DictConnection(_connection): class DictConnection(_connection):
"""A connection that uses DictCursor automatically.""" """A connection that uses DictCursor automatically."""
def cursor(self): def cursor(self):
@ -118,3 +127,95 @@ class SQL_IN(object):
__str__ = getquoted __str__ = getquoted
_RA(tuple, SQL_IN) _RA(tuple, SQL_IN)
class LoggingConnection(_connection):
"""A connection that logs all queries to a file or logger object."""
def initialize(self, logobj):
"""Initialize the connection to log to `logobj`.
The `logobj` parameter can be an open file object or a Logger instance
from the standard logging module.
"""
self._logobj = logobj
if logging and isinstance(logobj, logging.Logger):
self.log = self._logtologger
else:
self.log = self._logtofile
def filter(self, msg, curs):
"""Filter the query before logging it.
This is the method to overwrite to filter unwanted queries out of the
log or to add some extra data to the output. The default implementation
just does nothing.
"""
return msg
def _logtofile(self, msg, curs):
msg = self.filter(msg, curs)
if msg: self._logobj.write(msg + os.linesep)
def _logtologger(self, msg, curs):
msg = self.filter(msg, curs)
if msg: self._logobj.debug(msg)
def _check(self):
if not hasattr(self, '_logobj'):
raise self.ProgrammingError(
"LoggingConnection object has not been initialize()d")
def cursor(self):
self._check()
return _connection.cursor(self, cursor_factory=LoggingCursor)
class LoggingCursor(_cursor):
"""A cursor that logs queries using its connection logging facilities."""
def execute(self, query, vars=None, async=0):
try:
return _cursor.execute(self, query, vars, async)
finally:
self.connection.log(self.query, self)
def callproc(self, procname, vars=None):
try:
return _cursor.callproc(self, procname, vars)
finally:
self.connection.log(self.query, self)
class MinTimeLoggingConnection(LoggingConnection):
"""A connection that logs queries based on execution time.
This is just an example of how to sub-class LoggingConnection to provide
some extra filtering for the logged queries. Both the `.inizialize()` and
`.filter()` methods are overwritten to make sure that only queries
executing for more than `mintime` ms are logged.
Note that this connection uses the specialized cursor MinTimeLoggingCursor.
"""
def initialize(self, logobj, mintime=0):
LoggingConnection.initialize(self, logobj)
self._mintime = mintime
def filter(self, msg, curs):
t = (time.time() - curs.timestamp) * 1000
if t > self._mintime:
return msg + os.linesep + " (execution time: %d ms)" % t
def cursor(self):
self._check()
return _connection.cursor(self, cursor_factory=MinTimeLoggingCursor)
class MinTimeLoggingCursor(LoggingCursor):
"""The cursor sub-class companion to MinTimeLoggingConnection."""
def execute(self, query, vars=None, async=0):
self.timestamp = time.time()
return LoggingCursor.execute(self, query, vars, async)
def callproc(self, procname, vars=None):
self.timestamp = time.time()
return LoggingCursor.execute(self, procname, var)