]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/UPT/Logger/Log.py
BaseTools: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / BaseTools / Source / Python / UPT / Logger / Log.py
1 ## @file
2 # This file implements the log mechanism for Python tools.
3 #
4 # Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
5 #
6 # SPDX-License-Identifier: BSD-2-Clause-Patent
7 #
8
9 '''
10 Logger
11 '''
12
13 ## Import modules
14 from sys import argv
15 from sys import stdout
16 from sys import stderr
17 import os.path
18 from os import remove
19 from logging import getLogger
20 from logging import Formatter
21 from logging import StreamHandler
22 from logging import FileHandler
23 from traceback import extract_stack
24
25 from Logger.ToolError import FatalError
26 from Logger.ToolError import WARNING_AS_ERROR
27 from Logger.ToolError import gERROR_MESSAGE
28 from Logger.ToolError import UNKNOWN_ERROR
29 from Library import GlobalData
30
31 #
32 # Log level constants
33 #
34 DEBUG_0 = 1
35 DEBUG_1 = 2
36 DEBUG_2 = 3
37 DEBUG_3 = 4
38 DEBUG_4 = 5
39 DEBUG_5 = 6
40 DEBUG_6 = 7
41 DEBUG_7 = 8
42 DEBUG_8 = 9
43 DEBUG_9 = 10
44 VERBOSE = 15
45 INFO = 20
46 WARN = 30
47 QUIET = 40
48 QUIET_1 = 41
49 ERROR = 50
50 SILENT = 60
51
52 IS_RAISE_ERROR = True
53 SUPRESS_ERROR = False
54
55 #
56 # Tool name
57 #
58 _TOOL_NAME = os.path.basename(argv[0])
59 #
60 # For validation purpose
61 #
62 _LOG_LEVELS = [DEBUG_0, DEBUG_1, DEBUG_2, DEBUG_3, DEBUG_4, DEBUG_5, DEBUG_6, \
63 DEBUG_7, DEBUG_8, DEBUG_9, VERBOSE, WARN, INFO, ERROR, QUIET, \
64 QUIET_1, SILENT]
65 #
66 # For DEBUG level (All DEBUG_0~9 are applicable)
67 #
68 _DEBUG_LOGGER = getLogger("tool_debug")
69 _DEBUG_FORMATTER = Formatter("[%(asctime)s.%(msecs)d]: %(message)s", \
70 datefmt="%H:%M:%S")
71 #
72 # For VERBOSE, INFO, WARN level
73 #
74 _INFO_LOGGER = getLogger("tool_info")
75 _INFO_FORMATTER = Formatter("%(message)s")
76 #
77 # For ERROR level
78 #
79 _ERROR_LOGGER = getLogger("tool_error")
80 _ERROR_FORMATTER = Formatter("%(message)s")
81
82 #
83 # String templates for ERROR/WARN/DEBUG log message
84 #
85 _ERROR_MESSAGE_TEMPLATE = \
86 ('\n\n%(tool)s...\n%(file)s(%(line)s): error %(errorcode)04X: %(msg)s\n\t%(extra)s')
87
88 __ERROR_MESSAGE_TEMPLATE_WITHOUT_FILE = \
89 '\n\n%(tool)s...\n : error %(errorcode)04X: %(msg)s\n\t%(extra)s'
90
91 _WARNING_MESSAGE_TEMPLATE = '%(tool)s...\n%(file)s(%(line)s): warning: %(msg)s'
92 _WARNING_MESSAGE_TEMPLATE_WITHOUT_FILE = '%(tool)s: : warning: %(msg)s'
93 _DEBUG_MESSAGE_TEMPLATE = '%(file)s(%(line)s): debug: \n %(msg)s'
94
95
96 #
97 # Log INFO message
98 #
99 #Info = _INFO_LOGGER.info
100
101 def Info(msg, *args, **kwargs):
102 _INFO_LOGGER.info(msg, *args, **kwargs)
103
104 #
105 # Log information which should be always put out
106 #
107 def Quiet(msg, *args, **kwargs):
108 _ERROR_LOGGER.error(msg, *args, **kwargs)
109
110 ## Log debug message
111 #
112 # @param Level DEBUG level (DEBUG0~9)
113 # @param Message Debug information
114 # @param ExtraData More information associated with "Message"
115 #
116 def Debug(Level, Message, ExtraData=None):
117 if _DEBUG_LOGGER.level > Level:
118 return
119 if Level > DEBUG_9:
120 return
121 #
122 # Find out the caller method information
123 #
124 CallerStack = extract_stack()[-2]
125 TemplateDict = {
126 "file" : CallerStack[0],
127 "line" : CallerStack[1],
128 "msg" : Message,
129 }
130
131 if ExtraData is not None:
132 LogText = _DEBUG_MESSAGE_TEMPLATE % TemplateDict + "\n %s" % ExtraData
133 else:
134 LogText = _DEBUG_MESSAGE_TEMPLATE % TemplateDict
135
136 _DEBUG_LOGGER.log(Level, LogText)
137
138 ## Log verbose message
139 #
140 # @param Message Verbose information
141 #
142 def Verbose(Message):
143 return _INFO_LOGGER.log(VERBOSE, Message)
144
145 ## Log warning message
146 #
147 # Warning messages are those which might be wrong but won't fail the tool.
148 #
149 # @param ToolName The name of the tool. If not given, the name of caller
150 # method will be used.
151 # @param Message Warning information
152 # @param File The name of file which caused the warning.
153 # @param Line The line number in the "File" which caused the warning.
154 # @param ExtraData More information associated with "Message"
155 #
156 def Warn(ToolName, Message, File=None, Line=None, ExtraData=None):
157 if _INFO_LOGGER.level > WARN:
158 return
159 #
160 # if no tool name given, use caller's source file name as tool name
161 #
162 if ToolName is None or ToolName == "":
163 ToolName = os.path.basename(extract_stack()[-2][0])
164
165 if Line is None:
166 Line = "..."
167 else:
168 Line = "%d" % Line
169
170 TemplateDict = {
171 "tool" : ToolName,
172 "file" : File,
173 "line" : Line,
174 "msg" : Message,
175 }
176
177 if File is not None:
178 LogText = _WARNING_MESSAGE_TEMPLATE % TemplateDict
179 else:
180 LogText = _WARNING_MESSAGE_TEMPLATE_WITHOUT_FILE % TemplateDict
181
182 if ExtraData is not None:
183 LogText += "\n %s" % ExtraData
184
185 _INFO_LOGGER.log(WARN, LogText)
186 #
187 # Raise an exception if indicated
188 #
189 if GlobalData.gWARNING_AS_ERROR == True:
190 raise FatalError(WARNING_AS_ERROR)
191
192 ## Log ERROR message
193 #
194 # Once an error messages is logged, the tool's execution will be broken by
195 # raising an exception. If you don't want to break the execution later, you
196 # can give "RaiseError" with "False" value.
197 #
198 # @param ToolName The name of the tool. If not given, the name of caller
199 # method will be used.
200 # @param ErrorCode The error code
201 # @param Message Warning information
202 # @param File The name of file which caused the error.
203 # @param Line The line number in the "File" which caused the warning.
204 # @param ExtraData More information associated with "Message"
205 # @param RaiseError Raise an exception to break the tool's execution if
206 # it's True. This is the default behavior.
207 #
208 def Error(ToolName, ErrorCode, Message=None, File=None, Line=None, \
209 ExtraData=None, RaiseError=IS_RAISE_ERROR):
210 if ToolName:
211 pass
212 if Line is None:
213 Line = "..."
214 else:
215 Line = "%d" % Line
216
217 if Message is None:
218 if ErrorCode in gERROR_MESSAGE:
219 Message = gERROR_MESSAGE[ErrorCode]
220 else:
221 Message = gERROR_MESSAGE[UNKNOWN_ERROR]
222
223 if ExtraData is None:
224 ExtraData = ""
225
226 TemplateDict = {
227 "tool" : _TOOL_NAME,
228 "file" : File,
229 "line" : Line,
230 "errorcode" : ErrorCode,
231 "msg" : Message,
232 "extra" : ExtraData
233 }
234
235 if File is not None:
236 LogText = _ERROR_MESSAGE_TEMPLATE % TemplateDict
237 else:
238 LogText = __ERROR_MESSAGE_TEMPLATE_WITHOUT_FILE % TemplateDict
239
240 if not SUPRESS_ERROR:
241 _ERROR_LOGGER.log(ERROR, LogText)
242 if RaiseError:
243 raise FatalError(ErrorCode)
244
245
246 ## Initialize log system
247 #
248 def Initialize():
249 #
250 # Since we use different format to log different levels of message into
251 # different place (stdout or stderr), we have to use different "Logger"
252 # objects to do this.
253 #
254 # For DEBUG level (All DEBUG_0~9 are applicable)
255 _DEBUG_LOGGER.setLevel(INFO)
256 _DebugChannel = StreamHandler(stdout)
257 _DebugChannel.setFormatter(_DEBUG_FORMATTER)
258 _DEBUG_LOGGER.addHandler(_DebugChannel)
259 #
260 # For VERBOSE, INFO, WARN level
261 #
262 _INFO_LOGGER.setLevel(INFO)
263 _InfoChannel = StreamHandler(stdout)
264 _InfoChannel.setFormatter(_INFO_FORMATTER)
265 _INFO_LOGGER.addHandler(_InfoChannel)
266 #
267 # For ERROR level
268 #
269 _ERROR_LOGGER.setLevel(INFO)
270 _ErrorCh = StreamHandler(stderr)
271 _ErrorCh.setFormatter(_ERROR_FORMATTER)
272 _ERROR_LOGGER.addHandler(_ErrorCh)
273
274
275 ## Set log level
276 #
277 # @param Level One of log level in _LogLevel
278 #
279 def SetLevel(Level):
280 if Level not in _LOG_LEVELS:
281 Info("Not supported log level (%d). Use default level instead." % \
282 Level)
283 Level = INFO
284 _DEBUG_LOGGER.setLevel(Level)
285 _INFO_LOGGER.setLevel(Level)
286 _ERROR_LOGGER.setLevel(Level)
287
288 ## Get current log level
289 #
290 def GetLevel():
291 return _INFO_LOGGER.getEffectiveLevel()
292
293 ## Raise up warning as error
294 #
295 def SetWarningAsError():
296 GlobalData.gWARNING_AS_ERROR = True
297
298 ## Specify a file to store the log message as well as put on console
299 #
300 # @param LogFile The file path used to store the log message
301 #
302 def SetLogFile(LogFile):
303 if os.path.exists(LogFile):
304 remove(LogFile)
305
306 _Ch = FileHandler(LogFile)
307 _Ch.setFormatter(_DEBUG_FORMATTER)
308 _DEBUG_LOGGER.addHandler(_Ch)
309
310 _Ch = FileHandler(LogFile)
311 _Ch.setFormatter(_INFO_FORMATTER)
312 _INFO_LOGGER.addHandler(_Ch)
313
314 _Ch = FileHandler(LogFile)
315 _Ch.setFormatter(_ERROR_FORMATTER)
316 _ERROR_LOGGER.addHandler(_Ch)
317
318
319