Viewing file:      logger.py (6.77 KB)      -rw-r--r-- Select action/file-type:    (+) |   (+) |   (+) | Code (+) | Session (+) |   (+) | SDB (+) |   (+) |   (+) |   (+) |   (+) |   (+) |
 
# -*- Mode: Python; tab-width: 4 -*-
  import asynchat import socket import string import time         # these three are for the rotating logger import os           # | import stat         # v
  # # three types of log: # 1) file #    with optional flushing.  Also, one that rotates the log. # 2) socket #    dump output directly to a socket connection. [how do we #    keep it open?] # 3) syslog #    log to syslog via tcp.  this is a per-line protocol. #
  # # The 'standard' interface to a logging object is simply # log_object.log (message) #
  # a file-like object that captures output, and # makes sure to flush it always...  this could # be connected to: #  o    stdio file #  o    low-level file #  o    socket channel #  o    syslog output...
  class file_logger:                  # pass this either a path or a file object.     def __init__ (self, file, flush=1, mode='a'):         if type(file) == type(''):             if (file == '-'):                 import sys                 self.file = sys.stdout             else:                 self.file = open (file, mode)         else:             self.file = file         self.do_flush = flush
      def __repr__ (self):         return '<file logger: %s>' % self.file
      def write (self, data):         self.file.write (data)         self.maybe_flush()              def writeline (self, line):         self.file.writeline (line)         self.maybe_flush()              def writelines (self, lines):         self.file.writelines (lines)         self.maybe_flush()
      def maybe_flush (self):         if self.do_flush:             self.file.flush()
      def flush (self):         self.file.flush()
      def softspace (self, *args):         pass
      def log (self, message):         if message[-1] not in ('\r', '\n'):             self.write (message + '\n')         else:             self.write (message)
  # like a file_logger, but it must be attached to a filename. # When the log gets too full, or a certain time has passed, # it backs up the log and starts a new one.  Note that backing # up the log is done via "mv" because anything else (cp, gzip) # would take time, during which medusa would do nothing else.
  class rotating_file_logger (file_logger):                  # If freq is non-None we back up "daily", "weekly", or "monthly".     # Else if maxsize is non-None we back up whenever the log gets     # to big.  If both are None we never back up.     def __init__ (self, file, freq=None, maxsize=None, flush=1, mode='a'):         self.filename = file         self.mode = mode         self.file = open (file, mode)         self.freq = freq         self.maxsize = maxsize         self.rotate_when = self.next_backup(self.freq)         self.do_flush = flush
      def __repr__ (self):         return '<rotating-file logger: %s>' % self.file
      # We back up at midnight every 1) day, 2) monday, or 3) 1st of month     def next_backup (self, freq):         (yr, mo, day, hr, min, sec, wd, jday, dst) = time.localtime(time.time())         if freq == 'daily':             return time.mktime(yr,mo,day+1, 0,0,0, 0,0,-1)         elif freq == 'weekly':             return time.mktime(yr,mo,day-wd+7, 0,0,0, 0,0,-1)  # wd(monday)==0         elif freq == 'monthly':             return time.mktime(yr,mo+1,1, 0,0,0, 0,0,-1)         else:             return None                  # not a date-based backup
      def maybe_flush (self):              # rotate first if necessary         self.maybe_rotate()         if self.do_flush:                # from file_logger()             self.file.flush()
      def maybe_rotate (self):         if self.freq and time.time() > self.rotate_when:             self.rotate()             self.rotate_when = self.next_backup(self.freq)         elif self.maxsize:               # rotate when we get too big             try:                 if os.stat(self.filename)[stat.ST_SIZE] > self.maxsize:                     self.rotate()              except os.error:             # file not found, probably                 self.rotate()            # will create a new file
      def rotate (self):         (yr, mo, day, hr, min, sec, wd, jday, dst) = time.localtime(time.time())         try:             self.file.close()             newname = '%s.ends%04d%02d%02d' % (self.filename, yr, mo, day)             try:                 open(newname, "r").close()      # check if file exists                 newname = newname + "-%02d%02d%02d" % (hr, min, sec)             except:                             # YEARMODY is unique                 pass             os.rename(self.filename, newname)             self.file = open(self.filename, self.mode)         except:             pass
  # syslog is a line-oriented log protocol - this class would be # appropriate for FTP or HTTP logs, but not for dumping stderr to.
  # TODO: a simple safety wrapper that will ensure that the line sent # to syslog is reasonable.
  # TODO: async version of syslog_client: now, log entries use blocking # send()
  import m_syslog syslog_logger = m_syslog.syslog_client
  class syslog_logger (m_syslog.syslog_client):     def __init__ (self, address, facility='user'):         m_syslog.syslog_client.__init__ (self, address)         self.facility = m_syslog.facility_names[facility]         self.address=address
      def __repr__ (self):         return '<syslog logger address=%s>' % (repr(self.address))
      def log (self, message):         m_syslog.syslog_client.log (             self,             message,             facility=self.facility,             priority=m_syslog.LOG_INFO             )
  # log to a stream socket, asynchronously
  class socket_logger (asynchat.async_chat):
      def __init__ (self, address):
          if type(address) == type(''):             self.create_socket (socket.AF_UNIX, socket.SOCK_STREAM)         else:             self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
          self.connect (address)         self.address = address              def __repr__ (self):         return '<socket logger: address=%s>' % (self.address)
      def log (self, message):         if message[-2:] != '\r\n':             self.socket.push (message + '\r\n')         else:             self.socket.push (message)
  # log to multiple places class multi_logger:     def __init__ (self, loggers):         self.loggers = loggers
      def __repr__ (self):         return '<multi logger: %s>' % (repr(self.loggers))
      def log (self, message):         for logger in self.loggers:             logger.log (message)
  class resolving_logger:     """Feed (ip, message) combinations into this logger to get a     resolved hostname in front of the message.  The message will not     be logged until the PTR request finishes (or fails)."""
      def __init__ (self, resolver, logger):         self.resolver = resolver         self.logger = logger
      class logger_thunk:         def __init__ (self, message, logger):             self.message = message             self.logger = logger
          def __call__ (self, host, ttl, answer):             if not answer:                 answer = host             self.logger.log ('%s:%s' % (answer, self.message))
      def log (self, ip, message):         self.resolver.resolve_ptr (             ip,             self.logger_thunk (                 message,                 self.logger                 )             )
  class unresolving_logger:     "Just in case you don't want to resolve"     def __init__ (self, logger):         self.logger = logger
      def log (self, ip, message):         self.logger.log ('%s:%s' % (ip, message))
 
  def strip_eol (line):     while line and line[-1] in '\r\n':         line = line[:-1]     return line
  class tail_logger:     "Keep track of the last <size> log messages"     def __init__ (self, logger, size=500):         self.size = size         self.logger = logger         self.messages = []
      def log (self, message):         self.messages.append (strip_eol (message))         if len (self.messages) > self.size:             del self.messages[0]         self.logger.log (message) 
  |