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