00001
00002
00003
00004
00005
00006 """Logging"""
00007 ident = "$Id: logging.py 1395 2007-06-14 06:49:35Z boverhof $"
00008 import os, sys
00009
00010 WARN = 1
00011 DEBUG = 2
00012
00013
00014 class ILogger:
00015 '''Logger interface, by default this class
00016 will be used and logging calls are no-ops.
00017 '''
00018 level = 0
00019 def __init__(self, msg):
00020 return
00021 def warning(self, *args, **kw):
00022 return
00023 def debug(self, *args, **kw):
00024 return
00025 def error(self, *args, **kw):
00026 return
00027 def setLevel(cls, level):
00028 cls.level = level
00029 setLevel = classmethod(setLevel)
00030
00031 debugOn = lambda self: self.level >= DEBUG
00032 warnOn = lambda self: self.level >= WARN
00033
00034
00035 class BasicLogger(ILogger):
00036 last = ''
00037
00038 def __init__(self, msg, out=sys.stdout):
00039 self.msg, self.out = msg, out
00040
00041 def warning(self, msg, *args, **kw):
00042 if self.warnOn() is False: return
00043 if BasicLogger.last != self.msg:
00044 BasicLogger.last = self.msg
00045 print >>self, "---- ", self.msg, " ----"
00046 print >>self, " %s " %self.WARN,
00047 print >>self, msg %args
00048 WARN = '[WARN]'
00049 def debug(self, msg, *args, **kw):
00050 if self.debugOn() is False: return
00051 if BasicLogger.last != self.msg:
00052 BasicLogger.last = self.msg
00053 print >>self, "---- ", self.msg, " ----"
00054 print >>self, " %s " %self.DEBUG,
00055 print >>self, msg %args
00056 DEBUG = '[DEBUG]'
00057 def error(self, msg, *args, **kw):
00058 if BasicLogger.last != self.msg:
00059 BasicLogger.last = self.msg
00060 print >>self, "---- ", self.msg, " ----"
00061 print >>self, " %s " %self.ERROR,
00062 print >>self, msg %args
00063 ERROR = '[ERROR]'
00064
00065 def write(self, *args):
00066 '''Write convenience function; writes strings.
00067 '''
00068 for s in args: self.out.write(s)
00069 event = ''.join(*args)
00070
00071
00072 _LoggerClass = BasicLogger
00073
00074 class GridLogger(ILogger):
00075 def debug(self, msg, *args, **kw):
00076 kw['component'] = self.msg
00077 gridLog(event=msg %args, level='DEBUG', **kw)
00078
00079 def warning(self, msg, *args, **kw):
00080 kw['component'] = self.msg
00081 gridLog(event=msg %args, level='WARNING', **kw)
00082
00083 def error(self, msg, *args, **kw):
00084 kw['component'] = self.msg
00085 gridLog(event=msg %args, level='ERROR', **kw)
00086
00087
00088
00089
00090
00091 GLRegistry = {}
00092
00093 class GLRecord(dict):
00094 """Grid Logging Best Practices Record, Distributed Logging Utilities
00095
00096 The following names are reserved:
00097
00098 event -- log event name
00099 Below is EBNF for the event name part of a log message.
00100 name = <nodot> ( "." <name> )?
00101 nodot = {RFC3896-chars except "."}
00102
00103 Suffixes:
00104 start: Immediately before the first action in a task.
00105 end: Immediately after the last action in a task (that succeeded).
00106 error: an error condition that does not correspond to an end event.
00107
00108 ts -- timestamp
00109 level -- logging level (see levels below)
00110 status -- integer status code
00111 gid -- global grid identifier
00112 gid, cgid -- parent/child identifiers
00113 prog -- program name
00114
00115
00116 More info: http://www.cedps.net/wiki/index.php/LoggingBestPractices#Python
00117
00118 reserved -- list of reserved names,
00119 omitname -- list of reserved names, output only values ('ts', 'event',)
00120 levels -- dict of levels and description
00121 """
00122 reserved = ('ts', 'event', 'level', 'status', 'gid', 'prog')
00123 omitname = ()
00124 levels = dict(FATAL='Component cannot continue, or system is unusable.',
00125 ALERT='Action must be taken immediately.',
00126 CRITICAL='Critical conditions (on the system).',
00127 ERROR='Errors in the component; not errors from elsewhere.',
00128 WARNING='Problems that are recovered from, usually.',
00129 NOTICE='Normal but significant condition.',
00130 INFO='Informational messages that would be useful to a deployer or administrator.',
00131 DEBUG='Lower level information concerning program logic decisions, internal state, etc.',
00132 TRACE='Finest granularity, similar to "stepping through" the component or system.',
00133 )
00134
00135 def __init__(self, date=None, **kw):
00136 kw['ts'] = date or self.GLDate()
00137 kw['gid'] = kw.get('gid') or os.getpid()
00138 dict.__init__(self, kw)
00139
00140 def __str__(self):
00141 """
00142 """
00143 from cStringIO import StringIO
00144 s = StringIO(); n = " "
00145 reserved = self.reserved; omitname = self.omitname; levels = self.levels
00146
00147 for k in ( list(filter(lambda i: self.has_key(i), reserved)) +
00148 list(filter(lambda i: i not in reserved, self.keys()))
00149 ):
00150 v = self[k]
00151 if k in omitname:
00152 s.write( "%s " %self.format[type(v)](v) )
00153 continue
00154
00155 if k == reserved[2] and v not in levels:
00156 pass
00157
00158 s.write( "%s=%s " %(k, self.format[type(v)](v) ) )
00159
00160 s.write("\n")
00161 return s.getvalue()
00162
00163 class GLDate(str):
00164 """Grid logging Date Format
00165 all timestamps should all be in the same time zone (UTC).
00166 Grid timestamp value format that is a highly readable variant of the ISO8601 time standard [1]:
00167
00168 YYYY-MM-DDTHH:MM:SS.SSSSSSZ
00169
00170 """
00171 def __new__(self, args=None):
00172 """args -- datetime (year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
00173 """
00174 import datetime
00175 args = args or datetime.datetime.utcnow()
00176 l = (args.year, args.month, args.day, args.hour, args.minute, args.second,
00177 args.microsecond, args.tzinfo or 'Z')
00178
00179 return str.__new__(self, "%04d-%02d-%02dT%02d:%02d:%02d.%06d%s" %l)
00180
00181 format = { int:str, float:lambda x: "%lf" % x, long:str, str:lambda x:x,
00182 unicode:str, GLDate:str, }
00183
00184
00185 def gridLog(**kw):
00186 """Send GLRecord, Distributed Logging Utilities
00187 If the scheme is passed as a keyword parameter
00188 the value is expected to be a callable function
00189 that takes 2 parameters: url, outputStr
00190
00191 GRIDLOG_ON -- turn grid logging on
00192 GRIDLOG_DEST -- provide URL destination
00193 """
00194 import os
00195
00196 if not bool( int(os.environ.get('GRIDLOG_ON', 0)) ):
00197 return
00198
00199 url = os.environ.get('GRIDLOG_DEST')
00200 if url is None:
00201 return
00202
00203
00204 try:
00205 scheme = url[:url.find('://')]
00206 send = GLRegistry[scheme]
00207 send( url, str(GLRecord(**kw)), )
00208 except Exception, ex:
00209 print >>sys.stderr, "*** gridLog failed -- %s" %(str(kw))
00210
00211
00212 def sendUDP(url, outputStr):
00213 from socket import socket, AF_INET, SOCK_DGRAM
00214 idx1 = url.find('://') + 3; idx2 = url.find('/', idx1)
00215 if idx2 < idx1: idx2 = len(url)
00216 netloc = url[idx1:idx2]
00217 host,port = (netloc.split(':')+[80])[0:2]
00218 socket(AF_INET, SOCK_DGRAM).sendto( outputStr, (host,int(port)), )
00219
00220 def writeToFile(url, outputStr):
00221 print >> open(url.split('://')[1], 'a+'), outputStr
00222
00223 GLRegistry["gridlog-udp"] = sendUDP
00224 GLRegistry["file"] = writeToFile
00225
00226
00227 def setBasicLogger():
00228 '''Use Basic Logger.
00229 '''
00230 setLoggerClass(BasicLogger)
00231 BasicLogger.setLevel(0)
00232
00233 def setGridLogger():
00234 '''Use GridLogger for all logging events.
00235 '''
00236 setLoggerClass(GridLogger)
00237
00238 def setBasicLoggerWARN():
00239 '''Use Basic Logger.
00240 '''
00241 setLoggerClass(BasicLogger)
00242 BasicLogger.setLevel(WARN)
00243
00244 def setBasicLoggerDEBUG():
00245 '''Use Basic Logger.
00246 '''
00247 setLoggerClass(BasicLogger)
00248 BasicLogger.setLevel(DEBUG)
00249
00250 def setLoggerClass(loggingClass):
00251 '''Set Logging Class.
00252 '''
00253
00254 def setLoggerClass(loggingClass):
00255 '''Set Logging Class.
00256 '''
00257 assert issubclass(loggingClass, ILogger), 'loggingClass must subclass ILogger'
00258 global _LoggerClass
00259 _LoggerClass = loggingClass
00260
00261 def setLevel(level=0):
00262 '''Set Global Logging Level.
00263 '''
00264 ILogger.level = level
00265
00266 def getLevel():
00267 return ILogger.level
00268
00269 def getLogger(msg):
00270 '''Return instance of Logging class.
00271 '''
00272 return _LoggerClass(msg)
00273
00274