summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/msg.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/msg.py')
-rw-r--r--bitbake/lib/bb/msg.py353
1 files changed, 0 insertions, 353 deletions
diff --git a/bitbake/lib/bb/msg.py b/bitbake/lib/bb/msg.py
deleted file mode 100644
index 4f616ff42e..0000000000
--- a/bitbake/lib/bb/msg.py
+++ /dev/null
@@ -1,353 +0,0 @@
1"""
2BitBake 'msg' implementation
3
4Message handling infrastructure for bitbake
5
6"""
7
8# Copyright (C) 2006 Richard Purdie
9#
10# SPDX-License-Identifier: GPL-2.0-only
11#
12
13import sys
14import copy
15import logging
16import logging.config
17import os
18from itertools import groupby
19import bb
20import bb.event
21
22class BBLogFormatter(logging.Formatter):
23 """Formatter which ensures that our 'plain' messages (logging.INFO + 1) are used as is"""
24
25 DEBUG3 = logging.DEBUG - 2
26 DEBUG2 = logging.DEBUG - 1
27 DEBUG = logging.DEBUG
28 VERBOSE = logging.INFO - 1
29 NOTE = logging.INFO
30 PLAIN = logging.INFO + 1
31 VERBNOTE = logging.INFO + 2
32 ERROR = logging.ERROR
33 ERRORONCE = logging.ERROR - 1
34 WARNING = logging.WARNING
35 WARNONCE = logging.WARNING - 1
36 CRITICAL = logging.CRITICAL
37
38 levelnames = {
39 DEBUG3 : 'DEBUG',
40 DEBUG2 : 'DEBUG',
41 DEBUG : 'DEBUG',
42 VERBOSE: 'NOTE',
43 NOTE : 'NOTE',
44 PLAIN : '',
45 VERBNOTE: 'NOTE',
46 WARNING : 'WARNING',
47 WARNONCE : 'WARNING',
48 ERROR : 'ERROR',
49 ERRORONCE : 'ERROR',
50 CRITICAL: 'ERROR',
51 }
52
53 color_enabled = False
54 BASECOLOR, BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = list(range(29,38))
55
56 COLORS = {
57 DEBUG3 : CYAN,
58 DEBUG2 : CYAN,
59 DEBUG : CYAN,
60 VERBOSE : BASECOLOR,
61 NOTE : BASECOLOR,
62 PLAIN : BASECOLOR,
63 VERBNOTE: BASECOLOR,
64 WARNING : YELLOW,
65 WARNONCE : YELLOW,
66 ERROR : RED,
67 ERRORONCE : RED,
68 CRITICAL: RED,
69 }
70
71 BLD = '\033[1;%dm'
72 STD = '\033[%dm'
73 RST = '\033[0m'
74
75 def getLevelName(self, levelno):
76 try:
77 return self.levelnames[levelno]
78 except KeyError:
79 self.levelnames[levelno] = value = 'Level %d' % levelno
80 return value
81
82 def format(self, record):
83 record.levelname = self.getLevelName(record.levelno)
84 if record.levelno == self.PLAIN:
85 msg = record.getMessage()
86 else:
87 if self.color_enabled:
88 record = self.colorize(record)
89 msg = logging.Formatter.format(self, record)
90 if hasattr(record, 'bb_exc_formatted'):
91 msg += '\n' + ''.join(record.bb_exc_formatted)
92 return msg
93
94 def colorize(self, record):
95 color = self.COLORS[record.levelno]
96 if self.color_enabled and color is not None:
97 record = copy.copy(record)
98 record.levelname = "".join([self.BLD % color, record.levelname, self.RST])
99 record.msg = "".join([self.STD % color, record.msg, self.RST])
100 return record
101
102 def enable_color(self):
103 self.color_enabled = True
104
105 def __repr__(self):
106 return "%s fmt='%s' color=%s" % (self.__class__.__name__, self._fmt, "True" if self.color_enabled else "False")
107
108class BBLogFilter(object):
109 def __init__(self, handler, level, debug_domains):
110 self.stdlevel = level
111 self.debug_domains = debug_domains
112 loglevel = level
113 for domain in debug_domains:
114 if debug_domains[domain] < loglevel:
115 loglevel = debug_domains[domain]
116 handler.setLevel(loglevel)
117 handler.addFilter(self)
118
119 def filter(self, record):
120 if record.levelno >= self.stdlevel:
121 return True
122 if record.name in self.debug_domains and record.levelno >= self.debug_domains[record.name]:
123 return True
124 return False
125
126class LogFilterShowOnce(logging.Filter):
127 def __init__(self):
128 self.seen_warnings = set()
129 self.seen_errors = set()
130
131 def filter(self, record):
132 if record.levelno == bb.msg.BBLogFormatter.WARNONCE:
133 if record.msg in self.seen_warnings:
134 return False
135 self.seen_warnings.add(record.msg)
136 if record.levelno == bb.msg.BBLogFormatter.ERRORONCE:
137 if record.msg in self.seen_errors:
138 return False
139 self.seen_errors.add(record.msg)
140 return True
141
142class LogFilterGEQLevel(logging.Filter):
143 def __init__(self, level):
144 self.strlevel = str(level)
145 self.level = stringToLevel(level)
146
147 def __repr__(self):
148 return "%s level >= %s (%d)" % (self.__class__.__name__, self.strlevel, self.level)
149
150 def filter(self, record):
151 return (record.levelno >= self.level)
152
153class LogFilterLTLevel(logging.Filter):
154 def __init__(self, level):
155 self.strlevel = str(level)
156 self.level = stringToLevel(level)
157
158 def __repr__(self):
159 return "%s level < %s (%d)" % (self.__class__.__name__, self.strlevel, self.level)
160
161 def filter(self, record):
162 return (record.levelno < self.level)
163
164# Message control functions
165#
166
167loggerDefaultLogLevel = BBLogFormatter.NOTE
168loggerDefaultDomains = {}
169
170def init_msgconfig(verbose, debug, debug_domains=None):
171 """
172 Set default verbosity and debug levels config the logger
173 """
174 if debug:
175 bb.msg.loggerDefaultLogLevel = BBLogFormatter.DEBUG - debug + 1
176 elif verbose:
177 bb.msg.loggerDefaultLogLevel = BBLogFormatter.VERBOSE
178 else:
179 bb.msg.loggerDefaultLogLevel = BBLogFormatter.NOTE
180
181 bb.msg.loggerDefaultDomains = {}
182 if debug_domains:
183 for (domainarg, iterator) in groupby(debug_domains):
184 dlevel = len(tuple(iterator))
185 bb.msg.loggerDefaultDomains["BitBake.%s" % domainarg] = logging.DEBUG - dlevel + 1
186
187def constructLogOptions():
188 return loggerDefaultLogLevel, loggerDefaultDomains
189
190def addDefaultlogFilter(handler, cls = BBLogFilter, forcelevel=None):
191 level, debug_domains = constructLogOptions()
192
193 if forcelevel is not None:
194 level = forcelevel
195
196 cls(handler, level, debug_domains)
197
198def stringToLevel(level):
199 try:
200 return int(level)
201 except ValueError:
202 pass
203
204 try:
205 return getattr(logging, level)
206 except AttributeError:
207 pass
208
209 return getattr(BBLogFormatter, level)
210
211#
212# Message handling functions
213#
214
215def fatal(msgdomain, msg):
216 if msgdomain:
217 logger = logging.getLogger("BitBake.%s" % msgdomain)
218 else:
219 logger = logging.getLogger("BitBake")
220 logger.critical(msg)
221 sys.exit(1)
222
223def logger_create(name, output=sys.stderr, level=logging.INFO, preserve_handlers=False, color='auto'):
224 """Standalone logger creation function"""
225 logger = logging.getLogger(name)
226 console = logging.StreamHandler(output)
227 console.addFilter(bb.msg.LogFilterShowOnce())
228 format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
229 if color == 'always' or (color == 'auto' and output.isatty() and os.environ.get('NO_COLOR', '') == ''):
230 format.enable_color()
231 console.setFormatter(format)
232 if preserve_handlers:
233 logger.addHandler(console)
234 else:
235 logger.handlers = [console]
236 logger.setLevel(level)
237 return logger
238
239def has_console_handler(logger):
240 for handler in logger.handlers:
241 if isinstance(handler, logging.StreamHandler):
242 if handler.stream in [sys.stderr, sys.stdout]:
243 return True
244 return False
245
246def mergeLoggingConfig(logconfig, userconfig):
247 logconfig = copy.deepcopy(logconfig)
248 userconfig = copy.deepcopy(userconfig)
249
250 # Merge config with the default config
251 if userconfig.get('version') != logconfig['version']:
252 raise BaseException("Bad user configuration version. Expected %r, got %r" % (logconfig['version'], userconfig.get('version')))
253
254 # Set some defaults to make merging easier
255 userconfig.setdefault("loggers", {})
256
257 # If a handler, formatter, or filter is defined in the user
258 # config, it will replace an existing one in the default config
259 for k in ("handlers", "formatters", "filters"):
260 logconfig.setdefault(k, {}).update(userconfig.get(k, {}))
261
262 seen_loggers = set()
263 for name, l in logconfig["loggers"].items():
264 # If the merge option is set, merge the handlers and
265 # filters. Otherwise, if it is False, this logger won't get
266 # add to the set of seen loggers and will replace the
267 # existing one
268 if l.get('bitbake_merge', True):
269 ulogger = userconfig["loggers"].setdefault(name, {})
270 ulogger.setdefault("handlers", [])
271 ulogger.setdefault("filters", [])
272
273 # Merge lists
274 l.setdefault("handlers", []).extend(ulogger["handlers"])
275 l.setdefault("filters", []).extend(ulogger["filters"])
276
277 # Replace other properties if present
278 if "level" in ulogger:
279 l["level"] = ulogger["level"]
280
281 if "propagate" in ulogger:
282 l["propagate"] = ulogger["propagate"]
283
284 seen_loggers.add(name)
285
286 # Add all loggers present in the user config, but not any that
287 # have already been processed
288 for name in set(userconfig["loggers"].keys()) - seen_loggers:
289 logconfig["loggers"][name] = userconfig["loggers"][name]
290
291 return logconfig
292
293def setLoggingConfig(defaultconfig, userconfigfile=None):
294 logconfig = copy.deepcopy(defaultconfig)
295
296 if userconfigfile:
297 with open(os.path.normpath(userconfigfile), 'r') as f:
298 if userconfigfile.endswith('.yml') or userconfigfile.endswith('.yaml'):
299 import yaml
300 userconfig = yaml.safe_load(f)
301 elif userconfigfile.endswith('.json') or userconfigfile.endswith('.cfg'):
302 import json
303 userconfig = json.load(f)
304 else:
305 raise BaseException("Unrecognized file format: %s" % userconfigfile)
306
307 if userconfig.get('bitbake_merge', True):
308 logconfig = mergeLoggingConfig(logconfig, userconfig)
309 else:
310 # Replace the entire default config
311 logconfig = userconfig
312
313 # Convert all level parameters to integers in case users want to use the
314 # bitbake defined level names
315 for name, h in logconfig["handlers"].items():
316 if "level" in h:
317 h["level"] = bb.msg.stringToLevel(h["level"])
318
319 # Every handler needs its own instance of the once filter.
320 once_filter_name = name + ".showonceFilter"
321 logconfig.setdefault("filters", {})[once_filter_name] = {
322 "()": "bb.msg.LogFilterShowOnce",
323 }
324 h.setdefault("filters", []).append(once_filter_name)
325
326 for l in logconfig["loggers"].values():
327 if "level" in l:
328 l["level"] = bb.msg.stringToLevel(l["level"])
329
330 conf = logging.config.dictConfigClass(logconfig)
331 conf.configure()
332
333 # The user may have specified logging domains they want at a higher debug
334 # level than the standard.
335 for name, l in logconfig["loggers"].items():
336 if not name.startswith("BitBake."):
337 continue
338
339 if not "level" in l:
340 continue
341
342 curlevel = bb.msg.loggerDefaultDomains.get(name)
343 # Note: level parameter should already be a int because of conversion
344 # above
345 newlevel = int(l["level"])
346 if curlevel is None or newlevel < curlevel:
347 bb.msg.loggerDefaultDomains[name] = newlevel
348
349 # TODO: I don't think that setting the global log level should be necessary
350 #if newlevel < bb.msg.loggerDefaultLogLevel:
351 # bb.msg.loggerDefaultLogLevel = newlevel
352
353 return conf