# log.py - adapt python logging module to SQLAlchemy # Copyright (C) 2006, 2007, 2008 Michael Bayer mike_mp@zzzcomputing.com # # This module is part of SQLAlchemy and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php """Logging control and utilities. Provides a few functions used by instances to turn on/off their logging, including support for the usual "echo" parameter. Control of logging for SA can be performed from the regular python logging module. The regular dotted module namespace is used, starting at 'sqlalchemy'. For class-level logging, the class name is appended, and for instance-level logging, the hex id of the instance is appended. The "echo" keyword parameter which is available on some SA objects corresponds to an instance-level logger for that instance. E.g.:: engine.echo = True is equivalent to:: import logging logger = logging.getLogger('sqlalchemy.engine.Engine.%s' % hex(id(engine))) logger.setLevel(logging.DEBUG) """ import logging import sys import weakref rootlogger = logging.getLogger('sqlalchemy') if rootlogger.level == logging.NOTSET: rootlogger.setLevel(logging.WARN) default_enabled = False def default_logging(name): global default_enabled if logging.getLogger(name).getEffectiveLevel() < logging.WARN: default_enabled = True if not default_enabled: default_enabled = True handler = logging.StreamHandler(sys.stdout) handler.setFormatter(logging.Formatter( '%(asctime)s %(levelname)s %(name)s %(message)s')) rootlogger.addHandler(handler) def _get_instance_name(instance): # since getLogger() does not have any way of removing logger objects from # memory, instance logging displays the instance id as a modulus of 16 to # prevent endless memory growth also speeds performance as logger # initialization is apparently slow return "%s.%s.0x..%s" % (instance.__class__.__module__, instance.__class__.__name__, hex(id(instance))[-2:]) def class_logger(cls): return logging.getLogger(cls.__module__ + "." + cls.__name__) def is_debug_enabled(logger): return logger.isEnabledFor(logging.DEBUG) def is_info_enabled(logger): return logger.isEnabledFor(logging.INFO) def instance_logger(instance, echoflag=None): if echoflag is not None: name = _get_instance_name(instance) l = logging.getLogger(name) if echoflag == 'debug': default_logging(name) l.setLevel(logging.DEBUG) elif echoflag is True: default_logging(name) l.setLevel(logging.INFO) elif echoflag is False: l.setLevel(logging.NOTSET) def cleanup(ref): # the id() of an instance may be reused again after the # previous instance has been gc'ed. set a cleanup handler # to remove logging config logging.getLogger(name).setLevel(logging.NOTSET) instance._logging_cleanup = weakref.ref(instance, cleanup) else: l = logging.getLogger(_get_instance_name(instance)) instance._should_log_debug = l.isEnabledFor(logging.DEBUG) instance._should_log_info = l.isEnabledFor(logging.INFO) return l class echo_property(object): __doc__ = """\ When ``True``, enable log output for this element. This has the effect of setting the Python logging level for the namespace of this element's class and object reference. A value of boolean ``True`` indicates that the loglevel ``logging.INFO`` will be set for the logger, whereas the string value ``debug`` will set the loglevel to ``logging.DEBUG``. """ def __get__(self, instance, owner): if instance is None: return self else: return instance._should_log_debug and 'debug' or (instance._should_log_info and True or False) def __set__(self, instance, value): instance_logger(instance, echoflag=value)