]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Added "logging_name" argument to create_engine(), Pool() constructor
authorMike Bayer <mike_mp@zzzcomputing.com>
Sat, 13 Mar 2010 18:53:31 +0000 (13:53 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 13 Mar 2010 18:53:31 +0000 (13:53 -0500)
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]

CHANGES
doc/build/dbengine.rst
lib/sqlalchemy/engine/__init__.py
lib/sqlalchemy/engine/base.py
lib/sqlalchemy/engine/strategies.py
lib/sqlalchemy/log.py
lib/sqlalchemy/pool.py
test/engine/test_execute.py

diff --git a/CHANGES b/CHANGES
index 61aa45b1f96cbffa0c493aa026b8c756c128f4e4..35592b50f142fbcaad2c7553dae860c661e2e2dd 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -248,7 +248,14 @@ CHANGES
   - 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
index 60913a97e924156d4df88e0dc4bc1238e8ff78a9..dac982dcaa0fe68d4140691a379aa3ca3e19fb0d 100644 (file)
@@ -460,3 +460,5 @@ For example, to log SQL queries as well as unit of work debugging:
 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`.
index 8911485cbbcf65b021cf06da9044577160045fdd..9a53545dfa171b212815e269bf025d4c73bdeabb 100644 (file)
@@ -118,7 +118,7 @@ def create_engine(*args, **kwargs):
     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 @@ def create_engine(*args, **kwargs):
         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 @@ def create_engine(*args, **kwargs):
         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
index ea62829543f1289875da40ef2142c324607a8676..fa0059130fbbb9c525b59ccf3a90a3cf144f6c56 100644 (file)
@@ -1387,17 +1387,19 @@ class TwoPhaseTransaction(Transaction):
         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)
index 7a8856ba85bb10547b7f8a017809c0add32b44a3..7c434105c30e5928add59db94312071cc86a48ad 100644 (file)
@@ -90,7 +90,8 @@ class DefaultEngineStrategy(EngineStrategy):
 
             # 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'}
index 3f861d60a9fe5bf8040b685bbe66a48a74ac8bbd..49c779fed8f7d2fc15b9bc7ec5b150209b5c4bbb 100644 (file)
@@ -28,7 +28,7 @@ is equivalent to::
 
 import logging
 import sys
-
+from sqlalchemy import util
 
 rootlogger = logging.getLogger('sqlalchemy')
 if rootlogger.level == logging.NOTSET:
@@ -58,22 +58,28 @@ def class_logger(cls, enable=False):
     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)
index 8d6ae3292a267e01f47ce6331a7f70281dbd550d..3be63ced39d24d7d48a481d3da57f5b9ee0d1601 100644 (file)
@@ -56,12 +56,13 @@ def clear_managers():
         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 @@ class Pool(object):
           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 @@ class Pool(object):
           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 @@ class SingletonThreadPool(Pool):
       
     """
 
-    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
index 39d4144c8f02d8969624fec97366a1127798107c..10e0ab89ebf66a2c095ea3bbe3750378e143cc5f 100644 (file)
@@ -6,7 +6,7 @@ from sqlalchemy.test.schema import Table
 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 @@ 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')