You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

275 lines
8.0 KiB

  1. # Copyright (c) 2003, The Regents of the University of California,
  2. # through Lawrence Berkeley National Laboratory (subject to receipt of
  3. # any required approvals from the U.S. Dept. of Energy). All rights
  4. # reserved.
  5. #
  6. """Logging"""
  7. ident = "$Id$"
  8. import os, sys
  9. WARN = 1
  10. DEBUG = 2
  11. class ILogger:
  12. '''Logger interface, by default this class
  13. will be used and logging calls are no-ops.
  14. '''
  15. level = 0
  16. def __init__(self, msg):
  17. return
  18. def warning(self, *args, **kw):
  19. return
  20. def debug(self, *args, **kw):
  21. return
  22. def error(self, *args, **kw):
  23. return
  24. def setLevel(cls, level):
  25. cls.level = level
  26. setLevel = classmethod(setLevel)
  27. debugOn = lambda self: self.level >= DEBUG
  28. warnOn = lambda self: self.level >= WARN
  29. class BasicLogger(ILogger):
  30. last = ''
  31. def __init__(self, msg, out=sys.stdout):
  32. self.msg, self.out = msg, out
  33. def warning(self, msg, *args, **kw):
  34. if self.warnOn() is False: return
  35. if BasicLogger.last != self.msg:
  36. BasicLogger.last = self.msg
  37. print >>self, "---- ", self.msg, " ----"
  38. print >>self, " %s " %self.WARN,
  39. print >>self, msg %args
  40. WARN = '[WARN]'
  41. def debug(self, msg, *args, **kw):
  42. if self.debugOn() is False: return
  43. if BasicLogger.last != self.msg:
  44. BasicLogger.last = self.msg
  45. print >>self, "---- ", self.msg, " ----"
  46. print >>self, " %s " %self.DEBUG,
  47. print >>self, msg %args
  48. DEBUG = '[DEBUG]'
  49. def error(self, msg, *args, **kw):
  50. if BasicLogger.last != self.msg:
  51. BasicLogger.last = self.msg
  52. print >>self, "---- ", self.msg, " ----"
  53. print >>self, " %s " %self.ERROR,
  54. print >>self, msg %args
  55. ERROR = '[ERROR]'
  56. def write(self, *args):
  57. '''Write convenience function; writes strings.
  58. '''
  59. for s in args: self.out.write(s)
  60. event = ''.join(*args)
  61. _LoggerClass = BasicLogger
  62. class GridLogger(ILogger):
  63. def debug(self, msg, *args, **kw):
  64. kw['component'] = self.msg
  65. gridLog(event=msg %args, level='DEBUG', **kw)
  66. def warning(self, msg, *args, **kw):
  67. kw['component'] = self.msg
  68. gridLog(event=msg %args, level='WARNING', **kw)
  69. def error(self, msg, *args, **kw):
  70. kw['component'] = self.msg
  71. gridLog(event=msg %args, level='ERROR', **kw)
  72. #
  73. # Registry of send functions for gridLog
  74. #
  75. GLRegistry = {}
  76. class GLRecord(dict):
  77. """Grid Logging Best Practices Record, Distributed Logging Utilities
  78. The following names are reserved:
  79. event -- log event name
  80. Below is EBNF for the event name part of a log message.
  81. name = <nodot> ( "." <name> )?
  82. nodot = {RFC3896-chars except "."}
  83. Suffixes:
  84. start: Immediately before the first action in a task.
  85. end: Immediately after the last action in a task (that succeeded).
  86. error: an error condition that does not correspond to an end event.
  87. ts -- timestamp
  88. level -- logging level (see levels below)
  89. status -- integer status code
  90. gid -- global grid identifier
  91. gid, cgid -- parent/child identifiers
  92. prog -- program name
  93. More info: http://www.cedps.net/wiki/index.php/LoggingBestPractices#Python
  94. reserved -- list of reserved names,
  95. omitname -- list of reserved names, output only values ('ts', 'event',)
  96. levels -- dict of levels and description
  97. """
  98. reserved = ('ts', 'event', 'level', 'status', 'gid', 'prog')
  99. omitname = ()
  100. levels = dict(FATAL='Component cannot continue, or system is unusable.',
  101. ALERT='Action must be taken immediately.',
  102. CRITICAL='Critical conditions (on the system).',
  103. ERROR='Errors in the component; not errors from elsewhere.',
  104. WARNING='Problems that are recovered from, usually.',
  105. NOTICE='Normal but significant condition.',
  106. INFO='Informational messages that would be useful to a deployer or administrator.',
  107. DEBUG='Lower level information concerning program logic decisions, internal state, etc.',
  108. TRACE='Finest granularity, similar to "stepping through" the component or system.',
  109. )
  110. def __init__(self, date=None, **kw):
  111. kw['ts'] = date or self.GLDate()
  112. kw['gid'] = kw.get('gid') or os.getpid()
  113. dict.__init__(self, kw)
  114. def __str__(self):
  115. """
  116. """
  117. from cStringIO import StringIO
  118. s = StringIO(); n = " "
  119. reserved = self.reserved; omitname = self.omitname; levels = self.levels
  120. for k in ( list(filter(lambda i: self.has_key(i), reserved)) +
  121. list(filter(lambda i: i not in reserved, self.keys()))
  122. ):
  123. v = self[k]
  124. if k in omitname:
  125. s.write( "%s " %self.format[type(v)](v) )
  126. continue
  127. if k == reserved[2] and v not in levels:
  128. pass
  129. s.write( "%s=%s " %(k, self.format[type(v)](v) ) )
  130. s.write("\n")
  131. return s.getvalue()
  132. class GLDate(str):
  133. """Grid logging Date Format
  134. all timestamps should all be in the same time zone (UTC).
  135. Grid timestamp value format that is a highly readable variant of the ISO8601 time standard [1]:
  136. YYYY-MM-DDTHH:MM:SS.SSSSSSZ
  137. """
  138. def __new__(self, args=None):
  139. """args -- datetime (year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
  140. """
  141. import datetime
  142. args = args or datetime.datetime.utcnow()
  143. l = (args.year, args.month, args.day, args.hour, args.minute, args.second,
  144. args.microsecond, args.tzinfo or 'Z')
  145. return str.__new__(self, "%04d-%02d-%02dT%02d:%02d:%02d.%06d%s" %l)
  146. format = { int:str, float:lambda x: "%lf" % x, long:str, str:lambda x:x,
  147. unicode:str, GLDate:str, }
  148. def gridLog(**kw):
  149. """Send GLRecord, Distributed Logging Utilities
  150. If the scheme is passed as a keyword parameter
  151. the value is expected to be a callable function
  152. that takes 2 parameters: url, outputStr
  153. GRIDLOG_ON -- turn grid logging on
  154. GRIDLOG_DEST -- provide URL destination
  155. """
  156. import os
  157. if not bool( int(os.environ.get('GRIDLOG_ON', 0)) ):
  158. return
  159. url = os.environ.get('GRIDLOG_DEST')
  160. if url is None:
  161. return
  162. ## NOTE: urlparse problem w/customized schemes
  163. try:
  164. scheme = url[:url.find('://')]
  165. send = GLRegistry[scheme]
  166. send( url, str(GLRecord(**kw)), )
  167. except Exception, ex:
  168. print >>sys.stderr, "*** gridLog failed -- %s" %(str(kw))
  169. def sendUDP(url, outputStr):
  170. from socket import socket, AF_INET, SOCK_DGRAM
  171. idx1 = url.find('://') + 3; idx2 = url.find('/', idx1)
  172. if idx2 < idx1: idx2 = len(url)
  173. netloc = url[idx1:idx2]
  174. host,port = (netloc.split(':')+[80])[0:2]
  175. socket(AF_INET, SOCK_DGRAM).sendto( outputStr, (host,int(port)), )
  176. def writeToFile(url, outputStr):
  177. print >> open(url.split('://')[1], 'a+'), outputStr
  178. GLRegistry["gridlog-udp"] = sendUDP
  179. GLRegistry["file"] = writeToFile
  180. def setBasicLogger():
  181. '''Use Basic Logger.
  182. '''
  183. setLoggerClass(BasicLogger)
  184. BasicLogger.setLevel(0)
  185. def setGridLogger():
  186. '''Use GridLogger for all logging events.
  187. '''
  188. setLoggerClass(GridLogger)
  189. def setBasicLoggerWARN():
  190. '''Use Basic Logger.
  191. '''
  192. setLoggerClass(BasicLogger)
  193. BasicLogger.setLevel(WARN)
  194. def setBasicLoggerDEBUG():
  195. '''Use Basic Logger.
  196. '''
  197. setLoggerClass(BasicLogger)
  198. BasicLogger.setLevel(DEBUG)
  199. def setLoggerClass(loggingClass):
  200. '''Set Logging Class.
  201. '''
  202. def setLoggerClass(loggingClass):
  203. '''Set Logging Class.
  204. '''
  205. assert issubclass(loggingClass, ILogger), 'loggingClass must subclass ILogger'
  206. global _LoggerClass
  207. _LoggerClass = loggingClass
  208. def setLevel(level=0):
  209. '''Set Global Logging Level.
  210. '''
  211. ILogger.level = level
  212. def getLevel():
  213. return ILogger.level
  214. def getLogger(msg):
  215. '''Return instance of Logging class.
  216. '''
  217. return _LoggerClass(msg)