home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / arm / util / log.py < prev    next >
Encoding:
Python Source  |  2012-05-18  |  6.6 KB  |  205 lines

  1. """
  2. Tracks application events, both directing them to attached listeners and
  3. keeping a record of them. A limited space is provided for old events, keeping
  4. and trimming them on a per-runlevel basis (ie, too many DEBUG events will only
  5. result in entries from that runlevel being dropped). All functions are thread
  6. safe.
  7. """
  8.  
  9. import os
  10. import time
  11. from sys import maxint
  12. from threading import RLock
  13.  
  14. from util import enum
  15.  
  16. # Logging runlevels. These are *very* commonly used so including shorter
  17. # aliases (so they can be referenced as log.DEBUG, log.WARN, etc).
  18. Runlevel = enum.Enum(("DEBUG", "DEBUG"), ("INFO", "INFO"), ("NOTICE", "NOTICE"),
  19.                      ("WARN", "WARN"), ("ERR", "ERR"))
  20. DEBUG, INFO, NOTICE, WARN, ERR = Runlevel.values()
  21.  
  22. # provides thread safety for logging operations
  23. LOG_LOCK = RLock()
  24.  
  25. # chronologically ordered records of events for each runlevel, stored as tuples
  26. # consisting of: (time, message)
  27. _backlog = dict([(level, []) for level in Runlevel.values()])
  28.  
  29. # mapping of runlevels to the listeners interested in receiving events from it
  30. _listeners = dict([(level, []) for level in Runlevel.values()])
  31.  
  32. CONFIG = {"cache.armLog.size": 1000,
  33.           "cache.armLog.trimSize": 200}
  34.  
  35. DUMP_FILE = None
  36.  
  37. def loadConfig(config):
  38.   config.update(CONFIG, {
  39.     "cache.armLog.size": 10,
  40.     "cache.armLog.trimSize": 5})
  41.   
  42.   CONFIG["cache.armLog.trimSize"] = min(CONFIG["cache.armLog.trimSize"], CONFIG["cache.armLog.size"] / 2)
  43.  
  44. def setDumpFile(logPath):
  45.   """
  46.   Logs all future logged events to the given path. This raises an IOError if
  47.   the file fails to be opened. If the file already exists then this overwrites
  48.   it.
  49.   
  50.   Arguments:
  51.     logPath - path where to persist logs
  52.   """
  53.   
  54.   global DUMP_FILE
  55.   
  56.   # make sure that the parent directory exists
  57.   baseDir = os.path.dirname(logPath)
  58.   if not os.path.exists(baseDir): os.makedirs(baseDir)
  59.   
  60.   DUMP_FILE = open(logPath, "w")
  61.  
  62. def log(level, msg, eventTime = None):
  63.   """
  64.   Registers an event, directing it to interested listeners and preserving it in
  65.   the backlog. If the level is None then this is a no-op.
  66.   
  67.   Arguments:
  68.     level     - runlevel corresponding to the message severity
  69.     msg       - string associated with the message
  70.     eventTime - unix time at which the event occurred, current time if undefined
  71.   """
  72.   
  73.   global DUMP_FILE
  74.   if not level: return
  75.   if eventTime == None: eventTime = time.time()
  76.   
  77.   LOG_LOCK.acquire()
  78.   try:
  79.     newEvent = (eventTime, msg)
  80.     eventBacklog = _backlog[level]
  81.     
  82.     # inserts the new event into the backlog
  83.     if not eventBacklog or eventTime >= eventBacklog[-1][0]:
  84.       # newest event - append to end
  85.       eventBacklog.append(newEvent)
  86.     elif eventTime <= eventBacklog[0][0]:
  87.       # oldest event - insert at start
  88.       eventBacklog.insert(0, newEvent)
  89.     else:
  90.       # somewhere in the middle - start checking from the end
  91.       for i in range(len(eventBacklog) - 1, -1, -1):
  92.         if eventBacklog[i][0] <= eventTime:
  93.           eventBacklog.insert(i + 1, newEvent)
  94.           break
  95.     
  96.     # truncates backlog if too long
  97.     toDelete = len(eventBacklog) - CONFIG["cache.armLog.size"]
  98.     if toDelete >= 0: del eventBacklog[: toDelete + CONFIG["cache.armLog.trimSize"]]
  99.     
  100.     # persists the event if a debug file's been set
  101.     if DUMP_FILE:
  102.       try:
  103.         entryTime = time.localtime(eventTime)
  104.         timeLabel = "%i/%i/%i %02i:%02i:%02i" % (entryTime[1], entryTime[2], entryTime[0], entryTime[3], entryTime[4], entryTime[5])
  105.         logEntry = "%s [%s] %s\n" % (timeLabel, level, msg)
  106.         DUMP_FILE.write(logEntry)
  107.         DUMP_FILE.flush()
  108.       except IOError, exc:
  109.         DUMP_FILE = None
  110.         log(ERR, "Failed to write to the debug file - %s" % exc)
  111.     
  112.     # notifies listeners
  113.     for callback in _listeners[level]:
  114.       callback(level, msg, eventTime)
  115.   finally:
  116.     LOG_LOCK.release()
  117.  
  118. def addListener(level, callback):
  119.   """
  120.   Directs future events to the given callback function. The runlevels passed on
  121.   to listeners are provided as the corresponding strings ("DEBUG", "INFO",
  122.   "NOTICE", etc), and times in POSIX (unix) time.
  123.   
  124.   Arguments:
  125.     level    - event runlevel the listener should be notified of
  126.     callback - functor that'll accept the events, expected to be of the form:
  127.                myFunction(level, msg, time)
  128.   """
  129.   
  130.   if not callback in _listeners[level]:
  131.     _listeners[level].append(callback)
  132.  
  133. def addListeners(levels, callback, dumpBacklog = False):
  134.   """
  135.   Directs future events of multiple runlevels to the given callback function.
  136.   
  137.   Arguments:
  138.     levels      - list of runlevel events the listener should be notified of
  139.     callback    - functor that'll accept the events, expected to be of the
  140.                   form: myFunction(level, msg, time)
  141.     dumpBacklog - if true, any past events of the designated runlevels will be
  142.                   provided to the listener before returning (in chronological
  143.                   order)
  144.   """
  145.   
  146.   LOG_LOCK.acquire()
  147.   try:
  148.     for level in levels: addListener(level, callback)
  149.     
  150.     if dumpBacklog:
  151.       for level, msg, eventTime in _getEntries(levels):
  152.         callback(level, msg, eventTime)
  153.   finally:
  154.     LOG_LOCK.release()
  155.  
  156. def removeListener(level, callback):
  157.   """
  158.   Stops listener from being notified of further events. This returns true if a
  159.   listener's removed, false otherwise.
  160.   
  161.   Arguments:
  162.     level    - runlevel the listener to be removed
  163.     callback - functor to be removed
  164.   """
  165.   
  166.   if callback in _listeners[level]:
  167.     _listeners[level].remove(callback)
  168.     return True
  169.   else: return False
  170.  
  171. def _getEntries(levels):
  172.   """
  173.   Generator for providing past events belonging to the given runlevels (in
  174.   chronological order). This should be used under the LOG_LOCK to prevent
  175.   concurrent modifications.
  176.   
  177.   Arguments:
  178.     levels - runlevels for which events are provided
  179.   """
  180.   
  181.   # drops any runlevels if there aren't entries in it
  182.   toRemove = [level for level in levels if not _backlog[level]]
  183.   for level in toRemove: levels.remove(level)
  184.   
  185.   # tracks where unprocessed entries start in the backlog
  186.   backlogPtr = dict([(level, 0) for level in levels])
  187.   
  188.   while levels:
  189.     earliestLevel, earliestMsg, earliestTime = None, "", maxint
  190.     
  191.     # finds the earliest unprocessed event
  192.     for level in levels:
  193.       entry = _backlog[level][backlogPtr[level]]
  194.       
  195.       if entry[0] < earliestTime:
  196.         earliestLevel, earliestMsg, earliestTime = level, entry[1], entry[0]
  197.     
  198.     yield (earliestLevel, earliestMsg, earliestTime)
  199.     
  200.     # removes runlevel if there aren't any more entries
  201.     backlogPtr[earliestLevel] += 1
  202.     if len(_backlog[earliestLevel]) <= backlogPtr[earliestLevel]:
  203.       levels.remove(earliestLevel)
  204.  
  205.