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.
 
 
 

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