2 # This file implements the log mechanism for Python tools.
4 # Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
5 # SPDX-License-Identifier: BSD-2-Clause-Patent
8 # Copyright 2001-2016 by Vinay Sajip. All Rights Reserved.
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.
26 from __future__
import absolute_import
27 import Common
.LongFilePathOs
as os
, sys
, logging
29 from .BuildToolError
import *
31 from logging
.handlers
import QueueHandler
33 class QueueHandler(logging
.Handler
):
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
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.
44 def __init__(self
, queue
):
46 Initialise an instance, using the passed queue.
48 logging
.Handler
.__init
__(self
)
51 def enqueue(self
, record
):
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
59 self
.queue
.put_nowait(record
)
61 def prepare(self
, record
):
63 Prepares a record for queuing. The object returned by this method is
66 The base implementation formats the record to merge the message
67 and arguments, and removes unpickleable items from the record
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.
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
)
84 record
.exc_info
= None
85 record
.exc_text
= None
88 def emit(self
, record
):
92 Writes the LogRecord to the queue, preparing it for pickling first.
95 self
.enqueue(self
.prepare(record
))
97 self
.handleError(record
)
99 ## Log level constants
120 _ToolName
= os
.path
.basename(sys
.argv
[0])
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
]
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")
131 # For VERBOSE, INFO, WARN level
132 _InfoLogger
= logging
.getLogger("tool_info")
133 _InfoFormatter
= logging
.Formatter("%(message)s")
136 _ErrorLogger
= logging
.getLogger("tool_error")
137 _ErrorFormatter
= logging
.Formatter("%(message)s")
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'
147 # Flag used to take WARN as ERROR.
148 # By default, only ERROR message will break the tools execution.
150 _WarningAsError
= False
154 # @param Level DEBUG level (DEBUG0~9)
155 # @param Message Debug information
156 # @param ExtraData More information associated with "Message"
158 def debug(Level
, Message
, ExtraData
=None):
159 if _DebugLogger
.level
> Level
:
164 # Find out the caller method information
165 CallerStack
= traceback
.extract_stack()[-2]
167 "file" : CallerStack
[0],
168 "line" : CallerStack
[1],
172 if ExtraData
is not None:
173 LogText
= _DebugMessageTemplate
% TemplateDict
+ "\n %s" % ExtraData
175 LogText
= _DebugMessageTemplate
% TemplateDict
177 _DebugLogger
.log(Level
, LogText
)
179 ## Log verbose message
181 # @param Message Verbose information
183 def verbose(Message
):
184 return _InfoLogger
.log(VERBOSE
, Message
)
186 ## Log warning message
188 # Warning messages are those which might be wrong but won't fail the tool.
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"
197 def warn(ToolName
, Message
, File
=None, Line
=None, ExtraData
=None):
198 if _InfoLogger
.level
> WARN
:
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])
218 LogText
= _WarningMessageTemplate
% TemplateDict
220 LogText
= _WarningMessageTemplateWithoutFile
% TemplateDict
222 if ExtraData
is not None:
223 LogText
+= "\n %s" % ExtraData
225 _InfoLogger
.log(WARN
, LogText
)
227 # Raise an exception if indicated
228 if _WarningAsError
== True:
229 raise FatalError(WARNING_AS_ERROR
)
232 info
= _InfoLogger
.info
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.
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.
250 def error(ToolName
, ErrorCode
, Message
=None, File
=None, Line
=None, ExtraData
=None, RaiseError
=IsRaiseError
):
257 if ErrorCode
in gErrorMessage
:
258 Message
= gErrorMessage
[ErrorCode
]
260 Message
= gErrorMessage
[UNKNOWN_ERROR
]
262 if ExtraData
is None:
269 "errorcode" : ErrorCode
,
275 LogText
= _ErrorMessageTemplate
% TemplateDict
277 LogText
= _ErrorMessageTemplateWithoutFile
% TemplateDict
279 _ErrorLogger
.log(ERROR
, LogText
)
281 if RaiseError
and IsRaiseError
:
282 raise FatalError(ErrorCode
)
284 # Log information which should be always put out
285 quiet
= _ErrorLogger
.error
287 ## Initialize log system
288 def LogClientInitialize(log_q
):
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.
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
)
299 # For VERBOSE, INFO, WARN level
300 _InfoLogger
.setLevel(INFO
)
301 _InfoChannel
= QueueHandler(log_q
)
302 _InfoChannel
.setFormatter(_InfoFormatter
)
303 _InfoLogger
.addHandler(_InfoChannel
)
306 _ErrorLogger
.setLevel(INFO
)
307 _ErrorCh
= QueueHandler(log_q
)
308 _ErrorCh
.setFormatter(_ErrorFormatter
)
309 _ErrorLogger
.addHandler(_ErrorCh
)
313 # @param Level One of log level in _LogLevel
315 if Level
not in _LogLevels
:
316 info("Not supported log level (%d). Use default level instead." % Level
)
318 _DebugLogger
.setLevel(Level
)
319 _InfoLogger
.setLevel(Level
)
320 _ErrorLogger
.setLevel(Level
)
322 ## Initialize log system
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.
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
)
334 # For VERBOSE, INFO, WARN level
335 _InfoLogger
.setLevel(INFO
)
336 _InfoChannel
= logging
.StreamHandler(sys
.stdout
)
337 _InfoChannel
.setFormatter(_InfoFormatter
)
338 _InfoLogger
.addHandler(_InfoChannel
)
341 _ErrorLogger
.setLevel(INFO
)
342 _ErrorCh
= logging
.StreamHandler(sys
.stderr
)
343 _ErrorCh
.setFormatter(_ErrorFormatter
)
344 _ErrorLogger
.addHandler(_ErrorCh
)
346 def InitializeForUnitTest():
350 ## Get current log level
352 return _InfoLogger
.getEffectiveLevel()
354 ## Raise up warning as error
355 def SetWarningAsError():
356 global _WarningAsError
357 _WarningAsError
= True
359 ## Specify a file to store the log message as well as put on console
361 # @param LogFile The file path used to store the log message
363 def SetLogFile(LogFile
):
364 if os
.path
.exists(LogFile
):
367 _Ch
= logging
.FileHandler(LogFile
)
368 _Ch
.setFormatter(_DebugFormatter
)
369 _DebugLogger
.addHandler(_Ch
)
371 _Ch
= logging
.FileHandler(LogFile
)
372 _Ch
.setFormatter(_InfoFormatter
)
373 _InfoLogger
.addHandler(_Ch
)
375 _Ch
= logging
.FileHandler(LogFile
)
376 _Ch
.setFormatter(_ErrorFormatter
)
377 _ErrorLogger
.addHandler(_Ch
)
379 if __name__
== '__main__':