[Sqlalchemy-commits] sqlalchemy: - Added "logging_name" argument to create_engine(), ...
Brought to you by:
zzzeek
From: <co...@sq...> - 2010-03-13 18:56:23
|
details: http://hg.sqlalchemy.org/sqlalchemy/sqlalchemy/rev/883d4f454391 changeset: 6221:883d4f454391 user: zzzeek date: Sat Mar 13 13:53:31 2010 -0500 description: - 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] diffstat: CHANGES | 7 ++++++ doc/build/dbengine.rst | 2 + lib/sqlalchemy/engine/__init__.py | 12 ++++++++++- lib/sqlalchemy/engine/base.py | 6 +++- lib/sqlalchemy/engine/strategies.py | 3 +- lib/sqlalchemy/log.py | 26 +++++++++++++++--------- lib/sqlalchemy/pool.py | 16 +++++++++++--- test/engine/test_execute.py | 39 ++++++++++++++++++++++++++++++++++++- 8 files changed, 92 insertions(+), 19 deletions(-) diffs (259 lines): diff -r e69c5e84db6a -r 883d4f454391 CHANGES --- a/CHANGES Sat Mar 13 12:28:50 2010 -0500 +++ b/CHANGES Sat Mar 13 13:53:31 2010 -0500 @@ -248,7 +248,14 @@ - 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 diff -r e69c5e84db6a -r 883d4f454391 doc/build/dbengine.rst --- a/doc/build/dbengine.rst Sat Mar 13 12:28:50 2010 -0500 +++ b/doc/build/dbengine.rst Sat Mar 13 13:53:31 2010 -0500 @@ -460,3 +460,5 @@ 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`. diff -r e69c5e84db6a -r 883d4f454391 lib/sqlalchemy/engine/__init__.py --- a/lib/sqlalchemy/engine/__init__.py Sat Mar 13 12:28:50 2010 -0500 +++ b/lib/sqlalchemy/engine/__init__.py Sat Mar 13 13:53:31 2010 -0500 @@ -118,7 +118,7 @@ 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). @@ -144,6 +144,11 @@ 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 @@ -153,6 +158,11 @@ 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 diff -r e69c5e84db6a -r 883d4f454391 lib/sqlalchemy/engine/base.py --- a/lib/sqlalchemy/engine/base.py Sat Mar 13 12:28:50 2010 -0500 +++ b/lib/sqlalchemy/engine/base.py Sat Mar 13 13:53:31 2010 -0500 @@ -1387,17 +1387,19 @@ 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) diff -r e69c5e84db6a -r 883d4f454391 lib/sqlalchemy/engine/strategies.py --- a/lib/sqlalchemy/engine/strategies.py Sat Mar 13 12:28:50 2010 -0500 +++ b/lib/sqlalchemy/engine/strategies.py Sat Mar 13 13:53:31 2010 -0500 @@ -90,7 +90,8 @@ # 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'} diff -r e69c5e84db6a -r 883d4f454391 lib/sqlalchemy/log.py --- a/lib/sqlalchemy/log.py Sat Mar 13 12:28:50 2010 -0500 +++ b/lib/sqlalchemy/log.py Sat Mar 13 13:53:31 2010 -0500 @@ -28,7 +28,7 @@ import logging import sys - +from sqlalchemy import util rootlogger = logging.getLogger('sqlalchemy') if rootlogger.level == logging.NOTSET: @@ -58,22 +58,28 @@ 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) diff -r e69c5e84db6a -r 883d4f454391 lib/sqlalchemy/pool.py --- a/lib/sqlalchemy/pool.py Sat Mar 13 12:28:50 2010 -0500 +++ b/lib/sqlalchemy/pool.py Sat Mar 13 13:53:31 2010 -0500 @@ -56,12 +56,13 @@ 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. @@ -75,6 +76,11 @@ 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 @@ -102,6 +108,8 @@ pool. """ + if logging_name: + self.logging_name = logging_name self.logger = log.instance_logger(self, echoflag=echo) self._threadconns = threading.local() self._creator = creator @@ -477,9 +485,9 @@ """ - 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 diff -r e69c5e84db6a -r 883d4f454391 test/engine/test_execute.py --- a/test/engine/test_execute.py Sat Mar 13 12:28:50 2010 -0500 +++ b/test/engine/test_execute.py Sat Mar 13 13:53:31 2010 -0500 @@ -6,7 +6,7 @@ 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): @@ -111,6 +111,43 @@ (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') |