# Copyright (c) 2003, The Regents of the University of California, # through Lawrence Berkeley National Laboratory (subject to receipt of # any required approvals from the U.S. Dept. of Energy). All rights # reserved. # """Logging""" ident = "$Id$" import os, sys WARN = 1 DEBUG = 2 class ILogger: '''Logger interface, by default this class will be used and logging calls are no-ops. ''' level = 0 def __init__(self, msg): return def warning(self, *args, **kw): return def debug(self, *args, **kw): return def error(self, *args, **kw): return def setLevel(cls, level): cls.level = level setLevel = classmethod(setLevel) debugOn = lambda self: self.level >= DEBUG warnOn = lambda self: self.level >= WARN class BasicLogger(ILogger): last = '' def __init__(self, msg, out=sys.stdout): self.msg, self.out = msg, out def warning(self, msg, *args, **kw): if self.warnOn() is False: return if BasicLogger.last != self.msg: BasicLogger.last = self.msg print >>self, "---- ", self.msg, " ----" print >>self, " %s " %self.WARN, print >>self, msg %args WARN = '[WARN]' def debug(self, msg, *args, **kw): if self.debugOn() is False: return if BasicLogger.last != self.msg: BasicLogger.last = self.msg print >>self, "---- ", self.msg, " ----" print >>self, " %s " %self.DEBUG, print >>self, msg %args DEBUG = '[DEBUG]' def error(self, msg, *args, **kw): if BasicLogger.last != self.msg: BasicLogger.last = self.msg print >>self, "---- ", self.msg, " ----" print >>self, " %s " %self.ERROR, print >>self, msg %args ERROR = '[ERROR]' def write(self, *args): '''Write convenience function; writes strings. ''' for s in args: self.out.write(s) event = ''.join(*args) _LoggerClass = BasicLogger class GridLogger(ILogger): def debug(self, msg, *args, **kw): kw['component'] = self.msg gridLog(event=msg %args, level='DEBUG', **kw) def warning(self, msg, *args, **kw): kw['component'] = self.msg gridLog(event=msg %args, level='WARNING', **kw) def error(self, msg, *args, **kw): kw['component'] = self.msg gridLog(event=msg %args, level='ERROR', **kw) # # Registry of send functions for gridLog # GLRegistry = {} class GLRecord(dict): """Grid Logging Best Practices Record, Distributed Logging Utilities The following names are reserved: event -- log event name Below is EBNF for the event name part of a log message. name = ( "." )? nodot = {RFC3896-chars except "."} Suffixes: start: Immediately before the first action in a task. end: Immediately after the last action in a task (that succeeded). error: an error condition that does not correspond to an end event. ts -- timestamp level -- logging level (see levels below) status -- integer status code gid -- global grid identifier gid, cgid -- parent/child identifiers prog -- program name More info: http://www.cedps.net/wiki/index.php/LoggingBestPractices#Python reserved -- list of reserved names, omitname -- list of reserved names, output only values ('ts', 'event',) levels -- dict of levels and description """ reserved = ('ts', 'event', 'level', 'status', 'gid', 'prog') omitname = () levels = dict(FATAL='Component cannot continue, or system is unusable.', ALERT='Action must be taken immediately.', CRITICAL='Critical conditions (on the system).', ERROR='Errors in the component; not errors from elsewhere.', WARNING='Problems that are recovered from, usually.', NOTICE='Normal but significant condition.', INFO='Informational messages that would be useful to a deployer or administrator.', DEBUG='Lower level information concerning program logic decisions, internal state, etc.', TRACE='Finest granularity, similar to "stepping through" the component or system.', ) def __init__(self, date=None, **kw): kw['ts'] = date or self.GLDate() kw['gid'] = kw.get('gid') or os.getpid() dict.__init__(self, kw) def __str__(self): """ """ from cStringIO import StringIO s = StringIO(); n = " " reserved = self.reserved; omitname = self.omitname; levels = self.levels for k in ( list(filter(lambda i: self.has_key(i), reserved)) + list(filter(lambda i: i not in reserved, self.keys())) ): v = self[k] if k in omitname: s.write( "%s " %self.format[type(v)](v) ) continue if k == reserved[2] and v not in levels: pass s.write( "%s=%s " %(k, self.format[type(v)](v) ) ) s.write("\n") return s.getvalue() class GLDate(str): """Grid logging Date Format all timestamps should all be in the same time zone (UTC). Grid timestamp value format that is a highly readable variant of the ISO8601 time standard [1]: YYYY-MM-DDTHH:MM:SS.SSSSSSZ """ def __new__(self, args=None): """args -- datetime (year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]]) """ import datetime args = args or datetime.datetime.utcnow() l = (args.year, args.month, args.day, args.hour, args.minute, args.second, args.microsecond, args.tzinfo or 'Z') return str.__new__(self, "%04d-%02d-%02dT%02d:%02d:%02d.%06d%s" %l) format = { int:str, float:lambda x: "%lf" % x, long:str, str:lambda x:x, unicode:str, GLDate:str, } def gridLog(**kw): """Send GLRecord, Distributed Logging Utilities If the scheme is passed as a keyword parameter the value is expected to be a callable function that takes 2 parameters: url, outputStr GRIDLOG_ON -- turn grid logging on GRIDLOG_DEST -- provide URL destination """ import os if not bool( int(os.environ.get('GRIDLOG_ON', 0)) ): return url = os.environ.get('GRIDLOG_DEST') if url is None: return ## NOTE: urlparse problem w/customized schemes try: scheme = url[:url.find('://')] send = GLRegistry[scheme] send( url, str(GLRecord(**kw)), ) except Exception, ex: print >>sys.stderr, "*** gridLog failed -- %s" %(str(kw)) def sendUDP(url, outputStr): from socket import socket, AF_INET, SOCK_DGRAM idx1 = url.find('://') + 3; idx2 = url.find('/', idx1) if idx2 < idx1: idx2 = len(url) netloc = url[idx1:idx2] host,port = (netloc.split(':')+[80])[0:2] socket(AF_INET, SOCK_DGRAM).sendto( outputStr, (host,int(port)), ) def writeToFile(url, outputStr): print >> open(url.split('://')[1], 'a+'), outputStr GLRegistry["gridlog-udp"] = sendUDP GLRegistry["file"] = writeToFile def setBasicLogger(): '''Use Basic Logger. ''' setLoggerClass(BasicLogger) BasicLogger.setLevel(0) def setGridLogger(): '''Use GridLogger for all logging events. ''' setLoggerClass(GridLogger) def setBasicLoggerWARN(): '''Use Basic Logger. ''' setLoggerClass(BasicLogger) BasicLogger.setLevel(WARN) def setBasicLoggerDEBUG(): '''Use Basic Logger. ''' setLoggerClass(BasicLogger) BasicLogger.setLevel(DEBUG) def setLoggerClass(loggingClass): '''Set Logging Class. ''' def setLoggerClass(loggingClass): '''Set Logging Class. ''' assert issubclass(loggingClass, ILogger), 'loggingClass must subclass ILogger' global _LoggerClass _LoggerClass = loggingClass def setLevel(level=0): '''Set Global Logging Level. ''' ILogger.level = level def getLevel(): return ILogger.level def getLogger(msg): '''Return instance of Logging class. ''' return _LoggerClass(msg)