blob: f1723be797169dadd01d2c1b029ae83d561e1e44 [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
43 ERROR = logging.ERROR
44 WARNING = logging.WARNING
45 CRITICAL = logging.CRITICAL
46
47 levelnames = {
48 DEBUG3 : 'DEBUG',
49 DEBUG2 : 'DEBUG',
50 DEBUG : 'DEBUG',
51 VERBOSE: 'NOTE',
52 NOTE : 'NOTE',
53 PLAIN : '',
54 WARNING : 'WARNING',
55 ERROR : 'ERROR',
56 CRITICAL: 'ERROR',
57 }
58
59 color_enabled = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -060060 BASECOLOR, BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = list(range(29,38))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050061
62 COLORS = {
63 DEBUG3 : CYAN,
64 DEBUG2 : CYAN,
65 DEBUG : CYAN,
66 VERBOSE : BASECOLOR,
67 NOTE : BASECOLOR,
68 PLAIN : BASECOLOR,
69 WARNING : YELLOW,
70 ERROR : RED,
71 CRITICAL: RED,
72 }
73
74 BLD = '\033[1;%dm'
75 STD = '\033[%dm'
76 RST = '\033[0m'
77
78 def getLevelName(self, levelno):
79 try:
80 return self.levelnames[levelno]
81 except KeyError:
82 self.levelnames[levelno] = value = 'Level %d' % levelno
83 return value
84
85 def format(self, record):
86 record.levelname = self.getLevelName(record.levelno)
87 if record.levelno == self.PLAIN:
88 msg = record.getMessage()
89 else:
90 if self.color_enabled:
91 record = self.colorize(record)
92 msg = logging.Formatter.format(self, record)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060093 if hasattr(record, 'bb_exc_formatted'):
94 msg += '\n' + ''.join(record.bb_exc_formatted)
95 elif hasattr(record, 'bb_exc_info'):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050096 etype, value, tb = record.bb_exc_info
97 formatted = bb.exceptions.format_exception(etype, value, tb, limit=5)
98 msg += '\n' + ''.join(formatted)
99 return msg
100
101 def colorize(self, record):
102 color = self.COLORS[record.levelno]
103 if self.color_enabled and color is not None:
104 record = copy.copy(record)
105 record.levelname = "".join([self.BLD % color, record.levelname, self.RST])
106 record.msg = "".join([self.STD % color, record.msg, self.RST])
107 return record
108
109 def enable_color(self):
110 self.color_enabled = True
111
112class BBLogFilter(object):
113 def __init__(self, handler, level, debug_domains):
114 self.stdlevel = level
115 self.debug_domains = debug_domains
116 loglevel = level
117 for domain in debug_domains:
118 if debug_domains[domain] < loglevel:
119 loglevel = debug_domains[domain]
120 handler.setLevel(loglevel)
121 handler.addFilter(self)
122
123 def filter(self, record):
124 if record.levelno >= self.stdlevel:
125 return True
126 if record.name in self.debug_domains and record.levelno >= self.debug_domains[record.name]:
127 return True
128 return False
129
130class BBLogFilterStdErr(BBLogFilter):
131 def filter(self, record):
132 if not BBLogFilter.filter(self, record):
133 return False
134 if record.levelno >= logging.ERROR:
135 return True
136 return False
137
138class BBLogFilterStdOut(BBLogFilter):
139 def filter(self, record):
140 if not BBLogFilter.filter(self, record):
141 return False
142 if record.levelno < logging.ERROR:
143 return True
144 return False
145
146# Message control functions
147#
148
149loggerDefaultDebugLevel = 0
150loggerDefaultVerbose = False
151loggerVerboseLogs = False
152loggerDefaultDomains = []
153
154def init_msgconfig(verbose, debug, debug_domains=None):
155 """
156 Set default verbosity and debug levels config the logger
157 """
158 bb.msg.loggerDefaultDebugLevel = debug
159 bb.msg.loggerDefaultVerbose = verbose
160 if verbose:
161 bb.msg.loggerVerboseLogs = True
162 if debug_domains:
163 bb.msg.loggerDefaultDomains = debug_domains
164 else:
165 bb.msg.loggerDefaultDomains = []
166
167def constructLogOptions():
168 debug = loggerDefaultDebugLevel
169 verbose = loggerDefaultVerbose
170 domains = loggerDefaultDomains
171
172 if debug:
173 level = BBLogFormatter.DEBUG - debug + 1
174 elif verbose:
175 level = BBLogFormatter.VERBOSE
176 else:
177 level = BBLogFormatter.NOTE
178
179 debug_domains = {}
180 for (domainarg, iterator) in groupby(domains):
181 dlevel = len(tuple(iterator))
182 debug_domains["BitBake.%s" % domainarg] = logging.DEBUG - dlevel + 1
183 return level, debug_domains
184
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600185def addDefaultlogFilter(handler, cls = BBLogFilter, forcelevel=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500186 level, debug_domains = constructLogOptions()
187
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600188 if forcelevel is not None:
189 level = forcelevel
190
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500191 cls(handler, level, debug_domains)
192
193#
194# Message handling functions
195#
196
197def fatal(msgdomain, msg):
198 if msgdomain:
199 logger = logging.getLogger("BitBake.%s" % msgdomain)
200 else:
201 logger = logging.getLogger("BitBake")
202 logger.critical(msg)
203 sys.exit(1)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500204
205def logger_create(name, output=sys.stderr, level=logging.INFO, preserve_handlers=False, color='auto'):
206 """Standalone logger creation function"""
207 logger = logging.getLogger(name)
208 console = logging.StreamHandler(output)
209 format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
210 if color == 'always' or (color == 'auto' and output.isatty()):
211 format.enable_color()
212 console.setFormatter(format)
213 if preserve_handlers:
214 logger.addHandler(console)
215 else:
216 logger.handlers = [console]
217 logger.setLevel(level)
218 return logger
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500219
220def has_console_handler(logger):
221 for handler in logger.handlers:
222 if isinstance(handler, logging.StreamHandler):
223 if handler.stream in [sys.stderr, sys.stdout]:
224 return True
225 return False