#!/usr/bin/env python2.3
'''Logging support for getmail.

The new standard Python libary module logging didn't cut it for me; it doesn't
seem capable of handling some very simple requirements like logging messages of
a certain level to one fd, and other messages of higher levels to a different fd
(i.e. info to stdout, warnings to stderr).
'''

__all__ = [
    'logger',
]

import sys
import os.path
import traceback

from constants import *

__logger = None

#######################################
class __Logger(object):
    '''Class for logging.  Do not instantiate directly; use logger() instead.

    logger() enforces this as a singleton.
    '''
    def __init__(self):
        '''Create a logger.'''
        self.handlers = []
        self.newline = False

    def addhandler(self, stream, minlevel, maxlevel=CRITICAL):
        '''Add a handler for logged messages.

        Logged messages of at least level <minlevel> (and at most level
        <maxlevel>, default CRITICAL) will be output to <stream>.

        If no handlers are specified, messages of all levels will be output to
        stdout.
        '''
        self.handlers.append({'minlevel' : minlevel, 'stream' : stream,
            'newline' : True, 'maxlevel' : maxlevel})

    def clearhandlers(self):
        '''Clear the list of handlers.

        There should be a way to remove only one handler from a list.  But that
        would require an easy way for the caller to distinguish between them.
        '''
        self.handlers = []

    def log(self, msglevel, msgtxt):
        '''Log a message of level <msglevel> containing text <msgtxt>.'''
        for handler in self.handlers:
            if msglevel < handler['minlevel'] or msglevel > handler['maxlevel']:
                continue
            if not handler['newline'] and msglevel == DEBUG:
                handler['stream'].write('\n')
            handler['stream'].write(msgtxt)
            handler['stream'].flush()
            if msgtxt.endswith('\n'):
                handler['newline'] = True
            else:
                handler['newline'] = False
        if not self.handlers:
            if not self.newline and msglevel == DEBUG:
                sys.stdout.write('\n')
            sys.stdout.write(msgtxt)
            sys.stdout.flush()
            if msgtxt.endswith('\n'):
                self.newline = True
            else:
                self.newline = False

    def trace(self, msg='trace\n'):
        '''Log a message with level TRACE.

        The message will be prefixed with filename, line number, and function
        name of the calling code.
        '''
        trace = traceback.extract_stack()[-2]
        msg = '%s [%s:%i] %s' % (trace[FUNCNAME] + '()',
            os.path.basename(trace[FILENAME]),
            trace[LINENO],
            msg
        )
        self.log(TRACE, msg)

    def debug(self, msg):
        '''Log a message with level DEBUG.'''
        self.log(DEBUG, msg)

    def moreinfo(self, msg):
        '''Log a message with level MOREINFO.'''
        self.log(MOREINFO, msg)

    def info(self, msg):
        '''Log a message with level INFO.'''
        self.log(INFO, msg)

    def warning(self, msg):
        '''Log a message with level WARNING.'''
        self.log(WARNING, msg)

    def error(self, msg):
        '''Log a message with level ERROR.'''
        self.log(ERROR, msg)

    def critical(self, msg):
        '''Log a message with level CRITICAL.'''
        self.log(CRITICAL, msg)

def logger():
    '''Create a logger, or return the existing logger object.'''
    global __logger
    if __logger is None:
        __logger = __Logger()
    return __logger
