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