]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Source/Python/Common/EdkLogger.py
BaseTools: Add the support for python 2
[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
f51461c8
LG
98\r
99## Log level constants\r
100DEBUG_0 = 1\r
101DEBUG_1 = 2\r
102DEBUG_2 = 3\r
103DEBUG_3 = 4\r
104DEBUG_4 = 5\r
105DEBUG_5 = 6\r
106DEBUG_6 = 7\r
107DEBUG_7 = 8\r
108DEBUG_8 = 9\r
109DEBUG_9 = 10\r
110VERBOSE = 15\r
111INFO = 20\r
112WARN = 30\r
113QUIET = 40\r
114ERROR = 50\r
183ca964 115SILENT = 99\r
f51461c8
LG
116\r
117IsRaiseError = True\r
118\r
119# Tool name\r
120_ToolName = os.path.basename(sys.argv[0])\r
121\r
122# For validation purpose\r
183ca964
JJ
123_LogLevels = [DEBUG_0, DEBUG_1, DEBUG_2, DEBUG_3, DEBUG_4, DEBUG_5,\r
124 DEBUG_6, DEBUG_7, DEBUG_8, DEBUG_9, VERBOSE, WARN, INFO,\r
125 ERROR, QUIET, SILENT]\r
f51461c8
LG
126\r
127# For DEBUG level (All DEBUG_0~9 are applicable)\r
128_DebugLogger = logging.getLogger("tool_debug")\r
129_DebugFormatter = logging.Formatter("[%(asctime)s.%(msecs)d]: %(message)s", datefmt="%H:%M:%S")\r
130\r
131# For VERBOSE, INFO, WARN level\r
132_InfoLogger = logging.getLogger("tool_info")\r
133_InfoFormatter = logging.Formatter("%(message)s")\r
134\r
135# For ERROR level\r
136_ErrorLogger = logging.getLogger("tool_error")\r
137_ErrorFormatter = logging.Formatter("%(message)s")\r
138\r
139# String templates for ERROR/WARN/DEBUG log message\r
140_ErrorMessageTemplate = '\n\n%(tool)s...\n%(file)s(%(line)s): error %(errorcode)04X: %(msg)s\n\t%(extra)s'\r
141_ErrorMessageTemplateWithoutFile = '\n\n%(tool)s...\n : error %(errorcode)04X: %(msg)s\n\t%(extra)s'\r
142_WarningMessageTemplate = '%(tool)s...\n%(file)s(%(line)s): warning: %(msg)s'\r
143_WarningMessageTemplateWithoutFile = '%(tool)s: : warning: %(msg)s'\r
144_DebugMessageTemplate = '%(file)s(%(line)s): debug: \n %(msg)s'\r
145\r
146#\r
147# Flag used to take WARN as ERROR.\r
148# By default, only ERROR message will break the tools execution.\r
149#\r
150_WarningAsError = False\r
151\r
152## Log debug message\r
153#\r
154# @param Level DEBUG level (DEBUG0~9)\r
155# @param Message Debug information\r
156# @param ExtraData More information associated with "Message"\r
157#\r
158def debug(Level, Message, ExtraData=None):\r
159 if _DebugLogger.level > Level:\r
160 return\r
161 if Level > DEBUG_9:\r
162 return\r
163\r
164 # Find out the caller method information\r
165 CallerStack = traceback.extract_stack()[-2]\r
166 TemplateDict = {\r
167 "file" : CallerStack[0],\r
168 "line" : CallerStack[1],\r
169 "msg" : Message,\r
170 }\r
171\r
4231a819 172 if ExtraData is not None:\r
f51461c8
LG
173 LogText = _DebugMessageTemplate % TemplateDict + "\n %s" % ExtraData\r
174 else:\r
175 LogText = _DebugMessageTemplate % TemplateDict\r
176\r
177 _DebugLogger.log(Level, LogText)\r
178\r
179## Log verbose message\r
180#\r
181# @param Message Verbose information\r
182#\r
183def verbose(Message):\r
184 return _InfoLogger.log(VERBOSE, Message)\r
185\r
186## Log warning message\r
187#\r
188# Warning messages are those which might be wrong but won't fail the tool.\r
189#\r
190# @param ToolName The name of the tool. If not given, the name of caller\r
191# method will be used.\r
192# @param Message Warning information\r
193# @param File The name of file which caused the warning.\r
194# @param Line The line number in the "File" which caused the warning.\r
195# @param ExtraData More information associated with "Message"\r
196#\r
197def warn(ToolName, Message, File=None, Line=None, ExtraData=None):\r
198 if _InfoLogger.level > WARN:\r
199 return\r
200\r
201 # if no tool name given, use caller's source file name as tool name\r
4231a819 202 if ToolName is None or ToolName == "":\r
f51461c8
LG
203 ToolName = os.path.basename(traceback.extract_stack()[-2][0])\r
204\r
4231a819 205 if Line is None:\r
f51461c8
LG
206 Line = "..."\r
207 else:\r
208 Line = "%d" % Line\r
209\r
210 TemplateDict = {\r
211 "tool" : ToolName,\r
212 "file" : File,\r
213 "line" : Line,\r
214 "msg" : Message,\r
215 }\r
216\r
4231a819 217 if File is not None:\r
f51461c8
LG
218 LogText = _WarningMessageTemplate % TemplateDict\r
219 else:\r
220 LogText = _WarningMessageTemplateWithoutFile % TemplateDict\r
221\r
4231a819 222 if ExtraData is not None:\r
f51461c8
LG
223 LogText += "\n %s" % ExtraData\r
224\r
225 _InfoLogger.log(WARN, LogText)\r
226\r
fb0b35e0 227 # Raise an exception if indicated\r
f51461c8
LG
228 if _WarningAsError == True:\r
229 raise FatalError(WARNING_AS_ERROR)\r
230\r
231## Log INFO message\r
232info = _InfoLogger.info\r
233\r
234## Log ERROR message\r
235#\r
236# Once an error messages is logged, the tool's execution will be broken by raising\r
fb0b35e0 237# an exception. If you don't want to break the execution later, you can give\r
f51461c8
LG
238# "RaiseError" with "False" value.\r
239#\r
240# @param ToolName The name of the tool. If not given, the name of caller\r
241# method will be used.\r
242# @param ErrorCode The error code\r
243# @param Message Warning information\r
244# @param File The name of file which caused the error.\r
245# @param Line The line number in the "File" which caused the warning.\r
246# @param ExtraData More information associated with "Message"\r
fb0b35e0 247# @param RaiseError Raise an exception to break the tool's execution if\r
f51461c8
LG
248# it's True. This is the default behavior.\r
249#\r
250def error(ToolName, ErrorCode, Message=None, File=None, Line=None, ExtraData=None, RaiseError=IsRaiseError):\r
4231a819 251 if Line is None:\r
f51461c8
LG
252 Line = "..."\r
253 else:\r
254 Line = "%d" % Line\r
255\r
4231a819 256 if Message is None:\r
f51461c8
LG
257 if ErrorCode in gErrorMessage:\r
258 Message = gErrorMessage[ErrorCode]\r
259 else:\r
260 Message = gErrorMessage[UNKNOWN_ERROR]\r
261\r
4231a819 262 if ExtraData is None:\r
f51461c8
LG
263 ExtraData = ""\r
264\r
265 TemplateDict = {\r
266 "tool" : _ToolName,\r
267 "file" : File,\r
268 "line" : Line,\r
269 "errorcode" : ErrorCode,\r
270 "msg" : Message,\r
271 "extra" : ExtraData\r
272 }\r
273\r
4231a819 274 if File is not None:\r
f51461c8
LG
275 LogText = _ErrorMessageTemplate % TemplateDict\r
276 else:\r
277 LogText = _ErrorMessageTemplateWithoutFile % TemplateDict\r
278\r
279 _ErrorLogger.log(ERROR, LogText)\r
4423f0bc
YZ
280\r
281 if RaiseError and IsRaiseError:\r
f51461c8
LG
282 raise FatalError(ErrorCode)\r
283\r
284# Log information which should be always put out\r
285quiet = _ErrorLogger.error\r
286\r
287## Initialize log system\r
636ed13a 288def LogClientInitialize(log_q):\r
f51461c8
LG
289 #\r
290 # Since we use different format to log different levels of message into different\r
291 # place (stdout or stderr), we have to use different "Logger" objects to do this.\r
292 #\r
293 # For DEBUG level (All DEBUG_0~9 are applicable)\r
294 _DebugLogger.setLevel(INFO)\r
4acae2b3 295 _DebugChannel = QueueHandler(log_q)\r
f51461c8
LG
296 _DebugChannel.setFormatter(_DebugFormatter)\r
297 _DebugLogger.addHandler(_DebugChannel)\r
298\r
299 # For VERBOSE, INFO, WARN level\r
300 _InfoLogger.setLevel(INFO)\r
4acae2b3 301 _InfoChannel = QueueHandler(log_q)\r
f51461c8
LG
302 _InfoChannel.setFormatter(_InfoFormatter)\r
303 _InfoLogger.addHandler(_InfoChannel)\r
304\r
305 # For ERROR level\r
306 _ErrorLogger.setLevel(INFO)\r
4acae2b3 307 _ErrorCh = QueueHandler(log_q)\r
f51461c8
LG
308 _ErrorCh.setFormatter(_ErrorFormatter)\r
309 _ErrorLogger.addHandler(_ErrorCh)\r
310\r
311## Set log level\r
312#\r
313# @param Level One of log level in _LogLevel\r
314def SetLevel(Level):\r
315 if Level not in _LogLevels:\r
316 info("Not supported log level (%d). Use default level instead." % Level)\r
317 Level = INFO\r
318 _DebugLogger.setLevel(Level)\r
319 _InfoLogger.setLevel(Level)\r
320 _ErrorLogger.setLevel(Level)\r
321\r
636ed13a
FB
322## Initialize log system\r
323def Initialize():\r
324 #\r
325 # Since we use different format to log different levels of message into different\r
326 # place (stdout or stderr), we have to use different "Logger" objects to do this.\r
327 #\r
328 # For DEBUG level (All DEBUG_0~9 are applicable)\r
329 _DebugLogger.setLevel(INFO)\r
330 _DebugChannel = logging.StreamHandler(sys.stdout)\r
331 _DebugChannel.setFormatter(_DebugFormatter)\r
332 _DebugLogger.addHandler(_DebugChannel)\r
333\r
334 # For VERBOSE, INFO, WARN level\r
335 _InfoLogger.setLevel(INFO)\r
336 _InfoChannel = logging.StreamHandler(sys.stdout)\r
337 _InfoChannel.setFormatter(_InfoFormatter)\r
338 _InfoLogger.addHandler(_InfoChannel)\r
339\r
340 # For ERROR level\r
341 _ErrorLogger.setLevel(INFO)\r
342 _ErrorCh = logging.StreamHandler(sys.stderr)\r
343 _ErrorCh.setFormatter(_ErrorFormatter)\r
344 _ErrorLogger.addHandler(_ErrorCh)\r
345\r
183ca964
JJ
346def InitializeForUnitTest():\r
347 Initialize()\r
348 SetLevel(SILENT)\r
349\r
f51461c8
LG
350## Get current log level\r
351def GetLevel():\r
352 return _InfoLogger.getEffectiveLevel()\r
353\r
354## Raise up warning as error\r
355def SetWarningAsError():\r
356 global _WarningAsError\r
357 _WarningAsError = True\r
358\r
359## Specify a file to store the log message as well as put on console\r
360#\r
361# @param LogFile The file path used to store the log message\r
362#\r
363def SetLogFile(LogFile):\r
364 if os.path.exists(LogFile):\r
365 os.remove(LogFile)\r
366\r
367 _Ch = logging.FileHandler(LogFile)\r
368 _Ch.setFormatter(_DebugFormatter)\r
369 _DebugLogger.addHandler(_Ch)\r
370\r
371 _Ch= logging.FileHandler(LogFile)\r
372 _Ch.setFormatter(_InfoFormatter)\r
373 _InfoLogger.addHandler(_Ch)\r
374\r
375 _Ch = logging.FileHandler(LogFile)\r
376 _Ch.setFormatter(_ErrorFormatter)\r
377 _ErrorLogger.addHandler(_Ch)\r
378\r
379if __name__ == '__main__':\r
380 pass\r
381\r