####################################################################### # This file is part of PyBlosxom. # # Copyright (c) 2003, 2004, 2005, 2006 Wari Wahab # # PyBlosxom is distributed under the MIT license. See the file LICENSE # for distribution details. # # $Id: _logging.py 913 2006-08-08 20:29:42Z willhelm $ ####################################################################### """ Emulates part of the Python2.3 logging module. Note: This will probably not work with Python < 2.2. """ __revision__ = "$Revision: 913 $" import os import sys import time import string try: from cStringIO import StringIO except ImportError: from StringIO import StringIO _devlogfile = "/tmp/dev-log.log" _log = None def dump(msg, *args): """ Utility function to log stuff while working on the _logging module. """ global _log if _log == None or _log.closed: _log = open(_devlogfile, "a") if args: msg = msg % args _log.write(str(msg)) _log.write("\n") _log.flush() # Log levels CRITICAL = 50 FATAL = CRITICAL ERROR = 40 WARNING = 30 WARN = WARNING INFO = 20 DEBUG = 10 NOTSET = 0 # order matters! _names = ['FATAL', 'CRITICAL', 'ERROR', 'WARN', 'WARNING', \ 'INFO', 'DEBUG', 'NOTSET'] _levels = {} _level_names = {} def __populate_levels(): """ Fills in the _levels and _level_names dictionaries. """ for n in _names: i = eval(n) _levels[n] = i _level_names[i] = n __populate_levels() # _srcfile is used when walking the stack to check when we've got the first # caller stack frame. if string.lower(__file__[-4:]) in ['.pyc', '.pyo']: _srcfile = __file__[:-4] + '.py' else: _srcfile = __file__ _srcfile = os.path.normcase(_srcfile) # _startTime is used as the base when calculating the relative time of events _startTime = time.time() def getLevelName(level): """ Return the textual representation of logging level 'level'. @param level: Numeric representation of a logging level @type level: C{int} @return: Texutal representation of the logging level @rtype: C{str} """ return _level_names.get(level, ("Level %s" % level)) # repository of handlers (for flushing when shutdown is called) _handlers = {} class Filterer(object): """ A stripped down version of the logging.Filterer class. See the logging module for documentation. """ def __init__(self): self.filters = [] def addFilter(self, filter): if not (filter in self.filters): self.filters.append(filter) def removeFilter(self, filter): if filter in self.filters: self.filters.remove(filter) def filter(self, record): rv = 1 for f in self.filters: if not f.filter(record): rv = 0 break return rv class StreamHandler(Filterer): """ A stripped down version of the logging.StreamHandler class. See the logging module for documentation. """ def __init__(self, strm=sys.stderr): Filterer.__init__(self) #dump("StreamHandler.__init__: %s", strm) self.stream = strm self.formatter = None self.level = None global _handlers # store this handler in the global handler repository _handlers[self] = 1 def setLevel(self, level): self.level = level def format(self, record): return self.formatter.format(record) def emit(self, record): try: msg = self.format(record) try: self.stream.write("%s\n" % msg) except UnicodeError: self.stream.write("%s\n" % msg.encode("UTF-8")) self.flush() except: self.handleError(record) def handleError(self, record): # handle errors in the logging implementation import traceback ei = sys.exc_info() traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr) del ei def handle(self, record): rv = self.filter(record) if rv: self.emit(record) return rv def setFormatter(self, formatter): self.formatter = formatter def flush(self): self.stream.flush() def close(self): # do nothing here, could be sys.stdout/stderr pass class FileHandler(StreamHandler): """ A stripped down version of the logging.FileHandler class. See the logging module for documentation. """ def __init__(self, filename, mode="a"): super(FileHandler, self).__init__(open(filename, mode)) self.baseFilename = filename self.mode = mode def close(self): self.stream.close() class Formatter(object): """ A stripped down version of the logging.Formatter class. See the logging module for documentation. """ converter = time.localtime def __init__(self, fmt="%(message)s"): self._fmt = fmt self.datefmt = None def formatTime(self, record, datefmt=None): ct = self.converter(record.created) if datefmt: s = time.strftime(datefmt, ct) else: t = time.strftime("%Y-%m-%d %H:%M:%S", ct) s = "%s,%03d" % (t, record.msecs) return s def formatException(self, ei): import traceback sio = StringIO() traceback.print_exception(ei[0], ei[1], ei[2], None, sio) s = sio.getvalue() sio.close() if s[-1] == "\n": s = s[:-1] return s def format(self, record): record.message = record.getMessage() if string.find(self._fmt,"%(asctime)") >= 0: record.asctime = self.formatTime(record, self.datefmt) s = self._fmt % record.__dict__ if record.exc_info: if s[-1] != "\n": s = s + "\n" s = s + self.formatException(record.exc_info) return s class LogRecord: """ A stripped down version of the logging.LogRecord class. See the logging module for documentation. """ def __init__(self, name, level, pathname, lineno, msg, args, exc_info): ct = time.time() self.name = name self.msg = msg self.args = args self.levelname = getLevelName(level) self.levelno = level self.pathname = pathname try: self.filename = os.path.basename(pathname) self.module = os.path.splitext(self.filename)[0] except: self.filename = pathname self.module = "Unknown module" self.exc_info = exc_info self.lineno = lineno self.created = ct self.msecs = (ct - long(ct)) * 1000 self.relativeCreated = (self.created - _startTime) * 1000 if hasattr(os, 'getpid'): self.process = os.getpid() else: self.process = None def __str__(self): return '' % \ (self.name, self.levelno, self.pathname, self.lineno, self.msg) def getMessage(self): try: msg = str(self.msg) except UnicodeError: msg = self.msg #Defer encoding till later if self.args: msg = msg % self.args return msg class Logger(Filterer): """ A stripped down version of the logging.Logger class. See the logging module for documentation. """ def __init__(self, name, level=NOTSET): Filterer.__init__(self) self.name = name self.setLevel(level) self.handlers = [] self.disabled = 0 def _log(self, level, msg, args, exc_info=None): # log the message or not depending on the current loglevel if self.level > level: return if _srcfile: fn, lno = self.__findCaller() else: fn, lno = "", 0 if exc_info: exc_info = sys.exc_info() record = self.__makeRecord(self.name, level, fn, lno, \ msg, args, exc_info) self.__callHandlers(record) def __findCaller(self): f = sys._getframe(1) while 1: co = f.f_code filename = os.path.normcase(co.co_filename) if filename == _srcfile: f = f.f_back continue return filename, f.f_lineno def __makeRecord(self, name, level, fn, lno, msg, args, exc_info): return LogRecord(name, level, fn, lno, msg, args, exc_info) def __callHandlers(self, record): for hdlr in self.handlers: if record.levelno >= hdlr.level: hdlr.handle(record) # public methods def setLevel(self, level): self.level = level def critical(self, msg, *args, **kwargs): apply(self._log, (CRITICAL, msg, args), kwargs) fatal = critical def error(self, msg, *args, **kwargs): apply(self._log, (ERROR, msg, args), kwargs) def warning(self, msg, *args, **kwargs): apply(self._log, (WARNING, msg, args), kwargs) warn = warning def info(self, msg, *args, **kwargs): apply(self._log, (INFO, msg, args), kwargs) def debug(self, msg, *args, **kwargs): apply(self._log, (DEBUG, msg, args), kwargs) def exception(self, msg, *args): apply(self.error, (msg,) + args, {'exc_info': 1}) def addHandler(self, hdlr): if not (hdlr in self.handlers): self.handlers.append(hdlr) def removeHandler(self, hdlr): if hdlr in self.handlers: self.handlers.remove(hdlr) # A dict to keep track of created loggers. _logger_registry = {} def getLogger(log_name=None): """ Creates and returns a logging channel. Registers the created logger in the logger-registry for further reference. """ #dump("getLogger: %s", log_name) global _logger_registry if not log_name: log_name == "root" if not log_name in _logger_registry: _logger_registry[log_name] = Logger(log_name) return _logger_registry[log_name] def shutdown(): """ Close logfiles and clear the logger-registry. """ #dump("shutdown") global _handlers, _logger_registry for h in _handlers.keys(): h.flush() h.close() _handlers = {} _logger_registry = {}