| # sqlalchemy/log.py |
| # Copyright (C) 2006-2011 the SQLAlchemy authors and contributors <see AUTHORS file> |
| # Includes alterations by Vinay Sajip vinay_sajip@yahoo.co.uk |
| # |
| # 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. |
| |
| 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. |
| |
| The "echo" keyword parameter, available on SQLA :class:`.Engine` |
| and :class:`.Pool` objects, corresponds to a logger specific to that |
| instance only. |
| |
| """ |
| |
| import logging |
| import sys |
| from sqlalchemy import util |
| |
| # set initial level to WARN. This so that |
| # log statements don't occur in the absense of explicit |
| # logging being enabled for 'sqlalchemy'. |
| rootlogger = logging.getLogger('sqlalchemy') |
| if rootlogger.level == logging.NOTSET: |
| rootlogger.setLevel(logging.WARN) |
| |
| def _add_default_handler(logger): |
| handler = logging.StreamHandler(sys.stdout) |
| handler.setFormatter(logging.Formatter( |
| '%(asctime)s %(levelname)s %(name)s %(message)s')) |
| logger.addHandler(handler) |
| |
| _logged_classes = set() |
| def class_logger(cls, enable=False): |
| logger = logging.getLogger(cls.__module__ + "." + cls.__name__) |
| if enable == 'debug': |
| logger.setLevel(logging.DEBUG) |
| elif enable == 'info': |
| logger.setLevel(logging.INFO) |
| cls._should_log_debug = lambda self: logger.isEnabledFor(logging.DEBUG) |
| cls._should_log_info = lambda self: logger.isEnabledFor(logging.INFO) |
| cls.logger = logger |
| _logged_classes.add(cls) |
| |
| |
| class Identified(object): |
| logging_name = None |
| |
| def _should_log_debug(self): |
| return self.logger.isEnabledFor(logging.DEBUG) |
| |
| def _should_log_info(self): |
| return self.logger.isEnabledFor(logging.INFO) |
| |
| class InstanceLogger(object): |
| """A logger adapter (wrapper) for :class:`.Identified` subclasses. |
| |
| This allows multiple instances (e.g. Engine or Pool instances) |
| to share a logger, but have its verbosity controlled on a |
| per-instance basis. |
| |
| The basic functionality is to return a logging level |
| which is based on an instance's echo setting. |
| |
| Default implementation is: |
| |
| 'debug' -> logging.DEBUG |
| True -> logging.INFO |
| False -> Effective level of underlying logger |
| (logging.WARNING by default) |
| None -> same as False |
| """ |
| |
| # Map echo settings to logger levels |
| _echo_map = { |
| None: logging.NOTSET, |
| False: logging.NOTSET, |
| True: logging.INFO, |
| 'debug': logging.DEBUG, |
| } |
| |
| def __init__(self, echo, name): |
| self.echo = echo |
| self.logger = logging.getLogger(name) |
| |
| # if echo flag is enabled and no handlers, |
| # add a handler to the list |
| if self._echo_map[echo] <= logging.INFO \ |
| and not self.logger.handlers: |
| _add_default_handler(self.logger) |
| |
| # |
| # Boilerplate convenience methods |
| # |
| def debug(self, msg, *args, **kwargs): |
| """Delegate a debug call to the underlying logger.""" |
| |
| self.log(logging.DEBUG, msg, *args, **kwargs) |
| |
| def info(self, msg, *args, **kwargs): |
| """Delegate an info call to the underlying logger.""" |
| |
| self.log(logging.INFO, msg, *args, **kwargs) |
| |
| def warning(self, msg, *args, **kwargs): |
| """Delegate a warning call to the underlying logger.""" |
| |
| self.log(logging.WARNING, msg, *args, **kwargs) |
| |
| warn = warning |
| |
| def error(self, msg, *args, **kwargs): |
| """ |
| Delegate an error call to the underlying logger. |
| """ |
| self.log(logging.ERROR, msg, *args, **kwargs) |
| |
| def exception(self, msg, *args, **kwargs): |
| """Delegate an exception call to the underlying logger.""" |
| |
| kwargs["exc_info"] = 1 |
| self.log(logging.ERROR, msg, *args, **kwargs) |
| |
| def critical(self, msg, *args, **kwargs): |
| """Delegate a critical call to the underlying logger.""" |
| |
| self.log(logging.CRITICAL, msg, *args, **kwargs) |
| |
| def log(self, level, msg, *args, **kwargs): |
| """Delegate a log call to the underlying logger. |
| |
| The level here is determined by the echo |
| flag as well as that of the underlying logger, and |
| logger._log() is called directly. |
| |
| """ |
| |
| # inline the logic from isEnabledFor(), |
| # getEffectiveLevel(), to avoid overhead. |
| |
| if self.logger.manager.disable >= level: |
| return |
| |
| selected_level = self._echo_map[self.echo] |
| if selected_level == logging.NOTSET: |
| selected_level = self.logger.getEffectiveLevel() |
| |
| if level >= selected_level: |
| self.logger._log(level, msg, args, **kwargs) |
| |
| def isEnabledFor(self, level): |
| """Is this logger enabled for level 'level'?""" |
| |
| if self.logger.manager.disable >= level: |
| return False |
| return level >= self.getEffectiveLevel() |
| |
| def getEffectiveLevel(self): |
| """What's the effective level for this logger?""" |
| |
| level = self._echo_map[self.echo] |
| if level == logging.NOTSET: |
| level = self.logger.getEffectiveLevel() |
| return level |
| |
| def instance_logger(instance, echoflag=None): |
| """create a logger for an instance that implements :class:`.Identified`.""" |
| |
| if instance.logging_name: |
| name = "%s.%s.%s" % (instance.__class__.__module__, |
| instance.__class__.__name__, instance.logging_name) |
| else: |
| name = "%s.%s" % (instance.__class__.__module__, |
| instance.__class__.__name__) |
| |
| instance._echo = echoflag |
| |
| if echoflag in (False, None): |
| # if no echo setting or False, return a Logger directly, |
| # avoiding overhead of filtering |
| logger = logging.getLogger(name) |
| else: |
| # if a specified echo flag, return an EchoLogger, |
| # which checks the flag, overrides normal log |
| # levels by calling logger._log() |
| logger = InstanceLogger(echoflag, name) |
| |
| instance.logger = logger |
| |
| 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._echo |
| |
| def __set__(self, instance, value): |
| instance_logger(instance, echoflag=value) |