- Python unicode objects as binds result in the Unicode type,
not string, thus eliminating a certain class of unicode errors
on drivers that don't support unicode binds.
+
+ - Added "logging_name" argument to create_engine(), Pool() constructor
+ as well as "pool_logging_name" argument to create_engine() which
+ filters down to that of Pool. Issues the given string name
+ within the "name" field of logging messages instead of the default
+ hex identifier string. [ticket:1555]
+
- metadata
- Added the ability to strip schema information when using
"tometadata" by passing "schema=None" as an argument. If schema
By default, the log level is set to ``logging.ERROR`` within the entire ``sqlalchemy`` namespace so that no log operations occur, even within an application that has logging enabled otherwise.
The ``echo`` flags present as keyword arguments to :func:`~sqlalchemy.create_engine` and others as well as the ``echo`` property on :class:`~sqlalchemy.engine.base.Engine`, when set to ``True``, will first attempt to ensure that logging is enabled. Unfortunately, the ``logging`` module provides no way of determining if output has already been configured (note we are referring to if a logging configuration has been set up, not just that the logging level is set). For this reason, any ``echo=True`` flags will result in a call to ``logging.basicConfig()`` using sys.stdout as the destination. It also sets up a default format using the level name, timestamp, and logger name. Note that this configuration has the affect of being configured **in addition** to any existing logger configurations. Therefore, **when using Python logging, ensure all echo flags are set to False at all times**, to avoid getting duplicate log lines.
+
+The logger name of instance such as an :class:`~sqlalchemy.engine.base.Engine` or :class:`~sqlalchemy.pool.Pool` defaults to using a truncated hex identifier string. To set this to a specific name, use the "logging_name" and "pool_logging_name" keyword arguments with :func:`sqlalchemy.create_engine`.
Pool. Specific dialects also accept keyword arguments that
are unique to that dialect. Here, we describe the parameters
that are common to most ``create_engine()`` usage.
-
+
:param assert_unicode: Deprecated. A warning is raised in all cases when a non-Unicode
object is passed when SQLAlchemy would coerce into an encoding
(note: but **not** when the DBAPI handles unicode objects natively).
connections. Usage of this function causes connection
parameters specified in the URL argument to be bypassed.
+ :param logging_name: String identifier which will be used within
+ the "name" field of logging records generated within the
+ "sqlalchemy.engine" logger. Defaults to a hexstring of the
+ object's id.
+
:param echo=False: if True, the Engine will log all statements
as well as a repr() of their parameter lists to the engines
logger, which defaults to sys.stdout. The ``echo`` attribute of
controls a Python logger; see :ref:`dbengine_logging` for
information on how to configure logging directly.
+ :param pool_logging_name: String identifier which will be used within
+ the "name" field of logging records generated within the
+ "sqlalchemy.pool" logger. Defaults to a hexstring of the object's
+ id.
+
:param echo_pool=False: if True, the connection pool will log
all checkouts/checkins to the logging stream, which defaults to
sys.stdout. This flag ultimately controls a Python logger; see
self.connection._commit_twophase_impl(self.xid, self._is_prepared)
-class Engine(Connectable):
+class Engine(Connectable, log.Identified):
"""
Connects a :class:`~sqlalchemy.pool.Pool` and :class:`~sqlalchemy.engine.base.Dialect`
together to provide a source of database connectivity and behavior.
"""
- def __init__(self, pool, dialect, url, echo=None, proxy=None):
+ def __init__(self, pool, dialect, url, logging_name=None, echo=None, proxy=None):
self.pool = pool
self.url = url
self.dialect = dialect
+ if logging_name:
+ self.logging_name = logging_name
self.echo = echo
self.engine = self
self.logger = log.instance_logger(self, echoflag=echo)
# consume pool arguments from kwargs, translating a few of
# the arguments
- translate = {'echo': 'echo_pool',
+ translate = {'logging_name': 'pool_logging_name',
+ 'echo': 'echo_pool',
'timeout': 'pool_timeout',
'recycle': 'pool_recycle',
'use_threadlocal':'pool_threadlocal'}
import logging
import sys
-
+from sqlalchemy import util
rootlogger = logging.getLogger('sqlalchemy')
if rootlogger.level == logging.NOTSET:
cls.logger = logger
_logged_classes.add(cls)
+
+class Identified(object):
+ @util.memoized_property
+ def logging_name(self):
+ # limit the number of loggers by chopping off the hex(id).
+ # some novice users unfortunately create an unlimited number
+ # of Engines in their applications which would otherwise
+ # cause the app to run out of memory.
+ return "0x...%s" % hex(id(self))[-4:]
+
+
def instance_logger(instance, echoflag=None):
- """create a logger for an instance.
+ """create a logger for an instance that implements :class:`Identified`.
Warning: this is an expensive call which also results in a permanent
increase in memory overhead for each call. Use only for
low-volume, long-time-spanning objects.
"""
-
- # limit the number of loggers by chopping off the hex(id).
- # many novice users unfortunately create an unlimited number
- # of Engines in their applications which would otherwise
- # cause the app to run out of memory.
- name = "%s.%s.0x...%s" % (instance.__class__.__module__,
- instance.__class__.__name__,
- hex(id(instance))[-4:])
+
+ name = "%s.%s.%s" % (instance.__class__.__module__,
+ instance.__class__.__name__, instance.logging_name)
if echoflag is not None:
l = logging.getLogger(name)
manager.close()
proxies.clear()
-class Pool(object):
+class Pool(log.Identified):
"""Abstract base class for connection pools."""
def __init__(self,
creator, recycle=-1, echo=None,
use_threadlocal=False,
+ logging_name=None,
reset_on_return=True, listeners=None):
"""
Construct a Pool.
timeout is surpassed the connection will be closed and
replaced with a newly opened connection. Defaults to -1.
+ :param logging_name: String identifier which will be used within
+ the "name" field of logging records generated within the
+ "sqlalchemy.pool" logger. Defaults to a hexstring of the object's
+ id.
+
:param echo: If True, connections being pulled and retrieved
from the pool will be logged to the standard output, as well
as pool sizing information. Echoing can also be achieved by
pool.
"""
+ if logging_name:
+ self.logging_name = logging_name
self.logger = log.instance_logger(self, echoflag=echo)
self._threadconns = threading.local()
self._creator = creator
"""
- def __init__(self, creator, pool_size=5, **params):
- params['use_threadlocal'] = True
- Pool.__init__(self, creator, **params)
+ def __init__(self, creator, pool_size=5, **kw):
+ kw['use_threadlocal'] = True
+ Pool.__init__(self, creator, **kw)
self._conn = threading.local()
self._all_conns = set()
self.size = pool_size
from sqlalchemy.test.schema import Column
import sqlalchemy as tsa
from sqlalchemy.test import TestBase, testing, engines
-
+import logging
users, metadata = None, None
class ExecuteTest(TestBase):
(1, None)
])
+class LogTest(TestBase):
+ def _test_logger(self, eng, eng_name, pool_name):
+ buf = logging.handlers.BufferingHandler(100)
+ logs = [
+ logging.getLogger('sqlalchemy.engine'),
+ logging.getLogger('sqlalchemy.pool')
+ ]
+ for log in logs:
+ log.addHandler(buf)
+
+ eq_(eng.logging_name, eng_name)
+ eq_(eng.pool.logging_name, pool_name)
+ eng.execute("select 1")
+ for log in logs:
+ log.removeHandler(buf)
+
+ names = set([b.name for b in buf.buffer])
+ assert 'sqlalchemy.engine.base.Engine.%s' % (eng_name,) in names
+ assert 'sqlalchemy.pool.%s.%s' % (eng.pool.__class__.__name__, pool_name) in names
+
+ def test_named_logger(self):
+ options = {'echo':'debug', 'echo_pool':'debug',
+ 'logging_name':'myenginename',
+ 'pool_logging_name':'mypoolname'
+ }
+ eng = engines.testing_engine(options=options)
+ self._test_logger(eng, "myenginename", "mypoolname")
+
+ def test_unnamed_logger(self):
+ eng = engines.testing_engine(options={'echo':'debug', 'echo_pool':'debug'})
+ self._test_logger(
+ eng,
+ "0x...%s" % hex(id(eng))[-4:],
+ "0x...%s" % hex(id(eng.pool))[-4:],
+ )
+
+
class ProxyConnectionTest(TestBase):
@testing.fails_on('firebird', 'Data type unknown')