You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

207 lines
5.9KB

  1. """
  2. basic logging functionality based on a producer/consumer scheme.
  3. XXX implement this API: (maybe put it into slogger.py?)
  4. log = Logger(
  5. info=py.log.STDOUT,
  6. debug=py.log.STDOUT,
  7. command=None)
  8. log.info("hello", "world")
  9. log.command("hello", "world")
  10. log = Logger(info=Logger(something=...),
  11. debug=py.log.STDOUT,
  12. command=None)
  13. """
  14. import py
  15. import sys
  16. class Message(object):
  17. def __init__(self, keywords, args):
  18. self.keywords = keywords
  19. self.args = args
  20. def content(self):
  21. return " ".join(map(str, self.args))
  22. def prefix(self):
  23. return "[%s] " % (":".join(self.keywords))
  24. def __str__(self):
  25. return self.prefix() + self.content()
  26. class Producer(object):
  27. """ (deprecated) Log producer API which sends messages to be logged
  28. to a 'consumer' object, which then prints them to stdout,
  29. stderr, files, etc. Used extensively by PyPy-1.1.
  30. """
  31. Message = Message # to allow later customization
  32. keywords2consumer = {}
  33. def __init__(self, keywords, keywordmapper=None, **kw):
  34. if hasattr(keywords, 'split'):
  35. keywords = tuple(keywords.split())
  36. self._keywords = keywords
  37. if keywordmapper is None:
  38. keywordmapper = default_keywordmapper
  39. self._keywordmapper = keywordmapper
  40. def __repr__(self):
  41. return "<py.log.Producer %s>" % ":".join(self._keywords)
  42. def __getattr__(self, name):
  43. if '_' in name:
  44. raise AttributeError(name)
  45. producer = self.__class__(self._keywords + (name,))
  46. setattr(self, name, producer)
  47. return producer
  48. def __call__(self, *args):
  49. """ write a message to the appropriate consumer(s) """
  50. func = self._keywordmapper.getconsumer(self._keywords)
  51. if func is not None:
  52. func(self.Message(self._keywords, args))
  53. class KeywordMapper:
  54. def __init__(self):
  55. self.keywords2consumer = {}
  56. def getstate(self):
  57. return self.keywords2consumer.copy()
  58. def setstate(self, state):
  59. self.keywords2consumer.clear()
  60. self.keywords2consumer.update(state)
  61. def getconsumer(self, keywords):
  62. """ return a consumer matching the given keywords.
  63. tries to find the most suitable consumer by walking, starting from
  64. the back, the list of keywords, the first consumer matching a
  65. keyword is returned (falling back to py.log.default)
  66. """
  67. for i in range(len(keywords), 0, -1):
  68. try:
  69. return self.keywords2consumer[keywords[:i]]
  70. except KeyError:
  71. continue
  72. return self.keywords2consumer.get('default', default_consumer)
  73. def setconsumer(self, keywords, consumer):
  74. """ set a consumer for a set of keywords. """
  75. # normalize to tuples
  76. if isinstance(keywords, str):
  77. keywords = tuple(filter(None, keywords.split()))
  78. elif hasattr(keywords, '_keywords'):
  79. keywords = keywords._keywords
  80. elif not isinstance(keywords, tuple):
  81. raise TypeError("key %r is not a string or tuple" % (keywords,))
  82. if consumer is not None and not py.builtin.callable(consumer):
  83. if not hasattr(consumer, 'write'):
  84. raise TypeError(
  85. "%r should be None, callable or file-like" % (consumer,))
  86. consumer = File(consumer)
  87. self.keywords2consumer[keywords] = consumer
  88. def default_consumer(msg):
  89. """ the default consumer, prints the message to stdout (using 'print') """
  90. sys.stderr.write(str(msg)+"\n")
  91. default_keywordmapper = KeywordMapper()
  92. def setconsumer(keywords, consumer):
  93. default_keywordmapper.setconsumer(keywords, consumer)
  94. def setstate(state):
  95. default_keywordmapper.setstate(state)
  96. def getstate():
  97. return default_keywordmapper.getstate()
  98. #
  99. # Consumers
  100. #
  101. class File(object):
  102. """ log consumer wrapping a file(-like) object """
  103. def __init__(self, f):
  104. assert hasattr(f, 'write')
  105. # assert isinstance(f, file) or not hasattr(f, 'open')
  106. self._file = f
  107. def __call__(self, msg):
  108. """ write a message to the log """
  109. self._file.write(str(msg) + "\n")
  110. if hasattr(self._file, 'flush'):
  111. self._file.flush()
  112. class Path(object):
  113. """ log consumer that opens and writes to a Path """
  114. def __init__(self, filename, append=False,
  115. delayed_create=False, buffering=False):
  116. self._append = append
  117. self._filename = str(filename)
  118. self._buffering = buffering
  119. if not delayed_create:
  120. self._openfile()
  121. def _openfile(self):
  122. mode = self._append and 'a' or 'w'
  123. f = open(self._filename, mode)
  124. self._file = f
  125. def __call__(self, msg):
  126. """ write a message to the log """
  127. if not hasattr(self, "_file"):
  128. self._openfile()
  129. self._file.write(str(msg) + "\n")
  130. if not self._buffering:
  131. self._file.flush()
  132. def STDOUT(msg):
  133. """ consumer that writes to sys.stdout """
  134. sys.stdout.write(str(msg)+"\n")
  135. def STDERR(msg):
  136. """ consumer that writes to sys.stderr """
  137. sys.stderr.write(str(msg)+"\n")
  138. class Syslog:
  139. """ consumer that writes to the syslog daemon """
  140. def __init__(self, priority=None):
  141. if priority is None:
  142. priority = self.LOG_INFO
  143. self.priority = priority
  144. def __call__(self, msg):
  145. """ write a message to the log """
  146. import syslog
  147. syslog.syslog(self.priority, str(msg))
  148. try:
  149. import syslog
  150. except ImportError:
  151. pass
  152. else:
  153. for _prio in "EMERG ALERT CRIT ERR WARNING NOTICE INFO DEBUG".split():
  154. _prio = "LOG_" + _prio
  155. try:
  156. setattr(Syslog, _prio, getattr(syslog, _prio))
  157. except AttributeError:
  158. pass