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