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