mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-26 16:47:40 +01:00
384 lines
13 KiB
Python
384 lines
13 KiB
Python
|
## @file
|
||
|
# This file implements the log mechanism for Python tools.
|
||
|
#
|
||
|
# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
|
||
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||
|
#
|
||
|
|
||
|
# Copyright 2001-2016 by Vinay Sajip. All Rights Reserved.
|
||
|
#
|
||
|
# Permission to use, copy, modify, and distribute this software and its
|
||
|
# documentation for any purpose and without fee is hereby granted,
|
||
|
# provided that the above copyright notice appear in all copies and that
|
||
|
# both that copyright notice and this permission notice appear in
|
||
|
# supporting documentation, and that the name of Vinay Sajip
|
||
|
# not be used in advertising or publicity pertaining to distribution
|
||
|
# of the software without specific, written prior permission.
|
||
|
# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
|
||
|
# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
||
|
# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
|
||
|
# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||
|
# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||
|
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||
|
# This copyright is for QueueHandler.
|
||
|
|
||
|
## Import modules
|
||
|
from __future__ import absolute_import
|
||
|
import Common.LongFilePathOs as os, sys, logging
|
||
|
import traceback
|
||
|
from .BuildToolError import *
|
||
|
try:
|
||
|
from logging.handlers import QueueHandler
|
||
|
except:
|
||
|
class QueueHandler(logging.Handler):
|
||
|
"""
|
||
|
This handler sends events to a queue. Typically, it would be used together
|
||
|
with a multiprocessing Queue to centralise logging to file in one process
|
||
|
(in a multi-process application), so as to avoid file write contention
|
||
|
between processes.
|
||
|
|
||
|
This code is new in Python 3.2, but this class can be copy pasted into
|
||
|
user code for use with earlier Python versions.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, queue):
|
||
|
"""
|
||
|
Initialise an instance, using the passed queue.
|
||
|
"""
|
||
|
logging.Handler.__init__(self)
|
||
|
self.queue = queue
|
||
|
|
||
|
def enqueue(self, record):
|
||
|
"""
|
||
|
Enqueue a record.
|
||
|
|
||
|
The base implementation uses put_nowait. You may want to override
|
||
|
this method if you want to use blocking, timeouts or custom queue
|
||
|
implementations.
|
||
|
"""
|
||
|
self.queue.put_nowait(record)
|
||
|
|
||
|
def prepare(self, record):
|
||
|
"""
|
||
|
Prepares a record for queuing. The object returned by this method is
|
||
|
enqueued.
|
||
|
|
||
|
The base implementation formats the record to merge the message
|
||
|
and arguments, and removes unpickleable items from the record
|
||
|
in-place.
|
||
|
|
||
|
You might want to override this method if you want to convert
|
||
|
the record to a dict or JSON string, or send a modified copy
|
||
|
of the record while leaving the original intact.
|
||
|
"""
|
||
|
# The format operation gets traceback text into record.exc_text
|
||
|
# (if there's exception data), and also returns the formatted
|
||
|
# message. We can then use this to replace the original
|
||
|
# msg + args, as these might be unpickleable. We also zap the
|
||
|
# exc_info and exc_text attributes, as they are no longer
|
||
|
# needed and, if not None, will typically not be pickleable.
|
||
|
msg = self.format(record)
|
||
|
record.message = msg
|
||
|
record.msg = msg
|
||
|
record.args = None
|
||
|
record.exc_info = None
|
||
|
record.exc_text = None
|
||
|
return record
|
||
|
|
||
|
def emit(self, record):
|
||
|
"""
|
||
|
Emit a record.
|
||
|
|
||
|
Writes the LogRecord to the queue, preparing it for pickling first.
|
||
|
"""
|
||
|
try:
|
||
|
self.enqueue(self.prepare(record))
|
||
|
except Exception:
|
||
|
self.handleError(record)
|
||
|
class BlockQueueHandler(QueueHandler):
|
||
|
def enqueue(self, record):
|
||
|
self.queue.put(record,True)
|
||
|
## Log level constants
|
||
|
DEBUG_0 = 1
|
||
|
DEBUG_1 = 2
|
||
|
DEBUG_2 = 3
|
||
|
DEBUG_3 = 4
|
||
|
DEBUG_4 = 5
|
||
|
DEBUG_5 = 6
|
||
|
DEBUG_6 = 7
|
||
|
DEBUG_7 = 8
|
||
|
DEBUG_8 = 9
|
||
|
DEBUG_9 = 10
|
||
|
VERBOSE = 15
|
||
|
INFO = 20
|
||
|
WARN = 30
|
||
|
QUIET = 40
|
||
|
ERROR = 50
|
||
|
SILENT = 99
|
||
|
|
||
|
IsRaiseError = True
|
||
|
|
||
|
# Tool name
|
||
|
_ToolName = os.path.basename(sys.argv[0])
|
||
|
|
||
|
# For validation purpose
|
||
|
_LogLevels = [DEBUG_0, DEBUG_1, DEBUG_2, DEBUG_3, DEBUG_4, DEBUG_5,
|
||
|
DEBUG_6, DEBUG_7, DEBUG_8, DEBUG_9, VERBOSE, WARN, INFO,
|
||
|
ERROR, QUIET, SILENT]
|
||
|
|
||
|
# For DEBUG level (All DEBUG_0~9 are applicable)
|
||
|
_DebugLogger = logging.getLogger("tool_debug")
|
||
|
_DebugFormatter = logging.Formatter("[%(asctime)s.%(msecs)d]: %(message)s", datefmt="%H:%M:%S")
|
||
|
|
||
|
# For VERBOSE, INFO, WARN level
|
||
|
_InfoLogger = logging.getLogger("tool_info")
|
||
|
_InfoFormatter = logging.Formatter("%(message)s")
|
||
|
|
||
|
# For ERROR level
|
||
|
_ErrorLogger = logging.getLogger("tool_error")
|
||
|
_ErrorFormatter = logging.Formatter("%(message)s")
|
||
|
|
||
|
# String templates for ERROR/WARN/DEBUG log message
|
||
|
_ErrorMessageTemplate = '\n\n%(tool)s...\n%(file)s(%(line)s): error %(errorcode)04X: %(msg)s\n\t%(extra)s'
|
||
|
_ErrorMessageTemplateWithoutFile = '\n\n%(tool)s...\n : error %(errorcode)04X: %(msg)s\n\t%(extra)s'
|
||
|
_WarningMessageTemplate = '%(tool)s...\n%(file)s(%(line)s): warning: %(msg)s'
|
||
|
_WarningMessageTemplateWithoutFile = '%(tool)s: : warning: %(msg)s'
|
||
|
_DebugMessageTemplate = '%(file)s(%(line)s): debug: \n %(msg)s'
|
||
|
|
||
|
#
|
||
|
# Flag used to take WARN as ERROR.
|
||
|
# By default, only ERROR message will break the tools execution.
|
||
|
#
|
||
|
_WarningAsError = False
|
||
|
|
||
|
## Log debug message
|
||
|
#
|
||
|
# @param Level DEBUG level (DEBUG0~9)
|
||
|
# @param Message Debug information
|
||
|
# @param ExtraData More information associated with "Message"
|
||
|
#
|
||
|
def debug(Level, Message, ExtraData=None):
|
||
|
if _DebugLogger.level > Level:
|
||
|
return
|
||
|
if Level > DEBUG_9:
|
||
|
return
|
||
|
|
||
|
# Find out the caller method information
|
||
|
CallerStack = traceback.extract_stack()[-2]
|
||
|
TemplateDict = {
|
||
|
"file" : CallerStack[0],
|
||
|
"line" : CallerStack[1],
|
||
|
"msg" : Message,
|
||
|
}
|
||
|
|
||
|
if ExtraData is not None:
|
||
|
LogText = _DebugMessageTemplate % TemplateDict + "\n %s" % ExtraData
|
||
|
else:
|
||
|
LogText = _DebugMessageTemplate % TemplateDict
|
||
|
|
||
|
_DebugLogger.log(Level, LogText)
|
||
|
|
||
|
## Log verbose message
|
||
|
#
|
||
|
# @param Message Verbose information
|
||
|
#
|
||
|
def verbose(Message):
|
||
|
return _InfoLogger.log(VERBOSE, Message)
|
||
|
|
||
|
## Log warning message
|
||
|
#
|
||
|
# Warning messages are those which might be wrong but won't fail the tool.
|
||
|
#
|
||
|
# @param ToolName The name of the tool. If not given, the name of caller
|
||
|
# method will be used.
|
||
|
# @param Message Warning information
|
||
|
# @param File The name of file which caused the warning.
|
||
|
# @param Line The line number in the "File" which caused the warning.
|
||
|
# @param ExtraData More information associated with "Message"
|
||
|
#
|
||
|
def warn(ToolName, Message, File=None, Line=None, ExtraData=None):
|
||
|
if _InfoLogger.level > WARN:
|
||
|
return
|
||
|
|
||
|
# if no tool name given, use caller's source file name as tool name
|
||
|
if ToolName is None or ToolName == "":
|
||
|
ToolName = os.path.basename(traceback.extract_stack()[-2][0])
|
||
|
|
||
|
if Line is None:
|
||
|
Line = "..."
|
||
|
else:
|
||
|
Line = "%d" % Line
|
||
|
|
||
|
TemplateDict = {
|
||
|
"tool" : ToolName,
|
||
|
"file" : File,
|
||
|
"line" : Line,
|
||
|
"msg" : Message,
|
||
|
}
|
||
|
|
||
|
if File is not None:
|
||
|
LogText = _WarningMessageTemplate % TemplateDict
|
||
|
else:
|
||
|
LogText = _WarningMessageTemplateWithoutFile % TemplateDict
|
||
|
|
||
|
if ExtraData is not None:
|
||
|
LogText += "\n %s" % ExtraData
|
||
|
|
||
|
_InfoLogger.log(WARN, LogText)
|
||
|
|
||
|
# Raise an exception if indicated
|
||
|
if _WarningAsError == True:
|
||
|
raise FatalError(WARNING_AS_ERROR)
|
||
|
|
||
|
## Log INFO message
|
||
|
info = _InfoLogger.info
|
||
|
|
||
|
## Log ERROR message
|
||
|
#
|
||
|
# Once an error messages is logged, the tool's execution will be broken by raising
|
||
|
# an exception. If you don't want to break the execution later, you can give
|
||
|
# "RaiseError" with "False" value.
|
||
|
#
|
||
|
# @param ToolName The name of the tool. If not given, the name of caller
|
||
|
# method will be used.
|
||
|
# @param ErrorCode The error code
|
||
|
# @param Message Warning information
|
||
|
# @param File The name of file which caused the error.
|
||
|
# @param Line The line number in the "File" which caused the warning.
|
||
|
# @param ExtraData More information associated with "Message"
|
||
|
# @param RaiseError Raise an exception to break the tool's execution if
|
||
|
# it's True. This is the default behavior.
|
||
|
#
|
||
|
def error(ToolName, ErrorCode, Message=None, File=None, Line=None, ExtraData=None, RaiseError=IsRaiseError):
|
||
|
if Line is None:
|
||
|
Line = "..."
|
||
|
else:
|
||
|
Line = "%d" % Line
|
||
|
|
||
|
if Message is None:
|
||
|
if ErrorCode in gErrorMessage:
|
||
|
Message = gErrorMessage[ErrorCode]
|
||
|
else:
|
||
|
Message = gErrorMessage[UNKNOWN_ERROR]
|
||
|
|
||
|
if ExtraData is None:
|
||
|
ExtraData = ""
|
||
|
|
||
|
TemplateDict = {
|
||
|
"tool" : _ToolName,
|
||
|
"file" : File,
|
||
|
"line" : Line,
|
||
|
"errorcode" : ErrorCode,
|
||
|
"msg" : Message,
|
||
|
"extra" : ExtraData
|
||
|
}
|
||
|
|
||
|
if File is not None:
|
||
|
LogText = _ErrorMessageTemplate % TemplateDict
|
||
|
else:
|
||
|
LogText = _ErrorMessageTemplateWithoutFile % TemplateDict
|
||
|
|
||
|
_ErrorLogger.log(ERROR, LogText)
|
||
|
|
||
|
if RaiseError and IsRaiseError:
|
||
|
raise FatalError(ErrorCode)
|
||
|
|
||
|
# Log information which should be always put out
|
||
|
quiet = _ErrorLogger.error
|
||
|
|
||
|
## Initialize log system
|
||
|
def LogClientInitialize(log_q):
|
||
|
#
|
||
|
# Since we use different format to log different levels of message into different
|
||
|
# place (stdout or stderr), we have to use different "Logger" objects to do this.
|
||
|
#
|
||
|
# For DEBUG level (All DEBUG_0~9 are applicable)
|
||
|
_DebugLogger.setLevel(INFO)
|
||
|
_DebugChannel = BlockQueueHandler(log_q)
|
||
|
_DebugChannel.setFormatter(_DebugFormatter)
|
||
|
_DebugLogger.addHandler(_DebugChannel)
|
||
|
|
||
|
# For VERBOSE, INFO, WARN level
|
||
|
_InfoLogger.setLevel(INFO)
|
||
|
_InfoChannel = BlockQueueHandler(log_q)
|
||
|
_InfoChannel.setFormatter(_InfoFormatter)
|
||
|
_InfoLogger.addHandler(_InfoChannel)
|
||
|
|
||
|
# For ERROR level
|
||
|
_ErrorLogger.setLevel(INFO)
|
||
|
_ErrorCh = BlockQueueHandler(log_q)
|
||
|
_ErrorCh.setFormatter(_ErrorFormatter)
|
||
|
_ErrorLogger.addHandler(_ErrorCh)
|
||
|
|
||
|
## Set log level
|
||
|
#
|
||
|
# @param Level One of log level in _LogLevel
|
||
|
def SetLevel(Level):
|
||
|
if Level not in _LogLevels:
|
||
|
info("Not supported log level (%d). Use default level instead." % Level)
|
||
|
Level = INFO
|
||
|
_DebugLogger.setLevel(Level)
|
||
|
_InfoLogger.setLevel(Level)
|
||
|
_ErrorLogger.setLevel(Level)
|
||
|
|
||
|
## Initialize log system
|
||
|
def Initialize():
|
||
|
#
|
||
|
# Since we use different format to log different levels of message into different
|
||
|
# place (stdout or stderr), we have to use different "Logger" objects to do this.
|
||
|
#
|
||
|
# For DEBUG level (All DEBUG_0~9 are applicable)
|
||
|
_DebugLogger.setLevel(INFO)
|
||
|
_DebugChannel = logging.StreamHandler(sys.stdout)
|
||
|
_DebugChannel.setFormatter(_DebugFormatter)
|
||
|
_DebugLogger.addHandler(_DebugChannel)
|
||
|
|
||
|
# For VERBOSE, INFO, WARN level
|
||
|
_InfoLogger.setLevel(INFO)
|
||
|
_InfoChannel = logging.StreamHandler(sys.stdout)
|
||
|
_InfoChannel.setFormatter(_InfoFormatter)
|
||
|
_InfoLogger.addHandler(_InfoChannel)
|
||
|
|
||
|
# For ERROR level
|
||
|
_ErrorLogger.setLevel(INFO)
|
||
|
_ErrorCh = logging.StreamHandler(sys.stderr)
|
||
|
_ErrorCh.setFormatter(_ErrorFormatter)
|
||
|
_ErrorLogger.addHandler(_ErrorCh)
|
||
|
|
||
|
def InitializeForUnitTest():
|
||
|
Initialize()
|
||
|
SetLevel(SILENT)
|
||
|
|
||
|
## Get current log level
|
||
|
def GetLevel():
|
||
|
return _InfoLogger.getEffectiveLevel()
|
||
|
|
||
|
## Raise up warning as error
|
||
|
def SetWarningAsError():
|
||
|
global _WarningAsError
|
||
|
_WarningAsError = True
|
||
|
|
||
|
## Specify a file to store the log message as well as put on console
|
||
|
#
|
||
|
# @param LogFile The file path used to store the log message
|
||
|
#
|
||
|
def SetLogFile(LogFile):
|
||
|
if os.path.exists(LogFile):
|
||
|
os.remove(LogFile)
|
||
|
|
||
|
_Ch = logging.FileHandler(LogFile)
|
||
|
_Ch.setFormatter(_DebugFormatter)
|
||
|
_DebugLogger.addHandler(_Ch)
|
||
|
|
||
|
_Ch= logging.FileHandler(LogFile)
|
||
|
_Ch.setFormatter(_InfoFormatter)
|
||
|
_InfoLogger.addHandler(_Ch)
|
||
|
|
||
|
_Ch = logging.FileHandler(LogFile)
|
||
|
_Ch.setFormatter(_ErrorFormatter)
|
||
|
_ErrorLogger.addHandler(_Ch)
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
pass
|
||
|
|