diff --git a/logging.py b/logging.py index 04f2f56..6166de1 100644 --- a/logging.py +++ b/logging.py @@ -69,6 +69,122 @@ class BasicLogger(ILogger): _LoggerClass = BasicLogger + +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. + + date -- 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 + levels -- dict of levels and description + """ + reserved = ('date', 'event', 'level', 'status', 'gid', 'prog') + omitname = ('date', 'event',) + 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['date'] = date or self.GLDate() + 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 sendGridLog(**kw): + """Send GLRecord, Distributed Logging Utilities + """ + import os + from socket import socket, AF_INET, SOCK_DGRAM + if not bool(os.environ.get('GRIDLOG_ON', False)): + return + + url = os.environ.get('GRIDLOG_DEST') + if url is None: + return + + try: + 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( str(GLRecord(**kw)), + (host,int(port)),) + except Exception, ex: + print >>sys.stderr, "*** gridlog failed -- %s" %(str(kw)) + + def setBasicLogger(): '''Use Basic Logger. '''