Closed #9556: Allowed specifying a time-of-day for a TimedRotatingFileHandler to rotate.

This commit is contained in:
Vinay Sajip 2013-04-12 17:04:23 +01:00
parent 8a9e38e715
commit a713079ed8
4 changed files with 73 additions and 8 deletions

View File

@ -296,7 +296,7 @@ The :class:`TimedRotatingFileHandler` class, located in the
timed intervals.
.. class:: TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False)
.. class:: TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None)
Returns a new instance of the :class:`TimedRotatingFileHandler` class. The
specified file is opened and used as the stream for logging. On rotating it also
@ -346,6 +346,12 @@ timed intervals.
If *delay* is true, then file opening is deferred until the first call to
:meth:`emit`.
If *atTime* is not ``None``, it must be a ``datetime.time`` instance which
specifies the time of day when rollover occurs, for the cases where rollover
is set to happen "at midnight" or "on a particular weekday".
.. versionchanged:: 3.4
*atTime* parameter was added.
.. method:: doRollover()

View File

@ -1,4 +1,4 @@
# Copyright 2001-2012 by Vinay Sajip. All Rights Reserved.
# Copyright 2001-2013 by Vinay Sajip. All Rights Reserved.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose and without fee is hereby granted,
@ -18,7 +18,7 @@
Additional handlers for the logging package for Python. The core package is
based on PEP 282 and comments thereto in comp.lang.python.
Copyright (C) 2001-2012 Vinay Sajip. All Rights Reserved.
Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved.
To use, simply 'import logging.handlers' and log away!
"""
@ -196,11 +196,12 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
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, encoding=None, delay=False, utc=False):
def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None):
BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay)
self.when = when.upper()
self.backupCount = backupCount
self.utc = utc
self.atTime = atTime
# 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:
@ -270,9 +271,22 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
currentHour = t[3]
currentMinute = t[4]
currentSecond = t[5]
# r is the number of seconds left between now and midnight
r = _MIDNIGHT - ((currentHour * 60 + currentMinute) * 60 +
currentDay = t[6]
# r is the number of seconds left between now and the next rotation
if self.atTime is None:
rotate_ts = _MIDNIGHT
else:
rotate_ts = ((self.atTime.hour * 60 + self.atTime.minute)*60 +
self.atTime.second)
r = rotate_ts - ((currentHour * 60 + currentMinute) * 60 +
currentSecond)
if r < 0:
# Rotate time is before the current time (for example when
# self.rotateAt is 13:45 and it now 14:15), rotation is
# tomorrow.
r += _MIDNIGHT
currentDay = (currentDay + 1) % 7
result = 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
@ -290,7 +304,7 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
# This is because the above time calculation takes us to midnight on this
# day, i.e. the start of the next day.
if self.when.startswith('W'):
day = t[6] # 0 is Monday
day = currentDay # 0 is Monday
if day != self.dayOfWeek:
if day < self.dayOfWeek:
daysToWait = self.dayOfWeek - day

View File

@ -3949,6 +3949,48 @@ class TimedRotatingFileHandlerTest(BaseFileTest):
assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
self.fn, 'W7', delay=True)
def test_compute_rollover_daily_attime(self):
currentTime = 0
atTime = datetime.time(12, 0, 0)
rh = logging.handlers.TimedRotatingFileHandler(
self.fn, when='MIDNIGHT', interval=1, backupCount=0, utc=True,
atTime=atTime)
actual = rh.computeRollover(currentTime)
self.assertEqual(actual, currentTime + 12 * 60 * 60)
actual = rh.computeRollover(currentTime + 13 * 60 * 60)
self.assertEqual(actual, currentTime + 36 * 60 * 60)
rh.close()
def test_compute_rollover_weekly_attime(self):
currentTime = 0
atTime = datetime.time(12, 0, 0)
wday = datetime.datetime.fromtimestamp(currentTime).weekday()
for day in range(7):
rh = logging.handlers.TimedRotatingFileHandler(
self.fn, when='W%d' % day, interval=1, backupCount=0, utc=True,
atTime=atTime)
if wday > day:
expected = (7 - wday + day)
else:
expected = (day - wday)
expected *= 24 * 60 * 60
expected += 12 * 60 * 60
actual = rh.computeRollover(currentTime)
self.assertEqual(actual, expected)
if day == wday:
# goes into following week
expected += 7 * 24 * 60 * 60
actual = rh.computeRollover(currentTime + 13 * 60 * 60)
self.assertEqual(actual, expected)
rh.close()
def secs(**kw):
return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)

View File

@ -34,6 +34,9 @@ Core and Builtins
Library
-------
- Issue #9556: Allowed specifying a time-of-day for a TimedRotatingFileHandler
to rotate.
- Issue #14971: unittest test discovery no longer gets confused when a function
has a different __name__ than its name in the TestCase class dictionary.