1## @file
2# This file implements the log mechanism for Python tools.
3#
4# Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
5# This program and the accompanying materials
6# are licensed and made available under the terms and conditions of the BSD License
7# which accompanies this distribution.  The full text of the license may be found at
8# http://opensource.org/licenses/bsd-license.php
9#
10# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12#
13
14## Import modules
15import Common.LongFilePathOs as os, sys, logging
16import traceback
17from  BuildToolError import *
18
19## Log level constants
20DEBUG_0 = 1
21DEBUG_1 = 2
22DEBUG_2 = 3
23DEBUG_3 = 4
24DEBUG_4 = 5
25DEBUG_5 = 6
26DEBUG_6 = 7
27DEBUG_7 = 8
28DEBUG_8 = 9
29DEBUG_9 = 10
30VERBOSE = 15
31INFO    = 20
32WARN    = 30
33QUIET   = 40
34ERROR   = 50
35
36IsRaiseError = True
37
38# Tool name
39_ToolName = os.path.basename(sys.argv[0])
40
41# For validation purpose
42_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]
43
44# For DEBUG level (All DEBUG_0~9 are applicable)
45_DebugLogger = logging.getLogger("tool_debug")
46_DebugFormatter = logging.Formatter("[%(asctime)s.%(msecs)d]: %(message)s", datefmt="%H:%M:%S")
47
48# For VERBOSE, INFO, WARN level
49_InfoLogger = logging.getLogger("tool_info")
50_InfoFormatter = logging.Formatter("%(message)s")
51
52# For ERROR level
53_ErrorLogger = logging.getLogger("tool_error")
54_ErrorFormatter = logging.Formatter("%(message)s")
55
56# String templates for ERROR/WARN/DEBUG log message
57_ErrorMessageTemplate = '\n\n%(tool)s...\n%(file)s(%(line)s): error %(errorcode)04X: %(msg)s\n\t%(extra)s'
58_ErrorMessageTemplateWithoutFile = '\n\n%(tool)s...\n : error %(errorcode)04X: %(msg)s\n\t%(extra)s'
59_WarningMessageTemplate = '%(tool)s...\n%(file)s(%(line)s): warning: %(msg)s'
60_WarningMessageTemplateWithoutFile = '%(tool)s: : warning: %(msg)s'
61_DebugMessageTemplate = '%(file)s(%(line)s): debug: \n    %(msg)s'
62
63#
64# Flag used to take WARN as ERROR.
65# By default, only ERROR message will break the tools execution.
66#
67_WarningAsError = False
68
69## Log debug message
70#
71#   @param  Level       DEBUG level (DEBUG0~9)
72#   @param  Message     Debug information
73#   @param  ExtraData   More information associated with "Message"
74#
75def debug(Level, Message, ExtraData=None):
76    if _DebugLogger.level > Level:
77        return
78    if Level > DEBUG_9:
79        return
80
81    # Find out the caller method information
82    CallerStack = traceback.extract_stack()[-2]
83    TemplateDict = {
84        "file"      : CallerStack[0],
85        "line"      : CallerStack[1],
86        "msg"       : Message,
87    }
88
89    if ExtraData != None:
90        LogText = _DebugMessageTemplate % TemplateDict + "\n    %s" % ExtraData
91    else:
92        LogText = _DebugMessageTemplate % TemplateDict
93
94    _DebugLogger.log(Level, LogText)
95
96## Log verbose message
97#
98#   @param  Message     Verbose information
99#
100def verbose(Message):
101    return _InfoLogger.log(VERBOSE, Message)
102
103## Log warning message
104#
105#   Warning messages are those which might be wrong but won't fail the tool.
106#
107#   @param  ToolName    The name of the tool. If not given, the name of caller
108#                       method will be used.
109#   @param  Message     Warning information
110#   @param  File        The name of file which caused the warning.
111#   @param  Line        The line number in the "File" which caused the warning.
112#   @param  ExtraData   More information associated with "Message"
113#
114def warn(ToolName, Message, File=None, Line=None, ExtraData=None):
115    if _InfoLogger.level > WARN:
116        return
117
118    # if no tool name given, use caller's source file name as tool name
119    if ToolName == None or ToolName == "":
120        ToolName = os.path.basename(traceback.extract_stack()[-2][0])
121
122    if Line == None:
123        Line = "..."
124    else:
125        Line = "%d" % Line
126
127    TemplateDict = {
128        "tool"      : ToolName,
129        "file"      : File,
130        "line"      : Line,
131        "msg"       : Message,
132    }
133
134    if File != None:
135        LogText = _WarningMessageTemplate % TemplateDict
136    else:
137        LogText = _WarningMessageTemplateWithoutFile % TemplateDict
138
139    if ExtraData != None:
140        LogText += "\n    %s" % ExtraData
141
142    _InfoLogger.log(WARN, LogText)
143
144    # Raise an execption if indicated
145    if _WarningAsError == True:
146        raise FatalError(WARNING_AS_ERROR)
147
148## Log INFO message
149info    = _InfoLogger.info
150
151## Log ERROR message
152#
153#   Once an error messages is logged, the tool's execution will be broken by raising
154# an execption. If you don't want to break the execution later, you can give
155# "RaiseError" with "False" value.
156#
157#   @param  ToolName    The name of the tool. If not given, the name of caller
158#                       method will be used.
159#   @param  ErrorCode   The error code
160#   @param  Message     Warning information
161#   @param  File        The name of file which caused the error.
162#   @param  Line        The line number in the "File" which caused the warning.
163#   @param  ExtraData   More information associated with "Message"
164#   @param  RaiseError  Raise an exception to break the tool's executuion if
165#                       it's True. This is the default behavior.
166#
167def error(ToolName, ErrorCode, Message=None, File=None, Line=None, ExtraData=None, RaiseError=IsRaiseError):
168    if Line == None:
169        Line = "..."
170    else:
171        Line = "%d" % Line
172
173    if Message == None:
174        if ErrorCode in gErrorMessage:
175            Message = gErrorMessage[ErrorCode]
176        else:
177            Message = gErrorMessage[UNKNOWN_ERROR]
178
179    if ExtraData == None:
180        ExtraData = ""
181
182    TemplateDict = {
183        "tool"      : _ToolName,
184        "file"      : File,
185        "line"      : Line,
186        "errorcode" : ErrorCode,
187        "msg"       : Message,
188        "extra"     : ExtraData
189    }
190
191    if File != None:
192        LogText =  _ErrorMessageTemplate % TemplateDict
193    else:
194        LogText = _ErrorMessageTemplateWithoutFile % TemplateDict
195
196    _ErrorLogger.log(ERROR, LogText)
197    if RaiseError:
198        raise FatalError(ErrorCode)
199
200# Log information which should be always put out
201quiet   = _ErrorLogger.error
202
203## Initialize log system
204def Initialize():
205    #
206    # Since we use different format to log different levels of message into different
207    # place (stdout or stderr), we have to use different "Logger" objects to do this.
208    #
209    # For DEBUG level (All DEBUG_0~9 are applicable)
210    _DebugLogger.setLevel(INFO)
211    _DebugChannel = logging.StreamHandler(sys.stdout)
212    _DebugChannel.setFormatter(_DebugFormatter)
213    _DebugLogger.addHandler(_DebugChannel)
214
215    # For VERBOSE, INFO, WARN level
216    _InfoLogger.setLevel(INFO)
217    _InfoChannel = logging.StreamHandler(sys.stdout)
218    _InfoChannel.setFormatter(_InfoFormatter)
219    _InfoLogger.addHandler(_InfoChannel)
220
221    # For ERROR level
222    _ErrorLogger.setLevel(INFO)
223    _ErrorCh = logging.StreamHandler(sys.stderr)
224    _ErrorCh.setFormatter(_ErrorFormatter)
225    _ErrorLogger.addHandler(_ErrorCh)
226
227## Set log level
228#
229#   @param  Level   One of log level in _LogLevel
230def SetLevel(Level):
231    if Level not in _LogLevels:
232        info("Not supported log level (%d). Use default level instead." % Level)
233        Level = INFO
234    _DebugLogger.setLevel(Level)
235    _InfoLogger.setLevel(Level)
236    _ErrorLogger.setLevel(Level)
237
238## Get current log level
239def GetLevel():
240    return _InfoLogger.getEffectiveLevel()
241
242## Raise up warning as error
243def SetWarningAsError():
244    global _WarningAsError
245    _WarningAsError = True
246
247## Specify a file to store the log message as well as put on console
248#
249#   @param  LogFile     The file path used to store the log message
250#
251def SetLogFile(LogFile):
252    if os.path.exists(LogFile):
253        os.remove(LogFile)
254
255    _Ch = logging.FileHandler(LogFile)
256    _Ch.setFormatter(_DebugFormatter)
257    _DebugLogger.addHandler(_Ch)
258
259    _Ch= logging.FileHandler(LogFile)
260    _Ch.setFormatter(_InfoFormatter)
261    _InfoLogger.addHandler(_Ch)
262
263    _Ch = logging.FileHandler(LogFile)
264    _Ch.setFormatter(_ErrorFormatter)
265    _ErrorLogger.addHandler(_Ch)
266
267if __name__ == '__main__':
268    pass
269
270