]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/Common/EdkLogger.py
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / BaseTools / Source / Python / Common / EdkLogger.py
1 ## @file
2 # This file implements the log mechanism for Python tools.
3 #
4 # Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
5 # SPDX-License-Identifier: BSD-2-Clause-Patent
6 #
7
8 # Copyright 2001-2016 by Vinay Sajip. All Rights Reserved.
9 #
10 # Permission to use, copy, modify, and distribute this software and its
11 # documentation for any purpose and without fee is hereby granted,
12 # provided that the above copyright notice appear in all copies and that
13 # both that copyright notice and this permission notice appear in
14 # supporting documentation, and that the name of Vinay Sajip
15 # not be used in advertising or publicity pertaining to distribution
16 # of the software without specific, written prior permission.
17 # VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
18 # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
19 # VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
20 # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
21 # IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
22 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 # This copyright is for QueueHandler.
24
25 ## Import modules
26 from __future__ import absolute_import
27 import Common.LongFilePathOs as os, sys, logging
28 import traceback
29 from .BuildToolError import *
30 try:
31 from logging.handlers import QueueHandler
32 except:
33 class QueueHandler(logging.Handler):
34 """
35 This handler sends events to a queue. Typically, it would be used together
36 with a multiprocessing Queue to centralise logging to file in one process
37 (in a multi-process application), so as to avoid file write contention
38 between processes.
39
40 This code is new in Python 3.2, but this class can be copy pasted into
41 user code for use with earlier Python versions.
42 """
43
44 def __init__(self, queue):
45 """
46 Initialise an instance, using the passed queue.
47 """
48 logging.Handler.__init__(self)
49 self.queue = queue
50
51 def enqueue(self, record):
52 """
53 Enqueue a record.
54
55 The base implementation uses put_nowait. You may want to override
56 this method if you want to use blocking, timeouts or custom queue
57 implementations.
58 """
59 self.queue.put_nowait(record)
60
61 def prepare(self, record):
62 """
63 Prepares a record for queuing. The object returned by this method is
64 enqueued.
65
66 The base implementation formats the record to merge the message
67 and arguments, and removes unpickleable items from the record
68 in-place.
69
70 You might want to override this method if you want to convert
71 the record to a dict or JSON string, or send a modified copy
72 of the record while leaving the original intact.
73 """
74 # The format operation gets traceback text into record.exc_text
75 # (if there's exception data), and also returns the formatted
76 # message. We can then use this to replace the original
77 # msg + args, as these might be unpickleable. We also zap the
78 # exc_info and exc_text attributes, as they are no longer
79 # needed and, if not None, will typically not be pickleable.
80 msg = self.format(record)
81 record.message = msg
82 record.msg = msg
83 record.args = None
84 record.exc_info = None
85 record.exc_text = None
86 return record
87
88 def emit(self, record):
89 """
90 Emit a record.
91
92 Writes the LogRecord to the queue, preparing it for pickling first.
93 """
94 try:
95 self.enqueue(self.prepare(record))
96 except Exception:
97 self.handleError(record)
98 class BlockQueueHandler(QueueHandler):
99 def enqueue(self, record):
100 self.queue.put(record,True)
101 ## Log level constants
102 DEBUG_0 = 1
103 DEBUG_1 = 2
104 DEBUG_2 = 3
105 DEBUG_3 = 4
106 DEBUG_4 = 5
107 DEBUG_5 = 6
108 DEBUG_6 = 7
109 DEBUG_7 = 8
110 DEBUG_8 = 9
111 DEBUG_9 = 10
112 VERBOSE = 15
113 INFO = 20
114 WARN = 30
115 QUIET = 40
116 ERROR = 50
117 SILENT = 99
118
119 IsRaiseError = True
120
121 # Tool name
122 _ToolName = os.path.basename(sys.argv[0])
123
124 # For validation purpose
125 _LogLevels = [DEBUG_0, DEBUG_1, DEBUG_2, DEBUG_3, DEBUG_4, DEBUG_5,
126 DEBUG_6, DEBUG_7, DEBUG_8, DEBUG_9, VERBOSE, WARN, INFO,
127 ERROR, QUIET, SILENT]
128
129 # For DEBUG level (All DEBUG_0~9 are applicable)
130 _DebugLogger = logging.getLogger("tool_debug")
131 _DebugFormatter = logging.Formatter("[%(asctime)s.%(msecs)d]: %(message)s", datefmt="%H:%M:%S")
132
133 # For VERBOSE, INFO, WARN level
134 _InfoLogger = logging.getLogger("tool_info")
135 _InfoFormatter = logging.Formatter("%(message)s")
136
137 # For ERROR level
138 _ErrorLogger = logging.getLogger("tool_error")
139 _ErrorFormatter = logging.Formatter("%(message)s")
140
141 # String templates for ERROR/WARN/DEBUG log message
142 _ErrorMessageTemplate = '\n\n%(tool)s...\n%(file)s(%(line)s): error %(errorcode)04X: %(msg)s\n\t%(extra)s'
143 _ErrorMessageTemplateWithoutFile = '\n\n%(tool)s...\n : error %(errorcode)04X: %(msg)s\n\t%(extra)s'
144 _WarningMessageTemplate = '%(tool)s...\n%(file)s(%(line)s): warning: %(msg)s'
145 _WarningMessageTemplateWithoutFile = '%(tool)s: : warning: %(msg)s'
146 _DebugMessageTemplate = '%(file)s(%(line)s): debug: \n %(msg)s'
147
148 #
149 # Flag used to take WARN as ERROR.
150 # By default, only ERROR message will break the tools execution.
151 #
152 _WarningAsError = False
153
154 ## Log debug message
155 #
156 # @param Level DEBUG level (DEBUG0~9)
157 # @param Message Debug information
158 # @param ExtraData More information associated with "Message"
159 #
160 def debug(Level, Message, ExtraData=None):
161 if _DebugLogger.level > Level:
162 return
163 if Level > DEBUG_9:
164 return
165
166 # Find out the caller method information
167 CallerStack = traceback.extract_stack()[-2]
168 TemplateDict = {
169 "file" : CallerStack[0],
170 "line" : CallerStack[1],
171 "msg" : Message,
172 }
173
174 if ExtraData is not None:
175 LogText = _DebugMessageTemplate % TemplateDict + "\n %s" % ExtraData
176 else:
177 LogText = _DebugMessageTemplate % TemplateDict
178
179 _DebugLogger.log(Level, LogText)
180
181 ## Log verbose message
182 #
183 # @param Message Verbose information
184 #
185 def verbose(Message):
186 return _InfoLogger.log(VERBOSE, Message)
187
188 ## Log warning message
189 #
190 # Warning messages are those which might be wrong but won't fail the tool.
191 #
192 # @param ToolName The name of the tool. If not given, the name of caller
193 # method will be used.
194 # @param Message Warning information
195 # @param File The name of file which caused the warning.
196 # @param Line The line number in the "File" which caused the warning.
197 # @param ExtraData More information associated with "Message"
198 #
199 def warn(ToolName, Message, File=None, Line=None, ExtraData=None):
200 if _InfoLogger.level > WARN:
201 return
202
203 # if no tool name given, use caller's source file name as tool name
204 if ToolName is None or ToolName == "":
205 ToolName = os.path.basename(traceback.extract_stack()[-2][0])
206
207 if Line is None:
208 Line = "..."
209 else:
210 Line = "%d" % Line
211
212 TemplateDict = {
213 "tool" : ToolName,
214 "file" : File,
215 "line" : Line,
216 "msg" : Message,
217 }
218
219 if File is not None:
220 LogText = _WarningMessageTemplate % TemplateDict
221 else:
222 LogText = _WarningMessageTemplateWithoutFile % TemplateDict
223
224 if ExtraData is not None:
225 LogText += "\n %s" % ExtraData
226
227 _InfoLogger.log(WARN, LogText)
228
229 # Raise an exception if indicated
230 if _WarningAsError == True:
231 raise FatalError(WARNING_AS_ERROR)
232
233 ## Log INFO message
234 info = _InfoLogger.info
235
236 ## Log ERROR message
237 #
238 # Once an error messages is logged, the tool's execution will be broken by raising
239 # an exception. If you don't want to break the execution later, you can give
240 # "RaiseError" with "False" value.
241 #
242 # @param ToolName The name of the tool. If not given, the name of caller
243 # method will be used.
244 # @param ErrorCode The error code
245 # @param Message Warning information
246 # @param File The name of file which caused the error.
247 # @param Line The line number in the "File" which caused the warning.
248 # @param ExtraData More information associated with "Message"
249 # @param RaiseError Raise an exception to break the tool's execution if
250 # it's True. This is the default behavior.
251 #
252 def error(ToolName, ErrorCode, Message=None, File=None, Line=None, ExtraData=None, RaiseError=IsRaiseError):
253 if Line is None:
254 Line = "..."
255 else:
256 Line = "%d" % Line
257
258 if Message is None:
259 if ErrorCode in gErrorMessage:
260 Message = gErrorMessage[ErrorCode]
261 else:
262 Message = gErrorMessage[UNKNOWN_ERROR]
263
264 if ExtraData is None:
265 ExtraData = ""
266
267 TemplateDict = {
268 "tool" : _ToolName,
269 "file" : File,
270 "line" : Line,
271 "errorcode" : ErrorCode,
272 "msg" : Message,
273 "extra" : ExtraData
274 }
275
276 if File is not None:
277 LogText = _ErrorMessageTemplate % TemplateDict
278 else:
279 LogText = _ErrorMessageTemplateWithoutFile % TemplateDict
280
281 _ErrorLogger.log(ERROR, LogText)
282
283 if RaiseError and IsRaiseError:
284 raise FatalError(ErrorCode)
285
286 # Log information which should be always put out
287 quiet = _ErrorLogger.error
288
289 ## Initialize log system
290 def LogClientInitialize(log_q):
291 #
292 # Since we use different format to log different levels of message into different
293 # place (stdout or stderr), we have to use different "Logger" objects to do this.
294 #
295 # For DEBUG level (All DEBUG_0~9 are applicable)
296 _DebugLogger.setLevel(INFO)
297 _DebugChannel = BlockQueueHandler(log_q)
298 _DebugChannel.setFormatter(_DebugFormatter)
299 _DebugLogger.addHandler(_DebugChannel)
300
301 # For VERBOSE, INFO, WARN level
302 _InfoLogger.setLevel(INFO)
303 _InfoChannel = BlockQueueHandler(log_q)
304 _InfoChannel.setFormatter(_InfoFormatter)
305 _InfoLogger.addHandler(_InfoChannel)
306
307 # For ERROR level
308 _ErrorLogger.setLevel(INFO)
309 _ErrorCh = BlockQueueHandler(log_q)
310 _ErrorCh.setFormatter(_ErrorFormatter)
311 _ErrorLogger.addHandler(_ErrorCh)
312
313 ## Set log level
314 #
315 # @param Level One of log level in _LogLevel
316 def SetLevel(Level):
317 if Level not in _LogLevels:
318 info("Not supported log level (%d). Use default level instead." % Level)
319 Level = INFO
320 _DebugLogger.setLevel(Level)
321 _InfoLogger.setLevel(Level)
322 _ErrorLogger.setLevel(Level)
323
324 ## Initialize log system
325 def Initialize():
326 #
327 # Since we use different format to log different levels of message into different
328 # place (stdout or stderr), we have to use different "Logger" objects to do this.
329 #
330 # For DEBUG level (All DEBUG_0~9 are applicable)
331 _DebugLogger.setLevel(INFO)
332 _DebugChannel = logging.StreamHandler(sys.stdout)
333 _DebugChannel.setFormatter(_DebugFormatter)
334 _DebugLogger.addHandler(_DebugChannel)
335
336 # For VERBOSE, INFO, WARN level
337 _InfoLogger.setLevel(INFO)
338 _InfoChannel = logging.StreamHandler(sys.stdout)
339 _InfoChannel.setFormatter(_InfoFormatter)
340 _InfoLogger.addHandler(_InfoChannel)
341
342 # For ERROR level
343 _ErrorLogger.setLevel(INFO)
344 _ErrorCh = logging.StreamHandler(sys.stderr)
345 _ErrorCh.setFormatter(_ErrorFormatter)
346 _ErrorLogger.addHandler(_ErrorCh)
347
348 def InitializeForUnitTest():
349 Initialize()
350 SetLevel(SILENT)
351
352 ## Get current log level
353 def GetLevel():
354 return _InfoLogger.getEffectiveLevel()
355
356 ## Raise up warning as error
357 def SetWarningAsError():
358 global _WarningAsError
359 _WarningAsError = True
360
361 ## Specify a file to store the log message as well as put on console
362 #
363 # @param LogFile The file path used to store the log message
364 #
365 def SetLogFile(LogFile):
366 if os.path.exists(LogFile):
367 os.remove(LogFile)
368
369 _Ch = logging.FileHandler(LogFile)
370 _Ch.setFormatter(_DebugFormatter)
371 _DebugLogger.addHandler(_Ch)
372
373 _Ch= logging.FileHandler(LogFile)
374 _Ch.setFormatter(_InfoFormatter)
375 _InfoLogger.addHandler(_Ch)
376
377 _Ch = logging.FileHandler(LogFile)
378 _Ch.setFormatter(_ErrorFormatter)
379 _ErrorLogger.addHandler(_Ch)
380
381 if __name__ == '__main__':
382 pass
383