""" basic logging functionality based on a producer/consumer scheme. XXX implement this API: (maybe put it into slogger.py?) log = Logger( info=py.log.STDOUT, debug=py.log.STDOUT, command=None) log.info("hello", "world") log.command("hello", "world") log = Logger(info=Logger(something=...), debug=py.log.STDOUT, command=None) """ import py, sys class Message(object): def __init__(self, keywords, args): self.keywords = keywords self.args = args def content(self): return " ".join(map(str, self.args)) def prefix(self): return "[%s] " % (":".join(self.keywords)) def __str__(self): return self.prefix() + self.content() class Producer(object): """ (deprecated) Log producer API which sends messages to be logged to a 'consumer' object, which then prints them to stdout, stderr, files, etc. Used extensively by PyPy-1.1. """ Message = Message # to allow later customization keywords2consumer = {} def __init__(self, keywords, keywordmapper=None, **kw): if hasattr(keywords, 'split'): keywords = tuple(keywords.split()) self._keywords = keywords if keywordmapper is None: keywordmapper = default_keywordmapper self._keywordmapper = keywordmapper def __repr__(self): return "" % ":".join(self._keywords) def __getattr__(self, name): if '_' in name: raise AttributeError(name) producer = self.__class__(self._keywords + (name,)) setattr(self, name, producer) return producer def __call__(self, *args): """ write a message to the appropriate consumer(s) """ func = self._keywordmapper.getconsumer(self._keywords) if func is not None: func(self.Message(self._keywords, args)) class KeywordMapper: def __init__(self): self.keywords2consumer = {} def getstate(self): return self.keywords2consumer.copy() def setstate(self, state): self.keywords2consumer.clear() self.keywords2consumer.update(state) def getconsumer(self, keywords): """ return a consumer matching the given keywords. tries to find the most suitable consumer by walking, starting from the back, the list of keywords, the first consumer matching a keyword is returned (falling back to py.log.default) """ for i in range(len(keywords), 0, -1): try: return self.keywords2consumer[keywords[:i]] except KeyError: continue return self.keywords2consumer.get('default', default_consumer) def setconsumer(self, keywords, consumer): """ set a consumer for a set of keywords. """ # normalize to tuples if isinstance(keywords, str): keywords = tuple(map(None, keywords.split())) elif hasattr(keywords, '_keywords'): keywords = keywords._keywords elif not isinstance(keywords, tuple): raise TypeError("key %r is not a string or tuple" % (keywords,)) if consumer is not None and not callable(consumer): if not hasattr(consumer, 'write'): raise TypeError( "%r should be None, callable or file-like" % (consumer,)) consumer = File(consumer) self.keywords2consumer[keywords] = consumer def default_consumer(msg): """ the default consumer, prints the message to stdout (using 'print') """ sys.stderr.write(str(msg)+"\n") default_keywordmapper = KeywordMapper() def setconsumer(keywords, consumer): default_keywordmapper.setconsumer(keywords, consumer) def setstate(state): default_keywordmapper.setstate(state) def getstate(): return default_keywordmapper.getstate() # # Consumers # class File(object): """ log consumer wrapping a file(-like) object """ def __init__(self, f): assert hasattr(f, 'write') assert isinstance(f, file) or not hasattr(f, 'open') self._file = f def __call__(self, msg): """ write a message to the log """ self._file.write(str(msg) + "\n") class Path(object): """ log consumer able to write log messages into """ def __init__(self, filename, append=False, delayed_create=False, buffering=1): self._append = append self._filename = filename self._buffering = buffering if not delayed_create: self._openfile() def _openfile(self): mode = self._append and 'a' or 'w' f = open(str(self._filename), mode, buffering=self._buffering) self._file = f def __call__(self, msg): """ write a message to the log """ if not hasattr(self, "_file"): self._openfile() self._file.write(str(msg) + "\n") def STDOUT(msg): """ consumer that writes to sys.stdout """ sys.stdout.write(str(msg)+"\n") def STDERR(msg): """ consumer that writes to sys.stderr """ sys.stderr.write(str(msg)+"\n") class Syslog: """ consumer that writes to the syslog daemon """ for priority in "LOG_EMERG LOG_ALERT LOG_CRIT LOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_DEBUG".split(): try: exec("%s = py.std.syslog.%s" % (priority, priority)) except AttributeError: pass def __init__(self, priority = None): if priority is None: priority = self.LOG_INFO self.priority = priority def __call__(self, msg): """ write a message to the log """ py.std.syslog.syslog(self.priority, str(msg))