msg.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. # ex:ts=4:sw=4:sts=4:et
  2. # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
  3. """
  4. BitBake 'msg' implementation
  5. Message handling infrastructure for bitbake
  6. """
  7. # Copyright (C) 2006 Richard Purdie
  8. #
  9. # SPDX-License-Identifier: GPL-2.0-only
  10. #
  11. import sys
  12. import copy
  13. import logging
  14. import collections
  15. from itertools import groupby
  16. import warnings
  17. import bb
  18. import bb.event
  19. class BBLogFormatter(logging.Formatter):
  20. """Formatter which ensures that our 'plain' messages (logging.INFO + 1) are used as is"""
  21. DEBUG3 = logging.DEBUG - 2
  22. DEBUG2 = logging.DEBUG - 1
  23. DEBUG = logging.DEBUG
  24. VERBOSE = logging.INFO - 1
  25. NOTE = logging.INFO
  26. PLAIN = logging.INFO + 1
  27. VERBNOTE = logging.INFO + 2
  28. ERROR = logging.ERROR
  29. WARNING = logging.WARNING
  30. CRITICAL = logging.CRITICAL
  31. levelnames = {
  32. DEBUG3 : 'DEBUG',
  33. DEBUG2 : 'DEBUG',
  34. DEBUG : 'DEBUG',
  35. VERBOSE: 'NOTE',
  36. NOTE : 'NOTE',
  37. PLAIN : '',
  38. VERBNOTE: 'NOTE',
  39. WARNING : 'WARNING',
  40. ERROR : 'ERROR',
  41. CRITICAL: 'ERROR',
  42. }
  43. color_enabled = False
  44. BASECOLOR, BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = list(range(29,38))
  45. COLORS = {
  46. DEBUG3 : CYAN,
  47. DEBUG2 : CYAN,
  48. DEBUG : CYAN,
  49. VERBOSE : BASECOLOR,
  50. NOTE : BASECOLOR,
  51. PLAIN : BASECOLOR,
  52. VERBNOTE: BASECOLOR,
  53. WARNING : YELLOW,
  54. ERROR : RED,
  55. CRITICAL: RED,
  56. }
  57. BLD = '\033[1;%dm'
  58. STD = '\033[%dm'
  59. RST = '\033[0m'
  60. def getLevelName(self, levelno):
  61. try:
  62. return self.levelnames[levelno]
  63. except KeyError:
  64. self.levelnames[levelno] = value = 'Level %d' % levelno
  65. return value
  66. def format(self, record):
  67. record.levelname = self.getLevelName(record.levelno)
  68. if record.levelno == self.PLAIN:
  69. msg = record.getMessage()
  70. else:
  71. if self.color_enabled:
  72. record = self.colorize(record)
  73. msg = logging.Formatter.format(self, record)
  74. if hasattr(record, 'bb_exc_formatted'):
  75. msg += '\n' + ''.join(record.bb_exc_formatted)
  76. elif hasattr(record, 'bb_exc_info'):
  77. etype, value, tb = record.bb_exc_info
  78. formatted = bb.exceptions.format_exception(etype, value, tb, limit=5)
  79. msg += '\n' + ''.join(formatted)
  80. return msg
  81. def colorize(self, record):
  82. color = self.COLORS[record.levelno]
  83. if self.color_enabled and color is not None:
  84. record = copy.copy(record)
  85. record.levelname = "".join([self.BLD % color, record.levelname, self.RST])
  86. record.msg = "".join([self.STD % color, record.msg, self.RST])
  87. return record
  88. def enable_color(self):
  89. self.color_enabled = True
  90. class BBLogFilter(object):
  91. def __init__(self, handler, level, debug_domains):
  92. self.stdlevel = level
  93. self.debug_domains = debug_domains
  94. loglevel = level
  95. for domain in debug_domains:
  96. if debug_domains[domain] < loglevel:
  97. loglevel = debug_domains[domain]
  98. handler.setLevel(loglevel)
  99. handler.addFilter(self)
  100. def filter(self, record):
  101. if record.levelno >= self.stdlevel:
  102. return True
  103. if record.name in self.debug_domains and record.levelno >= self.debug_domains[record.name]:
  104. return True
  105. return False
  106. class BBLogFilterStdErr(BBLogFilter):
  107. def filter(self, record):
  108. if not BBLogFilter.filter(self, record):
  109. return False
  110. if record.levelno >= logging.ERROR:
  111. return True
  112. return False
  113. class BBLogFilterStdOut(BBLogFilter):
  114. def filter(self, record):
  115. if not BBLogFilter.filter(self, record):
  116. return False
  117. if record.levelno < logging.ERROR:
  118. return True
  119. return False
  120. # Message control functions
  121. #
  122. loggerDefaultDebugLevel = 0
  123. loggerDefaultVerbose = False
  124. loggerVerboseLogs = False
  125. loggerDefaultDomains = []
  126. def init_msgconfig(verbose, debug, debug_domains=None):
  127. """
  128. Set default verbosity and debug levels config the logger
  129. """
  130. bb.msg.loggerDefaultDebugLevel = debug
  131. bb.msg.loggerDefaultVerbose = verbose
  132. if verbose:
  133. bb.msg.loggerVerboseLogs = True
  134. if debug_domains:
  135. bb.msg.loggerDefaultDomains = debug_domains
  136. else:
  137. bb.msg.loggerDefaultDomains = []
  138. def constructLogOptions():
  139. debug = loggerDefaultDebugLevel
  140. verbose = loggerDefaultVerbose
  141. domains = loggerDefaultDomains
  142. if debug:
  143. level = BBLogFormatter.DEBUG - debug + 1
  144. elif verbose:
  145. level = BBLogFormatter.VERBOSE
  146. else:
  147. level = BBLogFormatter.NOTE
  148. debug_domains = {}
  149. for (domainarg, iterator) in groupby(domains):
  150. dlevel = len(tuple(iterator))
  151. debug_domains["BitBake.%s" % domainarg] = logging.DEBUG - dlevel + 1
  152. return level, debug_domains
  153. def addDefaultlogFilter(handler, cls = BBLogFilter, forcelevel=None):
  154. level, debug_domains = constructLogOptions()
  155. if forcelevel is not None:
  156. level = forcelevel
  157. cls(handler, level, debug_domains)
  158. #
  159. # Message handling functions
  160. #
  161. def fatal(msgdomain, msg):
  162. if msgdomain:
  163. logger = logging.getLogger("BitBake.%s" % msgdomain)
  164. else:
  165. logger = logging.getLogger("BitBake")
  166. logger.critical(msg)
  167. sys.exit(1)
  168. def logger_create(name, output=sys.stderr, level=logging.INFO, preserve_handlers=False, color='auto'):
  169. """Standalone logger creation function"""
  170. logger = logging.getLogger(name)
  171. console = logging.StreamHandler(output)
  172. format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
  173. if color == 'always' or (color == 'auto' and output.isatty()):
  174. format.enable_color()
  175. console.setFormatter(format)
  176. if preserve_handlers:
  177. logger.addHandler(console)
  178. else:
  179. logger.handlers = [console]
  180. logger.setLevel(level)
  181. return logger
  182. def has_console_handler(logger):
  183. for handler in logger.handlers:
  184. if isinstance(handler, logging.StreamHandler):
  185. if handler.stream in [sys.stderr, sys.stdout]:
  186. return True
  187. return False