From: Mike Bayer Date: Thu, 25 Mar 2010 21:02:50 +0000 (-0400) Subject: - The psycopg2 dialect will log NOTICE messages via the X-Git-Tag: rel_0_6beta3~12^2~10 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3f0bd7269bda6a9fa833c7d6ba2f393688ffd524;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - The psycopg2 dialect will log NOTICE messages via the "sqlalchemy.dialects.postgresql" logger name. [ticket:877] --- diff --git a/CHANGES b/CHANGES index 37e29a035f..db32156f94 100644 --- a/CHANGES +++ b/CHANGES @@ -66,6 +66,11 @@ CHANGES - Fixed bug introduced in 0.6beta2 where column labels would render inside of column expressions already assigned a label. [ticket:1747] + +- postgresql + - The psycopg2 dialect will log NOTICE messages via the + "sqlalchemy.dialects.postgresql" logger name. + [ticket:877] 0.6beta2 ======== diff --git a/doc/build/dbengine.rst b/doc/build/dbengine.rst index 644ede1ba9..246dc9f7fa 100644 --- a/doc/build/dbengine.rst +++ b/doc/build/dbengine.rst @@ -487,6 +487,7 @@ Python's standard `logging `_ This section assumes familiarity with the above linked logging module. All logging performed by SQLAlchemy exists underneath the ``sqlalchemy`` namespace, as used by ``logging.getLogger('sqlalchemy')``. When logging has been configured (i.e. such as via ``logging.basicConfig()``), the general namespace of SA loggers that can be turned on is as follows: * ``sqlalchemy.engine`` - controls SQL echoing. set to ``logging.INFO`` for SQL query output, ``logging.DEBUG`` for query + result set output. +* ``sqlalchemy.dialects`` - controls custom logging for SQL dialects. See the documentation of individual dialects for details. * ``sqlalchemy.pool`` - controls connection pool logging. set to ``logging.INFO`` or lower to log connection pool checkouts/checkins. * ``sqlalchemy.orm`` - controls logging of various ORM functions. set to ``logging.INFO`` for configurational logging as well as unit of work dumps, ``logging.DEBUG`` for extensive logging during query and flush() operations. Subcategories of ``sqlalchemy.orm`` include: * ``sqlalchemy.orm.attributes`` - logs certain instrumented attribute operations, such as triggered callables diff --git a/lib/sqlalchemy/dialects/postgresql/psycopg2.py b/lib/sqlalchemy/dialects/postgresql/psycopg2.py index c239a3ee06..5cbaaffd01 100644 --- a/lib/sqlalchemy/dialects/postgresql/psycopg2.py +++ b/lib/sqlalchemy/dialects/postgresql/psycopg2.py @@ -34,6 +34,15 @@ Transactions The psycopg2 dialect fully supports SAVEPOINT and two-phase commit operations. +NOTICE logging +--------------- + +The psycopg2 dialect will log Postgresql NOTICE messages via the +``sqlalchemy.dialects.postgresql`` logger:: + + import logging + logging.getLogger('sqlalchemy.dialects.postgresql').setLevel(logging.INFO) + Per-Statement Execution Options ------------------------------- @@ -46,8 +55,10 @@ The following per-statement execution options are respected: """ -import random, re +import random +import re import decimal +import logging from sqlalchemy import util from sqlalchemy import processors @@ -59,6 +70,10 @@ from sqlalchemy.dialects.postgresql.base import PGDialect, PGCompiler, \ PGIdentifierPreparer, PGExecutionContext, \ ENUM, ARRAY + +logger = logging.getLogger('sqlalchemy.dialects.postgresql') + + class _PGNumeric(sqltypes.Numeric): def bind_processor(self, dialect): return None @@ -130,11 +145,22 @@ class PGExecutionContext_psycopg2(PGExecutionContext): return self._connection.connection.cursor() def get_result_proxy(self): + if logger.isEnabledFor(logging.INFO): + self._log_notices(self.cursor) + if self.__is_server_side: return base.BufferedRowResultProxy(self) else: return base.ResultProxy(self) + def _log_notices(self, cursor): + for notice in cursor.connection.notices: + # NOTICE messages have a + # newline character at the end + logger.info(notice.rstrip()) + + cursor.connection.notices[:] = [] + class PGCompiler_psycopg2(PGCompiler): def visit_mod(self, binary, **kw): @@ -190,7 +216,7 @@ class PGDialect_psycopg2(PGDialect): return connect else: return base_on_connect - + def create_connect_args(self, url): opts = url.translate_connect_args(username='user') if 'port' in opts: diff --git a/test/dialect/test_postgresql.py b/test/dialect/test_postgresql.py index 472b12e508..bc3fbc6145 100644 --- a/test/dialect/test_postgresql.py +++ b/test/dialect/test_postgresql.py @@ -13,6 +13,7 @@ from sqlalchemy.test.util import round_decimal from sqlalchemy.sql import table, column from sqlalchemy.test.testing import eq_ from test.engine._base import TablesTest +import logging class SequenceTest(TestBase, AssertsCompiledSQL): def test_basic(self): @@ -395,9 +396,6 @@ class EnumTest(TestBase, AssertsExecutionResults, AssertsCompiledSQL): finally: metadata.drop_all() - - - class InsertTest(TestBase, AssertsExecutionResults): __only_on__ = 'postgresql' @@ -964,7 +962,6 @@ class DomainReflectionTest(TestBase, AssertsExecutionResults): finally: postgresql.PGDialect.ischema_names = ischema_names - class MiscTest(TestBase, AssertsExecutionResults, AssertsCompiledSQL): __only_on__ = 'postgresql' @@ -1001,6 +998,29 @@ class MiscTest(TestBase, AssertsExecutionResults, AssertsCompiledSQL): ]: eq_(testing.db.dialect._get_server_version_info(MockConn(string)), version) + + @testing.only_on('postgresql+psycopg2', 'psycopg2-specific feature') + def test_notice_logging(self): + log = logging.getLogger('sqlalchemy.dialects.postgresql') + buf = logging.handlers.BufferingHandler(100) + lev = log.level + log.addHandler(buf) + log.setLevel(logging.INFO) + try: + conn = testing.db.connect() + trans = conn.begin() + try: + conn.execute("create table foo (id serial primary key)") + finally: + trans.rollback() + finally: + log.removeHandler(buf) + log.setLevel(lev) + + msgs = " ".join(b.msg for b in buf.buffer) + assert "will create implicit sequence" in msgs + assert "will create implicit index" in msgs + def test_pg_weirdchar_reflection(self): meta1 = MetaData(testing.db)