Home | Trees | Indices | Help |
---|
|
1 ################################################################################ 2 # Ganga Project. http://cern.ch/ganga 3 # 4 # $Id: __init__.py,v 1.7 2009-06-25 13:30:00 moscicki Exp $ 5 ################################################################################ 6 7 # 8 # Thin wrapper for logging subsystem: 9 # - makes standard python 2.3 logging available in python 2.2 10 # - integrates loggers with Ganga configuration subsystem in a most covinient way 11 # - extra reporting utilities: 12 # log_user_exception 13 # 14 # Design principles: 15 # - getLogger() returns the standard logger object for the current execution context 16 # i.e. with the name which indicates the current package or module name 17 # - all loggers are automatically configured according to this modules config dictionary (see below) 18 # - special functions: 19 # - log_user_exception() allows to format nicely exception messages 20 21 try: 22 import logging 23 import logging.handlers as handlers 24 25 except ImportError: 26 import Ganga.Utility.external.logging as logging 27 import Ganga.Utility.external.logging.handlers as handlers 28 #print 'using logger shipped with Ganga (probably you are running python2.2 or older)' 29 30 # initialize the root logger for the logger created directly in python executable scripts which have no name starting by "Ganga." 31 logging.basicConfig() 32 33 _formats = { 34 'DEBUG' : '%(asctime)s "%(filename)s" at %(lineno)d, %(name)-35s: %(levelname)-8s %(message)s', 35 'VERBOSE' : '%(asctime)s %(name)-35s: %(levelname)-8s %(message)s', 36 'NORMAL' : '%(name)-35s: %(levelname)-8s %(message)s', 37 'TERSE' : 'Ganga: %(levelname)-8s %(message)s' 38 } 39 40 private_logger = None # private logger of this module 41 42 main_logger = logging.Logger.root # main logger corresponds to the root of the hierarchy 43 44 # this is the handler used to print on screen directly 45 direct_screen_handler = main_logger.handlers[0] # get default StreamHandler 46 47 # if defined this is the handler used for caching background messages at interactive prompt 48 cached_screen_handler = None 49 50 # this is the handler currenty in use by main_logger (either direct_screen_handler or cached_screen_handler) 51 # or it may be overriden by bootstrap() to be arbitrary handler 52 default_handler = direct_screen_handler 53 54 # if defined this is ADDITIONAL handler that is used for the logfile 55 file_handler = None 56 57 58 # FIXME: this should be probably an option in the config 59 _global_level = None # if defined, global level (string) overrides anything which is in config 60 61 # all loggers which are used by all modules 62 _allLoggers = {} 63 64 # logger configuration 65 # settings for new loggers may be added here at will (for example read from the config file) 66 import Ganga.Utility.Config 67 68 config = Ganga.Utility.Config.makeConfig("Logging","""control the messages printed by Ganga 69 The settings are applied hierarchically to the loggers. Ganga is the name of the top-level logger which 70 applies by default to all Ganga.* packages unless overriden in sub-packages. 71 You may define new loggers in this section. 72 The log level may be one of: CRITICAL ERROR WARNING INFO DEBUG 73 """,is_open=True) 74 75 # FIXME: Ganga WARNING should be turned into INFO level when the messages are reviewed in all the code 76 config.addOption('Ganga', "WARNING","top-level logger") 77 config.addOption('Ganga.Runtime.bootstrap',"INFO",'FIXME') 78 config.addOption('Ganga.GPIDev',"INFO","logger of Ganga.GPIDev.* packages") 79 config.addOption('Ganga.Utility.logging',"WARNING","logger of the Ganga logging package itself (use with care!)") 80 config.addOption('_format', "NORMAL","format of logging messages: TERSE,NORMAL,VERBOSE,DEBUG") 81 config.addOption('_colour',True,"enable ASCII colour formatting of messages e.g. errors in red") 82 config.addOption('_logfile', "~/.ganga.log","location of the logfile") 83 config.addOption('_logfile_size', 100000, "the size of the logfile (in bytes), the rotating log will never exceed this file size") # 100 K 84 config.addOption('_interactive_cache',True,'if True then the cache used for interactive sessions, False disables caching') 85 86114 11589 logging.Formatter.__init__(self,*args,**kwds) 90 import Ganga.Utility.ColourText as ColourText 91 fg = ColourText.Foreground() 92 fx = ColourText.Effects() 93 ColourFormatter.colours = { logging.INFO : fx.normal, 94 logging.WARNING : fg.orange, 95 logging.ERROR: fg.red, 96 logging.CRITICAL: fg.red, 97 logging.DEBUG: fx.normal } 98 self.markup = ColourText.ANSIMarkup()99101 s = logging.Formatter.format(self,record) 102 try: 103 code = ColourFormatter.colours[record.levelno] 104 return self.markup(s,code) 105 except KeyError: 106 return s107109 import Ganga.Utility.ColourText as ColourText 110 if yes: 111 self.markup = ColourText.ANSIMarkup() 112 else: 113 self.markup = ColourText.NoMarkup()117 formatter = ColourFormatter(_formats[config['_format']]) ## 118 formatter.setColour(config['_colour']) 119 handler.setFormatter(formatter)120 121123 import traceback 124 import os.path 125 logfile = os.path.expanduser(logfile) 126 global file_handler 127 if logfile: 128 try: 129 new_file_handler = handlers.RotatingFileHandler(logfile,maxBytes=logfile_size,backupCount=1) 130 except IOError,x: 131 private_logger.error('Cannot open the log file: %s',str(x)) 132 return 133 # remove old handler if exists 134 #print 'removing old file handler',file_handler 135 #print 'installing new file handler',new_file_handler 136 if file_handler: 137 main_logger.removeHandler(file_handler) 138 file_handler.flush() 139 file_handler.close() 140 # this is required to properly remove the file handler from the logging system 141 # otherwise I/O Error at shutdown 142 try: 143 del logging._handlers[file_handler] #WARNING: this relies on the implementation details of the logging module 144 except KeyError: 145 pass # don't complain if the handler was correctly unregistered by the logging system 146 147 new_file_handler.setFormatter(logging.Formatter(_formats['VERBOSE'])) 148 main_logger.addHandler(new_file_handler) 149 file_handler = new_file_handler150 151 # reflect all user changes immediately153 format,colour = config['_format'],config['_colour'] 154 155 if opt == '_format': 156 try: 157 format = _formats[value] 158 except KeyError: 159 private_logger.error('illegal name of format string (%s), possible values: %s' % (str(value),_formats.keys())) 160 return 161 162 if opt == '_colour': 163 colour = value 164 165 if opt in ['_format','_colour']: 166 fmt = ColourFormatter(format) 167 fmt.setColour(colour) 168 direct_screen_handler.setFormatter(fmt) 169 return 170 171 logfile,logfile_size = config['_logfile'], config['_logfile_size'] 172 173 if opt in ['_logfile','_logfile_size']: 174 global file_handler 175 _make_file_handler(logfile,logfile_size) 176 return 177 178 if opt == '_interactive_cache': # FIXME: has no effect at runtime, should raise a ConfigError 179 return 180 181 # set the logger level 182 private_logger.debug('setting loglevel: %s %s',opt,value) 183 _set_log_level(getLogger(opt),value)184 185 config.attachUserHandler(None,post_config_handler) 186 config.attachSessionHandler(None,post_config_handler) 187 188 # set the loglevel for a logger to a given string value (example: "DEBUG")190 191 if not _global_level is None: 192 value = _global_level 193 194 # convert a string "DEBUG" into enum object logging.DEBUG 195 def _string2level(name): 196 return getattr(logging,name)197 198 try: 199 logger.setLevel(_string2level(value)) 200 return value 201 except AttributeError,x: 202 logger.error('%s',str(x)) 203 logger.warning('possible configuration error: invalid level value (%s), using default level',value) 204 return None 205 206208 #print " _guess_module_logger_name",modulename 209 # find the filename of the calling module 210 import sys,os.path 211 if frame is None: 212 frame = sys._getframe(3) # assuming 2 nested calls to the module boundary! 213 else: 214 print 'using frame from the caller' 215 216 # accessing __file__ from globals() is much more reliable than f_code.co_filename (name = os.path.normcase(frame.f_code.co_filename)) 217 try: 218 name = os.path.realpath(os.path.abspath(frame.f_globals['__file__'])) 219 except KeyError: 220 # no file associated with the frame (e.g. interactive prompt, exec statement) 221 name = '_program_' 222 223 #print " _guess_module_logger_name",name 224 del frame 225 226 #if private_logger: 227 # private_logger.debug('searching for package matching calling module co_filename= %s',str(name)) 228 229 # sometimes the filename is an absolute path, try to find a relative module path from the PYTHONPATH 230 # and remove the trailing path -> the result will be used as the logger name 231 232 from Ganga.Utility.files import remove_prefix 233 234 name = remove_prefix(name,sys.path) 235 236 def remove_tail(s,tail): 237 idx = s.rfind(tail) 238 if idx != -1: 239 return s[:idx] 240 return s241 242 # get rid of trailing .py .pyc .pyo 243 name = remove_tail(name,'.py') 244 245 # replace slashes with dots 246 name = name.replace(os.sep,'.') 247 248 # return full module name 249 if modulename == 1: 250 return name 251 252 # remove module name 253 name = remove_tail(name,'.') 254 255 if name == 'ganga': #interactive IPython session 256 name = "Ganga.GPI" 257 258 # return package name 259 if not modulename: 260 return name 261 262 # return custom module name 263 return name+'.'+modulename 264 265 266 # use this function to get new loggers into your packages 267 # if you do not provide the name then logger will detect your package name 268 # if you specify the modulename as a string then it will be appended to the package name 269 # if you specify the modulename==1 then your module name will be guessed and appended to the package name 270 # the guessing algorithm may be modified by passing the frame object (to emulate a different physical location of the logger) 271 # this is only useful for special usage such as IBackend base class273 return _getLogger(name,modulename,frame=frame)274 275 # Caching will not be done for messages which are generated by the main thread. 276 import threading280284 285282 return threading.currentThread().getName().find("GANGA_Update_Thread") == -1 or \ 283 logging.handlers.MemoryHandler.shouldFlush(self,record)287 """ 288 Enable caching of log messages at interactive prompt. In the interactive IPython session, the messages from monitoring 289 loop will be cached until the next prompt. In non-interactive sessions no caching is required. 290 """ 291 292 if not config['_interactive_cache']: 293 return 294 295 private_logger.debug('CACHING ENABLED') 296 global default_handler, cached_screen_handler 297 main_logger.removeHandler(default_handler) 298 cached_screen_handler = FlushedMemoryHandler(1000,target=direct_screen_handler) 299 default_handler = cached_screen_handler 300 main_logger.addHandler(default_handler)301 302304 305 if handler is None: 306 handler = default_handler 307 308 requested_name = name 309 310 if name is None: 311 name = _guess_module_logger_name(modulename,frame=frame) 312 313 if name.split('.')[0] != 'Ganga' and name != 'Ganga': 314 name = 'Ganga.'+name 315 316 #if private_logger: 317 # private_logger.debug('getLogger: effective_name=%s original_name=%s',name,requested_name) 318 319 try: 320 logger= _allLoggers[name] 321 #print 'reusing existing logger: ',name 322 return logger 323 except KeyError: 324 325 #print 'creating logger: ',name 326 logger = logging.getLogger(name) 327 _allLoggers[name] = logger 328 329 try: 330 _set_log_level(logger,config[name]) 331 except Ganga.Utility.Config.ConfigError: 332 pass 333 334 if private_logger: 335 private_logger.debug('created logger %s in %s mode',name,logging.getLevelName(logger.getEffectiveLevel())) 336 private_logger.debug('applied %s format string to %s', config['_format'], name) 337 338 #print '------------>',logger 339 #logger.critical('initialization of logger for module %s',name) 340 #print '------------> should have printed the message' 341 342 return logger343 344 345 346 # this bootstrap method should be called very early in the bootstrap phase of the entire system to enable 347 # the log level configuration to take effect... 348 # the optional handler overrides the default handler and is used for GUI environment 349 # NOTE: the additional message buffering handler is available only for the default handler 350352 353 global private_logger,main_logger 354 private_logger = getLogger('Ganga.Utility.logging') 355 #main_logger = _getLogger('Ganga',_roothandler=1,handler=handler) 356 357 private_logger.debug('bootstrap') 358 359 if internal: 360 _make_file_handler(config['_logfile'],config['_logfile_size']) 361 else: 362 # override main_logger handler 363 global default_handler 364 if not handler is None and not handler is default_handler: 365 main_logger.removeHandler(default_handler) 366 main_logger.addHandler(handler) 367 default_handler = handler 368 369 _set_formatter(default_handler) 370 #main_logger.propagate = 0 # do not propagate messages upwards... 371 #main_logger.addHandler(default_handler) 372 if file_handler: 373 main_logger.addHandler(file_handler) 374 375 opts = filter(lambda o: o.find('Ganga') == 0,config) 376 for opt in opts: 377 378 # should reconfigure Ganga and private logger according to the config file contents 379 380 msg = 'logging %s in %s mode' % (opt,config[opt]) #logging.getLevelName(getLogger(opt).getEffectiveLevel())) 381 if internal: private_logger.debug(msg) 382 else: private_logger.info(msg) 383 384 _set_log_level(getLogger(opt),config[opt]) 385 386 if internal: 387 import atexit 388 atexit.register(shutdown) 389 390 private_logger.debug('end of bootstrap')391 392 396 397 398 # do the initial bootstrap automatically at import -> this will bootstrap the main and private loggers 399 bootstrap(internal=1) 400 401 # force all loggers to use the same level403 if not level is None: 404 for l in _allLoggers: 405 _set_log_level(_allLoggers[l],level) 406 global _global_level 407 _global_level = level408410 import traceback, StringIO 411 buf=StringIO.StringIO() 412 traceback.print_exc(file=buf) 413 banner = 10*'-'+' error in user/extension code '+10*'-' 414 if not logger: 415 logger = private_logger 416 logmethod = logger.warning 417 if debug: 418 logmethod = logger.debug 419 logmethod(banner) 420 for line in buf.getvalue().splitlines(): 421 logmethod(line) 422 logmethod('-'*len(banner))423 424 425 # extra imports for more convenient work with the logging module 426 getLevelName = logging.getLevelName 427 428 if __name__ == "__main__": 429 print 'Little test' 430 431 private_logger = logging.getLogger("TESTLOGGER.CHILD.GRANDCHILD") 432 formatter = logging.Formatter(_formats['DEBUG']) 433 console = logging.StreamHandler() 434 console.setFormatter(formatter) 435 private_logger.setLevel(logging.DEBUG) 436 private_logger.addHandler(console) 437 private_logger.critical('hello') 438
Home | Trees | Indices | Help |
---|
Generated by Epydoc 3.0.1 on Mon Jun 25 10:35:30 2012 | http://epydoc.sourceforge.net |