msg.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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. # This program is free software; you can redistribute it and/or modify
  12. # it under the terms of the GNU General Public License version 2 as
  13. # published by the Free Software Foundation.
  14. #
  15. # This program is distributed in the hope that it will be useful,
  16. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. # GNU General Public License for more details.
  19. #
  20. # You should have received a copy of the GNU General Public License along
  21. # with this program; if not, write to the Free Software Foundation, Inc.,
  22. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  23. import sys
  24. import copy
  25. import logging
  26. import collections
  27. from itertools import groupby
  28. import warnings
  29. import bb
  30. import bb.event
  31. class BBLogFormatter(logging.Formatter):
  32. """Formatter which ensures that our 'plain' messages (logging.INFO + 1) are used as is"""
  33. DEBUG3 = logging.DEBUG - 2
  34. DEBUG2 = logging.DEBUG - 1
  35. DEBUG = logging.DEBUG
  36. VERBOSE = logging.INFO - 1
  37. NOTE = logging.INFO
  38. PLAIN = logging.INFO + 1
  39. VERBNOTE = logging.INFO + 2
  40. ERROR = logging.ERROR
  41. WARNING = logging.WARNING
  42. CRITICAL = logging.CRITICAL
  43. levelnames = {
  44. DEBUG3 : 'DEBUG',
  45. DEBUG2 : 'DEBUG',
  46. DEBUG : 'DEBUG',
  47. VERBOSE: 'NOTE',
  48. NOTE : 'NOTE',
  49. PLAIN : '',
  50. VERBNOTE: 'NOTE',
  51. WARNING : 'WARNING',
  52. ERROR : 'ERROR',
  53. CRITICAL: 'ERROR',
  54. }
  55. color_enabled = False
  56. BASECOLOR, BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = list(range(29,38))
  57. COLORS = {
  58. DEBUG3 : CYAN,
  59. DEBUG2 : CYAN,
  60. DEBUG : CYAN,
  61. VERBOSE : BASECOLOR,
  62. NOTE : BASECOLOR,
  63. PLAIN : BASECOLOR,
  64. VERBNOTE: BASECOLOR,
  65. WARNING : YELLOW,
  66. ERROR : RED,
  67. CRITICAL: RED,
  68. }
  69. BLD = '\033[1;%dm'
  70. STD = '\033[%dm'
  71. RST = '\033[0m'
  72. def getLevelName(self, levelno):
  73. try:
  74. return self.levelnames[levelno]
  75. except KeyError:
  76. self.levelnames[levelno] = value = 'Level %d' % levelno
  77. return value
  78. def format(self, record):
  79. record.levelname = self.getLevelName(record.levelno)
  80. if record.levelno == self.PLAIN:
  81. msg = record.getMessage()
  82. else:
  83. if self.color_enabled:
  84. record = self.colorize(record)
  85. msg = logging.Formatter.format(self, record)
  86. if hasattr(record, 'bb_exc_formatted'):
  87. msg += '\n' + ''.join(record.bb_exc_formatted)
  88. elif hasattr(record, 'bb_exc_info'):
  89. etype, value, tb = record.bb_exc_info
  90. formatted = bb.exceptions.format_exception(etype, value, tb, limit=5)
  91. msg += '\n' + ''.join(formatted)
  92. return msg
  93. def colorize(self, record):
  94. color = self.COLORS[record.levelno]
  95. if self.color_enabled and color is not None:
  96. record = copy.copy(record)
  97. record.levelname = "".join([self.BLD % color, record.levelname, self.RST])
  98. record.msg = "".join([self.STD % color, record.msg, self.RST])
  99. return record
  100. def enable_color(self):
  101. self.color_enabled = True
  102. class BBLogFilter(object):
  103. def __init__(self, handler, level, debug_domains):
  104. self.stdlevel = level
  105. self.debug_domains = debug_domains
  106. loglevel = level
  107. for domain in debug_domains:
  108. if debug_domains[domain] < loglevel:
  109. loglevel = debug_domains[domain]
  110. handler.setLevel(loglevel)
  111. handler.addFilter(self)
  112. def filter(self, record):
  113. if record.levelno >= self.stdlevel:
  114. return True
  115. if record.name in self.debug_domains and record.levelno >= self.debug_domains[record.name]:
  116. return True
  117. return False
  118. class BBLogFilterStdErr(BBLogFilter):
  119. def filter(self, record):
  120. if not BBLogFilter.filter(self, record):
  121. return False
  122. if record.levelno >= logging.ERROR:
  123. return True
  124. return False
  125. class BBLogFilterStdOut(BBLogFilter):
  126. def filter(self, record):
  127. if not BBLogFilter.filter(self, record):
  128. return False
  129. if record.levelno < logging.ERROR:
  130. return True
  131. return False
  132. # Message control functions
  133. #
  134. loggerDefaultDebugLevel = 0
  135. loggerDefaultVerbose = False
  136. loggerVerboseLogs = False
  137. loggerDefaultDomains = []
  138. def init_msgconfig(verbose, debug, debug_domains=None):
  139. """
  140. Set default verbosity and debug levels config the logger
  141. """
  142. bb.msg.loggerDefaultDebugLevel = debug
  143. bb.msg.loggerDefaultVerbose = verbose
  144. if verbose:
  145. bb.msg.loggerVerboseLogs = True
  146. if debug_domains:
  147. bb.msg.loggerDefaultDomains = debug_domains
  148. else:
  149. bb.msg.loggerDefaultDomains = []
  150. def constructLogOptions():
  151. debug = loggerDefaultDebugLevel
  152. verbose = loggerDefaultVerbose
  153. domains = loggerDefaultDomains
  154. if debug:
  155. level = BBLogFormatter.DEBUG - debug + 1
  156. elif verbose:
  157. level = BBLogFormatter.VERBOSE
  158. else:
  159. level = BBLogFormatter.NOTE
  160. debug_domains = {}
  161. for (domainarg, iterator) in groupby(domains):
  162. dlevel = len(tuple(iterator))
  163. debug_domains["BitBake.%s" % domainarg] = logging.DEBUG - dlevel + 1
  164. return level, debug_domains
  165. def addDefaultlogFilter(handler, cls = BBLogFilter, forcelevel=None):
  166. level, debug_domains = constructLogOptions()
  167. if forcelevel is not None:
  168. level = forcelevel
  169. cls(handler, level, debug_domains)
  170. #
  171. # Message handling functions
  172. #
  173. def fatal(msgdomain, msg):
  174. if msgdomain:
  175. logger = logging.getLogger("BitBake.%s" % msgdomain)
  176. else:
  177. logger = logging.getLogger("BitBake")
  178. logger.critical(msg)
  179. sys.exit(1)
  180. def logger_create(name, output=sys.stderr, level=logging.INFO, preserve_handlers=False, color='auto'):
  181. """Standalone logger creation function"""
  182. logger = logging.getLogger(name)
  183. console = logging.StreamHandler(output)
  184. format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
  185. if color == 'always' or (color == 'auto' and output.isatty()):
  186. format.enable_color()
  187. console.setFormatter(format)
  188. if preserve_handlers:
  189. logger.addHandler(console)
  190. else:
  191. logger.handlers = [console]
  192. logger.setLevel(level)
  193. return logger
  194. def has_console_handler(logger):
  195. for handler in logger.handlers:
  196. if isinstance(handler, logging.StreamHandler):
  197. if handler.stream in [sys.stderr, sys.stdout]:
  198. return True
  199. return False