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