Logo Search packages:      
Sourcecode: ubuntuone-client version File versions

logger.py

# ubuntuone.syncdaemon.logger - logging utilities
#
# Author: Guillermo Gonzalez <guillermo.gonzalez@canonical.com>
#
# Copyright 2009 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.
""" SyncDaemon logging utilities and config. """

from itertools import imap
import logging
import sys
import os
import re
import xdg.BaseDirectory

from logging.handlers import TimedRotatingFileHandler

# define the location of the log folder
home = xdg.BaseDirectory.xdg_cache_home
LOGFOLDER = os.path.join(home, 'ubuntuone/log')
if not os.path.exists(LOGFOLDER):
    os.makedirs(LOGFOLDER)

LOGFILENAME = os.path.join(LOGFOLDER, 'syncdaemon.log')
EXLOGFILENAME = os.path.join(LOGFOLDER, 'syncdaemon-exceptions.log')

LOGBACKUP = 5 # the number of log files to keep around

basic_formatter = logging.Formatter(fmt="%(asctime)s - %(name)s - " \
                                    "%(levelname)s - %(message)s")
debug_formatter = logging.Formatter(fmt="%(asctime)s %(name)s %(module)s " \
                                    "%(lineno)s %(funcName)s %(message)s")
# extra levels
TRACE = 5 # be more verbose than logging.DEBUG(10)

# a constant to change the default DEBUG level value
_DEBUG_LOG_LEVEL = logging.DEBUG

# check if we are in TRACE mode
if 'trace' in os.environ.get('DEBUG', ''):
    _DEBUG_LOG_LEVEL = TRACE


00055 class DayRotatingFileHandler(TimedRotatingFileHandler):
    """ A TimedRotatingFileHandler configured for Day rotation but that uses
    the suffix and extMatch of Hourly rotation, in order to allow seconds based
    rotation on each startup.
    """

00061     def __init__(self, *args, **kwargs):
        """ create the instance and override the suffix and extMatch. """
        kwargs['when'] = 'D'
        kwargs['backupCount'] = LOGBACKUP
        # check if we are in 2.5, only for PQM
        if sys.version_info[:2] >= (2, 6):
            kwargs['delay'] = 1
        TimedRotatingFileHandler.__init__(self, *args, **kwargs)
        # override suffix
        self.suffix = "%Y-%m-%d_%H-%M-%S"
        self.extMatch = re.compile(r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}$")


# root logger
root_logger = logging.getLogger("ubuntuone.SyncDaemon")
root_logger.propagate = False
root_logger.setLevel(_DEBUG_LOG_LEVEL)
root_handler = DayRotatingFileHandler(filename=LOGFILENAME)
root_handler.addFilter(logging.Filter("ubuntuone.SyncDaemon"))
root_handler.setFormatter(basic_formatter)
root_handler.setLevel(_DEBUG_LOG_LEVEL)
root_logger.addHandler(root_handler)
# exception logs
exception_handler = DayRotatingFileHandler(filename=EXLOGFILENAME)
exception_handler.setFormatter(basic_formatter)
exception_handler.setLevel(logging.ERROR)
# add the exception handler to the root logger
logging.getLogger('').addHandler(exception_handler)
root_logger.addHandler(exception_handler)

# hook twisted.python.log with standard logging
from twisted.python import log
observer = log.PythonLoggingObserver('twisted')
observer.start()
# configure the logger to only show errors
twisted_logger = logging.getLogger('twisted')
twisted_logger.propagate = False
twisted_logger.setLevel(logging.ERROR)
twisted_handler = DayRotatingFileHandler(filename=LOGFILENAME)
twisted_handler.addFilter(logging.Filter("twisted"))
twisted_handler.setFormatter(basic_formatter)
twisted_handler.setLevel(logging.ERROR)
twisted_logger.addHandler(twisted_handler)
twisted_logger.addHandler(exception_handler)


00107 class mklog(object):
    """
    Create a logger that keeps track of the method where it's being
    called from, in order to make more informative messages.
    """
    def __init__(self, _logger, _method, _share, _uid, *args, **kwargs):
        # args are _-prepended to lower the chances of them
        # conflicting with kwargs

        all_args = []
        for arg in args:
            all_args.append(
                repr(arg).decode('ascii', 'replace').encode('ascii', 'replace')
                )
        for k, v in kwargs.items():
            v = repr(v).decode('ascii', 'replace').encode('ascii', 'replace')
            all_args.append("%s=%r" % (k, v))
        args = ", ".join(all_args)

        self.desc = "%-28s share:%-40r node:%-40r %s(%s) " \
            % (_method, _share, _uid, _method, args)
        self.logger = _logger
00129     def _log(self, logger_func, *args):
        """
        Generalized form of the different logging functions.
        """
        logger_func(self.desc + " ".join(imap(str, args)))
