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
)
98 class BlockQueueHandler(QueueHandler
):
99 def enqueue(self
, record
):
100 self
.queue
.put(record
,True)
101 ## Log level constants
122 _ToolName
= os
.path
.basename(sys
.argv
[0])
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
]
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")
133 # For VERBOSE, INFO, WARN level
134 _InfoLogger
= logging
.getLogger("tool_info")
135 _InfoFormatter
= logging
.Formatter("%(message)s")
138 _ErrorLogger
= logging
.getLogger("tool_error")
139 _ErrorFormatter
= logging
.Formatter("%(message)s")
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'
149 # Flag used to take WARN as ERROR.
150 # By default, only ERROR message will break the tools execution.
152 _WarningAsError
= False
156 # @param Level DEBUG level (DEBUG0~9)
157 # @param Message Debug information
158 # @param ExtraData More information associated with "Message"
160 def debug(Level
, Message
, ExtraData
=None):
161 if _DebugLogger
.level
> Level
:
166 # Find out the caller method information
167 CallerStack
= traceback
.extract_stack()[-2]
169 "file" : CallerStack
[0],
170 "line" : CallerStack
[1],
174 if ExtraData
is not None:
175 LogText
= _DebugMessageTemplate
% TemplateDict
+ "\n %s" % ExtraData
177 LogText
= _DebugMessageTemplate
% TemplateDict
179 _DebugLogger
.log(Level
, LogText
)
181 ## Log verbose message
183 # @param Message Verbose information
185 def verbose(Message
):
186 return _InfoLogger
.log(VERBOSE
, Message
)
188 ## Log warning message
190 # Warning messages are those which might be wrong but won't fail the tool.
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"
199 def warn(ToolName
, Message
, File
=None, Line
=None, ExtraData
=None):
200 if _InfoLogger
.level
> WARN
:
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])
220 LogText
= _WarningMessageTemplate
% TemplateDict
222 LogText
= _WarningMessageTemplateWithoutFile
% TemplateDict
224 if ExtraData
is not None:
225 LogText
+= "\n %s" % ExtraData
227 _InfoLogger
.log(WARN
, LogText
)
229 # Raise an exception if indicated
230 if _WarningAsError
== True:
231 raise FatalError(WARNING_AS_ERROR
)
234 info
= _InfoLogger
.info
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.
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.
252 def error(ToolName
, ErrorCode
, Message
=None, File
=None, Line
=None, ExtraData
=None, RaiseError
=IsRaiseError
):
259 if ErrorCode
in gErrorMessage
:
260 Message
= gErrorMessage
[ErrorCode
]
262 Message
= gErrorMessage
[UNKNOWN_ERROR
]
264 if ExtraData
is None:
271 "errorcode" : ErrorCode
,
277 LogText
= _ErrorMessageTemplate
% TemplateDict
279 LogText
= _ErrorMessageTemplateWithoutFile
% TemplateDict
281 _ErrorLogger
.log(ERROR
, LogText
)
283 if RaiseError
and IsRaiseError
:
284 raise FatalError(ErrorCode
)
286 # Log information which should be always put out
287 quiet
= _ErrorLogger
.error
289 ## Initialize log system
290 def LogClientInitialize(log_q
):
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.
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
)
301 # For VERBOSE, INFO, WARN level
302 _InfoLogger
.setLevel(INFO
)
303 _InfoChannel
= BlockQueueHandler(log_q
)
304 _InfoChannel
.setFormatter(_InfoFormatter
)
305 _InfoLogger
.addHandler(_InfoChannel
)
308 _ErrorLogger
.setLevel(INFO
)
309 _ErrorCh
= BlockQueueHandler(log_q
)
310 _ErrorCh
.setFormatter(_ErrorFormatter
)
311 _ErrorLogger
.addHandler(_ErrorCh
)
315 # @param Level One of log level in _LogLevel
317 if Level
not in _LogLevels
:
318 info("Not supported log level (%d). Use default level instead." % Level
)
320 _DebugLogger
.setLevel(Level
)
321 _InfoLogger
.setLevel(Level
)
322 _ErrorLogger
.setLevel(Level
)
324 ## Initialize log system
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.
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
)
336 # For VERBOSE, INFO, WARN level
337 _InfoLogger
.setLevel(INFO
)
338 _InfoChannel
= logging
.StreamHandler(sys
.stdout
)
339 _InfoChannel
.setFormatter(_InfoFormatter
)
340 _InfoLogger
.addHandler(_InfoChannel
)
343 _ErrorLogger
.setLevel(INFO
)
344 _ErrorCh
= logging
.StreamHandler(sys
.stderr
)
345 _ErrorCh
.setFormatter(_ErrorFormatter
)
346 _ErrorLogger
.addHandler(_ErrorCh
)
348 def InitializeForUnitTest():
352 ## Get current log level
354 return _InfoLogger
.getEffectiveLevel()
356 ## Raise up warning as error
357 def SetWarningAsError():
358 global _WarningAsError
359 _WarningAsError
= True
361 ## Specify a file to store the log message as well as put on console
363 # @param LogFile The file path used to store the log message
365 def SetLogFile(LogFile
):
366 if os
.path
.exists(LogFile
):
369 _Ch
= logging
.FileHandler(LogFile
)
370 _Ch
.setFormatter(_DebugFormatter
)
371 _DebugLogger
.addHandler(_Ch
)
373 _Ch
= logging
.FileHandler(LogFile
)
374 _Ch
.setFormatter(_InfoFormatter
)
375 _InfoLogger
.addHandler(_Ch
)
377 _Ch
= logging
.FileHandler(LogFile
)
378 _Ch
.setFormatter(_ErrorFormatter
)
379 _ErrorLogger
.addHandler(_Ch
)
381 if __name__
== '__main__':