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