blob: 6216eb3bc4461cc301f38378427c145723e6f32d [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001"""
2BitBake 'msg' implementation
3
4Message handling infrastructure for bitbake
5
6"""
7
8# Copyright (C) 2006 Richard Purdie
9#
Brad Bishopc342db32019-05-15 21:57:59 -040010# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -050011#
Patrick Williamsc124f4f2015-09-15 14:41:29 -050012
13import sys
14import copy
15import logging
16import collections
17from itertools import groupby
18import warnings
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
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080031 VERBNOTE = logging.INFO + 2
Patrick Williamsc124f4f2015-09-15 14:41:29 -050032 ERROR = logging.ERROR
33 WARNING = logging.WARNING
34 CRITICAL = logging.CRITICAL
35
36 levelnames = {
37 DEBUG3 : 'DEBUG',
38 DEBUG2 : 'DEBUG',
39 DEBUG : 'DEBUG',
40 VERBOSE: 'NOTE',
41 NOTE : 'NOTE',
42 PLAIN : '',
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080043 VERBNOTE: 'NOTE',
Patrick Williamsc124f4f2015-09-15 14:41:29 -050044 WARNING : 'WARNING',
45 ERROR : 'ERROR',
46 CRITICAL: 'ERROR',
47 }
48
49 color_enabled = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -060050 BASECOLOR, BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = list(range(29,38))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050051
52 COLORS = {
53 DEBUG3 : CYAN,
54 DEBUG2 : CYAN,
55 DEBUG : CYAN,
56 VERBOSE : BASECOLOR,
57 NOTE : BASECOLOR,
58 PLAIN : BASECOLOR,
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080059 VERBNOTE: BASECOLOR,
Patrick Williamsc124f4f2015-09-15 14:41:29 -050060 WARNING : YELLOW,
61 ERROR : RED,
62 CRITICAL: RED,
63 }
64
65 BLD = '\033[1;%dm'
66 STD = '\033[%dm'
67 RST = '\033[0m'
68
69 def getLevelName(self, levelno):
70 try:
71 return self.levelnames[levelno]
72 except KeyError:
73 self.levelnames[levelno] = value = 'Level %d' % levelno
74 return value
75
76 def format(self, record):
77 record.levelname = self.getLevelName(record.levelno)
78 if record.levelno == self.PLAIN:
79 msg = record.getMessage()
80 else:
81 if self.color_enabled:
82 record = self.colorize(record)
83 msg = logging.Formatter.format(self, record)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060084 if hasattr(record, 'bb_exc_formatted'):
85 msg += '\n' + ''.join(record.bb_exc_formatted)
86 elif hasattr(record, 'bb_exc_info'):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050087 etype, value, tb = record.bb_exc_info
88 formatted = bb.exceptions.format_exception(etype, value, tb, limit=5)
89 msg += '\n' + ''.join(formatted)
90 return msg
91
92 def colorize(self, record):
93 color = self.COLORS[record.levelno]
94 if self.color_enabled and color is not None:
95 record = copy.copy(record)
96 record.levelname = "".join([self.BLD % color, record.levelname, self.RST])
97 record.msg = "".join([self.STD % color, record.msg, self.RST])
98 return record
99
100 def enable_color(self):
101 self.color_enabled = True
102
103class BBLogFilter(object):
104 def __init__(self, handler, level, debug_domains):
105 self.stdlevel = level
106 self.debug_domains = debug_domains
107 loglevel = level
108 for domain in debug_domains:
109 if debug_domains[domain] < loglevel:
110 loglevel = debug_domains[domain]
111 handler.setLevel(loglevel)
112 handler.addFilter(self)
113
114 def filter(self, record):
115 if record.levelno >= self.stdlevel:
116 return True
117 if record.name in self.debug_domains and record.levelno >= self.debug_domains[record.name]:
118 return True
119 return False
120
121class BBLogFilterStdErr(BBLogFilter):
122 def filter(self, record):
123 if not BBLogFilter.filter(self, record):
124 return False
125 if record.levelno >= logging.ERROR:
126 return True
127 return False
128
129class BBLogFilterStdOut(BBLogFilter):
130 def filter(self, record):
131 if not BBLogFilter.filter(self, record):
132 return False
133 if record.levelno < logging.ERROR:
134 return True
135 return False
136
137# Message control functions
138#
139
140loggerDefaultDebugLevel = 0
141loggerDefaultVerbose = False
142loggerVerboseLogs = False
143loggerDefaultDomains = []
144
145def init_msgconfig(verbose, debug, debug_domains=None):
146 """
147 Set default verbosity and debug levels config the logger
148 """
149 bb.msg.loggerDefaultDebugLevel = debug
150 bb.msg.loggerDefaultVerbose = verbose
151 if verbose:
152 bb.msg.loggerVerboseLogs = True
153 if debug_domains:
154 bb.msg.loggerDefaultDomains = debug_domains
155 else:
156 bb.msg.loggerDefaultDomains = []
157
158def constructLogOptions():
159 debug = loggerDefaultDebugLevel
160 verbose = loggerDefaultVerbose
161 domains = loggerDefaultDomains
162
163 if debug:
164 level = BBLogFormatter.DEBUG - debug + 1
165 elif verbose:
166 level = BBLogFormatter.VERBOSE
167 else:
168 level = BBLogFormatter.NOTE
169
170 debug_domains = {}
171 for (domainarg, iterator) in groupby(domains):
172 dlevel = len(tuple(iterator))
173 debug_domains["BitBake.%s" % domainarg] = logging.DEBUG - dlevel + 1
174 return level, debug_domains
175
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600176def addDefaultlogFilter(handler, cls = BBLogFilter, forcelevel=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500177 level, debug_domains = constructLogOptions()
178
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600179 if forcelevel is not None:
180 level = forcelevel
181
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500182 cls(handler, level, debug_domains)
183
184#
185# Message handling functions
186#
187
188def fatal(msgdomain, msg):
189 if msgdomain:
190 logger = logging.getLogger("BitBake.%s" % msgdomain)
191 else:
192 logger = logging.getLogger("BitBake")
193 logger.critical(msg)
194 sys.exit(1)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500195
196def logger_create(name, output=sys.stderr, level=logging.INFO, preserve_handlers=False, color='auto'):
197 """Standalone logger creation function"""
198 logger = logging.getLogger(name)
199 console = logging.StreamHandler(output)
200 format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
201 if color == 'always' or (color == 'auto' and output.isatty()):
202 format.enable_color()
203 console.setFormatter(format)
204 if preserve_handlers:
205 logger.addHandler(console)
206 else:
207 logger.handlers = [console]
208 logger.setLevel(level)
209 return logger
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500210
211def has_console_handler(logger):
212 for handler in logger.handlers:
213 if isinstance(handler, logging.StreamHandler):
214 if handler.stream in [sys.stderr, sys.stdout]:
215 return True
216 return False