00134     def debug(self, *args):
        """Log at level DEBUG"""
        self._log(self.logger.debug, *args)
00137     def info(self, *args):
        """Log at level INFO"""
        self._log(self.logger.info, *args)
00140     def warn(self, *args):
        """Log at level WARN"""
        self._log(self.logger.warn, *args)
00143     def error(self, *args):
        """Log at level ERROR"""
        self._log(self.logger.error, *args)
00146     def exception(self, *args):
        """Log an exception"""
        self._log(self.logger.exception, *args)


00151     def callbacks(self, success_message='success', success_arg='',
                  failure_message='failure'):
        """
        Return a callback and an errback that log success or failure
        messages.

        The callback/errback pair are pass-throughs; they don't
        interfere in the callback/errback chain of the deferred you
        add them to.
        """
        def callback(arg, success_arg=success_arg):
            "it worked!"
            if callable(success_arg):
                success_arg = success_arg(arg)
            self.debug(success_message, success_arg)
            return arg
        def errback(failure):
            "it failed!"
            self.error(failure_message, failure.getErrorMessage())
            self.debug('traceback follows:\n\n' + failure.getTraceback(), '')
            return failure
        return callback, errback


def set_debug(dest):
    """ Set the level to debug of all registered loggers, and replace their
    handlers. if debug_level is file, syncdaemon-debug.log is used. If it's
    stdout, all the logging is redirected to stdout. If it's stderr, to stderr.

    @param dest: a string with a one or more of 'file', 'stdout', and 'stderr'
                 e.g. 'file stdout'
    """
    if not [ v for v in ['file', 'stdout', 'stderr'] if v in dest]:
        # invalid dest value, let the loggers alone
        return
    sd_filter = logging.Filter('ubuntuone.SyncDaemon')
    if 'file' in dest:
        # setup the existing loggers in debug
        root_handler.setLevel(_DEBUG_LOG_LEVEL)
        twisted_handler.setLevel(_DEBUG_LOG_LEVEL)
        logfile = os.path.join(LOGFOLDER, 'syncdaemon-debug.log')
        root_handler.baseFilename = os.path.abspath(logfile)
        twisted_handler.baseFilename = os.path.abspath(logfile)
    for name in ['ubuntuone.SyncDaemon', 'twisted']:
        logger = logging.getLogger(name)
        logger.setLevel(_DEBUG_LOG_LEVEL)
        if 'stderr' in dest:
            stderr_handler = logging.StreamHandler()
            stderr_handler.setFormatter(basic_formatter)
            stderr_handler.setLevel(_DEBUG_LOG_LEVEL)
            stderr_handler.addFilter(sd_filter)
            logger.addHandler(stderr_handler)
        if 'stdout' in dest:
            stdout_handler = logging.StreamHandler(sys.stdout)
            stdout_handler.setFormatter(basic_formatter)
            stdout_handler.setLevel(_DEBUG_LOG_LEVEL)
            stdout_handler.addFilter(sd_filter)
            logger.addHandler(stdout_handler)


def set_server_debug(dest):
    """ Set the level to debug of all registered loggers, and replace their
    handlers. if debug_level is file, syncdaemon-debug.log is used. If it's
    stdout, all the logging is redirected to stdout.

    @param dest: a string containing 'file' and/or 'stdout', e.g: 'file stdout'
    """
    logger = logging.getLogger("storage.server")
    logger.setLevel(5) # this shows server messages
    if 'file' in dest:
        handler = DayRotatingFileHandler(filename=os.path.join(LOGFOLDER,
                                                'syncdaemon-debug.log'))
        handler.setFormatter(basic_formatter)
        handler.setLevel(5) # this shows server messages
        logger.addHandler(handler)
    if 'stdout' in dest:
        stdout_handler = logging.StreamHandler(sys.stdout)
        stdout_handler.setFormatter(basic_formatter)
        stdout_handler.setLevel(5) # this shows server messages
        logger.addHandler(stdout_handler)
    if 'stderrt' in dest:
        stdout_handler = logging.StreamHandler(sys.stdout)
        stdout_handler.setFormatter(basic_formatter)
        stdout_handler.setLevel(5) # this shows server messages
        logger.addHandler(stdout_handler)


# if we are in debug mode, replace/add the handlers
DEBUG = os.environ.get("DEBUG", None)
if DEBUG:
    set_debug(DEBUG)

# configure server logging if SERVER_DEBUG != None
SERVER_DEBUG = os.environ.get("SERVER_DEBUG", None)
if SERVER_DEBUG:
    set_server_debug(SERVER_DEBUG)


# do a rollover to get an empty log file on each run
twisted_handler.close()
# ignore the missing file error on a failed rollover
# pylint: disable-msg=W0704
try:
    root_handler.doRollover()
except OSError:
    pass
try:
    exception_handler.doRollover()
except OSError:
    pass



Generated by  Doxygen 1.6.0   Back to index