Commit a713079e authored by Vinay Sajip's avatar Vinay Sajip

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

parent 8a9e38e7
...@@ -296,7 +296,7 @@ The :class:`TimedRotatingFileHandler` class, located in the ...@@ -296,7 +296,7 @@ The :class:`TimedRotatingFileHandler` class, located in the
timed intervals. 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 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 specified file is opened and used as the stream for logging. On rotating it also
...@@ -346,6 +346,12 @@ timed intervals. ...@@ -346,6 +346,12 @@ timed intervals.
If *delay* is true, then file opening is deferred until the first call to If *delay* is true, then file opening is deferred until the first call to
:meth:`emit`. :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() .. method:: doRollover()
......
# 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 # Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose and without fee is hereby granted, # documentation for any purpose and without fee is hereby granted,
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
Additional handlers for the logging package for Python. The core package is Additional handlers for the logging package for Python. The core package is
based on PEP 282 and comments thereto in comp.lang.python. 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! To use, simply 'import logging.handlers' and log away!
""" """
...@@ -196,11 +196,12 @@ class TimedRotatingFileHandler(BaseRotatingHandler): ...@@ -196,11 +196,12 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
If backupCount is > 0, when rollover is done, no more than backupCount If backupCount is > 0, when rollover is done, no more than backupCount
files are kept - the oldest ones are deleted. 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) BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay)
self.when = when.upper() self.when = when.upper()
self.backupCount = backupCount self.backupCount = backupCount
self.utc = utc self.utc = utc
self.atTime = atTime
# Calculate the real rollover interval, which is just the number of # Calculate the real rollover interval, which is just the number of
# seconds between rollovers. Also set the filename suffix used when # seconds between rollovers. Also set the filename suffix used when
# a rollover occurs. Current 'when' events supported: # a rollover occurs. Current 'when' events supported:
...@@ -270,9 +271,22 @@ class TimedRotatingFileHandler(BaseRotatingHandler): ...@@ -270,9 +271,22 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
currentHour = t[3] currentHour = t[3]
currentMinute = t[4] currentMinute = t[4]
currentSecond = t[5] currentSecond = t[5]
# r is the number of seconds left between now and midnight currentDay = t[6]
r = _MIDNIGHT - ((currentHour * 60 + currentMinute) * 60 + # 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) 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 result = currentTime + r
# If we are rolling over on a certain day, add in the number of days until # 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 # the next rollover, but offset by 1 since we just calculated the time
...@@ -290,7 +304,7 @@ class TimedRotatingFileHandler(BaseRotatingHandler): ...@@ -290,7 +304,7 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
# This is because the above time calculation takes us to midnight on this # This is because the above time calculation takes us to midnight on this
# day, i.e. the start of the next day. # day, i.e. the start of the next day.
if self.when.startswith('W'): if self.when.startswith('W'):
day = t[6] # 0 is Monday day = currentDay # 0 is Monday
if day != self.dayOfWeek: if day != self.dayOfWeek:
if day < self.dayOfWeek: if day < self.dayOfWeek:
daysToWait = self.dayOfWeek - day daysToWait = self.dayOfWeek - day
......
...@@ -3949,6 +3949,48 @@ class TimedRotatingFileHandlerTest(BaseFileTest): ...@@ -3949,6 +3949,48 @@ class TimedRotatingFileHandlerTest(BaseFileTest):
assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler, assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
self.fn, 'W7', delay=True) 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): def secs(**kw):
return datetime.timedelta(**kw) // datetime.timedelta(seconds=1) return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
......
...@@ -34,6 +34,9 @@ Core and Builtins ...@@ -34,6 +34,9 @@ Core and Builtins
Library 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 - Issue #14971: unittest test discovery no longer gets confused when a function
has a different __name__ than its name in the TestCase class dictionary. has a different __name__ than its name in the TestCase class dictionary.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment