[Python-checkins] python/dist/src/Lib/logging handlers.py, 1.12, 1.13

vsajip at users.sourceforge.net vsajip at users.sourceforge.net
Sat Jul 3 07:48:37 EDT 2004


Update of /cvsroot/python/python/dist/src/Lib/logging
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv27656

Modified Files:
	handlers.py 
Log Message:
Refactored RotatingFileHandler to create a base class  for rotating handlers. Added TimedRotatingFileHandler.

Index: handlers.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/logging/handlers.py,v
retrieving revision 1.12
retrieving revision 1.13
diff -C2 -d -r1.12 -r1.13
*** handlers.py	8 Mar 2004 16:57:19 -0000	1.12
--- handlers.py	3 Jul 2004 11:48:34 -0000	1.13
***************
*** 28,32 ****
  """
  
! import sys, logging, socket, types, os, string, cPickle, struct, time
  
  #
--- 28,32 ----
  """
  
! import sys, logging, socket, types, os, string, cPickle, struct, time, glob
  
  #
***************
*** 40,45 ****
  SYSLOG_UDP_PORT             = 514
  
  
! class RotatingFileHandler(logging.FileHandler):
      def __init__(self, filename, mode="a", maxBytes=0, backupCount=0):
          """
--- 40,71 ----
  SYSLOG_UDP_PORT             = 514
  
+ class BaseRotatingHandler(logging.FileHandler):
+     """
+     Base class for handlers that rotate log files at a certain point.
+     Not meant to be instantiated directly.  Instead, use RotatingFileHandler
+     or TimedRotatingFileHandler.
+     """
+     def __init__(self, filename, mode):
+         """
+         Use the specified filename for streamed logging
+         """
+         logging.FileHandler.__init__(self, filename, mode)
  
!     def emit(self, record):
!         """
!         Emit a record.
! 
!         Output the record to the file, catering for rollover as described
!         in doRollover().
!         """
!         if self.shouldRollover(record):
!             self.doRollover()
!         logging.FileHandler.emit(self, record)
! 
! class RotatingFileHandler(BaseRotatingHandler):
!     """
!     Handler for logging to a set of files, which switches from one file
!     to the next when the current file reaches a certain size.
!     """
      def __init__(self, filename, mode="a", maxBytes=0, backupCount=0):
          """
***************
*** 63,71 ****
          If maxBytes is zero, rollover never occurs.
          """
!         logging.FileHandler.__init__(self, filename, mode)
          self.maxBytes = maxBytes
          self.backupCount = backupCount
-         if maxBytes > 0:
-             self.mode = "a"
  
      def doRollover(self):
--- 89,98 ----
          If maxBytes is zero, rollover never occurs.
          """
!         self.mode = mode
!         if maxBytes > 0:
!             self.mode = "a" # doesn't make sense otherwise!
!         BaseRotatingHandler.__init__(self, filename, self.mode)
          self.maxBytes = maxBytes
          self.backupCount = backupCount
  
      def doRollover(self):
***************
*** 91,100 ****
          self.stream = open(self.baseFilename, "w")
  
!     def emit(self, record):
          """
!         Emit a record.
  
!         Output the record to the file, catering for rollover as described
!         in doRollover().
          """
          if self.maxBytes > 0:                   # are we rolling over?
--- 118,127 ----
          self.stream = open(self.baseFilename, "w")
  
!     def shouldRollover(self, record):
          """
!         Determine if rollover should occur.
  
!         Basically, see if the supplied record would cause the file to exceed
!         the size limit we have.
          """
          if self.maxBytes > 0:                   # are we rolling over?
***************
*** 102,108 ****
              self.stream.seek(0, 2)  #due to non-posix-compliant Windows feature
              if self.stream.tell() + len(msg) >= self.maxBytes:
!                 self.doRollover()
!         logging.FileHandler.emit(self, record)
  
  
  class SocketHandler(logging.Handler):
--- 129,264 ----
              self.stream.seek(0, 2)  #due to non-posix-compliant Windows feature
              if self.stream.tell() + len(msg) >= self.maxBytes:
!                 return 1
!         return 0
! 
! class TimedRotatingFileHandler(BaseRotatingHandler):
!     """
!     Handler for logging to a file, rotating the log file at certain timed
!     intervals.
! 
!     If backupCount is > 0, when rollover is done, no more than backupCount
!     files are kept - the oldest ones are deleted.
!     """
!     def __init__(self, filename, when='h', interval=1, backupCount=0):
!         BaseRotatingHandler.__init__(self, filename, 'a')
!         self.when = string.upper(when)
!         self.backupCount = backupCount
!         # Calculate the real rollover interval, which is just the number of
!         # seconds between rollovers.  Also set the filename suffix used when
!         # a rollover occurs.  Current 'when' events supported:
!         # S - Seconds
!         # M - Minutes
!         # H - Hours
!         # D - Days
!         # midnight - roll over at midnight
!         # W{0-6} - roll over on a certain day; 0 - Monday
!         #
!         # Case of the 'when' specifier is not important; lower or upper case
!         # will work.
!         currentTime = int(time.time())
!         if self.when == 'S':
!             self.interval = 1 # one second
!             self.suffix = "%Y-%m-%d_%H-%M-%S"
!         elif self.when == 'M':
!             self.interval = 60 # one minute
!             self.suffix = "%Y-%m-%d_%H-%M"
!         elif self.when == 'H':
!             self.interval = 60 * 60 # one hour
!             self.suffix = "%Y-%m-%d_%H"
!         elif self.when == 'D' or self.when == 'MIDNIGHT':
!             self.interval = 60 * 60 * 24 # one day
!             self.suffix = "%Y-%m-%d"
!         elif self.when.startswith('W'):
!             self.interval = 60 * 60 * 24 * 7 # one week
!             if len(self.when) != 2:
!                 raise ValueError("You must specify a day for weekly rollover from 0 to 6 (0 is Monday): %s" % self.when)
!             if self.when[1] < '0' or self.when[1] > '6':
!                 raise ValueError("Invalid day specified for weekly rollover: %s" % self.when)
!             self.dayOfWeek = int(self.when[1])
!             self.suffix = "%Y-%m-%d"
!         else:
!             raise ValueError("Invalid rollover interval specified: %s" % self.when)
! 
!         self.interval *= interval # multiply by units requested
!         self.rolloverAt = currentTime + self.interval
  
+         # If we are rolling over at midnight or weekly, then the interval is already known.
+         # What we need to figure out is WHEN the next interval is.  In other words,
+         # if you are rolling over at midnight, then your base interval is 1 day,
+         # but you want to start that one day clock at midnight, not now.  So, we
+         # have to fudge the rolloverAt value in order to trigger the first rollover
+         # at the right time.  After that, the regular interval will take care of
+         # the rest.  Note that this code doesn't care about leap seconds. :)
+         if self.when == 'MIDNIGHT' or self.when.startswith('W'):
+             # This could be done with less code, but I wanted it to be clear
+             t = time.localtime(currentTime)
+             currentHour = t[3]
+             currentMinute = t[4]
+             currentSecond = t[5]
+             # r is the number of seconds left between now and midnight
+             r = (24 - currentHour) * 60 * 60 # number of hours in seconds
+             r += (59 - currentMinute) * 60 # plus the number of minutes (in secs)
+             r += (59 - currentSecond) # plus the number of seconds
+             self.rolloverAt = currentTime + r
+             # If we are rolling over on a certain day, add in the number of days until
+             # the next rollover, but offset by 1 since we just calculated the time
+             # until the next day starts.  There are three cases:
+             # Case 1) The day to rollover is today; in this case, do nothing
+             # Case 2) The day to rollover is further in the interval (i.e., today is
+             #         day 2 (Wednesday) and rollover is on day 6 (Sunday).  Days to
+             #         next rollover is simply 6 - 2 - 1, or 3.
+             # Case 3) The day to rollover is behind us in the interval (i.e., today
+             #         is day 5 (Saturday) and rollover is on day 3 (Thursday).
+             #         Days to rollover is 6 - 5 + 3, or 4.  In this case, it's the
+             #         number of days left in the current week (1) plus the number
+             #         of days in the next week until the rollover day (3).
+             if when.startswith('W'):
+                 day = t[6] # 0 is Monday
+                 if day > self.dayOfWeek:
+                     daysToWait = (day - self.dayOfWeek) - 1
+                     self.rolloverAt += (daysToWait * (60 * 60 * 24))
+                 if day < self.dayOfWeek:
+                     daysToWait = (6 - self.dayOfWeek) + day
+                     self.rolloverAt += (daysToWait * (60 * 60 * 24))
+ 
+         print "Will rollover at %d, %d seconds from now" % (self.rolloverAt, self.rolloverAt - currentTime)
+ 
+     def shouldRollover(self, record):
+         """
+         Determine if rollover should occur
+ 
+         record is not used, as we are just comparing times, but it is needed so
+         the method siguratures are the same
+         """
+         t = int(time.time())
+         if t >= self.rolloverAt:
+             return 1
+         print "No need to rollover: %d, %d" % (t, self.rolloverAt)
+         return 0
+ 
+     def doRollover(self):
+         """
+         do a rollover; in this case, a date/time stamp is appended to the filename
+         when the rollover happens.  However, you want the file to be named for the
+         start of the interval, not the current time.  If there is a backup count,
+         then we have to get a list of matching filenames, sort them and remove
+         the one with the oldest suffix.
+         """
+         self.stream.close()
+         # get the time that this sequence started at and make it a TimeTuple
+         t = self.rolloverAt - self.interval
+         timeTuple = time.localtime(t)
+         dfn = self.baseFilename + "." + time.strftime(self.suffix, timeTuple)
+         if os.path.exists(dfn):
+             os.remove(dfn)
+         os.rename(self.baseFilename, dfn)
+         if self.backupCount > 0:
+             # find the oldest log file and delete it
+             s = glob.glob(self.baseFilename + ".20*")
+             if len(s) > self.backupCount:
+                 os.remove(s[0])
+         print "%s -> %s" % (self.baseFilename, dfn)
+         self.stream = open(self.baseFilename, "w")
+         self.rolloverAt = int(time.time()) + self.interval
  
  class SocketHandler(logging.Handler):




More information about the Python-checkins mailing list