view gub/logging.py @ 5460:bb0f1f52b5b4

epdfview: new package.
author Jan Nieuwenhuizen <janneke@gnu.org>
date Mon, 17 Aug 2009 21:13:18 +0200
parents 04c59bcd66ca
children
line wrap: on
line source

import time
import sys
import os
#
from gub import misc

'''
TODO: if we need more granularity, it is better to look at the stack
trace during a log call (), and have per .py file settings.
'''

default_logger = None
default_logger_interface = None

name_to_loglevel_mapping = {'quiet': 0,
         'error': 0,
         'stage': 0,
         'info': 1,
         'harmless': 2,
         'verbose': 2,
         'warning': 1,
         'command': 3,
         'action': 2,
         'output': 4,
         'debug': 5}

def now ():
    return time.asctime (time.localtime ())


class AbstractCommandLogger:
    """A logger takes care of the mechanics of storing and/or showing
    messages when approppriate.
    """
    def __init__ (self):
        class Writer:
            def __init__ (this, level):
                this.level = level
            def write (this, message):
                self.write_log (message, this.level)
        self.error = Writer ('error')
        self.stage = Writer ('stage')
        self.info = Writer ('info')
        self.harmless = Writer ('harmless')
        self.verbose = Writer ('verbose')
        self.warning = Writer ('warning')
        self.command = Writer ('command')
        self.output = Writer ('output')
        self.debug = Writer ('debug')
    def verbose_flag (self):
        return ''
    def read_tail (self, size=0, lines=0):
        return ['tail']
    def write_log (self, message, message_level):
        pass
    def log_env (self, env):
        pass
    def write_multilevel_message (self, message_types):
        pass
    
class NullCommandLogger (AbstractCommandLogger):
    pass

class CommandLogger (AbstractCommandLogger):
    def __init__ (self, log_file_name, threshold):
        AbstractCommandLogger.__init__ (self)
        # only print message under THRESHOLD.
        self.threshold = threshold
        self.log_file = None
        self.log_file_name = log_file_name
        self.relative_log_name = log_file_name

        if log_file_name:
            self.relative_log_name = log_file_name.replace (os.getcwd () + '/', '')
            if default_logger_interface:
                log_name = self.relative_log_name
                default_logger_interface.info ('Log file: %(log_name)s\n' % locals ())
            
            directory = os.path.split (log_file_name)[0]
            if not os.path.isdir (directory):
                os.makedirs (directory)
            self.log_file = open (self.log_file_name, 'a')
        self.start_marker = ' * Starting build: %s\n' %  now ()
        self.write_log_file ('\n\n' + self.start_marker)

    # ugh: the following should not be in the base class.
    def read_tail (self, size=0, lines=0):
        if not size or not lines:
            lines = 5 + 10 * self.threshold
            size = 200 * lines
        if self.log_file:
            return misc.read_tail (self.log_file_name, size, lines,
                                   self.start_marker)
        else:
            return ['(no log)']

    def dump_tail (self, output):
        indent = '    '
        tail = ('%(indent)s' % locals ()
                + ('\n%(indent)s' % locals ()).join (self.read_tail ())
                .rstrip ())
        log_name = self.relative_log_name
        output.write ('Tail of %(log_name)s >>>>>>>>\n%(tail)s\n<<<<<<<< Tail of %(log_name)s\n' % locals ())

    def write_multilevel_message (self, message_types):
        """Given a set of messages display the one fitting with our
        log level."""
        leveled = [(name_to_loglevel_mapping[type], message)
                   for (message, type) in message_types]
        leveled.sort ()
        leveled.reverse ()
        self.write_log_file (leveled[0][1])

        leveled = [msg for (l, msg) in leveled
                   if l <= self.threshold]
        if leveled:
            sys.stderr.write (leveled[0])
            
    def write_log_file (self, message):
        if self.log_file:
            self.log_file.write (message)
            self.log_file.flush ()

    def write_log (self, message, message_type):
        assert type (message_type) == str
        if not message:
            return 0
        message_level = name_to_loglevel_mapping[message_type]
        if message_level <= self.threshold:
            sys.stderr.write (message)

        self.write_log_file (message)

    def verbose_flag (self):
        if self.threshold >= name_to_loglevel_mapping['output']:
            return ' -v'
        return ''

    def log_env (self, env):
        if self.threshold >= name_to_loglevel_mapping['debug']:
            keys = list (env.keys ())
            keys.sort ()
            for k in keys:
                self.write_log ('%s=%s\n' % (k, env[k]), 'debug')
            self.write_log ('export %s\n' % ' '.join (keys), 'debug')

    def show_logfile (self):
        if self.log_file_name:
            sys.stdout.write ('Logfile: %s\n' % self.log_file_name)


class LoggerInterface:
    """LoggerInterface provides syntacic sugar for different types of messages."""
    
    def __init__ (self, logger):
        self.logger = logger
        
    # fixme: repetitive code.
    def action (self, str):
        self.logger.write_log (str, 'action')

    def stage (self, str):
        self.logger.write_log (str, 'stage')

    def error (self, str):
        self.logger.write_log (str, 'error')

    def info (self, str):
        self.logger.write_log (str, 'info')

    def command (self, str):
        self.logger.write_log (str, 'command')

    def debug (self, str):
        self.logger.write_log (str, 'debug')

    def warning (self, str):
        self.logger.write_log (str, 'warning')

    def harmless (self, str):
        self.logger.write_log (str, 'harmless')

    def verbose (self, str):
        self.logger.write_log (str, 'verbose')

    def output (self, str):
        self.logger.write_log (str, 'output')

    def verbose_flag (self):
        return self.logger.verbose_flag ()
    # end fixme

def get_numeric_loglevel (name):
    return name_to_loglevel_mapping[name]

default_logger = None
default_logger_interface = None
action = None
command = None
debug = None
error = None
harmless = None
info = None
stage = None
verbose = None
warning = None

def set_default_log (name, level):
    global default_logger, default_logger_interface
    global action, command, debug, error, harmless, info, stage, verbose, warning
    default_logger = CommandLogger (name, level)
    default_logger_interface = LoggerInterface (default_logger)
    action = default_logger_interface.action
    command = default_logger_interface.command
    debug = default_logger_interface.debug
    error = default_logger_interface.error
    harmless = default_logger_interface.harmless
    info = default_logger_interface.info
    stage = default_logger_interface.stage
    verbose = default_logger_interface.verbose
    warning = default_logger_interface.warning
    return default_logger

# ugh, makeme optional?
set_default_log ('', 0)