#!/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 getmailcore.constants import * ####################################### class _Logger(object): '''Class for logging. Do not instantiate directly; use Logger() instead, to keep this a singleton. ''' def __init__(self): '''Create a logger.''' self.handlers = [] self.newline = False def __call__(self): return self def addhandler(self, stream, minlevel, maxlevel=CRITICAL): '''Add a handler for logged messages. Logged messages of at least level (and at most level , default CRITICAL) will be output to . 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 containing text .''' 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) Logger = _Logger()